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