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