r12507: This file has had my grubby paws all over it ;-)
[sfrench/samba-autobuild/.git] / source4 / rpc_server / samr / samr_password.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    samr server password set/change handling
5
6    Copyright (C) Andrew Tridgell 2004
7    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25 #include "librpc/gen_ndr/ndr_samr.h"
26 #include "rpc_server/dcerpc_server.h"
27 #include "rpc_server/common/common.h"
28 #include "rpc_server/samr/dcesrv_samr.h"
29 #include "system/time.h"
30 #include "lib/crypto/crypto.h"
31 #include "lib/ldb/include/ldb.h"
32 #include "ads.h"
33
34 /* 
35   samr_ChangePasswordUser 
36 */
37 NTSTATUS samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
38                                  struct samr_ChangePasswordUser *r)
39 {
40         struct dcesrv_handle *h;
41         struct samr_account_state *a_state;
42         struct ldb_context *sam_ctx;
43         struct ldb_message **res, *msg;
44         int ret;
45         struct samr_Password new_lmPwdHash, new_ntPwdHash, checkHash;
46         struct samr_Password *lm_pwd, *nt_pwd;
47         NTSTATUS status = NT_STATUS_OK;
48         const char * const attrs[] = { "lmPwdHash", "ntPwdHash" , "unicodePwd", NULL };
49
50         DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
51
52         a_state = h->data;
53
54         /* basic sanity checking on parameters.  Do this before any database ops */
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         /* To change a password we need to open as system */
70         sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
71         if (sam_ctx == NULL) {
72                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
73         }
74
75         ret = ldb_transaction_start(sam_ctx);
76         if (ret) {
77                 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
78                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
79         }
80
81         /* fetch the old hashes */
82         ret = gendb_search_dn(sam_ctx, mem_ctx,
83                               a_state->account_dn, &res, attrs);
84         if (ret != 1) {
85                 ldb_transaction_cancel(sam_ctx);
86                 return NT_STATUS_WRONG_PASSWORD;
87         }
88         msg = res[0];
89
90         status = samdb_result_passwords(mem_ctx, msg, &lm_pwd, &nt_pwd);
91         if (!NT_STATUS_IS_OK(status) || !lm_pwd || !nt_pwd) {
92                 ldb_transaction_cancel(sam_ctx);
93                 return NT_STATUS_WRONG_PASSWORD;
94         }
95
96         /* decrypt and check the new lm hash */
97         D_P16(lm_pwd->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
98         D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
99         if (memcmp(checkHash.hash, lm_pwd, 16) != 0) {
100                 ldb_transaction_cancel(sam_ctx);
101                 return NT_STATUS_WRONG_PASSWORD;
102         }
103
104         /* decrypt and check the new nt hash */
105         D_P16(nt_pwd->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
106         D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
107         if (memcmp(checkHash.hash, nt_pwd, 16) != 0) {
108                 ldb_transaction_cancel(sam_ctx);
109                 return NT_STATUS_WRONG_PASSWORD;
110         }
111         
112         /* check the nt cross hash */
113         D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
114         if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
115                 ldb_transaction_cancel(sam_ctx);
116                 return NT_STATUS_WRONG_PASSWORD;
117         }
118
119         /* check the lm cross hash */
120         D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash);
121         if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
122                 ldb_transaction_cancel(sam_ctx);
123                 return NT_STATUS_WRONG_PASSWORD;
124         }
125
126         msg = ldb_msg_new(mem_ctx);
127         if (msg == NULL) {
128                 ldb_transaction_cancel(sam_ctx);
129                 return NT_STATUS_NO_MEMORY;
130         }
131
132         msg->dn = ldb_dn_copy(msg, a_state->account_dn);
133         if (!msg->dn) {
134                 ldb_transaction_cancel(sam_ctx);
135                 return NT_STATUS_NO_MEMORY;
136         }
137
138         /* set the password on the user DN specified.  This may fail
139          * due to password policies */
140         status = samdb_set_password(sam_ctx, mem_ctx,
141                                     a_state->account_dn, a_state->domain_state->domain_dn,
142                                     msg, NULL, &new_lmPwdHash, &new_ntPwdHash, 
143                                     True, /* this is a user password change */
144                                     True, /* run restriction tests */
145                                     NULL,
146                                     NULL);
147         if (!NT_STATUS_IS_OK(status)) {
148                 ldb_transaction_cancel(sam_ctx);
149                 return status;
150         }
151
152         /* The above call only setup the modifications, this actually
153          * makes the write to the database. */
154         ret = samdb_replace(sam_ctx, mem_ctx, msg);
155         if (ret != 0) {
156                 ldb_transaction_cancel(sam_ctx);
157                 return NT_STATUS_UNSUCCESSFUL;
158         }
159
160         /* And this confirms it in a transaction commit */
161         ret = ldb_transaction_commit(sam_ctx);
162         if (ret != 0) {
163                 DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
164                          ldb_dn_linearize(mem_ctx, a_state->account_dn),
165                          ldb_errstring(sam_ctx)));
166                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
167         }
168
169         return NT_STATUS_OK;
170 }
171
172 /* 
173   samr_OemChangePasswordUser2 
174 */
175 NTSTATUS samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
176                                      struct samr_OemChangePasswordUser2 *r)
177 {
178         NTSTATUS status;
179         char new_pass[512];
180         uint32_t new_pass_len;
181         struct samr_CryptPassword *pwbuf = r->in.password;
182         struct ldb_context *sam_ctx;
183         const struct ldb_dn *user_dn;
184         int ret;
185         struct ldb_message **res, *mod;
186         const char * const attrs[] = { "objectSid", "lmPwdHash", "unicodePwd", NULL };
187         struct samr_Password *lm_pwd;
188         DATA_BLOB lm_pwd_blob;
189         uint8_t new_lm_hash[16];
190         struct samr_Password lm_verifier;
191
192         if (pwbuf == NULL) {
193                 return NT_STATUS_WRONG_PASSWORD;
194         }
195
196         /* To change a password we need to open as system */
197         sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
198         if (sam_ctx == NULL) {
199                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
200         }
201
202         ret = ldb_transaction_start(sam_ctx);
203         if (ret) {
204                 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
205                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
206         }
207
208         /* we need the users dn and the domain dn (derived from the
209            user SID). We also need the current lm password hash in
210            order to decrypt the incoming password */
211         ret = gendb_search(sam_ctx, 
212                            mem_ctx, NULL, &res, attrs,
213                            "(&(sAMAccountName=%s)(objectclass=user))",
214                            r->in.account->string);
215         if (ret != 1) {
216                 ldb_transaction_cancel(sam_ctx);
217                 /* Don't give the game away:  (don't allow anonymous users to prove the existance of usernames) */
218                 return NT_STATUS_WRONG_PASSWORD;
219         }
220
221         user_dn = res[0]->dn;
222
223         status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, NULL);
224         if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
225                 ldb_transaction_cancel(sam_ctx);
226                 return NT_STATUS_WRONG_PASSWORD;
227         }
228
229         /* decrypt the password we have been given */
230         lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash)); 
231         arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
232         data_blob_free(&lm_pwd_blob);
233         
234         if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
235                               &new_pass_len, STR_ASCII)) {
236                 ldb_transaction_cancel(sam_ctx);
237                 DEBUG(3,("samr: failed to decode password buffer\n"));
238                 return NT_STATUS_WRONG_PASSWORD;
239         }
240
241         /* check LM verifier */
242         if (lm_pwd == NULL || r->in.hash == NULL) {
243                 ldb_transaction_cancel(sam_ctx);
244                 return NT_STATUS_WRONG_PASSWORD;
245         }
246
247         E_deshash(new_pass, new_lm_hash);
248         E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
249         if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
250                 ldb_transaction_cancel(sam_ctx);
251                 return NT_STATUS_WRONG_PASSWORD;
252         }
253
254         mod = ldb_msg_new(mem_ctx);
255         if (mod == NULL) {
256                 ldb_transaction_cancel(sam_ctx);
257                 return NT_STATUS_NO_MEMORY;
258         }
259
260         mod->dn = ldb_dn_copy(mod, user_dn);
261         if (!mod->dn) {
262                 ldb_transaction_cancel(sam_ctx);
263                 return NT_STATUS_NO_MEMORY;
264         }
265
266         /* set the password on the user DN specified.  This may fail
267          * due to password policies */
268         status = samdb_set_password(sam_ctx, mem_ctx,
269                                     user_dn, NULL, 
270                                     mod, new_pass, 
271                                     NULL, NULL,
272                                     True, /* this is a user password change */
273                                     True, /* run restriction tests */
274                                     NULL, 
275                                     NULL);
276         if (!NT_STATUS_IS_OK(status)) {
277                 ldb_transaction_cancel(sam_ctx);
278                 return status;
279         }
280
281         /* The above call only setup the modifications, this actually
282          * makes the write to the database. */
283         ret = samdb_replace(sam_ctx, mem_ctx, mod);
284         if (ret != 0) {
285                 ldb_transaction_cancel(sam_ctx);
286                 return NT_STATUS_UNSUCCESSFUL;
287         }
288
289         /* And this confirms it in a transaction commit */
290         ret = ldb_transaction_commit(sam_ctx);
291         if (ret != 0) {
292                 DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
293                          ldb_dn_linearize(mem_ctx, user_dn),
294                          ldb_errstring(sam_ctx)));
295                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
296         }
297
298         return NT_STATUS_OK;
299 }
300
301
302 /* 
303   samr_ChangePasswordUser3 
304 */
305 NTSTATUS samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call, 
306                                   TALLOC_CTX *mem_ctx,
307                                   struct samr_ChangePasswordUser3 *r)
308 {       
309         NTSTATUS status;
310         char new_pass[512];
311         uint32_t new_pass_len;
312         struct ldb_context *sam_ctx = NULL;
313         const struct ldb_dn *user_dn;
314         int ret;
315         struct ldb_message **res, *mod;
316         const char * const attrs[] = { "ntPwdHash", "lmPwdHash", "unicodePwd", NULL };
317         struct samr_Password *nt_pwd, *lm_pwd;
318         DATA_BLOB nt_pwd_blob;
319         struct samr_DomInfo1 *dominfo = NULL;
320         struct samr_ChangeReject *reject = NULL;
321         enum samr_RejectReason reason = SAMR_REJECT_OTHER;
322         uint8_t new_nt_hash[16], new_lm_hash[16];
323         struct samr_Password nt_verifier, lm_verifier;
324
325         ZERO_STRUCT(r->out);
326
327         if (r->in.nt_password == NULL ||
328             r->in.nt_verifier == NULL) {
329                 return NT_STATUS_INVALID_PARAMETER;
330         }
331
332         /* To change a password we need to open as system */
333         sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
334         if (sam_ctx == NULL) {
335                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
336         }
337
338         ret = ldb_transaction_start(sam_ctx);
339         if (ret) {
340                 talloc_free(sam_ctx);
341                 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
342                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
343         }
344
345         /* we need the users dn and the domain dn (derived from the
346            user SID). We also need the current lm and nt password hashes
347            in order to decrypt the incoming passwords */
348         ret = gendb_search(sam_ctx, 
349                            mem_ctx, NULL, &res, attrs,
350                            "(&(sAMAccountName=%s)(objectclass=user))",
351                            r->in.account->string);
352         if (ret != 1) {
353                 /* Don't give the game away:  (don't allow anonymous users to prove the existance of usernames) */
354                 status = NT_STATUS_WRONG_PASSWORD;
355                 goto failed;
356         }
357
358         user_dn = res[0]->dn;
359
360         status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, &nt_pwd);
361         if (!NT_STATUS_IS_OK(status) ) {
362                 goto failed;
363         }
364
365         if (!nt_pwd) {
366                 status = NT_STATUS_WRONG_PASSWORD;
367                 goto failed;
368         }
369
370         /* decrypt the password we have been given */
371         nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
372         arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
373         data_blob_free(&nt_pwd_blob);
374
375         if (!decode_pw_buffer(r->in.nt_password->data, new_pass, sizeof(new_pass),
376                               &new_pass_len, STR_UNICODE)) {
377                 DEBUG(3,("samr: failed to decode password buffer\n"));
378                 status = NT_STATUS_WRONG_PASSWORD;
379                 goto failed;
380         }
381
382         if (r->in.nt_verifier == NULL) {
383                 status = NT_STATUS_WRONG_PASSWORD;
384                 goto failed;
385         }
386
387         /* check NT verifier */
388         E_md4hash(new_pass, new_nt_hash);
389         E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
390         if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
391                 status = NT_STATUS_WRONG_PASSWORD;
392                 goto failed;
393         }
394
395         /* check LM verifier */
396         if (lm_pwd && r->in.lm_verifier != NULL) {
397                 E_deshash(new_pass, new_lm_hash);
398                 E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
399                 if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
400                         status = NT_STATUS_WRONG_PASSWORD;
401                         goto failed;
402                 }
403         }
404
405
406         mod = ldb_msg_new(mem_ctx);
407         if (mod == NULL) {
408                 return NT_STATUS_NO_MEMORY;
409         }
410
411         mod->dn = ldb_dn_copy(mod, user_dn);
412         if (!mod->dn) {
413                 status = NT_STATUS_NO_MEMORY;
414                 goto failed;
415         }
416
417         /* set the password on the user DN specified.  This may fail
418          * due to password policies */
419         status = samdb_set_password(sam_ctx, mem_ctx,
420                                     user_dn, NULL, 
421                                     mod, new_pass, 
422                                     NULL, NULL,
423                                     True, /* this is a user password change */
424                                     True, /* run restriction tests */
425                                     &reason, 
426                                     &dominfo);
427         if (!NT_STATUS_IS_OK(status)) {
428                 goto failed;
429         }
430
431         /* The above call only setup the modifications, this actually
432          * makes the write to the database. */
433         ret = samdb_replace(sam_ctx, mem_ctx, mod);
434         if (ret != 0) {
435                 status = NT_STATUS_UNSUCCESSFUL;
436                 goto failed;
437         }
438
439         /* And this confirms it in a transaction commit */
440         ret = ldb_transaction_commit(sam_ctx);
441         if (ret != 0) {
442                 DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
443                          ldb_dn_linearize(mem_ctx, user_dn),
444                          ldb_errstring(sam_ctx)));
445                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
446                 goto failed;
447         }
448
449         return NT_STATUS_OK;
450
451 failed:
452         ldb_transaction_cancel(sam_ctx);
453         talloc_free(sam_ctx);
454
455         reject = talloc(mem_ctx, struct samr_ChangeReject);
456         r->out.dominfo = dominfo;
457         r->out.reject = reject;
458
459         if (reject == NULL) {
460                 return status;
461         }
462         ZERO_STRUCTP(reject);
463
464         reject->reason = reason;
465
466         return status;
467 }
468
469
470 /* 
471   samr_ChangePasswordUser2 
472
473   easy - just a subset of samr_ChangePasswordUser3
474 */
475 NTSTATUS samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
476                                   struct samr_ChangePasswordUser2 *r)
477 {
478         struct samr_ChangePasswordUser3 r2;
479
480         r2.in.server = r->in.server;
481         r2.in.account = r->in.account;
482         r2.in.nt_password = r->in.nt_password;
483         r2.in.nt_verifier = r->in.nt_verifier;
484         r2.in.lm_change = r->in.lm_change;
485         r2.in.lm_password = r->in.lm_password;
486         r2.in.lm_verifier = r->in.lm_verifier;
487         r2.in.password3 = NULL;
488
489         return samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
490 }
491
492
493 /*
494   check that a password is sufficiently complex
495 */
496 static BOOL samdb_password_complexity_ok(const char *pass)
497 {
498         return check_password_quality(pass);
499 }
500
501 /*
502   set the user password using plaintext, obeying any user or domain
503   password restrictions
504
505   note that this function doesn't actually store the result in the
506   database, it just fills in the "mod" structure with ldb modify
507   elements to setup the correct change when samdb_replace() is
508   called. This allows the caller to combine the change with other
509   changes (as is needed by some of the set user info levels)
510
511   The caller should probably have a transaction wrapping this
512 */
513 NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
514                             const struct ldb_dn *user_dn,
515                             const struct ldb_dn *domain_dn,
516                             struct ldb_message *mod,
517                             const char *new_pass,
518                             struct samr_Password *lmNewHash, 
519                             struct samr_Password *ntNewHash,
520                             BOOL user_change,
521                             BOOL restrictions,
522                             enum samr_RejectReason *reject_reason,
523                             struct samr_DomInfo1 **_dominfo)
524 {
525         const char * const user_attrs[] = { "userAccountControl", "lmPwdHistory", 
526                                             "ntPwdHistory", "unicodePwd", 
527                                             "lmPwdHash", "ntPwdHash", "badPwdCount", 
528                                             "objectSid", NULL };
529         const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength", 
530                                               "maxPwdAge", "minPwdAge", 
531                                               "minPwdLength", "pwdLastSet", NULL };
532         const char *unicodePwd;
533         NTTIME pwdLastSet;
534         int64_t minPwdAge;
535         uint_t minPwdLength, pwdProperties, pwdHistoryLength;
536         uint_t userAccountControl, badPwdCount;
537         struct samr_Password *lmPwdHistory, *ntPwdHistory, lmPwdHash, ntPwdHash;
538         struct samr_Password *new_lmPwdHistory, *new_ntPwdHistory;
539         struct samr_Password local_lmNewHash, local_ntNewHash;
540         int lmPwdHistory_len, ntPwdHistory_len;
541         uint_t kvno;
542         struct dom_sid *domain_sid;
543         struct ldb_message **res;
544         int count;
545         time_t now = time(NULL);
546         NTTIME now_nt;
547         int i;
548
549         /* we need to know the time to compute password age */
550         unix_to_nt_time(&now_nt, now);
551
552         /* pull all the user parameters */
553         count = gendb_search_dn(ctx, mem_ctx, user_dn, &res, user_attrs);
554         if (count != 1) {
555                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
556         }
557         unicodePwd =         samdb_result_string(res[0], "unicodePwd", NULL);
558         userAccountControl = samdb_result_uint(res[0],   "userAccountControl", 0);
559         badPwdCount =        samdb_result_uint(res[0],   "badPwdCount", 0);
560         lmPwdHistory_len =   samdb_result_hashes(mem_ctx, res[0], 
561                                                  "lmPwdHistory", &lmPwdHistory);
562         ntPwdHistory_len =   samdb_result_hashes(mem_ctx, res[0], 
563                                                  "ntPwdHistory", &ntPwdHistory);
564         lmPwdHash =          samdb_result_hash(res[0],   "lmPwdHash");
565         ntPwdHash =          samdb_result_hash(res[0],   "ntPwdHash");
566         pwdLastSet =         samdb_result_uint64(res[0], "pwdLastSet", 0);
567         kvno =               samdb_result_uint(res[0],   "msDS-KeyVersionNumber", 0);
568
569         if (domain_dn) {
570                 /* pull the domain parameters */
571                 count = gendb_search_dn(ctx, mem_ctx, domain_dn, &res, domain_attrs);
572                 if (count != 1) {
573                         return NT_STATUS_NO_SUCH_DOMAIN;
574                 }
575         } else {
576                 /* work out the domain sid, and pull the domain from there */
577                 domain_sid =         samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
578                 if (domain_sid == NULL) {
579                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
580                 }
581
582                 count = gendb_search(ctx, mem_ctx, NULL, &res, domain_attrs, 
583                                      "(objectSid=%s)", 
584                                      ldap_encode_ndr_dom_sid(mem_ctx, domain_sid));
585                 if (count != 1) {
586                         return NT_STATUS_NO_SUCH_DOMAIN;
587                 }
588         }
589
590         pwdProperties =    samdb_result_uint(res[0],   "pwdProperties", 0);
591         pwdHistoryLength = samdb_result_uint(res[0],   "pwdHistoryLength", 0);
592         minPwdLength =     samdb_result_uint(res[0],   "minPwdLength", 0);
593         minPwdAge =        samdb_result_int64(res[0],  "minPwdAge", 0);
594
595         if (_dominfo) {
596                 struct samr_DomInfo1 *dominfo;
597                 /* on failure we need to fill in the reject reasons */
598                 dominfo = talloc(mem_ctx, struct samr_DomInfo1);
599                 if (dominfo == NULL) {
600                         return NT_STATUS_NO_MEMORY;
601                 }
602                 dominfo->min_password_length     = minPwdLength;
603                 dominfo->password_properties     = pwdProperties;
604                 dominfo->password_history_length = pwdHistoryLength;
605                 dominfo->max_password_age        = minPwdAge;
606                 dominfo->min_password_age        = minPwdAge;
607                 *_dominfo = dominfo;
608         }
609
610         if (new_pass) {
611                 /* check the various password restrictions */
612                 if (restrictions && minPwdLength > strlen_m(new_pass)) {
613                         if (reject_reason) {
614                                 *reject_reason = SAMR_REJECT_TOO_SHORT;
615                         }
616                         return NT_STATUS_PASSWORD_RESTRICTION;
617                 }
618                 
619                 /* possibly check password complexity */
620                 if (restrictions && pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
621                     !samdb_password_complexity_ok(new_pass)) {
622                         if (reject_reason) {
623                                 *reject_reason = SAMR_REJECT_COMPLEXITY;
624                         }
625                         return NT_STATUS_PASSWORD_RESTRICTION;
626                 }
627                 
628                 /* compute the new nt and lm hashes */
629                 if (E_deshash(new_pass, local_lmNewHash.hash)) {
630                         lmNewHash = &local_lmNewHash;
631                 }
632                 E_md4hash(new_pass, local_ntNewHash.hash);
633                 ntNewHash = &local_ntNewHash;
634         }
635
636         if (restrictions && user_change) {
637                 /* are all password changes disallowed? */
638                 if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
639                         if (reject_reason) {
640                                 *reject_reason = SAMR_REJECT_OTHER;
641                         }
642                         return NT_STATUS_PASSWORD_RESTRICTION;
643                 }
644                 
645                 /* can this user change password? */
646                 if (userAccountControl & UF_PASSWD_CANT_CHANGE) {
647                         if (reject_reason) {
648                                 *reject_reason = SAMR_REJECT_OTHER;
649                         }
650                         return NT_STATUS_PASSWORD_RESTRICTION;
651                 }
652                 
653                 /* yes, this is a minus. The ages are in negative 100nsec units! */
654                 if (pwdLastSet - minPwdAge > now_nt) {
655                         if (reject_reason) {
656                                 *reject_reason = SAMR_REJECT_OTHER;
657                         }
658                         return NT_STATUS_PASSWORD_RESTRICTION;
659                 }
660
661                 /* check the immediately past password */
662                 if (pwdHistoryLength > 0) {
663                         if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
664                                 if (reject_reason) {
665                                         *reject_reason = SAMR_REJECT_COMPLEXITY;
666                                 }
667                                 return NT_STATUS_PASSWORD_RESTRICTION;
668                         }
669                         if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
670                                 if (reject_reason) {
671                                         *reject_reason = SAMR_REJECT_COMPLEXITY;
672                                 }
673                                 return NT_STATUS_PASSWORD_RESTRICTION;
674                         }
675                 }
676                 
677                 /* check the password history */
678                 lmPwdHistory_len = MIN(lmPwdHistory_len, pwdHistoryLength);
679                 ntPwdHistory_len = MIN(ntPwdHistory_len, pwdHistoryLength);
680                 
681                 if (pwdHistoryLength > 0) {
682                         if (unicodePwd && new_pass && strcmp(unicodePwd, new_pass) == 0) {
683                                 if (reject_reason) {
684                                         *reject_reason = SAMR_REJECT_COMPLEXITY;
685                                 }
686                                 return NT_STATUS_PASSWORD_RESTRICTION;
687                         }
688                         if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
689                                 if (reject_reason) {
690                                         *reject_reason = SAMR_REJECT_COMPLEXITY;
691                                 }
692                                 return NT_STATUS_PASSWORD_RESTRICTION;
693                         }
694                         if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
695                                 if (reject_reason) {
696                                         *reject_reason = SAMR_REJECT_COMPLEXITY;
697                                 }
698                                 return NT_STATUS_PASSWORD_RESTRICTION;
699                         }
700                 }
701                 
702                 for (i=0; lmNewHash && i<lmPwdHistory_len;i++) {
703                         if (memcmp(lmNewHash->hash, lmPwdHistory[i].hash, 16) == 0) {
704                                 if (reject_reason) {
705                                         *reject_reason = SAMR_REJECT_COMPLEXITY;
706                                 }
707                                 return NT_STATUS_PASSWORD_RESTRICTION;
708                         }
709                 }
710                 for (i=0; ntNewHash && i<ntPwdHistory_len;i++) {
711                         if (memcmp(ntNewHash->hash, ntPwdHistory[i].hash, 16) == 0) {
712                                 if (reject_reason) {
713                                         *reject_reason = SAMR_REJECT_COMPLEXITY;
714                                 }
715                                 return NT_STATUS_PASSWORD_RESTRICTION;
716                         }
717                 }
718         }
719
720 #define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0)
721
722         /* the password is acceptable. Start forming the new fields */
723         if (lmNewHash) {
724                 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "lmPwdHash", lmNewHash));
725         } else {
726                 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHash"));
727         }
728
729         if (ntNewHash) {
730                 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "ntPwdHash", ntNewHash));
731         } else {
732                 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHash"));
733         }
734
735         if (new_pass && (pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT) &&
736             (userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
737                 CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod, 
738                                                "unicodePwd", new_pass));
739         } else {
740                 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "unicodePwd"));
741         }
742
743         CHECK_RET(samdb_msg_add_uint64(ctx, mem_ctx, mod, "pwdLastSet", now_nt));
744
745         CHECK_RET(samdb_msg_add_uint(ctx, mem_ctx, mod, "msDS-KeyVersionNumber", kvno + 1));
746         
747         if (pwdHistoryLength == 0) {
748                 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHistory"));
749                 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHistory"));
750                 return NT_STATUS_OK;
751         }
752         
753         /* store the password history */
754         new_lmPwdHistory = talloc_array(mem_ctx, struct samr_Password, 
755                                           pwdHistoryLength);
756         if (!new_lmPwdHistory) {
757                 return NT_STATUS_NO_MEMORY;
758         }
759         new_ntPwdHistory = talloc_array(mem_ctx, struct samr_Password, 
760                                           pwdHistoryLength);
761         if (!new_ntPwdHistory) {
762                 return NT_STATUS_NO_MEMORY;
763         }
764         for (i=0;i<MIN(pwdHistoryLength-1, lmPwdHistory_len);i++) {
765                 new_lmPwdHistory[i+1] = lmPwdHistory[i];
766         }
767         for (i=0;i<MIN(pwdHistoryLength-1, ntPwdHistory_len);i++) {
768                 new_ntPwdHistory[i+1] = ntPwdHistory[i];
769         }
770
771         /* Don't store 'long' passwords in the LM history, 
772            but make sure to 'expire' one password off the other end */
773         if (lmNewHash) {
774                 new_lmPwdHistory[0] = *lmNewHash;
775         } else {
776                 ZERO_STRUCT(new_lmPwdHistory[0]);
777         }
778         lmPwdHistory_len = MIN(lmPwdHistory_len + 1, pwdHistoryLength);
779
780         if (ntNewHash) {
781                 new_ntPwdHistory[0] = *ntNewHash;
782         } else {
783                 ZERO_STRUCT(new_ntPwdHistory[0]);
784         }
785         ntPwdHistory_len = MIN(ntPwdHistory_len + 1, pwdHistoryLength);
786         
787         CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod, 
788                                        "lmPwdHistory", 
789                                        new_lmPwdHistory, 
790                                        lmPwdHistory_len));
791
792         CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod, 
793                                        "ntPwdHistory", 
794                                        new_ntPwdHistory, 
795                                        ntPwdHistory_len));
796         return NT_STATUS_OK;
797 }
798
799 /*
800   set password via a samr_CryptPassword buffer
801   this will in the 'msg' with modify operations that will update the user
802   password when applied
803 */
804 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
805                            void *sam_ctx,
806                            const struct ldb_dn *account_dn, const struct ldb_dn *domain_dn,
807                            TALLOC_CTX *mem_ctx,
808                            struct ldb_message *msg, 
809                            struct samr_CryptPassword *pwbuf)
810 {
811         NTSTATUS nt_status;
812         char new_pass[512];
813         uint32_t new_pass_len;
814         DATA_BLOB session_key = data_blob(NULL, 0);
815
816         nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
817         if (!NT_STATUS_IS_OK(nt_status)) {
818                 return nt_status;
819         }
820
821         arcfour_crypt_blob(pwbuf->data, 516, &session_key);
822
823         if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
824                               &new_pass_len, STR_UNICODE)) {
825                 DEBUG(3,("samr: failed to decode password buffer\n"));
826                 return NT_STATUS_WRONG_PASSWORD;
827         }
828
829         /* set the password - samdb needs to know both the domain and user DNs,
830            so the domain password policy can be used */
831         return samdb_set_password(sam_ctx, mem_ctx,
832                                   account_dn, domain_dn, 
833                                   msg, new_pass, 
834                                   NULL, NULL,
835                                   False, /* This is a password set, not change */
836                                   True, /* run restriction tests */
837                                   NULL, NULL);
838 }
839
840
841 /*
842   set password via a samr_CryptPasswordEx buffer
843   this will in the 'msg' with modify operations that will update the user
844   password when applied
845 */
846 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
847                               struct ldb_context *sam_ctx,
848                               const struct ldb_dn *account_dn, const struct ldb_dn *domain_dn,
849                               TALLOC_CTX *mem_ctx,
850                               struct ldb_message *msg, 
851                               struct samr_CryptPasswordEx *pwbuf)
852 {
853         NTSTATUS nt_status;
854         char new_pass[512];
855         uint32_t new_pass_len;
856         DATA_BLOB co_session_key;
857         DATA_BLOB session_key = data_blob(NULL, 0);
858         struct MD5Context ctx;
859
860         nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
861         if (!NT_STATUS_IS_OK(nt_status)) {
862                 return nt_status;
863         }
864
865         co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
866         if (!co_session_key.data) {
867                 return NT_STATUS_NO_MEMORY;
868         }
869
870         MD5Init(&ctx);
871         MD5Update(&ctx, &pwbuf->data[516], 16);
872         MD5Update(&ctx, session_key.data, session_key.length);
873         MD5Final(co_session_key.data, &ctx);
874         
875         arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
876
877         if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
878                               &new_pass_len, STR_UNICODE)) {
879                 DEBUG(3,("samr: failed to decode password buffer\n"));
880                 return NT_STATUS_WRONG_PASSWORD;
881         }
882
883         /* set the password - samdb needs to know both the domain and user DNs,
884            so the domain password policy can be used */
885         return samdb_set_password(sam_ctx, mem_ctx,
886                                   account_dn, domain_dn, 
887                                   msg, new_pass, 
888                                   NULL, NULL,
889                                   False, /* This is a password set, not change */
890                                   True, /* run restriction tests */
891                                   NULL, NULL);
892 }
893
894 /*
895   set the user password using plaintext, obeying any user or domain
896   password restrictions
897
898   This wrapper function takes a SID as input, rather than a user DN,
899   and actually performs the password change
900
901 */
902 NTSTATUS samdb_set_password_sid(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
903                                 const struct dom_sid *user_sid,
904                                 const char *new_pass,
905                                 struct samr_Password *lmNewHash, 
906                                 struct samr_Password *ntNewHash,
907                                 BOOL user_change,
908                                 BOOL restrictions,
909                                 enum samr_RejectReason *reject_reason,
910                                 struct samr_DomInfo1 **_dominfo) 
911 {
912         NTSTATUS nt_status;
913         struct ldb_dn *user_dn;
914         struct ldb_message *msg;
915         int ret;
916
917         ret = ldb_transaction_start(ctx);
918         if (ret) {
919                 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(ctx)));
920                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
921         }
922
923         user_dn = samdb_search_dn(ctx, mem_ctx, NULL, 
924                                   "(&(objectSid=%s)(objectClass=user))", 
925                                   ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
926         if (!user_dn) {
927                 ldb_transaction_cancel(ctx);
928                 DEBUG(3, ("samdb_set_password_sid: SID %s not found in samdb, returning NO_SUCH_USER\n",
929                           dom_sid_string(mem_ctx, user_sid)));
930                 return NT_STATUS_NO_SUCH_USER;
931         }
932
933         msg = ldb_msg_new(mem_ctx);
934         if (msg == NULL) {
935                 ldb_transaction_cancel(ctx);
936                 return NT_STATUS_NO_MEMORY;
937         }
938
939         msg->dn = ldb_dn_copy(msg, user_dn);
940         if (!msg->dn) {
941                 ldb_transaction_cancel(ctx);
942                 return NT_STATUS_NO_MEMORY;
943         }
944
945         nt_status = samdb_set_password(ctx, mem_ctx,
946                                        user_dn, NULL,
947                                        msg, new_pass, 
948                                        lmNewHash, ntNewHash,
949                                        user_change, /* This is a password set, not change */
950                                        restrictions, /* run restriction tests */
951                                        reject_reason, _dominfo);
952         if (!NT_STATUS_IS_OK(nt_status)) {
953                 ldb_transaction_cancel(ctx);
954                 return nt_status;
955         }
956         
957         /* modify the samdb record */
958         ret = samdb_replace(ctx, mem_ctx, msg);
959         if (ret != 0) {
960                 ldb_transaction_cancel(ctx);
961                 return NT_STATUS_ACCESS_DENIED;
962         }
963
964         ret = ldb_transaction_commit(ctx);
965         if (ret != 0) {
966                 DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
967                          ldb_dn_linearize(mem_ctx, msg->dn),
968                          ldb_errstring(ctx)));
969                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
970         }
971         return NT_STATUS_OK;
972 }