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