s4: Use same function signature for convert_* as s3.
[bbaumbach/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) || !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         size_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, NULL, false)) {
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         if (!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, &unicode_pw_len, false)) {
269                 DEBUG(3,("samr: failed to convert incoming password buffer to UTF16 charset\n"));
270                 ldb_transaction_cancel(sam_ctx);
271                 return NT_STATUS_WRONG_PASSWORD;
272         }
273         new_unicode_password.length = unicode_pw_len;
274
275         E_deshash(new_pass, new_lm_hash);
276         E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
277         if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
278                 ldb_transaction_cancel(sam_ctx);
279                 return NT_STATUS_WRONG_PASSWORD;
280         }
281
282         mod = ldb_msg_new(mem_ctx);
283         if (mod == NULL) {
284                 ldb_transaction_cancel(sam_ctx);
285                 return NT_STATUS_NO_MEMORY;
286         }
287
288         mod->dn = ldb_dn_copy(mod, user_dn);
289         if (!mod->dn) {
290                 ldb_transaction_cancel(sam_ctx);
291                 return NT_STATUS_NO_MEMORY;
292         }
293
294         /* set the password on the user DN specified.  This may fail
295          * due to password policies */
296         status = samdb_set_password(sam_ctx, mem_ctx,
297                                     user_dn, NULL, 
298                                     mod, &new_unicode_password, 
299                                     NULL, NULL,
300                                     true, /* this is a user password change */
301                                     NULL, 
302                                     NULL);
303         if (!NT_STATUS_IS_OK(status)) {
304                 ldb_transaction_cancel(sam_ctx);
305                 return status;
306         }
307
308         /* The above call only setup the modifications, this actually
309          * makes the write to the database. */
310         ret = samdb_replace(sam_ctx, mem_ctx, mod);
311         if (ret != 0) {
312                 DEBUG(2,("Failed to modify record to change password on %s: %s\n",
313                          ldb_dn_get_linearized(user_dn),
314                          ldb_errstring(sam_ctx)));
315                 ldb_transaction_cancel(sam_ctx);
316                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
317         }
318
319         /* And this confirms it in a transaction commit */
320         ret = ldb_transaction_commit(sam_ctx);
321         if (ret != 0) {
322                 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
323                          ldb_dn_get_linearized(user_dn),
324                          ldb_errstring(sam_ctx)));
325                 return NT_STATUS_TRANSACTION_ABORTED;
326         }
327
328         return NT_STATUS_OK;
329 }
330
331
332 /* 
333   samr_ChangePasswordUser3 
334 */
335 NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call, 
336                                   TALLOC_CTX *mem_ctx,
337                                   struct samr_ChangePasswordUser3 *r)
338 {       
339         NTSTATUS status;
340         DATA_BLOB new_password;
341         struct ldb_context *sam_ctx = NULL;
342         struct ldb_dn *user_dn;
343         int ret;
344         struct ldb_message **res, *mod;
345         const char * const attrs[] = { "unicodePwd", "dBCSPwd", NULL };
346         struct samr_Password *nt_pwd, *lm_pwd;
347         DATA_BLOB nt_pwd_blob;
348         struct samr_DomInfo1 *dominfo = NULL;
349         struct samr_ChangeReject *reject = NULL;
350         enum samr_RejectReason reason = SAMR_REJECT_OTHER;
351         uint8_t new_nt_hash[16], new_lm_hash[16];
352         struct samr_Password nt_verifier, lm_verifier;
353
354         *r->out.dominfo = NULL;
355         *r->out.reject = NULL;
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_convenience(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, NULL, false)) {
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         struct samr_DomInfo1 *dominfo = NULL;
522         struct samr_ChangeReject *reject = NULL;
523
524         r2.in.server = r->in.server;
525         r2.in.account = r->in.account;
526         r2.in.nt_password = r->in.nt_password;
527         r2.in.nt_verifier = r->in.nt_verifier;
528         r2.in.lm_change = r->in.lm_change;
529         r2.in.lm_password = r->in.lm_password;
530         r2.in.lm_verifier = r->in.lm_verifier;
531         r2.in.password3 = NULL;
532         r2.out.dominfo = &dominfo;
533         r2.out.reject = &reject;
534
535         return dcesrv_samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
536 }
537
538
539 /*
540   set password via a samr_CryptPassword buffer
541   this will in the 'msg' with modify operations that will update the user
542   password when applied
543 */
544 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
545                            void *sam_ctx,
546                            struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
547                            TALLOC_CTX *mem_ctx,
548                            struct ldb_message *msg, 
549                            struct samr_CryptPassword *pwbuf)
550 {
551         NTSTATUS nt_status;
552         DATA_BLOB new_password;
553         DATA_BLOB session_key = data_blob(NULL, 0);
554
555         nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
556         if (!NT_STATUS_IS_OK(nt_status)) {
557                 return nt_status;
558         }
559
560         arcfour_crypt_blob(pwbuf->data, 516, &session_key);
561
562         if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
563                 DEBUG(3,("samr: failed to decode password buffer\n"));
564                 return NT_STATUS_WRONG_PASSWORD;
565         }
566                 
567         /* set the password - samdb needs to know both the domain and user DNs,
568            so the domain password policy can be used */
569         return samdb_set_password(sam_ctx, mem_ctx,
570                                   account_dn, domain_dn, 
571                                   msg, &new_password, 
572                                   NULL, NULL,
573                                   false, /* This is a password set, not change */
574                                   NULL, NULL);
575 }
576
577
578 /*
579   set password via a samr_CryptPasswordEx buffer
580   this will in the 'msg' with modify operations that will update the user
581   password when applied
582 */
583 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
584                               struct ldb_context *sam_ctx,
585                               struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
586                               TALLOC_CTX *mem_ctx,
587                               struct ldb_message *msg, 
588                               struct samr_CryptPasswordEx *pwbuf)
589 {
590         NTSTATUS nt_status;
591         DATA_BLOB new_password;
592         DATA_BLOB co_session_key;
593         DATA_BLOB session_key = data_blob(NULL, 0);
594         struct MD5Context ctx;
595
596         nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
597         if (!NT_STATUS_IS_OK(nt_status)) {
598                 return nt_status;
599         }
600
601         co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
602         if (!co_session_key.data) {
603                 return NT_STATUS_NO_MEMORY;
604         }
605
606         MD5Init(&ctx);
607         MD5Update(&ctx, &pwbuf->data[516], 16);
608         MD5Update(&ctx, session_key.data, session_key.length);
609         MD5Final(co_session_key.data, &ctx);
610         
611         arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
612
613         if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
614                 DEBUG(3,("samr: failed to decode password buffer\n"));
615                 return NT_STATUS_WRONG_PASSWORD;
616         }
617                 
618         /* set the password - samdb needs to know both the domain and user DNs,
619            so the domain password policy can be used */
620         return samdb_set_password(sam_ctx, mem_ctx,
621                                   account_dn, domain_dn, 
622                                   msg, &new_password, 
623                                   NULL, NULL,
624                                   false, /* This is a password set, not change */
625                                   NULL, NULL);
626 }
627
628