r19832: better prototypes for the linearization functions:
[kamenim/samba.git] / source4 / dsdb / samdb / samdb.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    interface functions for the sam database
5
6    Copyright (C) Andrew Tridgell 2004
7    Copyright (C) Volker Lendecke 2004
8    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006
9
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 #include "includes.h"
26 #include "librpc/gen_ndr/ndr_netlogon.h"
27 #include "librpc/gen_ndr/ndr_misc.h"
28 #include "librpc/gen_ndr/ndr_security.h"
29 #include "lib/ldb/include/ldb.h"
30 #include "lib/ldb/include/ldb_errors.h"
31 #include "libcli/security/security.h"
32 #include "libcli/auth/libcli_auth.h"
33 #include "libcli/ldap/ldap.h"
34 #include "system/time.h"
35 #include "system/filesys.h"
36 #include "db_wrap.h"
37 #include "dsdb/samdb/samdb.h"
38 #include "dsdb/common/flags.h"
39
40 /*
41   connect to the SAM database
42   return an opaque context pointer on success, or NULL on failure
43  */
44 struct ldb_context *samdb_connect(TALLOC_CTX *mem_ctx, 
45                                   struct auth_session_info *session_info)
46 {
47         struct ldb_context *ldb;
48         ldb = ldb_wrap_connect(mem_ctx, lp_sam_url(), session_info,
49                                NULL, 0, NULL);
50         if (!ldb) {
51                 return NULL;
52         }
53         return ldb;
54 }
55
56 /*
57   search the sam for the specified attributes in a specific domain, filter on
58   objectSid being in domain_sid.
59 */
60 int samdb_search_domain(struct ldb_context *sam_ldb,
61                         TALLOC_CTX *mem_ctx, 
62                         struct ldb_dn *basedn,
63                         struct ldb_message ***res,
64                         const char * const *attrs,
65                         const struct dom_sid *domain_sid,
66                         const char *format, ...)  _PRINTF_ATTRIBUTE(7,8)
67 {
68         va_list ap;
69         int i, count;
70
71         va_start(ap, format);
72         count = gendb_search_v(sam_ldb, mem_ctx, basedn,
73                                res, attrs, format, ap);
74         va_end(ap);
75
76         i=0;
77
78         while (i<count) {
79                 struct dom_sid *entry_sid;
80
81                 entry_sid = samdb_result_dom_sid(mem_ctx, (*res)[i], "objectSid");
82
83                 if ((entry_sid == NULL) ||
84                     (!dom_sid_in_domain(domain_sid, entry_sid))) {
85                         /* Delete that entry from the result set */
86                         (*res)[i] = (*res)[count-1];
87                         count -= 1;
88                         talloc_free(entry_sid);
89                         continue;
90                 }
91                 talloc_free(entry_sid);
92                 i += 1;
93         }
94
95         return count;
96 }
97
98 /*
99   search the sam for a single string attribute in exactly 1 record
100 */
101 const char *samdb_search_string_v(struct ldb_context *sam_ldb,
102                                   TALLOC_CTX *mem_ctx,
103                                   struct ldb_dn *basedn,
104                                   const char *attr_name,
105                                   const char *format, va_list ap) _PRINTF_ATTRIBUTE(5,0)
106 {
107         int count;
108         const char *attrs[2] = { NULL, NULL };
109         struct ldb_message **res = NULL;
110
111         attrs[0] = attr_name;
112
113         count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
114         if (count > 1) {                
115                 DEBUG(1,("samdb: search for %s %s not single valued (count=%d)\n", 
116                          attr_name, format, count));
117         }
118         if (count != 1) {
119                 talloc_free(res);
120                 return NULL;
121         }
122
123         return samdb_result_string(res[0], attr_name, NULL);
124 }
125                                  
126
127 /*
128   search the sam for a single string attribute in exactly 1 record
129 */
130 const char *samdb_search_string(struct ldb_context *sam_ldb,
131                                 TALLOC_CTX *mem_ctx,
132                                 struct ldb_dn *basedn,
133                                 const char *attr_name,
134                                 const char *format, ...) _PRINTF_ATTRIBUTE(5,6)
135 {
136         va_list ap;
137         const char *str;
138
139         va_start(ap, format);
140         str = samdb_search_string_v(sam_ldb, mem_ctx, basedn, attr_name, format, ap);
141         va_end(ap);
142
143         return str;
144 }
145
146 struct ldb_dn *samdb_search_dn(struct ldb_context *sam_ldb,
147                                TALLOC_CTX *mem_ctx,
148                                struct ldb_dn *basedn,
149                                const char *format, ...) _PRINTF_ATTRIBUTE(4,5)
150 {
151         va_list ap;
152         struct ldb_dn *ret;
153         struct ldb_message **res = NULL;
154         int count;
155
156         va_start(ap, format);
157         count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, NULL, format, ap);
158         va_end(ap);
159
160         if (count != 1) return NULL;
161
162         ret = talloc_steal(mem_ctx, res[0]->dn);
163         talloc_free(res);
164
165         return ret;
166 }
167
168 /*
169   search the sam for a dom_sid attribute in exactly 1 record
170 */
171 struct dom_sid *samdb_search_dom_sid(struct ldb_context *sam_ldb,
172                                      TALLOC_CTX *mem_ctx,
173                                      struct ldb_dn *basedn,
174                                      const char *attr_name,
175                                      const char *format, ...) _PRINTF_ATTRIBUTE(5,6)
176 {
177         va_list ap;
178         int count;
179         struct ldb_message **res;
180         const char *attrs[2] = { NULL, NULL };
181         struct dom_sid *sid;
182
183         attrs[0] = attr_name;
184
185         va_start(ap, format);
186         count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
187         va_end(ap);
188         if (count > 1) {                
189                 DEBUG(1,("samdb: search for %s %s not single valued (count=%d)\n", 
190                          attr_name, format, count));
191         }
192         if (count != 1) {
193                 talloc_free(res);
194                 return NULL;
195         }
196         sid = samdb_result_dom_sid(mem_ctx, res[0], attr_name);
197         talloc_free(res);
198         return sid;     
199 }
200
201 /*
202   return the count of the number of records in the sam matching the query
203 */
204 int samdb_search_count(struct ldb_context *sam_ldb,
205                        TALLOC_CTX *mem_ctx,
206                        struct ldb_dn *basedn,
207                        const char *format, ...) _PRINTF_ATTRIBUTE(4,5)
208 {
209         va_list ap;
210         struct ldb_message **res;
211         const char * const attrs[] = { NULL };
212         int ret;
213
214         va_start(ap, format);
215         ret = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
216         va_end(ap);
217
218         return ret;
219 }
220
221
222 /*
223   search the sam for a single integer attribute in exactly 1 record
224 */
225 uint_t samdb_search_uint(struct ldb_context *sam_ldb,
226                          TALLOC_CTX *mem_ctx,
227                          uint_t default_value,
228                          struct ldb_dn *basedn,
229                          const char *attr_name,
230                          const char *format, ...) _PRINTF_ATTRIBUTE(6,7)
231 {
232         va_list ap;
233         int count;
234         struct ldb_message **res;
235         const char *attrs[2] = { NULL, NULL };
236
237         attrs[0] = attr_name;
238
239         va_start(ap, format);
240         count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
241         va_end(ap);
242
243         if (count != 1) {
244                 return default_value;
245         }
246
247         return samdb_result_uint(res[0], attr_name, default_value);
248 }
249
250 /*
251   search the sam for a single signed 64 bit integer attribute in exactly 1 record
252 */
253 int64_t samdb_search_int64(struct ldb_context *sam_ldb,
254                            TALLOC_CTX *mem_ctx,
255                            int64_t default_value,
256                            struct ldb_dn *basedn,
257                            const char *attr_name,
258                            const char *format, ...) _PRINTF_ATTRIBUTE(6,7)
259 {
260         va_list ap;
261         int count;
262         struct ldb_message **res;
263         const char *attrs[2] = { NULL, NULL };
264
265         attrs[0] = attr_name;
266
267         va_start(ap, format);
268         count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
269         va_end(ap);
270
271         if (count != 1) {
272                 return default_value;
273         }
274
275         return samdb_result_int64(res[0], attr_name, default_value);
276 }
277
278 /*
279   search the sam for multipe records each giving a single string attribute
280   return the number of matches, or -1 on error
281 */
282 int samdb_search_string_multiple(struct ldb_context *sam_ldb,
283                                  TALLOC_CTX *mem_ctx,
284                                  struct ldb_dn *basedn,
285                                  const char ***strs,
286                                  const char *attr_name,
287                                  const char *format, ...) _PRINTF_ATTRIBUTE(6,7)
288 {
289         va_list ap;
290         int count, i;
291         const char *attrs[2] = { NULL, NULL };
292         struct ldb_message **res = NULL;
293
294         attrs[0] = attr_name;
295
296         va_start(ap, format);
297         count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
298         va_end(ap);
299
300         if (count <= 0) {
301                 return count;
302         }
303
304         /* make sure its single valued */
305         for (i=0;i<count;i++) {
306                 if (res[i]->num_elements != 1) {
307                         DEBUG(1,("samdb: search for %s %s not single valued\n", 
308                                  attr_name, format));
309                         talloc_free(res);
310                         return -1;
311                 }
312         }
313
314         *strs = talloc_array(mem_ctx, const char *, count+1);
315         if (! *strs) {
316                 talloc_free(res);
317                 return -1;
318         }
319
320         for (i=0;i<count;i++) {
321                 (*strs)[i] = samdb_result_string(res[i], attr_name, NULL);
322         }
323         (*strs)[count] = NULL;
324
325         return count;
326 }
327
328 /*
329   pull a uint from a result set. 
330 */
331 uint_t samdb_result_uint(const struct ldb_message *msg, const char *attr, uint_t default_value)
332 {
333         return ldb_msg_find_attr_as_uint(msg, attr, default_value);
334 }
335
336 /*
337   pull a (signed) int64 from a result set. 
338 */
339 int64_t samdb_result_int64(const struct ldb_message *msg, const char *attr, int64_t default_value)
340 {
341         return ldb_msg_find_attr_as_int64(msg, attr, default_value);
342 }
343
344 /*
345   pull a string from a result set. 
346 */
347 const char *samdb_result_string(const struct ldb_message *msg, const char *attr, 
348                                 const char *default_value)
349 {
350         return ldb_msg_find_attr_as_string(msg, attr, default_value);
351 }
352
353 struct ldb_dn *samdb_result_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const struct ldb_message *msg,
354                                const char *attr, struct ldb_dn *default_value)
355 {
356         struct ldb_dn *res_dn;
357         const char *string = samdb_result_string(msg, attr, NULL);
358         if (string == NULL) return default_value;
359         res_dn = ldb_dn_new(mem_ctx, ldb, string);
360         if ( ! ldb_dn_validate(res_dn)) {
361                 talloc_free(res_dn);
362                 return NULL;
363         }
364         return res_dn;
365 }
366
367 /*
368   pull a rid from a objectSid in a result set. 
369 */
370 uint32_t samdb_result_rid_from_sid(TALLOC_CTX *mem_ctx, const struct ldb_message *msg, 
371                                    const char *attr, uint32_t default_value)
372 {
373         struct dom_sid *sid;
374         uint32_t rid;
375
376         sid = samdb_result_dom_sid(mem_ctx, msg, attr);
377         if (sid == NULL) {
378                 return default_value;
379         }
380         rid = sid->sub_auths[sid->num_auths-1];
381         talloc_free(sid);
382         return rid;
383 }
384
385 /*
386   pull a dom_sid structure from a objectSid in a result set. 
387 */
388 struct dom_sid *samdb_result_dom_sid(TALLOC_CTX *mem_ctx, const struct ldb_message *msg, 
389                                      const char *attr)
390 {
391         const struct ldb_val *v;
392         struct dom_sid *sid;
393         NTSTATUS status;
394         v = ldb_msg_find_ldb_val(msg, attr);
395         if (v == NULL) {
396                 return NULL;
397         }
398         sid = talloc(mem_ctx, struct dom_sid);
399         if (sid == NULL) {
400                 return NULL;
401         }
402         status = ndr_pull_struct_blob(v, sid, sid, 
403                                       (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
404         if (!NT_STATUS_IS_OK(status)) {
405                 talloc_free(sid);
406                 return NULL;
407         }
408         return sid;
409 }
410
411 /*
412   pull a guid structure from a objectGUID in a result set. 
413 */
414 struct GUID samdb_result_guid(const struct ldb_message *msg, const char *attr)
415 {
416         const struct ldb_val *v;
417         NTSTATUS status;
418         struct GUID guid;
419         TALLOC_CTX *mem_ctx;
420
421         ZERO_STRUCT(guid);
422
423         v = ldb_msg_find_ldb_val(msg, attr);
424         if (!v) return guid;
425
426         mem_ctx = talloc_named_const(NULL, 0, "samdb_result_guid");
427         if (!mem_ctx) return guid;
428         status = ndr_pull_struct_blob(v, mem_ctx, &guid, 
429                                       (ndr_pull_flags_fn_t)ndr_pull_GUID);
430         talloc_free(mem_ctx);
431         if (!NT_STATUS_IS_OK(status)) {
432                 return guid;
433         }
434
435         return guid;
436 }
437
438 /*
439   pull a sid prefix from a objectSid in a result set. 
440   this is used to find the domain sid for a user
441 */
442 struct dom_sid *samdb_result_sid_prefix(TALLOC_CTX *mem_ctx, const struct ldb_message *msg, 
443                                         const char *attr)
444 {
445         struct dom_sid *sid = samdb_result_dom_sid(mem_ctx, msg, attr);
446         if (!sid || sid->num_auths < 1) return NULL;
447         sid->num_auths--;
448         return sid;
449 }
450
451 /*
452   pull a NTTIME in a result set. 
453 */
454 NTTIME samdb_result_nttime(struct ldb_message *msg, const char *attr, NTTIME default_value)
455 {
456         const char *str = ldb_msg_find_attr_as_string(msg, attr, NULL);
457         if (!str) return default_value;
458         return nttime_from_string(str);
459 }
460
461 /*
462   pull a uint64_t from a result set. 
463 */
464 uint64_t samdb_result_uint64(struct ldb_message *msg, const char *attr, uint64_t default_value)
465 {
466         return ldb_msg_find_attr_as_uint64(msg, attr, default_value);
467 }
468
469
470 /*
471   construct the allow_password_change field from the PwdLastSet attribute and the 
472   domain password settings
473 */
474 NTTIME samdb_result_allow_password_change(struct ldb_context *sam_ldb, 
475                                           TALLOC_CTX *mem_ctx, 
476                                           struct ldb_dn *domain_dn, 
477                                           struct ldb_message *msg, 
478                                           const char *attr)
479 {
480         uint64_t attr_time = samdb_result_uint64(msg, attr, 0);
481         int64_t minPwdAge;
482
483         if (attr_time == 0) {
484                 return 0;
485         }
486
487         minPwdAge = samdb_search_int64(sam_ldb, mem_ctx, 0, domain_dn, "minPwdAge", NULL);
488
489         /* yes, this is a -= not a += as minPwdAge is stored as the negative
490            of the number of 100-nano-seconds */
491         attr_time -= minPwdAge;
492
493         return attr_time;
494 }
495
496 /*
497   construct the force_password_change field from the PwdLastSet attribute and the 
498   domain password settings
499 */
500 NTTIME samdb_result_force_password_change(struct ldb_context *sam_ldb, 
501                                           TALLOC_CTX *mem_ctx, 
502                                           struct ldb_dn *domain_dn, 
503                                           struct ldb_message *msg)
504 {
505         uint64_t attr_time = samdb_result_uint64(msg, "pwdLastSet", 0);
506         uint32_t user_flags = samdb_result_uint64(msg, "userAccountControl", 0);
507         int64_t maxPwdAge;
508
509         if (user_flags & UF_DONT_EXPIRE_PASSWD) {
510                 return 0x7FFFFFFFFFFFFFFFULL;
511         }
512
513         if (attr_time == 0) {
514                 return 0;
515         }
516
517         maxPwdAge = samdb_search_int64(sam_ldb, mem_ctx, 0, domain_dn, "maxPwdAge", NULL);
518         if (maxPwdAge == 0) {
519                 return 0;
520         } else {
521                 attr_time -= maxPwdAge;
522         }
523
524         return attr_time;
525 }
526
527 /*
528   pull a samr_Password structutre from a result set. 
529 */
530 struct samr_Password *samdb_result_hash(TALLOC_CTX *mem_ctx, struct ldb_message *msg, const char *attr)
531 {
532         struct samr_Password *hash = NULL;
533         const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
534         if (val && (val->length >= sizeof(hash->hash))) {
535                 hash = talloc(mem_ctx, struct samr_Password);
536                 memcpy(hash->hash, val->data, MIN(val->length, sizeof(hash->hash)));
537         }
538         return hash;
539 }
540
541 /*
542   pull an array of samr_Password structutres from a result set. 
543 */
544 uint_t samdb_result_hashes(TALLOC_CTX *mem_ctx, struct ldb_message *msg, 
545                            const char *attr, struct samr_Password **hashes)
546 {
547         uint_t count = 0;
548         const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
549         int i;
550
551         *hashes = NULL;
552         if (!val) {
553                 return 0;
554         }
555         count = val->length / 16;
556         if (count == 0) {
557                 return 0;
558         }
559
560         *hashes = talloc_array(mem_ctx, struct samr_Password, count);
561         if (! *hashes) {
562                 return 0;
563         }
564
565         for (i=0;i<count;i++) {
566                 memcpy((*hashes)[i].hash, (i*16)+(char *)val->data, 16);
567         }
568
569         return count;
570 }
571
572 NTSTATUS samdb_result_passwords(TALLOC_CTX *mem_ctx, struct ldb_message *msg, 
573                                 struct samr_Password **lm_pwd, struct samr_Password **nt_pwd) 
574 {
575         struct samr_Password *lmPwdHash, *ntPwdHash;
576         if (nt_pwd) {
577                 int num_nt;
578                 num_nt = samdb_result_hashes(mem_ctx, msg, "ntPwdHash", &ntPwdHash);
579                 if (num_nt == 0) {
580                         *nt_pwd = NULL;
581                 } else if (num_nt > 1) {
582                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
583                 } else {
584                         *nt_pwd = &ntPwdHash[0];
585                 }
586         }
587         if (lm_pwd) {
588                 int num_lm;
589                 num_lm = samdb_result_hashes(mem_ctx, msg, "lmPwdHash", &lmPwdHash);
590                 if (num_lm == 0) {
591                         *lm_pwd = NULL;
592                 } else if (num_lm > 1) {
593                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
594                 } else {
595                         *lm_pwd = &lmPwdHash[0];
596                 }
597         }
598         return NT_STATUS_OK;
599 }
600
601 /*
602   pull a samr_LogonHours structutre from a result set. 
603 */
604 struct samr_LogonHours samdb_result_logon_hours(TALLOC_CTX *mem_ctx, struct ldb_message *msg, const char *attr)
605 {
606         struct samr_LogonHours hours;
607         const int units_per_week = 168;
608         const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
609         ZERO_STRUCT(hours);
610         hours.bits = talloc_array(mem_ctx, uint8_t, units_per_week);
611         if (!hours.bits) {
612                 return hours;
613         }
614         hours.units_per_week = units_per_week;
615         memset(hours.bits, 0xFF, units_per_week);
616         if (val) {
617                 memcpy(hours.bits, val->data, MIN(val->length, units_per_week));
618         }
619         return hours;
620 }
621
622 /*
623   pull a set of account_flags from a result set. 
624 */
625 uint16_t samdb_result_acct_flags(struct ldb_message *msg, const char *attr)
626 {
627         uint_t userAccountControl = ldb_msg_find_attr_as_uint(msg, attr, 0);
628         return samdb_uf2acb(userAccountControl);
629 }
630
631
632 /* Find an attribute, with a particular value */
633 struct ldb_message_element *samdb_find_attribute(struct ldb_context *ldb, 
634                                                  const struct ldb_message *msg, 
635                                                  const char *name, const char *value)
636 {
637         int i;
638         struct ldb_message_element *el = ldb_msg_find_element(msg, name);
639         struct ldb_val v;
640
641         v.data = discard_const_p(uint8_t, value);
642         v.length = strlen(value);
643
644         if (!el) {
645                 return NULL;
646         }
647
648         for (i=0;i<el->num_values;i++) {
649                 if (strcasecmp(value, (char *)el->values[i].data) == 0) {
650                         return el;
651                 }
652         }
653
654         return NULL;
655 }
656
657 int samdb_find_or_add_value(struct ldb_context *ldb, struct ldb_message *msg, const char *name, const char *set_value)
658 {
659         if (samdb_find_attribute(ldb, msg, name, set_value) == NULL) {
660                 return samdb_msg_add_string(ldb, msg, msg, name, set_value);
661         }
662         return LDB_SUCCESS;
663 }
664
665 int samdb_find_or_add_attribute(struct ldb_context *ldb, struct ldb_message *msg, const char *name, const char *set_value)
666 {
667         struct ldb_message_element *el;
668
669         el = ldb_msg_find_element(msg, name);
670         if (el) {
671                 return LDB_SUCCESS;
672         }
673                 
674         return samdb_msg_add_string(ldb, msg, msg, name, set_value);
675 }
676
677
678 /*
679   copy from a template record to a message
680 */
681 int samdb_copy_template(struct ldb_context *ldb, 
682                         struct ldb_message *msg, const char *filter,
683                         const char **errstring)
684 {
685         struct ldb_result *res;
686         struct ldb_message *t;
687         int ret, i, j;
688         struct ldb_dn *basedn = ldb_dn_new(ldb, ldb, "cn=Templates");
689
690         *errstring = NULL;      
691
692         /* pull the template record */
693         ret = ldb_search(ldb, basedn, LDB_SCOPE_SUBTREE, filter, NULL, &res);
694         talloc_free(basedn);
695         if (ret != LDB_SUCCESS) {
696                 *errstring = talloc_steal(msg, ldb_errstring(ldb));
697                 return ret;
698         }
699         if (res->count != 1) {
700                 *errstring = talloc_asprintf(msg, "samdb_copy_template: ERROR: template '%s' matched %d records, expected 1\n", filter, 
701                                              res->count);
702                 talloc_free(res);
703                 return LDB_ERR_OPERATIONS_ERROR;
704         }
705         t = res->msgs[0];
706
707         for (i = 0; i < t->num_elements; i++) {
708                 struct ldb_message_element *el = &t->elements[i];
709                 /* some elements should not be copied from the template */
710                 if (strcasecmp(el->name, "cn") == 0 ||
711                     strcasecmp(el->name, "name") == 0 ||
712                     strcasecmp(el->name, "sAMAccountName") == 0 ||
713                     strcasecmp(el->name, "sAMAccountName") == 0 ||
714                     strcasecmp(el->name, "distinguishedName") == 0 ||
715                     strcasecmp(el->name, "objectGUID") == 0) {
716                         continue;
717                 }
718                 for (j = 0; j < el->num_values; j++) {
719                         if (strcasecmp(el->name, "objectClass") == 0) {
720                                 if (strcasecmp((char *)el->values[j].data, "Template") == 0 ||
721                                     strcasecmp((char *)el->values[j].data, "userTemplate") == 0 ||
722                                     strcasecmp((char *)el->values[j].data, "groupTemplate") == 0 ||
723                                     strcasecmp((char *)el->values[j].data, "foreignSecurityPrincipalTemplate") == 0 ||
724                                     strcasecmp((char *)el->values[j].data, "aliasTemplate") == 0 || 
725                                     strcasecmp((char *)el->values[j].data, "trustedDomainTemplate") == 0 || 
726                                     strcasecmp((char *)el->values[j].data, "secretTemplate") == 0) {
727                                         continue;
728                                 }
729                                 ret = samdb_find_or_add_value(ldb, msg, el->name, 
730                                                               (char *)el->values[j].data);
731                                 if (ret) {
732                                         *errstring = talloc_asprintf(msg, "Adding objectClass %s failed.\n", el->values[j].data);
733                                         talloc_free(res);
734                                         return ret;
735                                 }
736                         } else {
737                                 ret = samdb_find_or_add_attribute(ldb, msg, el->name, 
738                                                                   (char *)el->values[j].data);
739                                 if (ret) {
740                                         *errstring = talloc_asprintf(msg, "Adding attribute %s failed.\n", el->name);
741                                         talloc_free(res);
742                                         return ret;
743                                 }
744                         }
745                 }
746         }
747
748         talloc_free(res);
749
750         return LDB_SUCCESS;
751 }
752
753
754 /*
755   add a string element to a message
756 */
757 int samdb_msg_add_string(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
758                          const char *attr_name, const char *str)
759 {
760         char *s = talloc_strdup(mem_ctx, str);
761         char *a = talloc_strdup(mem_ctx, attr_name);
762         if (s == NULL || a == NULL) {
763                 return LDB_ERR_OPERATIONS_ERROR;
764         }
765         return ldb_msg_add_string(msg, a, s);
766 }
767
768 /*
769   add a dom_sid element to a message
770 */
771 int samdb_msg_add_dom_sid(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
772                          const char *attr_name, struct dom_sid *sid)
773 {
774         struct ldb_val v;
775         NTSTATUS status;
776         status = ndr_push_struct_blob(&v, mem_ctx, sid, 
777                                       (ndr_push_flags_fn_t)ndr_push_dom_sid);
778         if (!NT_STATUS_IS_OK(status)) {
779                 return -1;
780         }
781         return ldb_msg_add_value(msg, attr_name, &v, NULL);
782 }
783
784
785 /*
786   add a delete element operation to a message
787 */
788 int samdb_msg_add_delete(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
789                          const char *attr_name)
790 {
791         /* we use an empty replace rather than a delete, as it allows for 
792            samdb_replace() to be used everywhere */
793         return ldb_msg_add_empty(msg, attr_name, LDB_FLAG_MOD_REPLACE, NULL);
794 }
795
796 /*
797   add a add attribute value to a message
798 */
799 int samdb_msg_add_addval(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
800                          const char *attr_name, const char *value)
801 {
802         struct ldb_message_element *el;
803         char *a, *v;
804         int ret;
805         a = talloc_strdup(mem_ctx, attr_name);
806         if (a == NULL)
807                 return -1;
808         v = talloc_strdup(mem_ctx, value);
809         if (v == NULL)
810                 return -1;
811         ret = ldb_msg_add_string(msg, a, v);
812         if (ret != 0)
813                 return ret;
814         el = ldb_msg_find_element(msg, a);
815         if (el == NULL)
816                 return -1;
817         el->flags = LDB_FLAG_MOD_ADD;
818         return 0;
819 }
820
821 /*
822   add a delete attribute value to a message
823 */
824 int samdb_msg_add_delval(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
825                          const char *attr_name, const char *value)
826 {
827         struct ldb_message_element *el;
828         char *a, *v;
829         int ret;
830         a = talloc_strdup(mem_ctx, attr_name);
831         if (a == NULL)
832                 return -1;
833         v = talloc_strdup(mem_ctx, value);
834         if (v == NULL)
835                 return -1;
836         ret = ldb_msg_add_string(msg, a, v);
837         if (ret != 0)
838                 return ret;
839         el = ldb_msg_find_element(msg, a);
840         if (el == NULL)
841                 return -1;
842         el->flags = LDB_FLAG_MOD_DELETE;
843         return 0;
844 }
845
846 /*
847   add a int element to a message
848 */
849 int samdb_msg_add_int(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
850                        const char *attr_name, int v)
851 {
852         const char *s = talloc_asprintf(mem_ctx, "%d", v);
853         return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
854 }
855
856 /*
857   add a uint_t element to a message
858 */
859 int samdb_msg_add_uint(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
860                        const char *attr_name, uint_t v)
861 {
862         const char *s = talloc_asprintf(mem_ctx, "%u", v);
863         return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
864 }
865
866 /*
867   add a (signed) int64_t element to a message
868 */
869 int samdb_msg_add_int64(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
870                         const char *attr_name, int64_t v)
871 {
872         const char *s = talloc_asprintf(mem_ctx, "%lld", (long long)v);
873         return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
874 }
875
876 /*
877   add a uint64_t element to a message
878 */
879 int samdb_msg_add_uint64(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
880                         const char *attr_name, uint64_t v)
881 {
882         const char *s = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)v);
883         return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
884 }
885
886 /*
887   add a samr_Password element to a message
888 */
889 int samdb_msg_add_hash(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
890                        const char *attr_name, struct samr_Password *hash)
891 {
892         struct ldb_val val;
893         val.data = talloc_memdup(mem_ctx, hash->hash, 16);
894         if (!val.data) {
895                 return -1;
896         }
897         val.length = 16;
898         return ldb_msg_add_value(msg, attr_name, &val, NULL);
899 }
900
901 /*
902   add a samr_Password array to a message
903 */
904 int samdb_msg_add_hashes(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
905                          const char *attr_name, struct samr_Password *hashes, uint_t count)
906 {
907         struct ldb_val val;
908         int i;
909         val.data = talloc_array_size(mem_ctx, 16, count);
910         val.length = count*16;
911         if (!val.data) {
912                 return -1;
913         }
914         for (i=0;i<count;i++) {
915                 memcpy(i*16 + (char *)val.data, hashes[i].hash, 16);
916         }
917         return ldb_msg_add_value(msg, attr_name, &val, NULL);
918 }
919
920 /*
921   add a acct_flags element to a message
922 */
923 int samdb_msg_add_acct_flags(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
924                              const char *attr_name, uint32_t v)
925 {
926         return samdb_msg_add_uint(sam_ldb, mem_ctx, msg, attr_name, samdb_acb2uf(v));
927 }
928
929 /*
930   add a logon_hours element to a message
931 */
932 int samdb_msg_add_logon_hours(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
933                               const char *attr_name, struct samr_LogonHours *hours)
934 {
935         struct ldb_val val;
936         val.length = hours->units_per_week / 8;
937         val.data = hours->bits;
938         return ldb_msg_add_value(msg, attr_name, &val, NULL);
939 }
940
941 /*
942   add a general value element to a message
943 */
944 int samdb_msg_add_value(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
945                               const char *attr_name, const struct ldb_val *val)
946 {
947         return ldb_msg_add_value(msg, attr_name, val, NULL);
948 }
949
950 /*
951   sets a general value element to a message
952 */
953 int samdb_msg_set_value(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
954                         const char *attr_name, const struct ldb_val *val)
955 {
956         struct ldb_message_element *el;
957
958         el = ldb_msg_find_element(msg, attr_name);
959         if (el) {
960                 el->num_values = 0;
961         }
962         return ldb_msg_add_value(msg, attr_name, val, NULL);
963 }
964
965 /*
966   set a string element in a message
967 */
968 int samdb_msg_set_string(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
969                          const char *attr_name, const char *str)
970 {
971         struct ldb_message_element *el;
972
973         el = ldb_msg_find_element(msg, attr_name);
974         if (el) {
975                 el->num_values = 0;
976         }
977         return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, str);
978 }
979
980 /*
981   add a record
982 */
983 int samdb_add(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg)
984 {
985         return ldb_add(sam_ldb, msg);
986 }
987
988 /*
989   delete a record
990 */
991 int samdb_delete(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_dn *dn)
992 {
993         return ldb_delete(sam_ldb, dn);
994 }
995
996 /*
997   modify a record
998 */
999 int samdb_modify(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg)
1000 {
1001         return ldb_modify(sam_ldb, msg);
1002 }
1003
1004 /*
1005   replace elements in a record
1006 */
1007 int samdb_replace(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg)
1008 {
1009         int i;
1010
1011         /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
1012         for (i=0;i<msg->num_elements;i++) {
1013                 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
1014         }
1015
1016         /* modify the samdb record */
1017         return samdb_modify(sam_ldb, mem_ctx, msg);
1018 }
1019
1020 /*
1021   return a default security descriptor
1022 */
1023 struct security_descriptor *samdb_default_security_descriptor(TALLOC_CTX *mem_ctx)
1024 {
1025         struct security_descriptor *sd;
1026
1027         sd = security_descriptor_initialise(mem_ctx);
1028
1029         return sd;
1030 }
1031
1032 struct ldb_dn *samdb_base_dn(struct ldb_context *sam_ctx) 
1033 {
1034         return ldb_get_default_basedn(sam_ctx);
1035 }
1036
1037
1038 struct ldb_dn *samdb_partitions_dn(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx)
1039 {
1040         struct ldb_dn *new_dn;
1041
1042         new_dn = ldb_dn_copy(mem_ctx, samdb_base_dn(sam_ctx));
1043         if ( ! ldb_dn_add_child_fmt(new_dn, "CN=Partitions,CN=Configuration")) {
1044                 talloc_free(new_dn);
1045                 return NULL;
1046         }
1047         return new_dn;
1048 }
1049
1050 /*
1051   work out the domain sid for the current open ldb
1052 */
1053 const struct dom_sid *samdb_domain_sid(struct ldb_context *ldb)
1054 {
1055         const char *attrs[] = { "rootDomainNamingContext", NULL };
1056         int ret;
1057         struct ldb_result *res = NULL;
1058         TALLOC_CTX *tmp_ctx;
1059         struct dom_sid *domain_sid;
1060         const char *basedn_s;
1061         struct ldb_dn *basedn;
1062
1063         /* see if we have a cached copy */
1064         domain_sid = ldb_get_opaque(ldb, "cache.domain_sid");
1065         if (domain_sid) {
1066                 return domain_sid;
1067         }
1068
1069         tmp_ctx = talloc_new(ldb);
1070         if (tmp_ctx == NULL) {
1071                 goto failed;
1072         }
1073
1074         basedn = ldb_dn_new(tmp_ctx, ldb, NULL);
1075         if (basedn == NULL) {
1076                 goto failed;
1077         }
1078         
1079         /* find the basedn of the domain from the rootdse */
1080         ret = ldb_search(ldb, basedn, LDB_SCOPE_BASE, NULL, attrs, &res);
1081         talloc_steal(tmp_ctx, res);
1082         if (ret != LDB_SUCCESS || res->count != 1) {
1083                 goto failed;
1084         }
1085
1086         basedn_s = ldb_msg_find_attr_as_string(res->msgs[0], "rootDomainNamingContext", NULL);
1087         if (basedn_s == NULL) {
1088                 goto failed;
1089         }
1090
1091         basedn = ldb_dn_new(tmp_ctx, ldb, basedn_s);
1092         if ( ! ldb_dn_validate(basedn)) {
1093                 goto failed;
1094         }
1095
1096         /* find the domain_sid */
1097         domain_sid = samdb_search_dom_sid(ldb, tmp_ctx, basedn, 
1098                                           "objectSid", "objectClass=domainDNS");
1099         if (domain_sid == NULL) {
1100                 goto failed;
1101         }
1102
1103         /* cache the domain_sid in the ldb */
1104         if (ldb_set_opaque(ldb, "cache.domain_sid", domain_sid) != LDB_SUCCESS) {
1105                 goto failed;
1106         }
1107
1108         talloc_steal(ldb, domain_sid);
1109         talloc_free(tmp_ctx);
1110
1111         return domain_sid;
1112
1113 failed:
1114         DEBUG(1,("Failed to find domain_sid for open ldb\n"));
1115         talloc_free(tmp_ctx);
1116         return NULL;
1117 }
1118
1119 /*
1120   check that a password is sufficiently complex
1121 */
1122 static BOOL samdb_password_complexity_ok(const char *pass)
1123 {
1124         return check_password_quality(pass);
1125 }
1126
1127
1128
1129 /*
1130   set the user password using plaintext, obeying any user or domain
1131   password restrictions
1132
1133   note that this function doesn't actually store the result in the
1134   database, it just fills in the "mod" structure with ldb modify
1135   elements to setup the correct change when samdb_replace() is
1136   called. This allows the caller to combine the change with other
1137   changes (as is needed by some of the set user info levels)
1138
1139   The caller should probably have a transaction wrapping this
1140 */
1141 _PUBLIC_ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
1142                             struct ldb_dn *user_dn,
1143                             struct ldb_dn *domain_dn,
1144                             struct ldb_message *mod,
1145                             const char *new_pass,
1146                             struct samr_Password *lmNewHash, 
1147                             struct samr_Password *ntNewHash,
1148                             BOOL user_change,
1149                             BOOL restrictions,
1150                             enum samr_RejectReason *reject_reason,
1151                             struct samr_DomInfo1 **_dominfo)
1152 {
1153         const char * const user_attrs[] = { "userAccountControl", "sambaLMPwdHistory", 
1154                                             "sambaNTPwdHistory", 
1155                                             "lmPwdHash", "ntPwdHash", 
1156                                             "objectSid", 
1157                                             "pwdLastSet", NULL };
1158         const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength", 
1159                                               "maxPwdAge", "minPwdAge", 
1160                                               "minPwdLength", NULL };
1161         NTTIME pwdLastSet;
1162         int64_t minPwdAge;
1163         uint_t minPwdLength, pwdProperties, pwdHistoryLength;
1164         uint_t userAccountControl;
1165         struct samr_Password *sambaLMPwdHistory, *sambaNTPwdHistory, *lmPwdHash, *ntPwdHash;
1166         struct samr_Password local_lmNewHash, local_ntNewHash;
1167         int sambaLMPwdHistory_len, sambaNTPwdHistory_len;
1168         struct dom_sid *domain_sid;
1169         struct ldb_message **res;
1170         int count;
1171         time_t now = time(NULL);
1172         NTTIME now_nt;
1173         int i;
1174
1175         /* we need to know the time to compute password age */
1176         unix_to_nt_time(&now_nt, now);
1177
1178         /* pull all the user parameters */
1179         count = gendb_search_dn(ctx, mem_ctx, user_dn, &res, user_attrs);
1180         if (count != 1) {
1181                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1182         }
1183         userAccountControl = samdb_result_uint(res[0],   "userAccountControl", 0);
1184         sambaLMPwdHistory_len =   samdb_result_hashes(mem_ctx, res[0], 
1185                                                  "sambaLMPwdHistory", &sambaLMPwdHistory);
1186         sambaNTPwdHistory_len =   samdb_result_hashes(mem_ctx, res[0], 
1187                                                  "sambaNTPwdHistory", &sambaNTPwdHistory);
1188         lmPwdHash =          samdb_result_hash(mem_ctx, res[0],   "lmPwdHash");
1189         ntPwdHash =          samdb_result_hash(mem_ctx, res[0],   "ntPwdHash");
1190         pwdLastSet =         samdb_result_uint64(res[0], "pwdLastSet", 0);
1191
1192         if (domain_dn) {
1193                 /* pull the domain parameters */
1194                 count = gendb_search_dn(ctx, mem_ctx, domain_dn, &res, domain_attrs);
1195                 if (count != 1) {
1196                         DEBUG(2, ("samdb_set_password: Domain DN %s is invalid, for user %s\n", 
1197                                   ldb_dn_get_linearized(domain_dn),
1198                                   ldb_dn_get_linearized(user_dn)));
1199                         return NT_STATUS_NO_SUCH_DOMAIN;
1200                 }
1201         } else {
1202                 /* work out the domain sid, and pull the domain from there */
1203                 domain_sid =         samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
1204                 if (domain_sid == NULL) {
1205                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
1206                 }
1207
1208                 count = gendb_search(ctx, mem_ctx, NULL, &res, domain_attrs, 
1209                                      "(objectSid=%s)", 
1210                                      ldap_encode_ndr_dom_sid(mem_ctx, domain_sid));
1211                 if (count != 1) {
1212                         DEBUG(2, ("samdb_set_password: Could not find domain to match SID: %s, for user %s\n", 
1213                                   dom_sid_string(mem_ctx, domain_sid),
1214                                   ldb_dn_get_linearized(user_dn)));
1215                         return NT_STATUS_NO_SUCH_DOMAIN;
1216                 }
1217         }
1218
1219         pwdProperties =    samdb_result_uint(res[0],   "pwdProperties", 0);
1220         pwdHistoryLength = samdb_result_uint(res[0],   "pwdHistoryLength", 0);
1221         minPwdLength =     samdb_result_uint(res[0],   "minPwdLength", 0);
1222         minPwdAge =        samdb_result_int64(res[0],  "minPwdAge", 0);
1223
1224         if (_dominfo) {
1225                 struct samr_DomInfo1 *dominfo;
1226                 /* on failure we need to fill in the reject reasons */
1227                 dominfo = talloc(mem_ctx, struct samr_DomInfo1);
1228                 if (dominfo == NULL) {
1229                         return NT_STATUS_NO_MEMORY;
1230                 }
1231                 dominfo->min_password_length     = minPwdLength;
1232                 dominfo->password_properties     = pwdProperties;
1233                 dominfo->password_history_length = pwdHistoryLength;
1234                 dominfo->max_password_age        = minPwdAge;
1235                 dominfo->min_password_age        = minPwdAge;
1236                 *_dominfo = dominfo;
1237         }
1238
1239         if (new_pass) {
1240                 /* check the various password restrictions */
1241                 if (restrictions && minPwdLength > strlen_m(new_pass)) {
1242                         if (reject_reason) {
1243                                 *reject_reason = SAMR_REJECT_TOO_SHORT;
1244                         }
1245                         return NT_STATUS_PASSWORD_RESTRICTION;
1246                 }
1247                 
1248                 /* possibly check password complexity */
1249                 if (restrictions && pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
1250                     !samdb_password_complexity_ok(new_pass)) {
1251                         if (reject_reason) {
1252                                 *reject_reason = SAMR_REJECT_COMPLEXITY;
1253                         }
1254                         return NT_STATUS_PASSWORD_RESTRICTION;
1255                 }
1256                 
1257                 /* compute the new nt and lm hashes */
1258                 if (E_deshash(new_pass, local_lmNewHash.hash)) {
1259                         lmNewHash = &local_lmNewHash;
1260                 }
1261                 if (!E_md4hash(new_pass, local_ntNewHash.hash)) {
1262                         /* If we can't convert this password to UCS2, then we should not accept it */
1263                         if (reject_reason) {
1264                                 *reject_reason = SAMR_REJECT_OTHER;
1265                         }
1266                         return NT_STATUS_PASSWORD_RESTRICTION;
1267                 }
1268                 ntNewHash = &local_ntNewHash;
1269         }
1270
1271         if (restrictions && user_change) {
1272                 /* are all password changes disallowed? */
1273                 if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
1274                         if (reject_reason) {
1275                                 *reject_reason = SAMR_REJECT_OTHER;
1276                         }
1277                         return NT_STATUS_PASSWORD_RESTRICTION;
1278                 }
1279                 
1280                 /* can this user change password? */
1281                 if (userAccountControl & UF_PASSWD_CANT_CHANGE) {
1282                         if (reject_reason) {
1283                                 *reject_reason = SAMR_REJECT_OTHER;
1284                         }
1285                         return NT_STATUS_PASSWORD_RESTRICTION;
1286                 }
1287                 
1288                 /* yes, this is a minus. The ages are in negative 100nsec units! */
1289                 if (pwdLastSet - minPwdAge > now_nt) {
1290                         if (reject_reason) {
1291                                 *reject_reason = SAMR_REJECT_OTHER;
1292                         }
1293                         return NT_STATUS_PASSWORD_RESTRICTION;
1294                 }
1295
1296                 /* check the immediately past password */
1297                 if (pwdHistoryLength > 0) {
1298                         if (lmNewHash && lmPwdHash && memcmp(lmNewHash->hash, lmPwdHash->hash, 16) == 0) {
1299                                 if (reject_reason) {
1300                                         *reject_reason = SAMR_REJECT_IN_HISTORY;
1301                                 }
1302                                 return NT_STATUS_PASSWORD_RESTRICTION;
1303                         }
1304                         if (ntNewHash && ntPwdHash && memcmp(ntNewHash->hash, ntPwdHash->hash, 16) == 0) {
1305                                 if (reject_reason) {
1306                                         *reject_reason = SAMR_REJECT_IN_HISTORY;
1307                                 }
1308                                 return NT_STATUS_PASSWORD_RESTRICTION;
1309                         }
1310                 }
1311                 
1312                 /* check the password history */
1313                 sambaLMPwdHistory_len = MIN(sambaLMPwdHistory_len, pwdHistoryLength);
1314                 sambaNTPwdHistory_len = MIN(sambaNTPwdHistory_len, pwdHistoryLength);
1315                 
1316                 for (i=0; lmNewHash && i<sambaLMPwdHistory_len;i++) {
1317                         if (memcmp(lmNewHash->hash, sambaLMPwdHistory[i].hash, 16) == 0) {
1318                                 if (reject_reason) {
1319                                         *reject_reason = SAMR_REJECT_IN_HISTORY;
1320                                 }
1321                                 return NT_STATUS_PASSWORD_RESTRICTION;
1322                         }
1323                 }
1324                 for (i=0; ntNewHash && i<sambaNTPwdHistory_len;i++) {
1325                         if (memcmp(ntNewHash->hash, sambaNTPwdHistory[i].hash, 16) == 0) {
1326                                 if (reject_reason) {
1327                                         *reject_reason = SAMR_REJECT_IN_HISTORY;
1328                                 }
1329                                 return NT_STATUS_PASSWORD_RESTRICTION;
1330                         }
1331                 }
1332         }
1333
1334 #define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0)
1335
1336         /* the password is acceptable. Start forming the new fields */
1337         if (new_pass) {
1338                 /* if we know the cleartext, then only set it.
1339                  * Modules in ldb will set all the appropriate
1340                  * hashes */
1341                 CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod, 
1342                                                "sambaPassword", new_pass));
1343         } else {
1344                 /* We don't have the cleartext, so delete the old one
1345                  * and set what we have of the hashes */
1346                 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "sambaPassword"));
1347
1348                 if (lmNewHash) {
1349                         CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "lmPwdHash", lmNewHash));
1350                 } else {
1351                         CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHash"));
1352                 }
1353                 
1354                 if (ntNewHash) {
1355                         CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "ntPwdHash", ntNewHash));
1356                 } else {
1357                         CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHash"));
1358                 }
1359         }
1360
1361         return NT_STATUS_OK;
1362 }
1363
1364
1365 /*
1366   set the user password using plaintext, obeying any user or domain
1367   password restrictions
1368
1369   This wrapper function takes a SID as input, rather than a user DN,
1370   and actually performs the password change
1371
1372 */
1373 _PUBLIC_ NTSTATUS samdb_set_password_sid(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
1374                                 const struct dom_sid *user_sid,
1375                                 const char *new_pass,
1376                                 struct samr_Password *lmNewHash, 
1377                                 struct samr_Password *ntNewHash,
1378                                 BOOL user_change,
1379                                 BOOL restrictions,
1380                                 enum samr_RejectReason *reject_reason,
1381                                 struct samr_DomInfo1 **_dominfo) 
1382 {
1383         NTSTATUS nt_status;
1384         struct ldb_dn *user_dn;
1385         struct ldb_message *msg;
1386         int ret;
1387
1388         ret = ldb_transaction_start(ctx);
1389         if (ret) {
1390                 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(ctx)));
1391                 return NT_STATUS_TRANSACTION_ABORTED;
1392         }
1393
1394         user_dn = samdb_search_dn(ctx, mem_ctx, NULL, 
1395                                   "(&(objectSid=%s)(objectClass=user))", 
1396                                   ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
1397         if (!user_dn) {
1398                 ldb_transaction_cancel(ctx);
1399                 DEBUG(3, ("samdb_set_password_sid: SID %s not found in samdb, returning NO_SUCH_USER\n",
1400                           dom_sid_string(mem_ctx, user_sid)));
1401                 return NT_STATUS_NO_SUCH_USER;
1402         }
1403
1404         msg = ldb_msg_new(mem_ctx);
1405         if (msg == NULL) {
1406                 ldb_transaction_cancel(ctx);
1407                 return NT_STATUS_NO_MEMORY;
1408         }
1409
1410         msg->dn = ldb_dn_copy(msg, user_dn);
1411         if (!msg->dn) {
1412                 ldb_transaction_cancel(ctx);
1413                 return NT_STATUS_NO_MEMORY;
1414         }
1415
1416         nt_status = samdb_set_password(ctx, mem_ctx,
1417                                        user_dn, NULL,
1418                                        msg, new_pass, 
1419                                        lmNewHash, ntNewHash,
1420                                        user_change, /* This is a password set, not change */
1421                                        restrictions, /* run restriction tests */
1422                                        reject_reason, _dominfo);
1423         if (!NT_STATUS_IS_OK(nt_status)) {
1424                 ldb_transaction_cancel(ctx);
1425                 return nt_status;
1426         }
1427         
1428         /* modify the samdb record */
1429         ret = samdb_replace(ctx, mem_ctx, msg);
1430         if (ret != 0) {
1431                 ldb_transaction_cancel(ctx);
1432                 return NT_STATUS_ACCESS_DENIED;
1433         }
1434
1435         ret = ldb_transaction_commit(ctx);
1436         if (ret != 0) {
1437                 DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
1438                          ldb_dn_get_linearized(msg->dn),
1439                          ldb_errstring(ctx)));
1440                 return NT_STATUS_TRANSACTION_ABORTED;
1441         }
1442         return NT_STATUS_OK;
1443 }
1444
1445 /****************************************************************************
1446  Create the SID list for this user.
1447 ****************************************************************************/
1448 NTSTATUS security_token_create(TALLOC_CTX *mem_ctx, 
1449                                struct dom_sid *user_sid,
1450                                struct dom_sid *group_sid, 
1451                                int n_groupSIDs,
1452                                struct dom_sid **groupSIDs, 
1453                                BOOL is_authenticated,
1454                                struct security_token **token)
1455 {
1456         struct security_token *ptoken;
1457         int i;
1458         NTSTATUS status;
1459
1460         ptoken = security_token_initialise(mem_ctx);
1461         NT_STATUS_HAVE_NO_MEMORY(ptoken);
1462
1463         ptoken->sids = talloc_array(ptoken, struct dom_sid *, n_groupSIDs + 5);
1464         NT_STATUS_HAVE_NO_MEMORY(ptoken->sids);
1465
1466         ptoken->user_sid = talloc_reference(ptoken, user_sid);
1467         ptoken->group_sid = talloc_reference(ptoken, group_sid);
1468         ptoken->privilege_mask = 0;
1469
1470         ptoken->sids[0] = ptoken->user_sid;
1471         ptoken->sids[1] = ptoken->group_sid;
1472
1473         /*
1474          * Finally add the "standard" SIDs.
1475          * The only difference between guest and "anonymous"
1476          * is the addition of Authenticated_Users.
1477          */
1478         ptoken->sids[2] = dom_sid_parse_talloc(ptoken->sids, SID_WORLD);
1479         NT_STATUS_HAVE_NO_MEMORY(ptoken->sids[2]);
1480         ptoken->sids[3] = dom_sid_parse_talloc(ptoken->sids, SID_NT_NETWORK);
1481         NT_STATUS_HAVE_NO_MEMORY(ptoken->sids[3]);
1482         ptoken->num_sids = 4;
1483
1484         if (is_authenticated) {
1485                 ptoken->sids[4] = dom_sid_parse_talloc(ptoken->sids, SID_NT_AUTHENTICATED_USERS);
1486                 NT_STATUS_HAVE_NO_MEMORY(ptoken->sids[4]);
1487                 ptoken->num_sids++;
1488         }
1489
1490         for (i = 0; i < n_groupSIDs; i++) {
1491                 size_t check_sid_idx;
1492                 for (check_sid_idx = 1; 
1493                      check_sid_idx < ptoken->num_sids; 
1494                      check_sid_idx++) {
1495                         if (dom_sid_equal(ptoken->sids[check_sid_idx], groupSIDs[i])) {
1496                                 break;
1497                         }
1498                 }
1499
1500                 if (check_sid_idx == ptoken->num_sids) {
1501                         ptoken->sids[ptoken->num_sids++] = talloc_reference(ptoken->sids, groupSIDs[i]);
1502                 }
1503         }
1504
1505         /* setup the privilege mask for this token */
1506         status = samdb_privilege_setup(ptoken);
1507         if (!NT_STATUS_IS_OK(status)) {
1508                 talloc_free(ptoken);
1509                 return status;
1510         }
1511
1512         security_token_debug(10, ptoken);
1513
1514         *token = ptoken;
1515
1516         return NT_STATUS_OK;
1517 }
1518
1519
1520 NTSTATUS samdb_create_foreign_security_principal(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, 
1521                                                  struct dom_sid *sid, struct ldb_dn **ret_dn) 
1522 {
1523         struct ldb_message *msg;
1524         struct ldb_dn *basedn;
1525         const char *sidstr;
1526         int ret;
1527         
1528         sidstr = dom_sid_string(mem_ctx, sid);
1529         NT_STATUS_HAVE_NO_MEMORY(sidstr);
1530         
1531         /* We might have to create a ForeignSecurityPrincipal, even if this user
1532          * is in our own domain */
1533         
1534         msg = ldb_msg_new(mem_ctx);
1535         if (msg == NULL) {
1536                 return NT_STATUS_NO_MEMORY;
1537         }
1538         
1539         /* TODO: Hmmm. This feels wrong. How do I find the base dn to
1540          * put the ForeignSecurityPrincipals? d_state->domain_dn does
1541          * not work, this is wrong for the Builtin domain, there's no
1542          * cn=For...,cn=Builtin,dc={BASEDN}.  -- vl
1543          */
1544         
1545         basedn = samdb_search_dn(sam_ctx, mem_ctx, NULL,
1546                                  "(&(objectClass=container)(cn=ForeignSecurityPrincipals))");
1547         
1548         if (basedn == NULL) {
1549                 DEBUG(0, ("Failed to find DN for "
1550                           "ForeignSecurityPrincipal container\n"));
1551                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1552         }
1553         
1554         /* add core elements to the ldb_message for the alias */
1555         msg->dn = ldb_dn_copy(mem_ctx, basedn);
1556         if ( ! ldb_dn_add_child_fmt(msg->dn, "CN=%s", sidstr))
1557                 return NT_STATUS_NO_MEMORY;
1558         
1559         samdb_msg_add_string(sam_ctx, mem_ctx, msg,
1560                              "objectClass",
1561                              "foreignSecurityPrincipal");
1562         
1563         /* create the alias */
1564         ret = samdb_add(sam_ctx, mem_ctx, msg);
1565         if (ret != 0) {
1566                 DEBUG(0,("Failed to create foreignSecurityPrincipal "
1567                          "record %s: %s\n", 
1568                          ldb_dn_get_linearized(msg->dn),
1569                          ldb_errstring(sam_ctx)));
1570                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1571         }
1572         *ret_dn = msg->dn;
1573         return NT_STATUS_OK;
1574 }