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