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