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