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