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