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