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