r11195: Add a new helper function (needed by my kpasswdd work, but hooked in
[jelmer/samba4-debian.git] / source / 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    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24 #include "librpc/gen_ndr/ndr_samr.h"
25 #include "rpc_server/dcerpc_server.h"
26 #include "rpc_server/common/common.h"
27 #include "rpc_server/samr/dcesrv_samr.h"
28 #include "system/time.h"
29 #include "lib/crypto/crypto.h"
30 #include "lib/ldb/include/ldb.h"
31 #include "ads.h"
32
33 /* 
34   samr_ChangePasswordUser 
35 */
36 NTSTATUS samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
37                                  struct samr_ChangePasswordUser *r)
38 {
39         struct dcesrv_handle *h;
40         struct samr_account_state *a_state;
41         struct ldb_context *sam_ctx;
42         struct ldb_message **res, *msg;
43         int ret;
44         struct samr_Password new_lmPwdHash, new_ntPwdHash, checkHash;
45         struct samr_Password *lm_pwd, *nt_pwd;
46         NTSTATUS status = NT_STATUS_OK;
47         const char * const attrs[] = { "lmPwdHash", "ntPwdHash" , "unicodePwd", NULL };
48
49         DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
50
51         a_state = h->data;
52
53         /* To change a password we need to open as system */
54         sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
55         if (sam_ctx == NULL) {
56                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
57         }
58
59         /* fetch the old hashes */
60         ret = gendb_search_dn(sam_ctx, mem_ctx,
61                               a_state->account_dn, &res, attrs);
62         if (ret != 1) {
63                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
64         }
65         msg = res[0];
66
67         /* basic sanity checking on parameters */
68         if (!r->in.lm_present || !r->in.nt_present ||
69             !r->in.old_lm_crypted || !r->in.new_lm_crypted ||
70             !r->in.old_nt_crypted || !r->in.new_nt_crypted) {
71                 /* we should really handle a change with lm not
72                    present */
73                 return NT_STATUS_INVALID_PARAMETER_MIX;
74         }
75         if (!r->in.cross1_present || !r->in.nt_cross) {
76                 return NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED;
77         }
78         if (!r->in.cross2_present || !r->in.lm_cross) {
79                 return NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED;
80         }
81
82         status = samdb_result_passwords(mem_ctx, msg, &lm_pwd, &nt_pwd);
83         if (!NT_STATUS_IS_OK(status) || !lm_pwd || !nt_pwd) {
84                 return NT_STATUS_WRONG_PASSWORD;
85         }
86
87         /* decrypt and check the new lm hash */
88         D_P16(lm_pwd->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
89         D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
90         if (memcmp(checkHash.hash, lm_pwd, 16) != 0) {
91                 return NT_STATUS_WRONG_PASSWORD;
92         }
93
94         /* decrypt and check the new nt hash */
95         D_P16(nt_pwd->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
96         D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
97         if (memcmp(checkHash.hash, nt_pwd, 16) != 0) {
98                 return NT_STATUS_WRONG_PASSWORD;
99         }
100         
101         /* check the nt cross hash */
102         D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
103         if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
104                 return NT_STATUS_WRONG_PASSWORD;
105         }
106
107         /* check the lm cross hash */
108         D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash);
109         if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
110                 return NT_STATUS_WRONG_PASSWORD;
111         }
112
113         msg = ldb_msg_new(mem_ctx);
114         if (msg == NULL) {
115                 return NT_STATUS_NO_MEMORY;
116         }
117
118         msg->dn = ldb_dn_copy(msg, a_state->account_dn);
119         if (!msg->dn) {
120                 return NT_STATUS_NO_MEMORY;
121         }
122
123         status = samdb_set_password(sam_ctx, mem_ctx,
124                                     a_state->account_dn, a_state->domain_state->domain_dn,
125                                     msg, NULL, &new_lmPwdHash, &new_ntPwdHash, 
126                                     True, /* this is a user password change */
127                                     True, /* run restriction tests */
128                                     NULL,
129                                     NULL);
130         if (!NT_STATUS_IS_OK(status)) {
131                 return status;
132         }
133
134         /* modify the samdb record */
135         ret = samdb_replace(sam_ctx, mem_ctx, msg);
136         if (ret != 0) {
137                 return NT_STATUS_UNSUCCESSFUL;
138         }
139
140         return NT_STATUS_OK;
141 }
142
143 /* 
144   samr_OemChangePasswordUser2 
145 */
146 NTSTATUS samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
147                                      struct samr_OemChangePasswordUser2 *r)
148 {
149         NTSTATUS status;
150         char new_pass[512];
151         uint32_t new_pass_len;
152         struct samr_CryptPassword *pwbuf = r->in.password;
153         struct ldb_context *sam_ctx;
154         const struct ldb_dn *user_dn;
155         int ret;
156         struct ldb_message **res, *mod;
157         const char * const attrs[] = { "objectSid", "lmPwdHash", "unicodePwd", NULL };
158         struct samr_Password *lm_pwd;
159         DATA_BLOB lm_pwd_blob;
160         uint8_t new_lm_hash[16];
161         struct samr_Password lm_verifier;
162
163         if (pwbuf == NULL) {
164                 return NT_STATUS_WRONG_PASSWORD;
165         }
166
167         /* To change a password we need to open as system */
168         sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
169         if (sam_ctx == NULL) {
170                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
171         }
172
173         /* we need the users dn and the domain dn (derived from the
174            user SID). We also need the current lm password hash in
175            order to decrypt the incoming password */
176         ret = gendb_search(sam_ctx, 
177                            mem_ctx, NULL, &res, attrs,
178                            "(&(sAMAccountName=%s)(objectclass=user))",
179                            r->in.account->string);
180         if (ret != 1) {
181                 return NT_STATUS_NO_SUCH_USER;
182         }
183
184         user_dn = res[0]->dn;
185
186         status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, NULL);
187         if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
188                 return NT_STATUS_WRONG_PASSWORD;
189         }
190
191         /* decrypt the password we have been given */
192         lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash)); 
193         arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
194         data_blob_free(&lm_pwd_blob);
195         
196         if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
197                               &new_pass_len, STR_ASCII)) {
198                 DEBUG(3,("samr: failed to decode password buffer\n"));
199                 return NT_STATUS_WRONG_PASSWORD;
200         }
201
202         /* check LM verifier */
203         if (lm_pwd == NULL || r->in.hash == NULL) {
204                 return NT_STATUS_WRONG_PASSWORD;
205         }
206
207         E_deshash(new_pass, new_lm_hash);
208         E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
209         if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
210                 return NT_STATUS_WRONG_PASSWORD;
211         }
212
213         mod = ldb_msg_new(mem_ctx);
214         if (mod == NULL) {
215                 return NT_STATUS_NO_MEMORY;
216         }
217
218         mod->dn = ldb_dn_copy(mod, user_dn);
219         if (!mod->dn) {
220                 return NT_STATUS_NO_MEMORY;
221         }
222
223         /* set the password - samdb needs to know both the domain and user DNs,
224            so the domain password policy can be used */
225         status = samdb_set_password(sam_ctx, mem_ctx,
226                                     user_dn, NULL, 
227                                     mod, new_pass, 
228                                     NULL, NULL,
229                                     True, /* this is a user password change */
230                                     True, /* run restriction tests */
231                                     NULL, 
232                                     NULL);
233         if (!NT_STATUS_IS_OK(status)) {
234                 return status;
235         }
236
237         /* modify the samdb record */
238         ret = samdb_replace(sam_ctx, mem_ctx, mod);
239         if (ret != 0) {
240                 return NT_STATUS_UNSUCCESSFUL;
241         }
242
243         return NT_STATUS_OK;
244 }
245
246
247 /* 
248   samr_ChangePasswordUser3 
249 */
250 NTSTATUS samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call, 
251                                   TALLOC_CTX *mem_ctx,
252                                   struct samr_ChangePasswordUser3 *r)
253 {       
254         NTSTATUS status;
255         char new_pass[512];
256         uint32_t new_pass_len;
257         struct ldb_context *sam_ctx;
258         const struct ldb_dn *user_dn;
259         int ret;
260         struct ldb_message **res, *mod;
261         const char * const attrs[] = { "ntPwdHash", "lmPwdHash", "unicodePwd", NULL };
262         struct samr_Password *nt_pwd, *lm_pwd;
263         DATA_BLOB nt_pwd_blob;
264         struct samr_DomInfo1 *dominfo;
265         struct samr_ChangeReject *reject;
266         uint32_t reason = 0;
267         uint8_t new_nt_hash[16], new_lm_hash[16];
268         struct samr_Password nt_verifier, lm_verifier;
269
270         ZERO_STRUCT(r->out);
271
272         if (r->in.nt_password == NULL ||
273             r->in.nt_verifier == NULL) {
274                 status = NT_STATUS_INVALID_PARAMETER;
275                 goto failed;
276         }
277
278         /* To change a password we need to open as system */
279         sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
280         if (sam_ctx == NULL) {
281                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
282                 goto failed;
283         }
284
285         /* we need the users dn and the domain dn (derived from the
286            user SID). We also need the current lm and nt password hashes
287            in order to decrypt the incoming passwords */
288         ret = gendb_search(sam_ctx, 
289                            mem_ctx, NULL, &res, attrs,
290                            "(&(sAMAccountName=%s)(objectclass=user))",
291                            r->in.account->string);
292         if (ret != 1) {
293                 status = NT_STATUS_NO_SUCH_USER;
294                 goto failed;
295         }
296
297         user_dn = res[0]->dn;
298
299         status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, &nt_pwd);
300         if (!NT_STATUS_IS_OK(status) ) {
301                 goto failed;
302         }
303
304         if (!nt_pwd) {
305                 status = NT_STATUS_WRONG_PASSWORD;
306                 goto failed;
307         }
308
309         /* decrypt the password we have been given */
310         nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
311         arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
312         data_blob_free(&nt_pwd_blob);
313
314         if (!decode_pw_buffer(r->in.nt_password->data, new_pass, sizeof(new_pass),
315                               &new_pass_len, STR_UNICODE)) {
316                 DEBUG(3,("samr: failed to decode password buffer\n"));
317                 status = NT_STATUS_WRONG_PASSWORD;
318                 goto failed;
319         }
320
321         if (r->in.nt_verifier == NULL) {
322                 status = NT_STATUS_WRONG_PASSWORD;
323                 goto failed;
324         }
325
326         /* check NT verifier */
327         E_md4hash(new_pass, new_nt_hash);
328         E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
329         if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
330                 status = NT_STATUS_WRONG_PASSWORD;
331                 goto failed;
332         }
333
334         /* check LM verifier */
335         if (lm_pwd && r->in.lm_verifier != NULL) {
336                 E_deshash(new_pass, new_lm_hash);
337                 E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
338                 if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
339                         status = NT_STATUS_WRONG_PASSWORD;
340                         goto failed;
341                 }
342         }
343
344
345         mod = ldb_msg_new(mem_ctx);
346         if (mod == NULL) {
347                 return NT_STATUS_NO_MEMORY;
348         }
349
350         mod->dn = ldb_dn_copy(mod, user_dn);
351         if (!mod->dn) {
352                 status = NT_STATUS_NO_MEMORY;
353                 goto failed;
354         }
355
356         /* set the password - samdb needs to know both the domain and user DNs,
357            so the domain password policy can be used */
358         status = samdb_set_password(sam_ctx, mem_ctx,
359                                     user_dn, NULL, 
360                                     mod, new_pass, 
361                                     NULL, NULL,
362                                     True, /* this is a user password change */
363                                     True, /* run restriction tests */
364                                     &reason, 
365                                     &dominfo);
366         if (!NT_STATUS_IS_OK(status)) {
367                 goto failed;
368         }
369
370         /* modify the samdb record */
371         ret = samdb_replace(sam_ctx, mem_ctx, mod);
372         if (ret != 0) {
373                 status = NT_STATUS_UNSUCCESSFUL;
374                 goto failed;
375         }
376
377         return NT_STATUS_OK;
378
379 failed:
380
381         reject = talloc(mem_ctx, struct samr_ChangeReject);
382         if (reject == NULL) {
383                 return status;
384         }
385         ZERO_STRUCTP(reject);
386
387         reject->reason = reason;
388
389         r->out.dominfo = dominfo;
390         r->out.reject = reject;
391
392         return status;
393 }
394
395
396 /* 
397   samr_ChangePasswordUser2 
398
399   easy - just a subset of samr_ChangePasswordUser3
400 */
401 NTSTATUS samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
402                                   struct samr_ChangePasswordUser2 *r)
403 {
404         struct samr_ChangePasswordUser3 r2;
405
406         r2.in.server = r->in.server;
407         r2.in.account = r->in.account;
408         r2.in.nt_password = r->in.nt_password;
409         r2.in.nt_verifier = r->in.nt_verifier;
410         r2.in.lm_change = r->in.lm_change;
411         r2.in.lm_password = r->in.lm_password;
412         r2.in.lm_verifier = r->in.lm_verifier;
413         r2.in.password3 = NULL;
414
415         return samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
416 }
417
418
419 /*
420   check that a password is sufficiently complex
421 */
422 static BOOL samdb_password_complexity_ok(const char *pass)
423 {
424         return check_password_quality(pass);
425 }
426
427 /*
428   set the user password using plaintext, obeying any user or domain
429   password restrictions
430
431   note that this function doesn't actually store the result in the
432   database, it just fills in the "mod" structure with ldb modify
433   elements to setup the correct change when samdb_replace() is
434   called. This allows the caller to combine the change with other
435   changes (as is needed by some of the set user info levels)
436 */
437 NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
438                             const struct ldb_dn *user_dn,
439                             const struct ldb_dn *domain_dn,
440                             struct ldb_message *mod,
441                             const char *new_pass,
442                             struct samr_Password *lmNewHash, 
443                             struct samr_Password *ntNewHash,
444                             BOOL user_change,
445                             BOOL restrictions,
446                             uint32_t *reject_reason,
447                             struct samr_DomInfo1 **_dominfo)
448 {
449         const char * const user_attrs[] = { "userAccountControl", "lmPwdHistory", 
450                                             "ntPwdHistory", "unicodePwd", 
451                                             "lmPwdHash", "ntPwdHash", "badPwdCount", 
452                                             "objectSid", NULL };
453         const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength", 
454                                               "maxPwdAge", "minPwdAge", 
455                                               "minPwdLength", "pwdLastSet", NULL };
456         const char *unicodePwd;
457         NTTIME pwdLastSet;
458         int64_t minPwdAge;
459         uint_t minPwdLength, pwdProperties, pwdHistoryLength;
460         uint_t userAccountControl, badPwdCount;
461         struct samr_Password *lmPwdHistory, *ntPwdHistory, lmPwdHash, ntPwdHash;
462         struct samr_Password *new_lmPwdHistory, *new_ntPwdHistory;
463         struct samr_Password local_lmNewHash, local_ntNewHash;
464         int lmPwdHistory_len, ntPwdHistory_len;
465         uint_t kvno;
466         struct dom_sid *domain_sid;
467         struct ldb_message **res;
468         int count;
469         time_t now = time(NULL);
470         NTTIME now_nt;
471         int i;
472
473         /* we need to know the time to compute password age */
474         unix_to_nt_time(&now_nt, now);
475
476         /* pull all the user parameters */
477         count = gendb_search_dn(ctx, mem_ctx, user_dn, &res, user_attrs);
478         if (count != 1) {
479                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
480         }
481         unicodePwd =         samdb_result_string(res[0], "unicodePwd", NULL);
482         userAccountControl = samdb_result_uint(res[0],   "userAccountControl", 0);
483         badPwdCount =        samdb_result_uint(res[0],   "badPwdCount", 0);
484         lmPwdHistory_len =   samdb_result_hashes(mem_ctx, res[0], 
485                                                  "lmPwdHistory", &lmPwdHistory);
486         ntPwdHistory_len =   samdb_result_hashes(mem_ctx, res[0], 
487                                                  "ntPwdHistory", &ntPwdHistory);
488         lmPwdHash =          samdb_result_hash(res[0],   "lmPwdHash");
489         ntPwdHash =          samdb_result_hash(res[0],   "ntPwdHash");
490         pwdLastSet =         samdb_result_uint64(res[0], "pwdLastSet", 0);
491         kvno =               samdb_result_uint(res[0],   "msDS-KeyVersionNumber", 0);
492
493         if (domain_dn) {
494                 /* pull the domain parameters */
495                 count = gendb_search_dn(ctx, mem_ctx, domain_dn, &res, domain_attrs);
496                 if (count != 1) {
497                         return NT_STATUS_NO_SUCH_DOMAIN;
498                 }
499         } else {
500                 /* work out the domain sid, and pull the domain from there */
501                 domain_sid =         samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
502                 if (domain_sid == NULL) {
503                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
504                 }
505
506                 count = gendb_search(ctx, mem_ctx, NULL, &res, domain_attrs, 
507                                      "(objectSid=%s)", 
508                                      ldap_encode_ndr_dom_sid(mem_ctx, domain_sid));
509                 if (count != 1) {
510                         return NT_STATUS_NO_SUCH_DOMAIN;
511                 }
512         }
513
514         pwdProperties =    samdb_result_uint(res[0],   "pwdProperties", 0);
515         pwdHistoryLength = samdb_result_uint(res[0],   "pwdHistoryLength", 0);
516         minPwdLength =     samdb_result_uint(res[0],   "minPwdLength", 0);
517         minPwdAge =        samdb_result_int64(res[0],  "minPwdAge", 0);
518
519         if (_dominfo) {
520                 struct samr_DomInfo1 *dominfo;
521                 /* on failure we need to fill in the reject reasons */
522                 dominfo = talloc(mem_ctx, struct samr_DomInfo1);
523                 if (dominfo == NULL) {
524                         return NT_STATUS_NO_MEMORY;
525                 }
526                 dominfo->min_password_length     = minPwdLength;
527                 dominfo->password_properties     = pwdProperties;
528                 dominfo->password_history_length = pwdHistoryLength;
529                 dominfo->max_password_age        = minPwdAge;
530                 dominfo->min_password_age        = minPwdAge;
531                 *_dominfo = dominfo;
532         }
533
534         if (new_pass) {
535                 /* check the various password restrictions */
536                 if (restrictions && minPwdLength > strlen_m(new_pass)) {
537                         if (reject_reason) {
538                                 *reject_reason = SAMR_REJECT_TOO_SHORT;
539                         }
540                         return NT_STATUS_PASSWORD_RESTRICTION;
541                 }
542                 
543                 /* possibly check password complexity */
544                 if (restrictions && pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
545                     !samdb_password_complexity_ok(new_pass)) {
546                         if (reject_reason) {
547                                 *reject_reason = SAMR_REJECT_COMPLEXITY;
548                         }
549                         return NT_STATUS_PASSWORD_RESTRICTION;
550                 }
551                 
552                 /* compute the new nt and lm hashes */
553                 if (E_deshash(new_pass, local_lmNewHash.hash)) {
554                         lmNewHash = &local_lmNewHash;
555                 }
556                 E_md4hash(new_pass, local_ntNewHash.hash);
557                 ntNewHash = &local_ntNewHash;
558         }
559
560         if (restrictions && user_change) {
561                 /* are all password changes disallowed? */
562                 if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
563                         if (reject_reason) {
564                                 *reject_reason = SAMR_REJECT_OTHER;
565                         }
566                         return NT_STATUS_PASSWORD_RESTRICTION;
567                 }
568                 
569                 /* can this user change password? */
570                 if (userAccountControl & UF_PASSWD_CANT_CHANGE) {
571                         if (reject_reason) {
572                                 *reject_reason = SAMR_REJECT_OTHER;
573                         }
574                         return NT_STATUS_PASSWORD_RESTRICTION;
575                 }
576                 
577                 /* yes, this is a minus. The ages are in negative 100nsec units! */
578                 if (pwdLastSet - minPwdAge > now_nt) {
579                         if (reject_reason) {
580                                 *reject_reason = SAMR_REJECT_OTHER;
581                         }
582                         return NT_STATUS_PASSWORD_RESTRICTION;
583                 }
584
585                 /* check the immediately past password */
586                 if (pwdHistoryLength > 0) {
587                         if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
588                                 if (reject_reason) {
589                                         *reject_reason = SAMR_REJECT_COMPLEXITY;
590                                 }
591                                 return NT_STATUS_PASSWORD_RESTRICTION;
592                         }
593                         if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
594                                 if (reject_reason) {
595                                         *reject_reason = SAMR_REJECT_COMPLEXITY;
596                                 }
597                                 return NT_STATUS_PASSWORD_RESTRICTION;
598                         }
599                 }
600                 
601                 /* check the password history */
602                 lmPwdHistory_len = MIN(lmPwdHistory_len, pwdHistoryLength);
603                 ntPwdHistory_len = MIN(ntPwdHistory_len, pwdHistoryLength);
604                 
605                 if (pwdHistoryLength > 0) {
606                         if (unicodePwd && new_pass && strcmp(unicodePwd, new_pass) == 0) {
607                                 if (reject_reason) {
608                                         *reject_reason = SAMR_REJECT_COMPLEXITY;
609                                 }
610                                 return NT_STATUS_PASSWORD_RESTRICTION;
611                         }
612                         if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
613                                 if (reject_reason) {
614                                         *reject_reason = SAMR_REJECT_COMPLEXITY;
615                                 }
616                                 return NT_STATUS_PASSWORD_RESTRICTION;
617                         }
618                         if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
619                                 if (reject_reason) {
620                                         *reject_reason = SAMR_REJECT_COMPLEXITY;
621                                 }
622                                 return NT_STATUS_PASSWORD_RESTRICTION;
623                         }
624                 }
625                 
626                 for (i=0; lmNewHash && i<lmPwdHistory_len;i++) {
627                         if (memcmp(lmNewHash->hash, lmPwdHistory[i].hash, 16) == 0) {
628                                 if (reject_reason) {
629                                         *reject_reason = SAMR_REJECT_COMPLEXITY;
630                                 }
631                                 return NT_STATUS_PASSWORD_RESTRICTION;
632                         }
633                 }
634                 for (i=0; ntNewHash && i<ntPwdHistory_len;i++) {
635                         if (memcmp(ntNewHash->hash, ntPwdHistory[i].hash, 16) == 0) {
636                                 if (reject_reason) {
637                                         *reject_reason = SAMR_REJECT_COMPLEXITY;
638                                 }
639                                 return NT_STATUS_PASSWORD_RESTRICTION;
640                         }
641                 }
642         }
643
644 #define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0)
645
646         /* the password is acceptable. Start forming the new fields */
647         if (lmNewHash) {
648                 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "lmPwdHash", lmNewHash));
649         } else {
650                 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHash"));
651         }
652
653         if (ntNewHash) {
654                 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "ntPwdHash", ntNewHash));
655         } else {
656                 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHash"));
657         }
658
659         if (new_pass && (pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT) &&
660             (userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
661                 CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod, 
662                                                "unicodePwd", new_pass));
663         } else {
664                 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "unicodePwd"));
665         }
666
667         CHECK_RET(samdb_msg_add_uint64(ctx, mem_ctx, mod, "pwdLastSet", now_nt));
668
669         CHECK_RET(samdb_msg_add_uint(ctx, mem_ctx, mod, "msDS-KeyVersionNumber", kvno + 1));
670         
671         if (pwdHistoryLength == 0) {
672                 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHistory"));
673                 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHistory"));
674                 return NT_STATUS_OK;
675         }
676         
677         /* store the password history */
678         new_lmPwdHistory = talloc_array(mem_ctx, struct samr_Password, 
679                                           pwdHistoryLength);
680         if (!new_lmPwdHistory) {
681                 return NT_STATUS_NO_MEMORY;
682         }
683         new_ntPwdHistory = talloc_array(mem_ctx, struct samr_Password, 
684                                           pwdHistoryLength);
685         if (!new_ntPwdHistory) {
686                 return NT_STATUS_NO_MEMORY;
687         }
688         for (i=0;i<MIN(pwdHistoryLength-1, lmPwdHistory_len);i++) {
689                 new_lmPwdHistory[i+1] = lmPwdHistory[i];
690         }
691         for (i=0;i<MIN(pwdHistoryLength-1, ntPwdHistory_len);i++) {
692                 new_ntPwdHistory[i+1] = ntPwdHistory[i];
693         }
694
695         /* Don't store 'long' passwords in the LM history, 
696            but make sure to 'expire' one password off the other end */
697         if (lmNewHash) {
698                 new_lmPwdHistory[0] = *lmNewHash;
699         } else {
700                 ZERO_STRUCT(new_lmPwdHistory[0]);
701         }
702         lmPwdHistory_len = MIN(lmPwdHistory_len + 1, pwdHistoryLength);
703
704         if (ntNewHash) {
705                 new_ntPwdHistory[0] = *ntNewHash;
706         } else {
707                 ZERO_STRUCT(new_ntPwdHistory[0]);
708         }
709         ntPwdHistory_len = MIN(ntPwdHistory_len + 1, pwdHistoryLength);
710         
711         CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod, 
712                                        "lmPwdHistory", 
713                                        new_lmPwdHistory, 
714                                        lmPwdHistory_len));
715
716         CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod, 
717                                        "ntPwdHistory", 
718                                        new_ntPwdHistory, 
719                                        ntPwdHistory_len));
720         return NT_STATUS_OK;
721 }
722
723 /*
724   set password via a samr_CryptPassword buffer
725   this will in the 'msg' with modify operations that will update the user
726   password when applied
727 */
728 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
729                            void *sam_ctx,
730                            const struct ldb_dn *account_dn, const struct ldb_dn *domain_dn,
731                            TALLOC_CTX *mem_ctx,
732                            struct ldb_message *msg, 
733                            struct samr_CryptPassword *pwbuf)
734 {
735         NTSTATUS nt_status;
736         char new_pass[512];
737         uint32_t new_pass_len;
738         DATA_BLOB session_key = data_blob(NULL, 0);
739
740         nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
741         if (!NT_STATUS_IS_OK(nt_status)) {
742                 return nt_status;
743         }
744
745         arcfour_crypt_blob(pwbuf->data, 516, &session_key);
746
747         if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
748                               &new_pass_len, STR_UNICODE)) {
749                 DEBUG(3,("samr: failed to decode password buffer\n"));
750                 return NT_STATUS_WRONG_PASSWORD;
751         }
752
753         /* set the password - samdb needs to know both the domain and user DNs,
754            so the domain password policy can be used */
755         return samdb_set_password(sam_ctx, mem_ctx,
756                                   account_dn, domain_dn, 
757                                   msg, new_pass, 
758                                   NULL, NULL,
759                                   False, /* This is a password set, not change */
760                                   True, /* run restriction tests */
761                                   NULL, NULL);
762 }
763
764
765 /*
766   set password via a samr_CryptPasswordEx buffer
767   this will in the 'msg' with modify operations that will update the user
768   password when applied
769 */
770 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
771                               struct ldb_context *sam_ctx,
772                               const struct ldb_dn *account_dn, const struct ldb_dn *domain_dn,
773                               TALLOC_CTX *mem_ctx,
774                               struct ldb_message *msg, 
775                               struct samr_CryptPasswordEx *pwbuf)
776 {
777         NTSTATUS nt_status;
778         char new_pass[512];
779         uint32_t new_pass_len;
780         DATA_BLOB co_session_key;
781         DATA_BLOB session_key = data_blob(NULL, 0);
782         struct MD5Context ctx;
783
784         nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
785         if (!NT_STATUS_IS_OK(nt_status)) {
786                 return nt_status;
787         }
788
789         co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
790         if (!co_session_key.data) {
791                 return NT_STATUS_NO_MEMORY;
792         }
793
794         MD5Init(&ctx);
795         MD5Update(&ctx, &pwbuf->data[516], 16);
796         MD5Update(&ctx, session_key.data, session_key.length);
797         MD5Final(co_session_key.data, &ctx);
798         
799         arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
800
801         if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
802                               &new_pass_len, STR_UNICODE)) {
803                 DEBUG(3,("samr: failed to decode password buffer\n"));
804                 return NT_STATUS_WRONG_PASSWORD;
805         }
806
807         /* set the password - samdb needs to know both the domain and user DNs,
808            so the domain password policy can be used */
809         return samdb_set_password(sam_ctx, mem_ctx,
810                                   account_dn, domain_dn, 
811                                   msg, new_pass, 
812                                   NULL, NULL,
813                                   False, /* This is a password set, not change */
814                                   True, /* run restriction tests */
815                                   NULL, NULL);
816 }
817
818 /*
819   set the user password using plaintext, obeying any user or domain
820   password restrictions
821
822   This wrapper function takes a SID as input, rather than a user DN,
823   and actually performs the password change
824
825 */
826 NTSTATUS samdb_set_password_sid(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
827                                 const struct dom_sid *user_sid,
828                                 const char *new_pass,
829                                 struct samr_Password *lmNewHash, 
830                                 struct samr_Password *ntNewHash,
831                                 BOOL user_change,
832                                 BOOL restrictions,
833                                 uint32_t *reject_reason,
834                                 struct samr_DomInfo1 **_dominfo) 
835 {
836         NTSTATUS nt_status;
837         struct ldb_dn *user_dn;
838         struct ldb_message *msg;
839         int count;
840         int ret;
841
842         user_dn = samdb_search_dn(ctx, mem_ctx, NULL, 
843                                   "((objectSid=%s)(objectClass=user))", 
844                                   ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
845         if (count != 1) {
846                 return NT_STATUS_NO_SUCH_USER;
847         }
848
849         msg = ldb_msg_new(mem_ctx);
850         if (msg == NULL) {
851                 return NT_STATUS_NO_MEMORY;
852         }
853
854         msg->dn = ldb_dn_copy(msg, user_dn);
855         if (!msg->dn) {
856                 return NT_STATUS_NO_MEMORY;
857         }
858
859         nt_status = samdb_set_password(ctx, mem_ctx,
860                                        user_dn, NULL,
861                                        msg, new_pass, 
862                                        lmNewHash, ntNewHash,
863                                        user_change, /* This is a password set, not change */
864                                        restrictions, /* run restriction tests */
865                                        reject_reason, _dominfo);
866         if (!NT_STATUS_IS_OK(nt_status)) {
867                 return nt_status;
868         }
869         
870         /* modify the samdb record */
871         ret = samdb_replace(ctx, mem_ctx, msg);
872         if (ret != 0) {
873                 return NT_STATUS_ACCESS_DENIED;
874         }
875
876         return NT_STATUS_OK;
877 }