more include minimisation
[samba.git] / source4 / rpc_server / samr / samr_password.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    samr server password set/change handling
5
6    Copyright (C) Andrew Tridgell 2004
7    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24 #include "rpc_server/dcerpc_server.h"
25 #include "rpc_server/samr/dcesrv_samr.h"
26 #include "system/time.h"
27 #include "../lib/crypto/crypto.h"
28 #include "dsdb/samdb/samdb.h"
29 #include "auth/auth.h"
30 #include "libcli/auth/libcli_auth.h"
31 #include "../lib/util/util_ldb.h"
32
33 /* 
34   samr_ChangePasswordUser 
35 */
36 NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, 
37                                         TALLOC_CTX *mem_ctx,
38                                         struct samr_ChangePasswordUser *r)
39 {
40         struct dcesrv_handle *h;
41         struct samr_account_state *a_state;
42         struct ldb_context *sam_ctx;
43         struct ldb_message **res, *msg;
44         int ret;
45         struct samr_Password new_lmPwdHash, new_ntPwdHash, checkHash;
46         struct samr_Password *lm_pwd, *nt_pwd;
47         NTSTATUS status = NT_STATUS_OK;
48         const char * const attrs[] = { "dBCSPwd", "unicodePwd" , NULL };
49
50         DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
51
52         a_state = h->data;
53
54         /* basic sanity checking on parameters.  Do this before any database ops */
55         if (!r->in.lm_present || !r->in.nt_present ||
56             !r->in.old_lm_crypted || !r->in.new_lm_crypted ||
57             !r->in.old_nt_crypted || !r->in.new_nt_crypted) {
58                 /* we should really handle a change with lm not
59                    present */
60                 return NT_STATUS_INVALID_PARAMETER_MIX;
61         }
62
63         /* To change a password we need to open as system */
64         sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(mem_ctx, dce_call->conn->dce_ctx->lp_ctx));
65         if (sam_ctx == NULL) {
66                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
67         }
68
69         ret = ldb_transaction_start(sam_ctx);
70         if (ret) {
71                 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
72                 return NT_STATUS_TRANSACTION_ABORTED;
73         }
74
75         /* fetch the old hashes */
76         ret = gendb_search_dn(sam_ctx, mem_ctx,
77                               a_state->account_dn, &res, attrs);
78         if (ret != 1) {
79                 ldb_transaction_cancel(sam_ctx);
80                 return NT_STATUS_WRONG_PASSWORD;
81         }
82         msg = res[0];
83
84         status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
85                                         msg, &lm_pwd, &nt_pwd);
86         if (!NT_STATUS_IS_OK(status) || !nt_pwd) {
87                 ldb_transaction_cancel(sam_ctx);
88                 return NT_STATUS_WRONG_PASSWORD;
89         }
90
91         /* decrypt and check the new lm hash */
92         if (lm_pwd) {
93                 D_P16(lm_pwd->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
94                 D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
95                 if (memcmp(checkHash.hash, lm_pwd, 16) != 0) {
96                         ldb_transaction_cancel(sam_ctx);
97                         return NT_STATUS_WRONG_PASSWORD;
98                 }
99         }
100
101         /* decrypt and check the new nt hash */
102         D_P16(nt_pwd->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
103         D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
104         if (memcmp(checkHash.hash, nt_pwd, 16) != 0) {
105                 ldb_transaction_cancel(sam_ctx);
106                 return NT_STATUS_WRONG_PASSWORD;
107         }
108         
109         /* The NT Cross is not required by Win2k3 R2, but if present
110            check the nt cross hash */
111         if (r->in.cross1_present && r->in.nt_cross && lm_pwd) {
112                 D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
113                 if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
114                         ldb_transaction_cancel(sam_ctx);
115                         return NT_STATUS_WRONG_PASSWORD;
116                 }
117         }
118
119         /* The LM Cross is not required by Win2k3 R2, but if present
120            check the lm cross hash */
121         if (r->in.cross2_present && r->in.lm_cross && lm_pwd) {
122                 D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash);
123                 if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
124                         ldb_transaction_cancel(sam_ctx);
125                         return NT_STATUS_WRONG_PASSWORD;
126                 }
127         }
128
129         msg = ldb_msg_new(mem_ctx);
130         if (msg == NULL) {
131                 ldb_transaction_cancel(sam_ctx);
132                 return NT_STATUS_NO_MEMORY;
133         }
134
135         msg->dn = ldb_dn_copy(msg, a_state->account_dn);
136         if (!msg->dn) {
137                 ldb_transaction_cancel(sam_ctx);
138                 return NT_STATUS_NO_MEMORY;
139         }
140
141         /* setup password modify mods on the user DN specified.  This may fail
142          * due to password policies.  */
143         status = samdb_set_password(sam_ctx, mem_ctx,
144                                     a_state->account_dn, a_state->domain_state->domain_dn,
145                                     msg, NULL, &new_lmPwdHash, &new_ntPwdHash, 
146                                     true, /* this is a user password change */
147                                     NULL,
148                                     NULL);
149         if (!NT_STATUS_IS_OK(status)) {
150                 ldb_transaction_cancel(sam_ctx);
151                 return status;
152         }
153
154         /* The above call only setup the modifications, this actually
155          * makes the write to the database. */
156         ret = samdb_replace(sam_ctx, mem_ctx, msg);
157         if (ret != 0) {
158                 DEBUG(2,("Failed to modify record to change password on %s: %s\n",
159                          ldb_dn_get_linearized(a_state->account_dn),
160                          ldb_errstring(sam_ctx)));
161                 ldb_transaction_cancel(sam_ctx);
162                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
163         }
164
165         /* And this confirms it in a transaction commit */
166         ret = ldb_transaction_commit(sam_ctx);
167         if (ret != 0) {
168                 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
169                          ldb_dn_get_linearized(a_state->account_dn),
170                          ldb_errstring(sam_ctx)));
171                 return NT_STATUS_TRANSACTION_ABORTED;
172         }
173
174         return NT_STATUS_OK;
175 }
176
177 /* 
178   samr_OemChangePasswordUser2 
179 */
180 NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
181                                      struct samr_OemChangePasswordUser2 *r)
182 {
183         NTSTATUS status;
184         DATA_BLOB new_password, new_unicode_password;
185         char *new_pass;
186         struct samr_CryptPassword *pwbuf = r->in.password;
187         struct ldb_context *sam_ctx;
188         struct ldb_dn *user_dn;
189         int ret;
190         struct ldb_message **res, *mod;
191         const char * const attrs[] = { "objectSid", "dBCSPwd", NULL };
192         struct samr_Password *lm_pwd;
193         DATA_BLOB lm_pwd_blob;
194         uint8_t new_lm_hash[16];
195         struct samr_Password lm_verifier;
196         size_t unicode_pw_len;
197
198         if (pwbuf == NULL) {
199                 return NT_STATUS_INVALID_PARAMETER;
200         }
201
202         if (r->in.hash == NULL) {
203                 return NT_STATUS_INVALID_PARAMETER;
204         }
205
206         /* this call can only work with lanman auth */
207         if (!lp_lanman_auth(dce_call->conn->dce_ctx->lp_ctx)) {
208                 return NT_STATUS_NOT_SUPPORTED;
209         }
210
211         /* To change a password we need to open as system */
212         sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(mem_ctx, dce_call->conn->dce_ctx->lp_ctx));
213         if (sam_ctx == NULL) {
214                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
215         }
216
217         ret = ldb_transaction_start(sam_ctx);
218         if (ret) {
219                 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
220                 return NT_STATUS_TRANSACTION_ABORTED;
221         }
222
223         /* we need the users dn and the domain dn (derived from the
224            user SID). We also need the current lm password hash in
225            order to decrypt the incoming password */
226         ret = gendb_search(sam_ctx, 
227                            mem_ctx, NULL, &res, attrs,
228                            "(&(sAMAccountName=%s)(objectclass=user))",
229                            r->in.account->string);
230         if (ret != 1) {
231                 ldb_transaction_cancel(sam_ctx);
232                 /* Don't give the game away:  (don't allow anonymous users to prove the existance of usernames) */
233                 return NT_STATUS_WRONG_PASSWORD;
234         }
235
236         user_dn = res[0]->dn;
237
238         status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
239                                         res[0], &lm_pwd, NULL);
240         if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
241                 ldb_transaction_cancel(sam_ctx);
242                 return NT_STATUS_WRONG_PASSWORD;
243         }
244
245         /* decrypt the password we have been given */
246         lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash)); 
247         arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
248         data_blob_free(&lm_pwd_blob);
249         
250         if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
251                 ldb_transaction_cancel(sam_ctx);
252                 DEBUG(3,("samr: failed to decode password buffer\n"));
253                 return NT_STATUS_WRONG_PASSWORD;
254         }
255                 
256         if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx), 
257                                   CH_DOS, CH_UNIX, 
258                                   (const char *)new_password.data, 
259                                   new_password.length,
260                                   (void **)&new_pass, NULL, false)) {
261                 DEBUG(3,("samr: failed to convert incoming password buffer to unix charset\n"));
262                 ldb_transaction_cancel(sam_ctx);
263                 return NT_STATUS_WRONG_PASSWORD;
264         }
265
266         if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx), 
267                                                CH_DOS, CH_UTF16, 
268                                                (const char *)new_password.data, 
269                                                new_password.length,
270                                                (void **)&new_unicode_password.data, &unicode_pw_len, false)) {
271                 DEBUG(3,("samr: failed to convert incoming password buffer to UTF16 charset\n"));
272                 ldb_transaction_cancel(sam_ctx);
273                 return NT_STATUS_WRONG_PASSWORD;
274         }
275         new_unicode_password.length = unicode_pw_len;
276
277         E_deshash(new_pass, new_lm_hash);
278         E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
279         if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
280                 ldb_transaction_cancel(sam_ctx);
281                 return NT_STATUS_WRONG_PASSWORD;
282         }
283
284         mod = ldb_msg_new(mem_ctx);
285         if (mod == NULL) {
286                 ldb_transaction_cancel(sam_ctx);
287                 return NT_STATUS_NO_MEMORY;
288         }
289
290         mod->dn = ldb_dn_copy(mod, user_dn);
291         if (!mod->dn) {
292                 ldb_transaction_cancel(sam_ctx);
293                 return NT_STATUS_NO_MEMORY;
294         }
295
296         /* set the password on the user DN specified.  This may fail
297          * due to password policies */
298         status = samdb_set_password(sam_ctx, mem_ctx,
299                                     user_dn, NULL, 
300                                     mod, &new_unicode_password, 
301                                     NULL, NULL,
302                                     true, /* this is a user password change */
303                                     NULL, 
304                                     NULL);
305         if (!NT_STATUS_IS_OK(status)) {
306                 ldb_transaction_cancel(sam_ctx);
307                 return status;
308         }
309
310         /* The above call only setup the modifications, this actually
311          * makes the write to the database. */
312         ret = samdb_replace(sam_ctx, mem_ctx, mod);
313         if (ret != 0) {
314                 DEBUG(2,("Failed to modify record to change password on %s: %s\n",
315                          ldb_dn_get_linearized(user_dn),
316                          ldb_errstring(sam_ctx)));
317                 ldb_transaction_cancel(sam_ctx);
318                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
319         }
320
321         /* And this confirms it in a transaction commit */
322         ret = ldb_transaction_commit(sam_ctx);
323         if (ret != 0) {
324                 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
325                          ldb_dn_get_linearized(user_dn),
326                          ldb_errstring(sam_ctx)));
327                 return NT_STATUS_TRANSACTION_ABORTED;
328         }
329
330         return NT_STATUS_OK;
331 }
332
333
334 /* 
335   samr_ChangePasswordUser3 
336 */
337 NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call, 
338                                   TALLOC_CTX *mem_ctx,
339                                   struct samr_ChangePasswordUser3 *r)
340 {       
341         NTSTATUS status;
342         DATA_BLOB new_password;
343         struct ldb_context *sam_ctx = NULL;
344         struct ldb_dn *user_dn;
345         int ret;
346         struct ldb_message **res, *mod;
347         const char * const attrs[] = { "unicodePwd", "dBCSPwd", NULL };
348         struct samr_Password *nt_pwd, *lm_pwd;
349         DATA_BLOB nt_pwd_blob;
350         struct samr_DomInfo1 *dominfo = NULL;
351         struct samr_ChangeReject *reject = NULL;
352         enum samr_RejectReason reason = SAMR_REJECT_OTHER;
353         uint8_t new_nt_hash[16], new_lm_hash[16];
354         struct samr_Password nt_verifier, lm_verifier;
355
356         *r->out.dominfo = NULL;
357         *r->out.reject = NULL;
358
359         if (r->in.nt_password == NULL ||
360             r->in.nt_verifier == NULL) {
361                 return NT_STATUS_INVALID_PARAMETER;
362         }
363
364         /* To change a password we need to open as system */
365         sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(mem_ctx, dce_call->conn->dce_ctx->lp_ctx));
366         if (sam_ctx == NULL) {
367                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
368         }
369
370         ret = ldb_transaction_start(sam_ctx);
371         if (ret) {
372                 talloc_free(sam_ctx);
373                 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
374                 return NT_STATUS_TRANSACTION_ABORTED;
375         }
376
377         /* we need the users dn and the domain dn (derived from the
378            user SID). We also need the current lm and nt password hashes
379            in order to decrypt the incoming passwords */
380         ret = gendb_search(sam_ctx, 
381                            mem_ctx, NULL, &res, attrs,
382                            "(&(sAMAccountName=%s)(objectclass=user))",
383                            r->in.account->string);
384         if (ret != 1) {
385                 /* Don't give the game away:  (don't allow anonymous users to prove the existance of usernames) */
386                 status = NT_STATUS_WRONG_PASSWORD;
387                 goto failed;
388         }
389
390         user_dn = res[0]->dn;
391
392         status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx, 
393                                         res[0], &lm_pwd, &nt_pwd);
394         if (!NT_STATUS_IS_OK(status) ) {
395                 goto failed;
396         }
397
398         if (!nt_pwd) {
399                 status = NT_STATUS_WRONG_PASSWORD;
400                 goto failed;
401         }
402
403         /* decrypt the password we have been given */
404         nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
405         arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
406         data_blob_free(&nt_pwd_blob);
407
408         if (!extract_pw_from_buffer(mem_ctx, r->in.nt_password->data, &new_password)) {
409                 ldb_transaction_cancel(sam_ctx);
410                 DEBUG(3,("samr: failed to decode password buffer\n"));
411                 return NT_STATUS_WRONG_PASSWORD;
412         }
413                 
414         if (r->in.nt_verifier == NULL) {
415                 status = NT_STATUS_WRONG_PASSWORD;
416                 goto failed;
417         }
418
419         /* check NT verifier */
420         mdfour(new_nt_hash, new_password.data, new_password.length);
421
422         E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
423         if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
424                 status = NT_STATUS_WRONG_PASSWORD;
425                 goto failed;
426         }
427
428         /* check LM verifier (really not needed as we just checked the
429          * much stronger NT hash, but the RPC-SAMR test checks for
430          * this) */
431         if (lm_pwd && r->in.lm_verifier != NULL) {
432                 char *new_pass;
433                 if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx), 
434                                           CH_UTF16, CH_UNIX, 
435                                           (const char *)new_password.data, 
436                                           new_password.length,
437                                           (void **)&new_pass, NULL, false)) {
438                         E_deshash(new_pass, new_lm_hash);
439                         E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
440                         if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
441                                 status = NT_STATUS_WRONG_PASSWORD;
442                                 goto failed;
443                         }
444                 }
445         }
446
447         mod = ldb_msg_new(mem_ctx);
448         if (mod == NULL) {
449                 status = NT_STATUS_NO_MEMORY;
450                 goto failed;
451         }
452
453         mod->dn = ldb_dn_copy(mod, user_dn);
454         if (!mod->dn) {
455                 status = NT_STATUS_NO_MEMORY;
456                 goto failed;
457         }
458
459         /* set the password on the user DN specified.  This may fail
460          * due to password policies */
461         status = samdb_set_password(sam_ctx, mem_ctx,
462                                     user_dn, NULL, 
463                                     mod, &new_password, 
464                                     NULL, NULL,
465                                     true, /* this is a user password change */
466                                     &reason, 
467                                     &dominfo);
468         if (!NT_STATUS_IS_OK(status)) {
469                 goto failed;
470         }
471
472         /* The above call only setup the modifications, this actually
473          * makes the write to the database. */
474         ret = samdb_replace(sam_ctx, mem_ctx, mod);
475         if (ret != 0) {
476                 DEBUG(2,("samdb_replace failed to change password for %s: %s\n",
477                          ldb_dn_get_linearized(user_dn),
478                          ldb_errstring(sam_ctx)));
479                 status = NT_STATUS_UNSUCCESSFUL;
480                 goto failed;
481         }
482
483         /* And this confirms it in a transaction commit */
484         ret = ldb_transaction_commit(sam_ctx);
485         if (ret != 0) {
486                 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
487                          ldb_dn_get_linearized(user_dn),
488                          ldb_errstring(sam_ctx)));
489                 status = NT_STATUS_TRANSACTION_ABORTED;
490                 goto failed;
491         }
492
493         return NT_STATUS_OK;
494
495 failed:
496         ldb_transaction_cancel(sam_ctx);
497         talloc_free(sam_ctx);
498
499         reject = talloc(mem_ctx, struct samr_ChangeReject);
500         *r->out.dominfo = dominfo;
501         *r->out.reject = reject;
502
503         if (reject == NULL) {
504                 return status;
505         }
506         ZERO_STRUCTP(reject);
507
508         reject->reason = reason;
509
510         return status;
511 }
512
513
514 /* 
515   samr_ChangePasswordUser2 
516
517   easy - just a subset of samr_ChangePasswordUser3
518 */
519 NTSTATUS dcesrv_samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
520                                   struct samr_ChangePasswordUser2 *r)
521 {
522         struct samr_ChangePasswordUser3 r2;
523         struct samr_DomInfo1 *dominfo = NULL;
524         struct samr_ChangeReject *reject = NULL;
525
526         r2.in.server = r->in.server;
527         r2.in.account = r->in.account;
528         r2.in.nt_password = r->in.nt_password;
529         r2.in.nt_verifier = r->in.nt_verifier;
530         r2.in.lm_change = r->in.lm_change;
531         r2.in.lm_password = r->in.lm_password;
532         r2.in.lm_verifier = r->in.lm_verifier;
533         r2.in.password3 = NULL;
534         r2.out.dominfo = &dominfo;
535         r2.out.reject = &reject;
536
537         return dcesrv_samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
538 }
539
540
541 /*
542   set password via a samr_CryptPassword buffer
543   this will in the 'msg' with modify operations that will update the user
544   password when applied
545 */
546 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
547                            void *sam_ctx,
548                            struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
549                            TALLOC_CTX *mem_ctx,
550                            struct ldb_message *msg, 
551                            struct samr_CryptPassword *pwbuf)
552 {
553         NTSTATUS nt_status;
554         DATA_BLOB new_password;
555         DATA_BLOB session_key = data_blob(NULL, 0);
556
557         nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
558         if (!NT_STATUS_IS_OK(nt_status)) {
559                 return nt_status;
560         }
561
562         arcfour_crypt_blob(pwbuf->data, 516, &session_key);
563
564         if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
565                 DEBUG(3,("samr: failed to decode password buffer\n"));
566                 return NT_STATUS_WRONG_PASSWORD;
567         }
568                 
569         /* set the password - samdb needs to know both the domain and user DNs,
570            so the domain password policy can be used */
571         return samdb_set_password(sam_ctx, mem_ctx,
572                                   account_dn, domain_dn, 
573                                   msg, &new_password, 
574                                   NULL, NULL,
575                                   false, /* This is a password set, not change */
576                                   NULL, NULL);
577 }
578
579
580 /*
581   set password via a samr_CryptPasswordEx buffer
582   this will in the 'msg' with modify operations that will update the user
583   password when applied
584 */
585 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
586                               struct ldb_context *sam_ctx,
587                               struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
588                               TALLOC_CTX *mem_ctx,
589                               struct ldb_message *msg, 
590                               struct samr_CryptPasswordEx *pwbuf)
591 {
592         NTSTATUS nt_status;
593         DATA_BLOB new_password;
594         DATA_BLOB co_session_key;
595         DATA_BLOB session_key = data_blob(NULL, 0);
596         struct MD5Context ctx;
597
598         nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
599         if (!NT_STATUS_IS_OK(nt_status)) {
600                 return nt_status;
601         }
602
603         co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
604         if (!co_session_key.data) {
605                 return NT_STATUS_NO_MEMORY;
606         }
607
608         MD5Init(&ctx);
609         MD5Update(&ctx, &pwbuf->data[516], 16);
610         MD5Update(&ctx, session_key.data, session_key.length);
611         MD5Final(co_session_key.data, &ctx);
612         
613         arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
614
615         if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
616                 DEBUG(3,("samr: failed to decode password buffer\n"));
617                 return NT_STATUS_WRONG_PASSWORD;
618         }
619                 
620         /* set the password - samdb needs to know both the domain and user DNs,
621            so the domain password policy can be used */
622         return samdb_set_password(sam_ctx, mem_ctx,
623                                   account_dn, domain_dn, 
624                                   msg, &new_password, 
625                                   NULL, NULL,
626                                   false, /* This is a password set, not change */
627                                   NULL, NULL);
628 }
629
630