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