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