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