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