s4-dsdb: change samdb_replace() to dsdb_replace() and allow for dsdb_flags
[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/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(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 != LDB_SUCCESS) {
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 = dsdb_replace(sam_ctx, msg, 0);
157         if (ret != LDB_SUCCESS) {
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 != LDB_SUCCESS) {
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,
181                                             TALLOC_CTX *mem_ctx,
182                                             struct samr_OemChangePasswordUser2 *r)
183 {
184         NTSTATUS status;
185         DATA_BLOB new_password, new_unicode_password;
186         char *new_pass;
187         struct samr_CryptPassword *pwbuf = r->in.password;
188         struct ldb_context *sam_ctx;
189         struct ldb_dn *user_dn;
190         int ret;
191         struct ldb_message **res, *mod;
192         const char * const attrs[] = { "objectSid", "dBCSPwd", NULL };
193         struct samr_Password *lm_pwd;
194         DATA_BLOB lm_pwd_blob;
195         uint8_t new_lm_hash[16];
196         struct samr_Password lm_verifier;
197         size_t unicode_pw_len;
198
199         if (pwbuf == NULL) {
200                 return NT_STATUS_INVALID_PARAMETER;
201         }
202
203         if (r->in.hash == NULL) {
204                 return NT_STATUS_INVALID_PARAMETER;
205         }
206
207         /* this call can only work with lanman auth */
208         if (!lp_lanman_auth(dce_call->conn->dce_ctx->lp_ctx)) {
209                 return NT_STATUS_NOT_SUPPORTED;
210         }
211
212         /* To change a password we need to open as system */
213         sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(dce_call->conn->dce_ctx->lp_ctx));
214         if (sam_ctx == NULL) {
215                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
216         }
217
218         ret = ldb_transaction_start(sam_ctx);
219         if (ret != LDB_SUCCESS) {
220                 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
221                 return NT_STATUS_TRANSACTION_ABORTED;
222         }
223
224         /* we need the users dn and the domain dn (derived from the
225            user SID). We also need the current lm password hash in
226            order to decrypt the incoming password */
227         ret = gendb_search(sam_ctx, 
228                            mem_ctx, NULL, &res, attrs,
229                            "(&(sAMAccountName=%s)(objectclass=user))",
230                            r->in.account->string);
231         if (ret != 1) {
232                 ldb_transaction_cancel(sam_ctx);
233                 /* Don't give the game away:  (don't allow anonymous users to prove the existance of usernames) */
234                 return NT_STATUS_WRONG_PASSWORD;
235         }
236
237         user_dn = res[0]->dn;
238
239         status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
240                                         res[0], &lm_pwd, NULL);
241         if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
242                 ldb_transaction_cancel(sam_ctx);
243                 return NT_STATUS_WRONG_PASSWORD;
244         }
245
246         /* decrypt the password we have been given */
247         lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash)); 
248         arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
249         data_blob_free(&lm_pwd_blob);
250         
251         if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
252                 ldb_transaction_cancel(sam_ctx);
253                 DEBUG(3,("samr: failed to decode password buffer\n"));
254                 return NT_STATUS_WRONG_PASSWORD;
255         }
256                 
257         if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx), 
258                                   CH_DOS, CH_UNIX, 
259                                   (const char *)new_password.data, 
260                                   new_password.length,
261                                   (void **)&new_pass, NULL, false)) {
262                 DEBUG(3,("samr: failed to convert incoming password buffer to unix charset\n"));
263                 ldb_transaction_cancel(sam_ctx);
264                 return NT_STATUS_WRONG_PASSWORD;
265         }
266
267         if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx), 
268                                                CH_DOS, CH_UTF16, 
269                                                (const char *)new_password.data, 
270                                                new_password.length,
271                                                (void **)&new_unicode_password.data, &unicode_pw_len, false)) {
272                 DEBUG(3,("samr: failed to convert incoming password buffer to UTF16 charset\n"));
273                 ldb_transaction_cancel(sam_ctx);
274                 return NT_STATUS_WRONG_PASSWORD;
275         }
276         new_unicode_password.length = unicode_pw_len;
277
278         E_deshash(new_pass, new_lm_hash);
279         E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
280         if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
281                 ldb_transaction_cancel(sam_ctx);
282                 return NT_STATUS_WRONG_PASSWORD;
283         }
284
285         mod = ldb_msg_new(mem_ctx);
286         if (mod == NULL) {
287                 ldb_transaction_cancel(sam_ctx);
288                 return NT_STATUS_NO_MEMORY;
289         }
290
291         mod->dn = ldb_dn_copy(mod, user_dn);
292         if (!mod->dn) {
293                 ldb_transaction_cancel(sam_ctx);
294                 return NT_STATUS_NO_MEMORY;
295         }
296
297         /* set the password on the user DN specified.  This may fail
298          * due to password policies */
299         status = samdb_set_password(sam_ctx, mem_ctx,
300                                     user_dn, NULL, 
301                                     mod, &new_unicode_password, 
302                                     NULL, NULL,
303                                     true, /* this is a user password change */
304                                     NULL, 
305                                     NULL);
306         if (!NT_STATUS_IS_OK(status)) {
307                 ldb_transaction_cancel(sam_ctx);
308                 return status;
309         }
310
311         /* The above call only setup the modifications, this actually
312          * makes the write to the database. */
313         ret = dsdb_replace(sam_ctx, mod, 0);
314         if (ret != LDB_SUCCESS) {
315                 DEBUG(2,("Failed to modify record to change password on %s: %s\n",
316                          ldb_dn_get_linearized(user_dn),
317                          ldb_errstring(sam_ctx)));
318                 ldb_transaction_cancel(sam_ctx);
319                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
320         }
321
322         /* And this confirms it in a transaction commit */
323         ret = ldb_transaction_commit(sam_ctx);
324         if (ret != LDB_SUCCESS) {
325                 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
326                          ldb_dn_get_linearized(user_dn),
327                          ldb_errstring(sam_ctx)));
328                 return NT_STATUS_TRANSACTION_ABORTED;
329         }
330
331         return NT_STATUS_OK;
332 }
333
334
335 /* 
336   samr_ChangePasswordUser3 
337 */
338 NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call, 
339                                          TALLOC_CTX *mem_ctx,
340                                          struct samr_ChangePasswordUser3 *r)
341 {       
342         NTSTATUS status;
343         DATA_BLOB new_password;
344         struct ldb_context *sam_ctx = NULL;
345         struct ldb_dn *user_dn;
346         int ret;
347         struct ldb_message **res, *mod;
348         const char * const attrs[] = { "unicodePwd", "dBCSPwd", NULL };
349         struct samr_Password *nt_pwd, *lm_pwd;
350         DATA_BLOB nt_pwd_blob;
351         struct samr_DomInfo1 *dominfo = NULL;
352         struct userPwdChangeFailureInformation *reject = NULL;
353         enum samPwdChangeReason reason = SAM_PWD_CHANGE_NO_ERROR;
354         uint8_t new_nt_hash[16], new_lm_hash[16];
355         struct samr_Password nt_verifier, lm_verifier;
356
357         *r->out.dominfo = NULL;
358         *r->out.reject = NULL;
359
360         if (r->in.nt_password == NULL ||
361             r->in.nt_verifier == NULL) {
362                 return NT_STATUS_INVALID_PARAMETER;
363         }
364
365         /* To change a password we need to open as system */
366         sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(dce_call->conn->dce_ctx->lp_ctx));
367         if (sam_ctx == NULL) {
368                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
369         }
370
371         ret = ldb_transaction_start(sam_ctx);
372         if (ret != LDB_SUCCESS) {
373                 talloc_free(sam_ctx);
374                 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
375                 return NT_STATUS_TRANSACTION_ABORTED;
376         }
377
378         /* we need the users dn and the domain dn (derived from the
379            user SID). We also need the current lm and nt password hashes
380            in order to decrypt the incoming passwords */
381         ret = gendb_search(sam_ctx, 
382                            mem_ctx, NULL, &res, attrs,
383                            "(&(sAMAccountName=%s)(objectclass=user))",
384                            r->in.account->string);
385         if (ret != 1) {
386                 /* Don't give the game away:  (don't allow anonymous users to prove the existance of usernames) */
387                 status = NT_STATUS_WRONG_PASSWORD;
388                 goto failed;
389         }
390
391         user_dn = res[0]->dn;
392
393         status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx, 
394                                         res[0], &lm_pwd, &nt_pwd);
395         if (!NT_STATUS_IS_OK(status) ) {
396                 goto failed;
397         }
398
399         if (!nt_pwd) {
400                 status = NT_STATUS_WRONG_PASSWORD;
401                 goto failed;
402         }
403
404         /* decrypt the password we have been given */
405         nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
406         arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
407         data_blob_free(&nt_pwd_blob);
408
409         if (!extract_pw_from_buffer(mem_ctx, r->in.nt_password->data, &new_password)) {
410                 ldb_transaction_cancel(sam_ctx);
411                 DEBUG(3,("samr: failed to decode password buffer\n"));
412                 return NT_STATUS_WRONG_PASSWORD;
413         }
414                 
415         if (r->in.nt_verifier == NULL) {
416                 status = NT_STATUS_WRONG_PASSWORD;
417                 goto failed;
418         }
419
420         /* check NT verifier */
421         mdfour(new_nt_hash, new_password.data, new_password.length);
422
423         E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
424         if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
425                 status = NT_STATUS_WRONG_PASSWORD;
426                 goto failed;
427         }
428
429         /* check LM verifier (really not needed as we just checked the
430          * much stronger NT hash, but the RPC-SAMR test checks for
431          * this) */
432         if (lm_pwd && r->in.lm_verifier != NULL) {
433                 char *new_pass;
434                 if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx), 
435                                           CH_UTF16, CH_UNIX, 
436                                           (const char *)new_password.data, 
437                                           new_password.length,
438                                           (void **)&new_pass, NULL, false)) {
439                         E_deshash(new_pass, new_lm_hash);
440                         E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
441                         if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
442                                 status = NT_STATUS_WRONG_PASSWORD;
443                                 goto failed;
444                         }
445                 }
446         }
447
448         mod = ldb_msg_new(mem_ctx);
449         if (mod == NULL) {
450                 status = NT_STATUS_NO_MEMORY;
451                 goto failed;
452         }
453
454         mod->dn = ldb_dn_copy(mod, user_dn);
455         if (!mod->dn) {
456                 status = NT_STATUS_NO_MEMORY;
457                 goto failed;
458         }
459
460         /* set the password on the user DN specified.  This may fail
461          * due to password policies */
462         status = samdb_set_password(sam_ctx, mem_ctx,
463                                     user_dn, NULL, 
464                                     mod, &new_password, 
465                                     NULL, NULL,
466                                     true, /* this is a user password change */
467                                     &reason, 
468                                     &dominfo);
469
470         if (!NT_STATUS_IS_OK(status)) {
471                 goto failed;
472         }
473
474         /* The above call only setup the modifications, this actually
475          * makes the write to the database. */
476         ret = dsdb_replace(sam_ctx, mod, 0);
477         if (ret != LDB_SUCCESS) {
478                 DEBUG(2,("dsdb_replace failed to change password for %s: %s\n",
479                          ldb_dn_get_linearized(user_dn),
480                          ldb_errstring(sam_ctx)));
481                 status = NT_STATUS_UNSUCCESSFUL;
482                 goto failed;
483         }
484
485         /* And this confirms it in a transaction commit */
486         ret = ldb_transaction_commit(sam_ctx);
487         if (ret != LDB_SUCCESS) {
488                 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
489                          ldb_dn_get_linearized(user_dn),
490                          ldb_errstring(sam_ctx)));
491                 status = NT_STATUS_TRANSACTION_ABORTED;
492                 goto failed;
493         }
494
495         return NT_STATUS_OK;
496
497 failed:
498         ldb_transaction_cancel(sam_ctx);
499
500         reject = talloc(mem_ctx, struct userPwdChangeFailureInformation);
501         if (reject != NULL) {
502                 ZERO_STRUCTP(reject);
503                 reject->extendedFailureReason = reason;
504
505                 *r->out.reject = reject;
506         }
507
508         *r->out.dominfo = dominfo;
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,
520                                          TALLOC_CTX *mem_ctx,
521                                          struct samr_ChangePasswordUser2 *r)
522 {
523         struct samr_ChangePasswordUser3 r2;
524         struct samr_DomInfo1 *dominfo = NULL;
525         struct userPwdChangeFailureInformation *reject = NULL;
526
527         r2.in.server = r->in.server;
528         r2.in.account = r->in.account;
529         r2.in.nt_password = r->in.nt_password;
530         r2.in.nt_verifier = r->in.nt_verifier;
531         r2.in.lm_change = r->in.lm_change;
532         r2.in.lm_password = r->in.lm_password;
533         r2.in.lm_verifier = r->in.lm_verifier;
534         r2.in.password3 = NULL;
535         r2.out.dominfo = &dominfo;
536         r2.out.reject = &reject;
537
538         return dcesrv_samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
539 }
540
541
542 /*
543   set password via a samr_CryptPassword buffer
544   this will in the 'msg' with modify operations that will update the user
545   password when applied
546 */
547 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
548                            void *sam_ctx,
549                            struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
550                            TALLOC_CTX *mem_ctx,
551                            struct ldb_message *msg, 
552                            struct samr_CryptPassword *pwbuf)
553 {
554         NTSTATUS nt_status;
555         DATA_BLOB new_password;
556         DATA_BLOB session_key = data_blob(NULL, 0);
557
558         nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
559         if (!NT_STATUS_IS_OK(nt_status)) {
560                 return nt_status;
561         }
562
563         arcfour_crypt_blob(pwbuf->data, 516, &session_key);
564
565         if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
566                 DEBUG(3,("samr: failed to decode password buffer\n"));
567                 return NT_STATUS_WRONG_PASSWORD;
568         }
569                 
570         /* set the password - samdb needs to know both the domain and user DNs,
571            so the domain password policy can be used */
572         return samdb_set_password(sam_ctx, mem_ctx,
573                                   account_dn, domain_dn, 
574                                   msg, &new_password, 
575                                   NULL, NULL,
576                                   false, /* This is a password set, not change */
577                                   NULL, NULL);
578 }
579
580
581 /*
582   set password via a samr_CryptPasswordEx buffer
583   this will in the 'msg' with modify operations that will update the user
584   password when applied
585 */
586 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
587                               struct ldb_context *sam_ctx,
588                               struct ldb_dn *account_dn,
589                               struct ldb_dn *domain_dn,
590                               TALLOC_CTX *mem_ctx,
591                               struct ldb_message *msg, 
592                               struct samr_CryptPasswordEx *pwbuf)
593 {
594         NTSTATUS nt_status;
595         DATA_BLOB new_password;
596         DATA_BLOB co_session_key;
597         DATA_BLOB session_key = data_blob(NULL, 0);
598         struct MD5Context ctx;
599
600         nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
601         if (!NT_STATUS_IS_OK(nt_status)) {
602                 return nt_status;
603         }
604
605         co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
606         if (!co_session_key.data) {
607                 return NT_STATUS_NO_MEMORY;
608         }
609
610         MD5Init(&ctx);
611         MD5Update(&ctx, &pwbuf->data[516], 16);
612         MD5Update(&ctx, session_key.data, session_key.length);
613         MD5Final(co_session_key.data, &ctx);
614         
615         arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
616
617         if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
618                 DEBUG(3,("samr: failed to decode password buffer\n"));
619                 return NT_STATUS_WRONG_PASSWORD;
620         }
621                 
622         /* set the password - samdb needs to know both the domain and user DNs,
623            so the domain password policy can be used */
624         return samdb_set_password(sam_ctx, mem_ctx,
625                                   account_dn, domain_dn, 
626                                   msg, &new_password, 
627                                   NULL, NULL,
628                                   false, /* This is a password set, not change */
629                                   NULL, NULL);
630 }
631