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