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