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