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