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