Merge branch 'master' of ssh://git.samba.org/data/git/samba into talloc-next
[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_convenience(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_convenience(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         *r->out.dominfo = NULL;
356         *r->out.reject = NULL;
357
358         if (r->in.nt_password == NULL ||
359             r->in.nt_verifier == NULL) {
360                 return NT_STATUS_INVALID_PARAMETER;
361         }
362
363         /* To change a password we need to open as system */
364         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));
365         if (sam_ctx == NULL) {
366                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
367         }
368
369         ret = ldb_transaction_start(sam_ctx);
370         if (ret) {
371                 talloc_free(sam_ctx);
372                 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
373                 return NT_STATUS_TRANSACTION_ABORTED;
374         }
375
376         /* we need the users dn and the domain dn (derived from the
377            user SID). We also need the current lm and nt password hashes
378            in order to decrypt the incoming passwords */
379         ret = gendb_search(sam_ctx, 
380                            mem_ctx, NULL, &res, attrs,
381                            "(&(sAMAccountName=%s)(objectclass=user))",
382                            r->in.account->string);
383         if (ret != 1) {
384                 /* Don't give the game away:  (don't allow anonymous users to prove the existance of usernames) */
385                 status = NT_STATUS_WRONG_PASSWORD;
386                 goto failed;
387         }
388
389         user_dn = res[0]->dn;
390
391         status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx, 
392                                         res[0], &lm_pwd, &nt_pwd);
393         if (!NT_STATUS_IS_OK(status) ) {
394                 goto failed;
395         }
396
397         if (!nt_pwd) {
398                 status = NT_STATUS_WRONG_PASSWORD;
399                 goto failed;
400         }
401
402         /* decrypt the password we have been given */
403         nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
404         arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
405         data_blob_free(&nt_pwd_blob);
406
407         if (!extract_pw_from_buffer(mem_ctx, r->in.nt_password->data, &new_password)) {
408                 ldb_transaction_cancel(sam_ctx);
409                 DEBUG(3,("samr: failed to decode password buffer\n"));
410                 return NT_STATUS_WRONG_PASSWORD;
411         }
412                 
413         if (r->in.nt_verifier == NULL) {
414                 status = NT_STATUS_WRONG_PASSWORD;
415                 goto failed;
416         }
417
418         /* check NT verifier */
419         mdfour(new_nt_hash, new_password.data, new_password.length);
420
421         E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
422         if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
423                 status = NT_STATUS_WRONG_PASSWORD;
424                 goto failed;
425         }
426
427         /* check LM verifier (really not needed as we just checked the
428          * much stronger NT hash, but the RPC-SAMR test checks for
429          * this) */
430         if (lm_pwd && r->in.lm_verifier != NULL) {
431                 char *new_pass;
432                 if (convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx), 
433                                           CH_UTF16, CH_UNIX, 
434                                           (const char *)new_password.data, 
435                                           new_password.length,
436                                           (void **)&new_pass) != -1) {
437                         E_deshash(new_pass, new_lm_hash);
438                         E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
439                         if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
440                                 status = NT_STATUS_WRONG_PASSWORD;
441                                 goto failed;
442                         }
443                 }
444         }
445
446         mod = ldb_msg_new(mem_ctx);
447         if (mod == NULL) {
448                 status = NT_STATUS_NO_MEMORY;
449                 goto failed;
450         }
451
452         mod->dn = ldb_dn_copy(mod, user_dn);
453         if (!mod->dn) {
454                 status = NT_STATUS_NO_MEMORY;
455                 goto failed;
456         }
457
458         /* set the password on the user DN specified.  This may fail
459          * due to password policies */
460         status = samdb_set_password(sam_ctx, mem_ctx,
461                                     user_dn, NULL, 
462                                     mod, &new_password, 
463                                     NULL, NULL,
464                                     true, /* this is a user password change */
465                                     &reason, 
466                                     &dominfo);
467         if (!NT_STATUS_IS_OK(status)) {
468                 goto failed;
469         }
470
471         /* The above call only setup the modifications, this actually
472          * makes the write to the database. */
473         ret = samdb_replace(sam_ctx, mem_ctx, mod);
474         if (ret != 0) {
475                 DEBUG(2,("samdb_replace failed to change password for %s: %s\n",
476                          ldb_dn_get_linearized(user_dn),
477                          ldb_errstring(sam_ctx)));
478                 status = NT_STATUS_UNSUCCESSFUL;
479                 goto failed;
480         }
481
482         /* And this confirms it in a transaction commit */
483         ret = ldb_transaction_commit(sam_ctx);
484         if (ret != 0) {
485                 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
486                          ldb_dn_get_linearized(user_dn),
487                          ldb_errstring(sam_ctx)));
488                 status = NT_STATUS_TRANSACTION_ABORTED;
489                 goto failed;
490         }
491
492         return NT_STATUS_OK;
493
494 failed:
495         ldb_transaction_cancel(sam_ctx);
496         talloc_free(sam_ctx);
497
498         reject = talloc(mem_ctx, struct samr_ChangeReject);
499         *r->out.dominfo = dominfo;
500         *r->out.reject = reject;
501
502         if (reject == NULL) {
503                 return status;
504         }
505         ZERO_STRUCTP(reject);
506
507         reject->reason = reason;
508
509         return status;
510 }
511
512
513 /* 
514   samr_ChangePasswordUser2 
515
516   easy - just a subset of samr_ChangePasswordUser3
517 */
518 NTSTATUS dcesrv_samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
519                                   struct samr_ChangePasswordUser2 *r)
520 {
521         struct samr_ChangePasswordUser3 r2;
522         struct samr_DomInfo1 *dominfo = NULL;
523         struct samr_ChangeReject *reject = NULL;
524
525         r2.in.server = r->in.server;
526         r2.in.account = r->in.account;
527         r2.in.nt_password = r->in.nt_password;
528         r2.in.nt_verifier = r->in.nt_verifier;
529         r2.in.lm_change = r->in.lm_change;
530         r2.in.lm_password = r->in.lm_password;
531         r2.in.lm_verifier = r->in.lm_verifier;
532         r2.in.password3 = NULL;
533         r2.out.dominfo = &dominfo;
534         r2.out.reject = &reject;
535
536         return dcesrv_samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
537 }
538
539
540 /*
541   set password via a samr_CryptPassword buffer
542   this will in the 'msg' with modify operations that will update the user
543   password when applied
544 */
545 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
546                            void *sam_ctx,
547                            struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
548                            TALLOC_CTX *mem_ctx,
549                            struct ldb_message *msg, 
550                            struct samr_CryptPassword *pwbuf)
551 {
552         NTSTATUS nt_status;
553         DATA_BLOB new_password;
554         DATA_BLOB session_key = data_blob(NULL, 0);
555
556         nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
557         if (!NT_STATUS_IS_OK(nt_status)) {
558                 return nt_status;
559         }
560
561         arcfour_crypt_blob(pwbuf->data, 516, &session_key);
562
563         if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
564                 DEBUG(3,("samr: failed to decode password buffer\n"));
565                 return NT_STATUS_WRONG_PASSWORD;
566         }
567                 
568         /* set the password - samdb needs to know both the domain and user DNs,
569            so the domain password policy can be used */
570         return samdb_set_password(sam_ctx, mem_ctx,
571                                   account_dn, domain_dn, 
572                                   msg, &new_password, 
573                                   NULL, NULL,
574                                   false, /* This is a password set, not change */
575                                   NULL, NULL);
576 }
577
578
579 /*
580   set password via a samr_CryptPasswordEx buffer
581   this will in the 'msg' with modify operations that will update the user
582   password when applied
583 */
584 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
585                               struct ldb_context *sam_ctx,
586                               struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
587                               TALLOC_CTX *mem_ctx,
588                               struct ldb_message *msg, 
589                               struct samr_CryptPasswordEx *pwbuf)
590 {
591         NTSTATUS nt_status;
592         DATA_BLOB new_password;
593         DATA_BLOB co_session_key;
594         DATA_BLOB session_key = data_blob(NULL, 0);
595         struct MD5Context ctx;
596
597         nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
598         if (!NT_STATUS_IS_OK(nt_status)) {
599                 return nt_status;
600         }
601
602         co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
603         if (!co_session_key.data) {
604                 return NT_STATUS_NO_MEMORY;
605         }
606
607         MD5Init(&ctx);
608         MD5Update(&ctx, &pwbuf->data[516], 16);
609         MD5Update(&ctx, session_key.data, session_key.length);
610         MD5Final(co_session_key.data, &ctx);
611         
612         arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
613
614         if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
615                 DEBUG(3,("samr: failed to decode password buffer\n"));
616                 return NT_STATUS_WRONG_PASSWORD;
617         }
618                 
619         /* set the password - samdb needs to know both the domain and user DNs,
620            so the domain password policy can be used */
621         return samdb_set_password(sam_ctx, mem_ctx,
622                                   account_dn, domain_dn, 
623                                   msg, &new_password, 
624                                   NULL, NULL,
625                                   false, /* This is a password set, not change */
626                                   NULL, NULL);
627 }
628
629