5845c7ad1bd496dc70014b4b1acbf79d16743cf0
[ira/wip.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    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_INTERNAL_DB_CORRUPTION;
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(1,("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_INTERNAL_DB_CORRUPTION;
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_INTERNAL_DB_CORRUPTION;
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(1,("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(0,("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_INTERNAL_DB_CORRUPTION;
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_INTERNAL_DB_CORRUPTION;
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                 status = NT_STATUS_UNSUCCESSFUL;
443                 goto failed;
444         }
445
446         /* And this confirms it in a transaction commit */
447         ret = ldb_transaction_commit(sam_ctx);
448         if (ret != 0) {
449                 DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
450                          ldb_dn_linearize(mem_ctx, user_dn),
451                          ldb_errstring(sam_ctx)));
452                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
453                 goto failed;
454         }
455
456         return NT_STATUS_OK;
457
458 failed:
459         ldb_transaction_cancel(sam_ctx);
460         talloc_free(sam_ctx);
461
462         reject = talloc(mem_ctx, struct samr_ChangeReject);
463         r->out.dominfo = dominfo;
464         r->out.reject = reject;
465
466         if (reject == NULL) {
467                 return status;
468         }
469         ZERO_STRUCTP(reject);
470
471         reject->reason = reason;
472
473         return status;
474 }
475
476
477 /* 
478   samr_ChangePasswordUser2 
479
480   easy - just a subset of samr_ChangePasswordUser3
481 */
482 NTSTATUS samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
483                                   struct samr_ChangePasswordUser2 *r)
484 {
485         struct samr_ChangePasswordUser3 r2;
486
487         r2.in.server = r->in.server;
488         r2.in.account = r->in.account;
489         r2.in.nt_password = r->in.nt_password;
490         r2.in.nt_verifier = r->in.nt_verifier;
491         r2.in.lm_change = r->in.lm_change;
492         r2.in.lm_password = r->in.lm_password;
493         r2.in.lm_verifier = r->in.lm_verifier;
494         r2.in.password3 = NULL;
495
496         return samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
497 }
498
499
500 /*
501   check that a password is sufficiently complex
502 */
503 static BOOL samdb_password_complexity_ok(const char *pass)
504 {
505         return check_password_quality(pass);
506 }
507
508 /*
509   set the user password using plaintext, obeying any user or domain
510   password restrictions
511
512   note that this function doesn't actually store the result in the
513   database, it just fills in the "mod" structure with ldb modify
514   elements to setup the correct change when samdb_replace() is
515   called. This allows the caller to combine the change with other
516   changes (as is needed by some of the set user info levels)
517
518   The caller should probably have a transaction wrapping this
519 */
520 NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
521                             const struct ldb_dn *user_dn,
522                             const struct ldb_dn *domain_dn,
523                             struct ldb_message *mod,
524                             const char *new_pass,
525                             struct samr_Password *lmNewHash, 
526                             struct samr_Password *ntNewHash,
527                             BOOL user_change,
528                             BOOL restrictions,
529                             enum samr_RejectReason *reject_reason,
530                             struct samr_DomInfo1 **_dominfo)
531 {
532         const char * const user_attrs[] = { "userAccountControl", "lmPwdHistory", 
533                                             "ntPwdHistory", 
534                                             "lmPwdHash", "ntPwdHash", 
535                                             "objectSid", 
536                                             "pwdLastSet", NULL };
537         const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength", 
538                                               "maxPwdAge", "minPwdAge", 
539                                               "minPwdLength", NULL };
540         NTTIME pwdLastSet;
541         int64_t minPwdAge;
542         uint_t minPwdLength, pwdProperties, pwdHistoryLength;
543         uint_t userAccountControl;
544         struct samr_Password *lmPwdHistory, *ntPwdHistory, *lmPwdHash, *ntPwdHash;
545         struct samr_Password local_lmNewHash, local_ntNewHash;
546         int lmPwdHistory_len, ntPwdHistory_len;
547         struct dom_sid *domain_sid;
548         struct ldb_message **res;
549         int count;
550         time_t now = time(NULL);
551         NTTIME now_nt;
552         int i;
553
554         /* we need to know the time to compute password age */
555         unix_to_nt_time(&now_nt, now);
556
557         /* pull all the user parameters */
558         count = gendb_search_dn(ctx, mem_ctx, user_dn, &res, user_attrs);
559         if (count != 1) {
560                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
561         }
562         userAccountControl = samdb_result_uint(res[0],   "userAccountControl", 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(mem_ctx, res[0],   "lmPwdHash");
568         ntPwdHash =          samdb_result_hash(mem_ctx, res[0],   "ntPwdHash");
569         pwdLastSet =         samdb_result_uint64(res[0], "pwdLastSet", 0);
570
571         if (domain_dn) {
572                 /* pull the domain parameters */
573                 count = gendb_search_dn(ctx, mem_ctx, domain_dn, &res, domain_attrs);
574                 if (count != 1) {
575                         return NT_STATUS_NO_SUCH_DOMAIN;
576                 }
577         } else {
578                 /* work out the domain sid, and pull the domain from there */
579                 domain_sid =         samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
580                 if (domain_sid == NULL) {
581                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
582                 }
583
584                 count = gendb_search(ctx, mem_ctx, NULL, &res, domain_attrs, 
585                                      "(objectSid=%s)", 
586                                      ldap_encode_ndr_dom_sid(mem_ctx, domain_sid));
587                 if (count != 1) {
588                         return NT_STATUS_NO_SUCH_DOMAIN;
589                 }
590         }
591
592         pwdProperties =    samdb_result_uint(res[0],   "pwdProperties", 0);
593         pwdHistoryLength = samdb_result_uint(res[0],   "pwdHistoryLength", 0);
594         minPwdLength =     samdb_result_uint(res[0],   "minPwdLength", 0);
595         minPwdAge =        samdb_result_int64(res[0],  "minPwdAge", 0);
596
597         if (_dominfo) {
598                 struct samr_DomInfo1 *dominfo;
599                 /* on failure we need to fill in the reject reasons */
600                 dominfo = talloc(mem_ctx, struct samr_DomInfo1);
601                 if (dominfo == NULL) {
602                         return NT_STATUS_NO_MEMORY;
603                 }
604                 dominfo->min_password_length     = minPwdLength;
605                 dominfo->password_properties     = pwdProperties;
606                 dominfo->password_history_length = pwdHistoryLength;
607                 dominfo->max_password_age        = minPwdAge;
608                 dominfo->min_password_age        = minPwdAge;
609                 *_dominfo = dominfo;
610         }
611
612         if (new_pass) {
613                 /* check the various password restrictions */
614                 if (restrictions && minPwdLength > strlen_m(new_pass)) {
615                         if (reject_reason) {
616                                 *reject_reason = SAMR_REJECT_TOO_SHORT;
617                         }
618                         return NT_STATUS_PASSWORD_RESTRICTION;
619                 }
620                 
621                 /* possibly check password complexity */
622                 if (restrictions && pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
623                     !samdb_password_complexity_ok(new_pass)) {
624                         if (reject_reason) {
625                                 *reject_reason = SAMR_REJECT_COMPLEXITY;
626                         }
627                         return NT_STATUS_PASSWORD_RESTRICTION;
628                 }
629                 
630                 /* compute the new nt and lm hashes */
631                 if (E_deshash(new_pass, local_lmNewHash.hash)) {
632                         lmNewHash = &local_lmNewHash;
633                 }
634                 E_md4hash(new_pass, local_ntNewHash.hash);
635                 ntNewHash = &local_ntNewHash;
636         }
637
638         if (restrictions && user_change) {
639                 /* are all password changes disallowed? */
640                 if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
641                         if (reject_reason) {
642                                 *reject_reason = SAMR_REJECT_OTHER;
643                         }
644                         return NT_STATUS_PASSWORD_RESTRICTION;
645                 }
646                 
647                 /* can this user change password? */
648                 if (userAccountControl & UF_PASSWD_CANT_CHANGE) {
649                         if (reject_reason) {
650                                 *reject_reason = SAMR_REJECT_OTHER;
651                         }
652                         return NT_STATUS_PASSWORD_RESTRICTION;
653                 }
654                 
655                 /* yes, this is a minus. The ages are in negative 100nsec units! */
656                 if (pwdLastSet - minPwdAge > now_nt) {
657                         if (reject_reason) {
658                                 *reject_reason = SAMR_REJECT_OTHER;
659                         }
660                         return NT_STATUS_PASSWORD_RESTRICTION;
661                 }
662
663                 /* check the immediately past password */
664                 if (pwdHistoryLength > 0) {
665                         if (lmNewHash && lmPwdHash && memcmp(lmNewHash->hash, lmPwdHash->hash, 16) == 0) {
666                                 if (reject_reason) {
667                                         *reject_reason = SAMR_REJECT_COMPLEXITY;
668                                 }
669                                 return NT_STATUS_PASSWORD_RESTRICTION;
670                         }
671                         if (ntNewHash && ntPwdHash && memcmp(ntNewHash->hash, ntPwdHash->hash, 16) == 0) {
672                                 if (reject_reason) {
673                                         *reject_reason = SAMR_REJECT_COMPLEXITY;
674                                 }
675                                 return NT_STATUS_PASSWORD_RESTRICTION;
676                         }
677                 }
678                 
679                 /* check the password history */
680                 lmPwdHistory_len = MIN(lmPwdHistory_len, pwdHistoryLength);
681                 ntPwdHistory_len = MIN(ntPwdHistory_len, pwdHistoryLength);
682                 
683                 for (i=0; lmNewHash && i<lmPwdHistory_len;i++) {
684                         if (memcmp(lmNewHash->hash, lmPwdHistory[i].hash, 16) == 0) {
685                                 if (reject_reason) {
686                                         *reject_reason = SAMR_REJECT_COMPLEXITY;
687                                 }
688                                 return NT_STATUS_PASSWORD_RESTRICTION;
689                         }
690                 }
691                 for (i=0; ntNewHash && i<ntPwdHistory_len;i++) {
692                         if (memcmp(ntNewHash->hash, ntPwdHistory[i].hash, 16) == 0) {
693                                 if (reject_reason) {
694                                         *reject_reason = SAMR_REJECT_COMPLEXITY;
695                                 }
696                                 return NT_STATUS_PASSWORD_RESTRICTION;
697                         }
698                 }
699         }
700
701 #define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0)
702
703         /* the password is acceptable. Start forming the new fields */
704         if (new_pass) {
705                 /* if we know the cleartext, then only set it.
706                  * Modules in ldb will set all the appropriate
707                  * hashes */
708                 CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod, 
709                                                "unicodePwd", new_pass));
710         } else {
711                 /* We don't have the cleartext, so delete the old one
712                  * and set what we have of the hashes */
713                 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "unicodePwd"));
714
715                 if (lmNewHash) {
716                         CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "lmPwdHash", lmNewHash));
717                 } else {
718                         CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHash"));
719                 }
720                 
721                 if (ntNewHash) {
722                         CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "ntPwdHash", ntNewHash));
723                 } else {
724                         CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHash"));
725                 }
726         }
727
728         return NT_STATUS_OK;
729 }
730
731 /*
732   set password via a samr_CryptPassword buffer
733   this will in the 'msg' with modify operations that will update the user
734   password when applied
735 */
736 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
737                            void *sam_ctx,
738                            const struct ldb_dn *account_dn, const struct ldb_dn *domain_dn,
739                            TALLOC_CTX *mem_ctx,
740                            struct ldb_message *msg, 
741                            struct samr_CryptPassword *pwbuf)
742 {
743         NTSTATUS nt_status;
744         char new_pass[512];
745         uint32_t new_pass_len;
746         DATA_BLOB session_key = data_blob(NULL, 0);
747
748         nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
749         if (!NT_STATUS_IS_OK(nt_status)) {
750                 return nt_status;
751         }
752
753         arcfour_crypt_blob(pwbuf->data, 516, &session_key);
754
755         if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
756                               &new_pass_len, STR_UNICODE)) {
757                 DEBUG(3,("samr: failed to decode password buffer\n"));
758                 return NT_STATUS_WRONG_PASSWORD;
759         }
760
761         /* set the password - samdb needs to know both the domain and user DNs,
762            so the domain password policy can be used */
763         return samdb_set_password(sam_ctx, mem_ctx,
764                                   account_dn, domain_dn, 
765                                   msg, new_pass, 
766                                   NULL, NULL,
767                                   False, /* This is a password set, not change */
768                                   True, /* run restriction tests */
769                                   NULL, NULL);
770 }
771
772
773 /*
774   set password via a samr_CryptPasswordEx buffer
775   this will in the 'msg' with modify operations that will update the user
776   password when applied
777 */
778 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
779                               struct ldb_context *sam_ctx,
780                               const struct ldb_dn *account_dn, const struct ldb_dn *domain_dn,
781                               TALLOC_CTX *mem_ctx,
782                               struct ldb_message *msg, 
783                               struct samr_CryptPasswordEx *pwbuf)
784 {
785         NTSTATUS nt_status;
786         char new_pass[512];
787         uint32_t new_pass_len;
788         DATA_BLOB co_session_key;
789         DATA_BLOB session_key = data_blob(NULL, 0);
790         struct MD5Context ctx;
791
792         nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
793         if (!NT_STATUS_IS_OK(nt_status)) {
794                 return nt_status;
795         }
796
797         co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
798         if (!co_session_key.data) {
799                 return NT_STATUS_NO_MEMORY;
800         }
801
802         MD5Init(&ctx);
803         MD5Update(&ctx, &pwbuf->data[516], 16);
804         MD5Update(&ctx, session_key.data, session_key.length);
805         MD5Final(co_session_key.data, &ctx);
806         
807         arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
808
809         if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
810                               &new_pass_len, STR_UNICODE)) {
811                 DEBUG(3,("samr: failed to decode password buffer\n"));
812                 return NT_STATUS_WRONG_PASSWORD;
813         }
814
815         /* set the password - samdb needs to know both the domain and user DNs,
816            so the domain password policy can be used */
817         return samdb_set_password(sam_ctx, mem_ctx,
818                                   account_dn, domain_dn, 
819                                   msg, new_pass, 
820                                   NULL, NULL,
821                                   False, /* This is a password set, not change */
822                                   True, /* run restriction tests */
823                                   NULL, NULL);
824 }
825
826 /*
827   set the user password using plaintext, obeying any user or domain
828   password restrictions
829
830   This wrapper function takes a SID as input, rather than a user DN,
831   and actually performs the password change
832
833 */
834 NTSTATUS samdb_set_password_sid(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
835                                 const struct dom_sid *user_sid,
836                                 const char *new_pass,
837                                 struct samr_Password *lmNewHash, 
838                                 struct samr_Password *ntNewHash,
839                                 BOOL user_change,
840                                 BOOL restrictions,
841                                 enum samr_RejectReason *reject_reason,
842                                 struct samr_DomInfo1 **_dominfo) 
843 {
844         NTSTATUS nt_status;
845         struct ldb_dn *user_dn;
846         struct ldb_message *msg;
847         int ret;
848
849         ret = ldb_transaction_start(ctx);
850         if (ret) {
851                 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(ctx)));
852                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
853         }
854
855         user_dn = samdb_search_dn(ctx, mem_ctx, NULL, 
856                                   "(&(objectSid=%s)(objectClass=user))", 
857                                   ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
858         if (!user_dn) {
859                 ldb_transaction_cancel(ctx);
860                 DEBUG(3, ("samdb_set_password_sid: SID %s not found in samdb, returning NO_SUCH_USER\n",
861                           dom_sid_string(mem_ctx, user_sid)));
862                 return NT_STATUS_NO_SUCH_USER;
863         }
864
865         msg = ldb_msg_new(mem_ctx);
866         if (msg == NULL) {
867                 ldb_transaction_cancel(ctx);
868                 return NT_STATUS_NO_MEMORY;
869         }
870
871         msg->dn = ldb_dn_copy(msg, user_dn);
872         if (!msg->dn) {
873                 ldb_transaction_cancel(ctx);
874                 return NT_STATUS_NO_MEMORY;
875         }
876
877         nt_status = samdb_set_password(ctx, mem_ctx,
878                                        user_dn, NULL,
879                                        msg, new_pass, 
880                                        lmNewHash, ntNewHash,
881                                        user_change, /* This is a password set, not change */
882                                        restrictions, /* run restriction tests */
883                                        reject_reason, _dominfo);
884         if (!NT_STATUS_IS_OK(nt_status)) {
885                 ldb_transaction_cancel(ctx);
886                 return nt_status;
887         }
888         
889         /* modify the samdb record */
890         ret = samdb_replace(ctx, mem_ctx, msg);
891         if (ret != 0) {
892                 ldb_transaction_cancel(ctx);
893                 return NT_STATUS_ACCESS_DENIED;
894         }
895
896         ret = ldb_transaction_commit(ctx);
897         if (ret != 0) {
898                 DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
899                          ldb_dn_linearize(mem_ctx, msg->dn),
900                          ldb_errstring(ctx)));
901                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
902         }
903         return NT_STATUS_OK;
904 }