8a7a22a641e98dd429f7771ed336a362d6e4ccf4
[samba.git] / source4 / dsdb / common / util.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Samba utility functions
4
5    Copyright (C) Andrew Tridgell 2004
6    Copyright (C) Volker Lendecke 2004
7    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006
8    Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007
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 3 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, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "includes.h"
25 #include "ldb.h"
26 #include "ldb_errors.h"
27 #include "lib/util/util_ldb.h"
28 #include "dsdb/samdb/samdb.h"
29 #include "libcli/security/security.h"
30 #include "librpc/gen_ndr/ndr_security.h"
31 #include "dsdb/common/flags.h"
32 #include "dsdb/common/proto.h"
33 #include "libcli/ldap/ldap_ndr.h"
34 #include "param/param.h"
35 #include "libcli/auth/libcli_auth.h"
36
37 /*
38   search the sam for the specified attributes in a specific domain, filter on
39   objectSid being in domain_sid.
40 */
41 int samdb_search_domain(struct ldb_context *sam_ldb,
42                         TALLOC_CTX *mem_ctx, 
43                         struct ldb_dn *basedn,
44                         struct ldb_message ***res,
45                         const char * const *attrs,
46                         const struct dom_sid *domain_sid,
47                         const char *format, ...)  _PRINTF_ATTRIBUTE(7,8)
48 {
49         va_list ap;
50         int i, count;
51
52         va_start(ap, format);
53         count = gendb_search_v(sam_ldb, mem_ctx, basedn,
54                                res, attrs, format, ap);
55         va_end(ap);
56
57         i=0;
58
59         while (i<count) {
60                 struct dom_sid *entry_sid;
61
62                 entry_sid = samdb_result_dom_sid(mem_ctx, (*res)[i], "objectSid");
63
64                 if ((entry_sid == NULL) ||
65                     (!dom_sid_in_domain(domain_sid, entry_sid))) {
66                         /* Delete that entry from the result set */
67                         (*res)[i] = (*res)[count-1];
68                         count -= 1;
69                         talloc_free(entry_sid);
70                         continue;
71                 }
72                 talloc_free(entry_sid);
73                 i += 1;
74         }
75
76         return count;
77 }
78
79 /*
80   search the sam for a single string attribute in exactly 1 record
81 */
82 const char *samdb_search_string_v(struct ldb_context *sam_ldb,
83                                   TALLOC_CTX *mem_ctx,
84                                   struct ldb_dn *basedn,
85                                   const char *attr_name,
86                                   const char *format, va_list ap) _PRINTF_ATTRIBUTE(5,0)
87 {
88         int count;
89         const char *attrs[2] = { NULL, NULL };
90         struct ldb_message **res = NULL;
91
92         attrs[0] = attr_name;
93
94         count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
95         if (count > 1) {                
96                 DEBUG(1,("samdb: search for %s %s not single valued (count=%d)\n", 
97                          attr_name, format, count));
98         }
99         if (count != 1) {
100                 talloc_free(res);
101                 return NULL;
102         }
103
104         return samdb_result_string(res[0], attr_name, NULL);
105 }
106                                  
107
108 /*
109   search the sam for a single string attribute in exactly 1 record
110 */
111 const char *samdb_search_string(struct ldb_context *sam_ldb,
112                                 TALLOC_CTX *mem_ctx,
113                                 struct ldb_dn *basedn,
114                                 const char *attr_name,
115                                 const char *format, ...) _PRINTF_ATTRIBUTE(5,6)
116 {
117         va_list ap;
118         const char *str;
119
120         va_start(ap, format);
121         str = samdb_search_string_v(sam_ldb, mem_ctx, basedn, attr_name, format, ap);
122         va_end(ap);
123
124         return str;
125 }
126
127 struct ldb_dn *samdb_search_dn(struct ldb_context *sam_ldb,
128                                TALLOC_CTX *mem_ctx,
129                                struct ldb_dn *basedn,
130                                const char *format, ...) _PRINTF_ATTRIBUTE(4,5)
131 {
132         va_list ap;
133         struct ldb_dn *ret;
134         struct ldb_message **res = NULL;
135         int count;
136
137         va_start(ap, format);
138         count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, NULL, format, ap);
139         va_end(ap);
140
141         if (count != 1) return NULL;
142
143         ret = talloc_steal(mem_ctx, res[0]->dn);
144         talloc_free(res);
145
146         return ret;
147 }
148
149 /*
150   search the sam for a dom_sid attribute in exactly 1 record
151 */
152 struct dom_sid *samdb_search_dom_sid(struct ldb_context *sam_ldb,
153                                      TALLOC_CTX *mem_ctx,
154                                      struct ldb_dn *basedn,
155                                      const char *attr_name,
156                                      const char *format, ...) _PRINTF_ATTRIBUTE(5,6)
157 {
158         va_list ap;
159         int count;
160         struct ldb_message **res;
161         const char *attrs[2] = { NULL, NULL };
162         struct dom_sid *sid;
163
164         attrs[0] = attr_name;
165
166         va_start(ap, format);
167         count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
168         va_end(ap);
169         if (count > 1) {                
170                 DEBUG(1,("samdb: search for %s %s not single valued (count=%d)\n", 
171                          attr_name, format, count));
172         }
173         if (count != 1) {
174                 talloc_free(res);
175                 return NULL;
176         }
177         sid = samdb_result_dom_sid(mem_ctx, res[0], attr_name);
178         talloc_free(res);
179         return sid;     
180 }
181
182 /*
183   return the count of the number of records in the sam matching the query
184 */
185 int samdb_search_count(struct ldb_context *sam_ldb,
186                        TALLOC_CTX *mem_ctx,
187                        struct ldb_dn *basedn,
188                        const char *format, ...) _PRINTF_ATTRIBUTE(4,5)
189 {
190         va_list ap;
191         struct ldb_message **res;
192         const char * const attrs[] = { NULL };
193         int ret;
194
195         va_start(ap, format);
196         ret = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
197         va_end(ap);
198
199         return ret;
200 }
201
202
203 /*
204   search the sam for a single integer attribute in exactly 1 record
205 */
206 uint_t samdb_search_uint(struct ldb_context *sam_ldb,
207                          TALLOC_CTX *mem_ctx,
208                          uint_t default_value,
209                          struct ldb_dn *basedn,
210                          const char *attr_name,
211                          const char *format, ...) _PRINTF_ATTRIBUTE(6,7)
212 {
213         va_list ap;
214         int count;
215         struct ldb_message **res;
216         const char *attrs[2] = { NULL, NULL };
217
218         attrs[0] = attr_name;
219
220         va_start(ap, format);
221         count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
222         va_end(ap);
223
224         if (count != 1) {
225                 return default_value;
226         }
227
228         return samdb_result_uint(res[0], attr_name, default_value);
229 }
230
231 /*
232   search the sam for a single signed 64 bit integer attribute in exactly 1 record
233 */
234 int64_t samdb_search_int64(struct ldb_context *sam_ldb,
235                            TALLOC_CTX *mem_ctx,
236                            int64_t default_value,
237                            struct ldb_dn *basedn,
238                            const char *attr_name,
239                            const char *format, ...) _PRINTF_ATTRIBUTE(6,7)
240 {
241         va_list ap;
242         int count;
243         struct ldb_message **res;
244         const char *attrs[2] = { NULL, NULL };
245
246         attrs[0] = attr_name;
247
248         va_start(ap, format);
249         count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
250         va_end(ap);
251
252         if (count != 1) {
253                 return default_value;
254         }
255
256         return samdb_result_int64(res[0], attr_name, default_value);
257 }
258
259 /*
260   search the sam for multipe records each giving a single string attribute
261   return the number of matches, or -1 on error
262 */
263 int samdb_search_string_multiple(struct ldb_context *sam_ldb,
264                                  TALLOC_CTX *mem_ctx,
265                                  struct ldb_dn *basedn,
266                                  const char ***strs,
267                                  const char *attr_name,
268                                  const char *format, ...) _PRINTF_ATTRIBUTE(6,7)
269 {
270         va_list ap;
271         int count, i;
272         const char *attrs[2] = { NULL, NULL };
273         struct ldb_message **res = NULL;
274
275         attrs[0] = attr_name;
276
277         va_start(ap, format);
278         count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
279         va_end(ap);
280
281         if (count <= 0) {
282                 return count;
283         }
284
285         /* make sure its single valued */
286         for (i=0;i<count;i++) {
287                 if (res[i]->num_elements != 1) {
288                         DEBUG(1,("samdb: search for %s %s not single valued\n", 
289                                  attr_name, format));
290                         talloc_free(res);
291                         return -1;
292                 }
293         }
294
295         *strs = talloc_array(mem_ctx, const char *, count+1);
296         if (! *strs) {
297                 talloc_free(res);
298                 return -1;
299         }
300
301         for (i=0;i<count;i++) {
302                 (*strs)[i] = samdb_result_string(res[i], attr_name, NULL);
303         }
304         (*strs)[count] = NULL;
305
306         return count;
307 }
308
309 /*
310   pull a uint from a result set. 
311 */
312 uint_t samdb_result_uint(const struct ldb_message *msg, const char *attr, uint_t default_value)
313 {
314         return ldb_msg_find_attr_as_uint(msg, attr, default_value);
315 }
316
317 /*
318   pull a (signed) int64 from a result set. 
319 */
320 int64_t samdb_result_int64(const struct ldb_message *msg, const char *attr, int64_t default_value)
321 {
322         return ldb_msg_find_attr_as_int64(msg, attr, default_value);
323 }
324
325 /*
326   pull a string from a result set. 
327 */
328 const char *samdb_result_string(const struct ldb_message *msg, const char *attr, 
329                                 const char *default_value)
330 {
331         return ldb_msg_find_attr_as_string(msg, attr, default_value);
332 }
333
334 struct ldb_dn *samdb_result_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const struct ldb_message *msg,
335                                const char *attr, struct ldb_dn *default_value)
336 {
337         struct ldb_dn *ret_dn = ldb_msg_find_attr_as_dn(ldb, mem_ctx, msg, attr);
338         if (!ret_dn) {
339                 return default_value;
340         }
341         return ret_dn;
342 }
343
344 /*
345   pull a rid from a objectSid in a result set. 
346 */
347 uint32_t samdb_result_rid_from_sid(TALLOC_CTX *mem_ctx, const struct ldb_message *msg, 
348                                    const char *attr, uint32_t default_value)
349 {
350         struct dom_sid *sid;
351         uint32_t rid;
352
353         sid = samdb_result_dom_sid(mem_ctx, msg, attr);
354         if (sid == NULL) {
355                 return default_value;
356         }
357         rid = sid->sub_auths[sid->num_auths-1];
358         talloc_free(sid);
359         return rid;
360 }
361
362 /*
363   pull a dom_sid structure from a objectSid in a result set. 
364 */
365 struct dom_sid *samdb_result_dom_sid(TALLOC_CTX *mem_ctx, const struct ldb_message *msg, 
366                                      const char *attr)
367 {
368         const struct ldb_val *v;
369         struct dom_sid *sid;
370         enum ndr_err_code ndr_err;
371         v = ldb_msg_find_ldb_val(msg, attr);
372         if (v == NULL) {
373                 return NULL;
374         }
375         sid = talloc(mem_ctx, struct dom_sid);
376         if (sid == NULL) {
377                 return NULL;
378         }
379         ndr_err = ndr_pull_struct_blob(v, sid, NULL, sid,
380                                        (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
381         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
382                 talloc_free(sid);
383                 return NULL;
384         }
385         return sid;
386 }
387
388 /*
389   pull a guid structure from a objectGUID in a result set. 
390 */
391 struct GUID samdb_result_guid(const struct ldb_message *msg, const char *attr)
392 {
393         const struct ldb_val *v;
394         enum ndr_err_code ndr_err;
395         struct GUID guid;
396         TALLOC_CTX *mem_ctx;
397
398         ZERO_STRUCT(guid);
399
400         v = ldb_msg_find_ldb_val(msg, attr);
401         if (!v) return guid;
402
403         mem_ctx = talloc_named_const(NULL, 0, "samdb_result_guid");
404         if (!mem_ctx) return guid;
405         ndr_err = ndr_pull_struct_blob(v, mem_ctx, NULL, &guid,
406                                        (ndr_pull_flags_fn_t)ndr_pull_GUID);
407         talloc_free(mem_ctx);
408         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
409                 return guid;
410         }
411
412         return guid;
413 }
414
415 /*
416   pull a sid prefix from a objectSid in a result set. 
417   this is used to find the domain sid for a user
418 */
419 struct dom_sid *samdb_result_sid_prefix(TALLOC_CTX *mem_ctx, const struct ldb_message *msg, 
420                                         const char *attr)
421 {
422         struct dom_sid *sid = samdb_result_dom_sid(mem_ctx, msg, attr);
423         if (!sid || sid->num_auths < 1) return NULL;
424         sid->num_auths--;
425         return sid;
426 }
427
428 /*
429   pull a NTTIME in a result set. 
430 */
431 NTTIME samdb_result_nttime(struct ldb_message *msg, const char *attr, NTTIME default_value)
432 {
433         return ldb_msg_find_attr_as_uint64(msg, attr, default_value);
434 }
435
436 /*
437  * Windows uses both 0 and 9223372036854775807 (0x7FFFFFFFFFFFFFFFULL) to
438  * indicate an account doesn't expire.
439  *
440  * When Windows initially creates an account, it sets
441  * accountExpires = 9223372036854775807 (0x7FFFFFFFFFFFFFFF).  However,
442  * when changing from an account having a specific expiration date to
443  * that account never expiring, it sets accountExpires = 0.
444  *
445  * Consolidate that logic here to allow clearer logic for account expiry in
446  * the rest of the code.
447  */
448 NTTIME samdb_result_account_expires(struct ldb_message *msg)
449 {
450         NTTIME ret = ldb_msg_find_attr_as_uint64(msg, "accountExpires",
451                                                  0);
452
453         if (ret == (NTTIME)0)
454                 ret = 0x7FFFFFFFFFFFFFFFULL;
455
456         return ret;
457 }
458
459 /*
460   pull a uint64_t from a result set. 
461 */
462 uint64_t samdb_result_uint64(struct ldb_message *msg, const char *attr, uint64_t default_value)
463 {
464         return ldb_msg_find_attr_as_uint64(msg, attr, default_value);
465 }
466
467
468 /*
469   construct the allow_password_change field from the PwdLastSet attribute and the 
470   domain password settings
471 */
472 NTTIME samdb_result_allow_password_change(struct ldb_context *sam_ldb, 
473                                           TALLOC_CTX *mem_ctx, 
474                                           struct ldb_dn *domain_dn, 
475                                           struct ldb_message *msg, 
476                                           const char *attr)
477 {
478         uint64_t attr_time = samdb_result_uint64(msg, attr, 0);
479         int64_t minPwdAge;
480
481         if (attr_time == 0) {
482                 return 0;
483         }
484
485         minPwdAge = samdb_search_int64(sam_ldb, mem_ctx, 0, domain_dn, "minPwdAge", NULL);
486
487         /* yes, this is a -= not a += as minPwdAge is stored as the negative
488            of the number of 100-nano-seconds */
489         attr_time -= minPwdAge;
490
491         return attr_time;
492 }
493
494 /*
495   construct the force_password_change field from the PwdLastSet
496   attribute, the userAccountControl and the domain password settings
497 */
498 NTTIME samdb_result_force_password_change(struct ldb_context *sam_ldb, 
499                                           TALLOC_CTX *mem_ctx, 
500                                           struct ldb_dn *domain_dn, 
501                                           struct ldb_message *msg)
502 {
503         uint64_t attr_time = samdb_result_uint64(msg, "pwdLastSet", 0);
504         uint32_t userAccountControl = samdb_result_uint64(msg, "userAccountControl", 0);
505         int64_t maxPwdAge;
506
507         /* Machine accounts don't expire, and there is a flag for 'no expiry' */
508         if (!(userAccountControl & UF_NORMAL_ACCOUNT)
509             || (userAccountControl & 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 0x7FFFFFFFFFFFFFFFULL;
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, "unicodePwd", &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, "dBCSPwd", &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   This requires that the attributes: 
626    pwdLastSet
627    userAccountControl
628   be included in 'msg'
629 */
630 uint32_t samdb_result_acct_flags(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, 
631                                  struct ldb_message *msg, struct ldb_dn *domain_dn)
632 {
633         uint32_t userAccountControl = ldb_msg_find_attr_as_uint(msg, "userAccountControl", 0);
634         uint32_t acct_flags = samdb_uf2acb(userAccountControl); 
635         NTTIME must_change_time;
636         NTTIME now;
637         
638         must_change_time = samdb_result_force_password_change(sam_ctx, mem_ctx, 
639                                                               domain_dn, msg);
640         
641         /* Test account expire time */
642         unix_to_nt_time(&now, time(NULL));
643         /* check for expired password */
644         if (must_change_time < now) {
645                 acct_flags |= ACB_PW_EXPIRED;
646         }
647         return acct_flags;
648 }
649
650
651 /* Find an attribute, with a particular value */
652
653 /* The current callers of this function expect a very specific
654  * behaviour: In particular, objectClass subclass equivilance is not
655  * wanted.  This means that we should not lookup the schema for the
656  * comparison function */
657 struct ldb_message_element *samdb_find_attribute(struct ldb_context *ldb, 
658                                                  const struct ldb_message *msg, 
659                                                  const char *name, const char *value)
660 {
661         int i;
662         struct ldb_message_element *el = ldb_msg_find_element(msg, name);
663
664         if (!el) {
665                 return NULL;
666         }
667
668         for (i=0;i<el->num_values;i++) {
669                 if (ldb_attr_cmp(value, (char *)el->values[i].data) == 0) {
670                         return el;
671                 }
672         }
673
674         return NULL;
675 }
676
677 int samdb_find_or_add_value(struct ldb_context *ldb, struct ldb_message *msg, const char *name, const char *set_value)
678 {
679         if (samdb_find_attribute(ldb, msg, name, set_value) == NULL) {
680                 return samdb_msg_add_string(ldb, msg, msg, name, set_value);
681         }
682         return LDB_SUCCESS;
683 }
684
685 int samdb_find_or_add_attribute(struct ldb_context *ldb, struct ldb_message *msg, const char *name, const char *set_value)
686 {
687         struct ldb_message_element *el;
688
689         el = ldb_msg_find_element(msg, name);
690         if (el) {
691                 return LDB_SUCCESS;
692         }
693                 
694         return samdb_msg_add_string(ldb, msg, msg, name, set_value);
695 }
696
697
698
699 /*
700   add a string element to a message
701 */
702 int samdb_msg_add_string(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
703                          const char *attr_name, const char *str)
704 {
705         char *s = talloc_strdup(mem_ctx, str);
706         char *a = talloc_strdup(mem_ctx, attr_name);
707         if (s == NULL || a == NULL) {
708                 return LDB_ERR_OPERATIONS_ERROR;
709         }
710         return ldb_msg_add_string(msg, a, s);
711 }
712
713 /*
714   add a dom_sid element to a message
715 */
716 int samdb_msg_add_dom_sid(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
717                          const char *attr_name, struct dom_sid *sid)
718 {
719         struct ldb_val v;
720         enum ndr_err_code ndr_err;
721
722         ndr_err = ndr_push_struct_blob(&v, mem_ctx, 
723                                        lp_iconv_convenience(ldb_get_opaque(sam_ldb, "loadparm")),
724                                        sid,
725                                        (ndr_push_flags_fn_t)ndr_push_dom_sid);
726         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
727                 return -1;
728         }
729         return ldb_msg_add_value(msg, attr_name, &v, NULL);
730 }
731
732
733 /*
734   add a delete element operation to a message
735 */
736 int samdb_msg_add_delete(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
737                          const char *attr_name)
738 {
739         /* we use an empty replace rather than a delete, as it allows for 
740            samdb_replace() to be used everywhere */
741         return ldb_msg_add_empty(msg, attr_name, LDB_FLAG_MOD_REPLACE, NULL);
742 }
743
744 /*
745   add a add attribute value to a message
746 */
747 int samdb_msg_add_addval(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
748                          const char *attr_name, const char *value)
749 {
750         struct ldb_message_element *el;
751         char *a, *v;
752         int ret;
753         a = talloc_strdup(mem_ctx, attr_name);
754         if (a == NULL)
755                 return -1;
756         v = talloc_strdup(mem_ctx, value);
757         if (v == NULL)
758                 return -1;
759         ret = ldb_msg_add_string(msg, a, v);
760         if (ret != 0)
761                 return ret;
762         el = ldb_msg_find_element(msg, a);
763         if (el == NULL)
764                 return -1;
765         el->flags = LDB_FLAG_MOD_ADD;
766         return 0;
767 }
768
769 /*
770   add a delete attribute value to a message
771 */
772 int samdb_msg_add_delval(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
773                          const char *attr_name, const char *value)
774 {
775         struct ldb_message_element *el;
776         char *a, *v;
777         int ret;
778         a = talloc_strdup(mem_ctx, attr_name);
779         if (a == NULL)
780                 return -1;
781         v = talloc_strdup(mem_ctx, value);
782         if (v == NULL)
783                 return -1;
784         ret = ldb_msg_add_string(msg, a, v);
785         if (ret != 0)
786                 return ret;
787         el = ldb_msg_find_element(msg, a);
788         if (el == NULL)
789                 return -1;
790         el->flags = LDB_FLAG_MOD_DELETE;
791         return 0;
792 }
793
794 /*
795   add a int element to a message
796 */
797 int samdb_msg_add_int(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
798                        const char *attr_name, int v)
799 {
800         const char *s = talloc_asprintf(mem_ctx, "%d", v);
801         return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
802 }
803
804 /*
805   add a uint_t element to a message
806 */
807 int samdb_msg_add_uint(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
808                        const char *attr_name, uint_t v)
809 {
810         const char *s = talloc_asprintf(mem_ctx, "%u", v);
811         return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
812 }
813
814 /*
815   add a (signed) int64_t element to a message
816 */
817 int samdb_msg_add_int64(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
818                         const char *attr_name, int64_t v)
819 {
820         const char *s = talloc_asprintf(mem_ctx, "%lld", (long long)v);
821         return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
822 }
823
824 /*
825   add a uint64_t element to a message
826 */
827 int samdb_msg_add_uint64(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
828                         const char *attr_name, uint64_t v)
829 {
830         const char *s = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)v);
831         return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
832 }
833
834 /*
835   add a samr_Password element to a message
836 */
837 int samdb_msg_add_hash(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
838                        const char *attr_name, struct samr_Password *hash)
839 {
840         struct ldb_val val;
841         val.data = talloc_memdup(mem_ctx, hash->hash, 16);
842         if (!val.data) {
843                 return -1;
844         }
845         val.length = 16;
846         return ldb_msg_add_value(msg, attr_name, &val, NULL);
847 }
848
849 /*
850   add a samr_Password array to a message
851 */
852 int samdb_msg_add_hashes(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
853                          const char *attr_name, struct samr_Password *hashes, uint_t count)
854 {
855         struct ldb_val val;
856         int i;
857         val.data = talloc_array_size(mem_ctx, 16, count);
858         val.length = count*16;
859         if (!val.data) {
860                 return -1;
861         }
862         for (i=0;i<count;i++) {
863                 memcpy(i*16 + (char *)val.data, hashes[i].hash, 16);
864         }
865         return ldb_msg_add_value(msg, attr_name, &val, NULL);
866 }
867
868 /*
869   add a acct_flags element to a message
870 */
871 int samdb_msg_add_acct_flags(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
872                              const char *attr_name, uint32_t v)
873 {
874         return samdb_msg_add_uint(sam_ldb, mem_ctx, msg, attr_name, samdb_acb2uf(v));
875 }
876
877 /*
878   add a logon_hours element to a message
879 */
880 int samdb_msg_add_logon_hours(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
881                               const char *attr_name, struct samr_LogonHours *hours)
882 {
883         struct ldb_val val;
884         val.length = hours->units_per_week / 8;
885         val.data = hours->bits;
886         return ldb_msg_add_value(msg, attr_name, &val, NULL);
887 }
888
889 /*
890   add a general value element to a message
891 */
892 int samdb_msg_add_value(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
893                               const char *attr_name, const struct ldb_val *val)
894 {
895         return ldb_msg_add_value(msg, attr_name, val, NULL);
896 }
897
898 /*
899   sets a general value element to a message
900 */
901 int samdb_msg_set_value(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
902                         const char *attr_name, const struct ldb_val *val)
903 {
904         struct ldb_message_element *el;
905
906         el = ldb_msg_find_element(msg, attr_name);
907         if (el) {
908                 el->num_values = 0;
909         }
910         return ldb_msg_add_value(msg, attr_name, val, NULL);
911 }
912
913 /*
914   set a string element in a message
915 */
916 int samdb_msg_set_string(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
917                          const char *attr_name, const char *str)
918 {
919         struct ldb_message_element *el;
920
921         el = ldb_msg_find_element(msg, attr_name);
922         if (el) {
923                 el->num_values = 0;
924         }
925         return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, str);
926 }
927
928 /*
929   replace elements in a record
930 */
931 int samdb_replace(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg)
932 {
933         int i;
934
935         /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
936         for (i=0;i<msg->num_elements;i++) {
937                 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
938         }
939
940         /* modify the samdb record */
941         return ldb_modify(sam_ldb, msg);
942 }
943
944 /*
945   return a default security descriptor
946 */
947 struct security_descriptor *samdb_default_security_descriptor(TALLOC_CTX *mem_ctx)
948 {
949         struct security_descriptor *sd;
950
951         sd = security_descriptor_initialise(mem_ctx);
952
953         return sd;
954 }
955
956 struct ldb_dn *samdb_base_dn(struct ldb_context *sam_ctx) 
957 {
958         return ldb_get_default_basedn(sam_ctx);
959 }
960
961 struct ldb_dn *samdb_config_dn(struct ldb_context *sam_ctx) 
962 {
963         return ldb_get_config_basedn(sam_ctx);
964 }
965
966 struct ldb_dn *samdb_schema_dn(struct ldb_context *sam_ctx) 
967 {
968         return ldb_get_schema_basedn(sam_ctx);
969 }
970
971 struct ldb_dn *samdb_root_dn(struct ldb_context *sam_ctx) 
972 {
973         return ldb_get_root_basedn(sam_ctx);
974 }
975
976 struct ldb_dn *samdb_partitions_dn(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx)
977 {
978         struct ldb_dn *new_dn;
979
980         new_dn = ldb_dn_copy(mem_ctx, samdb_config_dn(sam_ctx));
981         if ( ! ldb_dn_add_child_fmt(new_dn, "CN=Partitions")) {
982                 talloc_free(new_dn);
983                 return NULL;
984         }
985         return new_dn;
986 }
987
988 struct ldb_dn *samdb_sites_dn(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx)
989 {
990         struct ldb_dn *new_dn;
991
992         new_dn = ldb_dn_copy(mem_ctx, samdb_config_dn(sam_ctx));
993         if ( ! ldb_dn_add_child_fmt(new_dn, "CN=Sites")) {
994                 talloc_free(new_dn);
995                 return NULL;
996         }
997         return new_dn;
998 }
999
1000 /*
1001   work out the domain sid for the current open ldb
1002 */
1003 const struct dom_sid *samdb_domain_sid(struct ldb_context *ldb)
1004 {
1005         TALLOC_CTX *tmp_ctx;
1006         const struct dom_sid *domain_sid;
1007         const char *attrs[] = {
1008                 "objectSid",
1009                 NULL
1010         };
1011         struct ldb_result *res;
1012         int ret;
1013
1014         /* see if we have a cached copy */
1015         domain_sid = (struct dom_sid *)ldb_get_opaque(ldb, "cache.domain_sid");
1016         if (domain_sid) {
1017                 return domain_sid;
1018         }
1019
1020         tmp_ctx = talloc_new(ldb);
1021         if (tmp_ctx == NULL) {
1022                 goto failed;
1023         }
1024
1025         ret = ldb_search_exp_fmt(ldb, tmp_ctx, &res, ldb_get_default_basedn(ldb), LDB_SCOPE_BASE, attrs, "objectSid=*");
1026
1027         if (ret != LDB_SUCCESS) {
1028                 goto failed;
1029         }
1030         
1031         if (res->count != 1) {
1032                 goto failed;
1033         }
1034
1035         domain_sid = samdb_result_dom_sid(tmp_ctx, res->msgs[0], "objectSid");
1036         if (domain_sid == NULL) {
1037                 goto failed;
1038         }
1039
1040         /* cache the domain_sid in the ldb */
1041         if (ldb_set_opaque(ldb, "cache.domain_sid", domain_sid) != LDB_SUCCESS) {
1042                 goto failed;
1043         }
1044
1045         talloc_steal(ldb, domain_sid);
1046         talloc_free(tmp_ctx);
1047
1048         return domain_sid;
1049
1050 failed:
1051         DEBUG(1,("Failed to find domain_sid for open ldb\n"));
1052         talloc_free(tmp_ctx);
1053         return NULL;
1054 }
1055
1056 bool samdb_set_domain_sid(struct ldb_context *ldb, const struct dom_sid *dom_sid_in)
1057 {
1058         TALLOC_CTX *tmp_ctx;
1059         struct dom_sid *dom_sid_new;
1060         struct dom_sid *dom_sid_old;
1061
1062         /* see if we have a cached copy */
1063         dom_sid_old = talloc_get_type(ldb_get_opaque(ldb, 
1064                                                      "cache.domain_sid"), struct dom_sid);
1065
1066         tmp_ctx = talloc_new(ldb);
1067         if (tmp_ctx == NULL) {
1068                 goto failed;
1069         }
1070
1071         dom_sid_new = dom_sid_dup(tmp_ctx, dom_sid_in);
1072         if (!dom_sid_new) {
1073                 goto failed;
1074         }
1075
1076         /* cache the domain_sid in the ldb */
1077         if (ldb_set_opaque(ldb, "cache.domain_sid", dom_sid_new) != LDB_SUCCESS) {
1078                 goto failed;
1079         }
1080
1081         talloc_steal(ldb, dom_sid_new);
1082         talloc_free(tmp_ctx);
1083         talloc_free(dom_sid_old);
1084
1085         return true;
1086
1087 failed:
1088         DEBUG(1,("Failed to set our own cached domain SID in the ldb!\n"));
1089         talloc_free(tmp_ctx);
1090         return false;
1091 }
1092
1093 /* Obtain the short name of the flexible single master operator
1094  * (FSMO), such as the PDC Emulator */
1095 const char *samdb_result_fsmo_name(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const struct ldb_message *msg, 
1096                              const char *attr)
1097 {
1098         /* Format is cn=NTDS Settings,cn=<NETBIOS name of FSMO>,.... */
1099         struct ldb_dn *fsmo_dn = ldb_msg_find_attr_as_dn(ldb, mem_ctx, msg, attr);
1100         const struct ldb_val *val = ldb_dn_get_component_val(fsmo_dn, 1);
1101         const char *name = ldb_dn_get_component_name(fsmo_dn, 1);
1102
1103         if (!name || (ldb_attr_cmp(name, "cn") != 0)) {
1104                 /* Ensure this matches the format.  This gives us a
1105                  * bit more confidence that a 'cn' value will be a
1106                  * ascii string */
1107                 return NULL;
1108         }
1109         if (val) {
1110                 return (char *)val->data;
1111         }
1112         return NULL;
1113 }
1114
1115 /*
1116   work out the ntds settings dn for the current open ldb
1117 */
1118 struct ldb_dn *samdb_ntds_settings_dn(struct ldb_context *ldb)
1119 {
1120         TALLOC_CTX *tmp_ctx;
1121         const char *root_attrs[] = { "dsServiceName", NULL };
1122         int ret;
1123         struct ldb_result *root_res;
1124         struct ldb_dn *settings_dn;
1125         
1126         /* see if we have a cached copy */
1127         settings_dn = (struct ldb_dn *)ldb_get_opaque(ldb, "cache.settings_dn");
1128         if (settings_dn) {
1129                 return settings_dn;
1130         }
1131
1132         tmp_ctx = talloc_new(ldb);
1133         if (tmp_ctx == NULL) {
1134                 goto failed;
1135         }
1136         
1137
1138         ret = ldb_search(ldb, ldb_dn_new(tmp_ctx, ldb, ""), LDB_SCOPE_BASE, NULL, root_attrs, &root_res);
1139         if (ret) {
1140                 DEBUG(1,("Searching for dsServiceName in rootDSE failed: %s\n", 
1141                          ldb_errstring(ldb)));
1142                 goto failed;
1143         }
1144         talloc_steal(tmp_ctx, root_res);
1145
1146         if (root_res->count != 1) {
1147                 goto failed;
1148         }
1149
1150         settings_dn = ldb_msg_find_attr_as_dn(ldb, tmp_ctx, root_res->msgs[0], "dsServiceName");
1151
1152         /* cache the domain_sid in the ldb */
1153         if (ldb_set_opaque(ldb, "cache.settings_dn", settings_dn) != LDB_SUCCESS) {
1154                 goto failed;
1155         }
1156
1157         talloc_steal(ldb, settings_dn);
1158         talloc_free(tmp_ctx);
1159
1160         return settings_dn;
1161
1162 failed:
1163         DEBUG(1,("Failed to find our own NTDS Settings DN in the ldb!\n"));
1164         talloc_free(tmp_ctx);
1165         return NULL;
1166 }
1167
1168 /*
1169   work out the ntds settings invocationId for the current open ldb
1170 */
1171 const struct GUID *samdb_ntds_invocation_id(struct ldb_context *ldb)
1172 {
1173         TALLOC_CTX *tmp_ctx;
1174         const char *attrs[] = { "invocationId", NULL };
1175         int ret;
1176         struct ldb_result *res;
1177         struct GUID *invocation_id;
1178         
1179         /* see if we have a cached copy */
1180         invocation_id = (struct GUID *)ldb_get_opaque(ldb, "cache.invocation_id");
1181         if (invocation_id) {
1182                 return invocation_id;
1183         }
1184
1185         tmp_ctx = talloc_new(ldb);
1186         if (tmp_ctx == NULL) {
1187                 goto failed;
1188         }
1189
1190         ret = ldb_search(ldb, samdb_ntds_settings_dn(ldb), LDB_SCOPE_BASE, NULL, attrs, &res);
1191         if (ret) {
1192                 goto failed;
1193         }
1194         talloc_steal(tmp_ctx, res);
1195
1196         if (res->count != 1) {
1197                 goto failed;
1198         }
1199
1200         invocation_id = talloc(tmp_ctx, struct GUID);
1201         if (!invocation_id) {
1202                 goto failed;
1203         }
1204
1205         *invocation_id = samdb_result_guid(res->msgs[0], "invocationId");
1206
1207         /* cache the domain_sid in the ldb */
1208         if (ldb_set_opaque(ldb, "cache.invocation_id", invocation_id) != LDB_SUCCESS) {
1209                 goto failed;
1210         }
1211
1212         talloc_steal(ldb, invocation_id);
1213         talloc_free(tmp_ctx);
1214
1215         return invocation_id;
1216
1217 failed:
1218         DEBUG(1,("Failed to find our own NTDS Settings invocationId in the ldb!\n"));
1219         talloc_free(tmp_ctx);
1220         return NULL;
1221 }
1222
1223 bool samdb_set_ntds_invocation_id(struct ldb_context *ldb, const struct GUID *invocation_id_in)
1224 {
1225         TALLOC_CTX *tmp_ctx;
1226         struct GUID *invocation_id_new;
1227         struct GUID *invocation_id_old;
1228
1229         /* see if we have a cached copy */
1230         invocation_id_old = (struct GUID *)ldb_get_opaque(ldb, 
1231                                                          "cache.invocation_id");
1232
1233         tmp_ctx = talloc_new(ldb);
1234         if (tmp_ctx == NULL) {
1235                 goto failed;
1236         }
1237
1238         invocation_id_new = talloc(tmp_ctx, struct GUID);
1239         if (!invocation_id_new) {
1240                 goto failed;
1241         }
1242
1243         *invocation_id_new = *invocation_id_in;
1244
1245         /* cache the domain_sid in the ldb */
1246         if (ldb_set_opaque(ldb, "cache.invocation_id", invocation_id_new) != LDB_SUCCESS) {
1247                 goto failed;
1248         }
1249
1250         talloc_steal(ldb, invocation_id_new);
1251         talloc_free(tmp_ctx);
1252         talloc_free(invocation_id_old);
1253
1254         return true;
1255
1256 failed:
1257         DEBUG(1,("Failed to set our own cached invocationId in the ldb!\n"));
1258         talloc_free(tmp_ctx);
1259         return false;
1260 }
1261
1262 /*
1263   work out the ntds settings objectGUID for the current open ldb
1264 */
1265 const struct GUID *samdb_ntds_objectGUID(struct ldb_context *ldb)
1266 {
1267         TALLOC_CTX *tmp_ctx;
1268         const char *attrs[] = { "objectGUID", NULL };
1269         int ret;
1270         struct ldb_result *res;
1271         struct GUID *ntds_guid;
1272         
1273         /* see if we have a cached copy */
1274         ntds_guid = (struct GUID *)ldb_get_opaque(ldb, "cache.ntds_guid");
1275         if (ntds_guid) {
1276                 return ntds_guid;
1277         }
1278
1279         tmp_ctx = talloc_new(ldb);
1280         if (tmp_ctx == NULL) {
1281                 goto failed;
1282         }
1283
1284         ret = ldb_search(ldb, samdb_ntds_settings_dn(ldb), LDB_SCOPE_BASE, NULL, attrs, &res);
1285         if (ret) {
1286                 goto failed;
1287         }
1288         talloc_steal(tmp_ctx, res);
1289
1290         if (res->count != 1) {
1291                 goto failed;
1292         }
1293
1294         ntds_guid = talloc(tmp_ctx, struct GUID);
1295         if (!ntds_guid) {
1296                 goto failed;
1297         }
1298
1299         *ntds_guid = samdb_result_guid(res->msgs[0], "objectGUID");
1300
1301         /* cache the domain_sid in the ldb */
1302         if (ldb_set_opaque(ldb, "cache.ntds_guid", ntds_guid) != LDB_SUCCESS) {
1303                 goto failed;
1304         }
1305
1306         talloc_steal(ldb, ntds_guid);
1307         talloc_free(tmp_ctx);
1308
1309         return ntds_guid;
1310
1311 failed:
1312         DEBUG(1,("Failed to find our own NTDS Settings objectGUID in the ldb!\n"));
1313         talloc_free(tmp_ctx);
1314         return NULL;
1315 }
1316
1317 bool samdb_set_ntds_objectGUID(struct ldb_context *ldb, const struct GUID *ntds_guid_in)
1318 {
1319         TALLOC_CTX *tmp_ctx;
1320         struct GUID *ntds_guid_new;
1321         struct GUID *ntds_guid_old;
1322         
1323         /* see if we have a cached copy */
1324         ntds_guid_old = (struct GUID *)ldb_get_opaque(ldb, "cache.ntds_guid");
1325
1326         tmp_ctx = talloc_new(ldb);
1327         if (tmp_ctx == NULL) {
1328                 goto failed;
1329         }
1330
1331         ntds_guid_new = talloc(tmp_ctx, struct GUID);
1332         if (!ntds_guid_new) {
1333                 goto failed;
1334         }
1335
1336         *ntds_guid_new = *ntds_guid_in;
1337
1338         /* cache the domain_sid in the ldb */
1339         if (ldb_set_opaque(ldb, "cache.ntds_guid", ntds_guid_new) != LDB_SUCCESS) {
1340                 goto failed;
1341         }
1342
1343         talloc_steal(ldb, ntds_guid_new);
1344         talloc_free(tmp_ctx);
1345         talloc_free(ntds_guid_old);
1346
1347         return true;
1348
1349 failed:
1350         DEBUG(1,("Failed to set our own cached invocationId in the ldb!\n"));
1351         talloc_free(tmp_ctx);
1352         return false;
1353 }
1354
1355 /*
1356   work out the server dn for the current open ldb
1357 */
1358 struct ldb_dn *samdb_server_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx)
1359 {
1360         return ldb_dn_get_parent(mem_ctx, samdb_ntds_settings_dn(ldb));
1361 }
1362
1363 /*
1364   work out the server dn for the current open ldb
1365 */
1366 struct ldb_dn *samdb_server_site_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx)
1367 {
1368         struct ldb_dn *server_dn;
1369         struct ldb_dn *server_site_dn;
1370
1371         server_dn = samdb_server_dn(ldb, mem_ctx);
1372         if (!server_dn) return NULL;
1373
1374         server_site_dn = ldb_dn_get_parent(mem_ctx, server_dn);
1375
1376         talloc_free(server_dn);
1377         return server_site_dn;
1378 }
1379
1380 /*
1381   work out if we are the PDC for the domain of the current open ldb
1382 */
1383 bool samdb_is_pdc(struct ldb_context *ldb)
1384 {
1385         const char *dom_attrs[] = { "fSMORoleOwner", NULL };
1386         int ret;
1387         struct ldb_result *dom_res;
1388         TALLOC_CTX *tmp_ctx;
1389         bool is_pdc;
1390         struct ldb_dn *pdc;
1391
1392         tmp_ctx = talloc_new(ldb);
1393         if (tmp_ctx == NULL) {
1394                 DEBUG(1, ("talloc_new failed in samdb_is_pdc"));
1395                 return false;
1396         }
1397
1398         ret = ldb_search(ldb, ldb_get_default_basedn(ldb), LDB_SCOPE_BASE, NULL, dom_attrs, &dom_res);
1399         if (ret) {
1400                 DEBUG(1,("Searching for fSMORoleOwner in %s failed: %s\n", 
1401                          ldb_dn_get_linearized(ldb_get_default_basedn(ldb)), 
1402                          ldb_errstring(ldb)));
1403                 goto failed;
1404         }
1405         talloc_steal(tmp_ctx, dom_res);
1406         if (dom_res->count != 1) {
1407                 goto failed;
1408         }
1409
1410         pdc = ldb_msg_find_attr_as_dn(ldb, tmp_ctx, dom_res->msgs[0], "fSMORoleOwner");
1411
1412         if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), pdc) == 0) {
1413                 is_pdc = true;
1414         } else {
1415                 is_pdc = false;
1416         }
1417
1418         talloc_free(tmp_ctx);
1419
1420         return is_pdc;
1421
1422 failed:
1423         DEBUG(1,("Failed to find if we are the PDC for this ldb\n"));
1424         talloc_free(tmp_ctx);
1425         return false;
1426 }
1427
1428 /*
1429   work out if we are a Global Catalog server for the domain of the current open ldb
1430 */
1431 bool samdb_is_gc(struct ldb_context *ldb)
1432 {
1433         const char *attrs[] = { "options", NULL };
1434         int ret, options;
1435         struct ldb_result *res;
1436         TALLOC_CTX *tmp_ctx;
1437
1438         tmp_ctx = talloc_new(ldb);
1439         if (tmp_ctx == NULL) {
1440                 DEBUG(1, ("talloc_new failed in samdb_is_pdc"));
1441                 return false;
1442         }
1443
1444         /* Query cn=ntds settings,.... */
1445         ret = ldb_search(ldb, samdb_ntds_settings_dn(ldb), LDB_SCOPE_BASE, NULL, attrs, &res);
1446         if (ret) {
1447                 return false;
1448         }
1449         if (res->count != 1) {
1450                 talloc_free(res);
1451                 return false;
1452         }
1453
1454         options = ldb_msg_find_attr_as_int(res->msgs[0], "options", 0);
1455         talloc_free(res);
1456         talloc_free(tmp_ctx);
1457
1458         /* if options attribute has the 0x00000001 flag set, then enable the global catlog */
1459         if (options & 0x000000001) {
1460                 return true;
1461         }
1462         return false;
1463 }
1464
1465 /* Find a domain object in the parents of a particular DN.  */
1466 int samdb_search_for_parent_domain(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, struct ldb_dn *dn,
1467                                    struct ldb_dn **parent_dn, const char **errstring)
1468 {
1469         TALLOC_CTX *local_ctx;
1470         struct ldb_dn *sdn = dn;
1471         struct ldb_result *res = NULL;
1472         int ret = 0;
1473         const char *attrs[] = { NULL };
1474
1475         local_ctx = talloc_new(mem_ctx);
1476         if (local_ctx == NULL) return LDB_ERR_OPERATIONS_ERROR;
1477         
1478         while ((sdn = ldb_dn_get_parent(local_ctx, sdn))) {
1479                 ret = ldb_search(ldb, sdn, LDB_SCOPE_BASE, 
1480                                  "(|(|(objectClass=domain)(objectClass=builtinDomain))(objectClass=samba4LocalDomain))", attrs, &res);
1481                 if (ret == LDB_SUCCESS) {
1482                         talloc_steal(local_ctx, res);
1483                         if (res->count == 1) {
1484                                 break;
1485                         }
1486                 } else {
1487                         break;
1488                 }
1489         }
1490
1491         if (ret != LDB_SUCCESS) {
1492                 *errstring = talloc_asprintf(mem_ctx, "Error searching for parent domain of %s, failed searching for %s: %s",
1493                                              ldb_dn_get_linearized(dn),
1494                                              ldb_dn_get_linearized(sdn),
1495                                              ldb_errstring(ldb));
1496                 talloc_free(local_ctx);
1497                 return ret;
1498         }
1499         if (res->count != 1) {
1500                 *errstring = talloc_asprintf(mem_ctx, "Invalid dn (%s), not child of a domain object",
1501                                              ldb_dn_get_linearized(dn));
1502                 talloc_free(local_ctx);
1503                 return LDB_ERR_CONSTRAINT_VIOLATION;
1504         }
1505
1506         *parent_dn = talloc_steal(mem_ctx, res->msgs[0]->dn);
1507         talloc_free(local_ctx);
1508         return ret;
1509 }
1510
1511 /*
1512   check that a password is sufficiently complex
1513 */
1514 static bool samdb_password_complexity_ok(const char *pass)
1515 {
1516         return check_password_quality(pass);
1517 }
1518
1519
1520
1521 /*
1522   set the user password using plaintext, obeying any user or domain
1523   password restrictions
1524
1525   note that this function doesn't actually store the result in the
1526   database, it just fills in the "mod" structure with ldb modify
1527   elements to setup the correct change when samdb_replace() is
1528   called. This allows the caller to combine the change with other
1529   changes (as is needed by some of the set user info levels)
1530
1531   The caller should probably have a transaction wrapping this
1532 */
1533 _PUBLIC_ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
1534                             struct ldb_dn *user_dn,
1535                             struct ldb_dn *domain_dn,
1536                             struct ldb_message *mod,
1537                             const char *new_pass,
1538                             struct samr_Password *lmNewHash, 
1539                             struct samr_Password *ntNewHash,
1540                             bool user_change,
1541                             enum samr_RejectReason *reject_reason,
1542                             struct samr_DomInfo1 **_dominfo)
1543 {
1544         const char * const user_attrs[] = { "userAccountControl", "lmPwdHistory", 
1545                                             "ntPwdHistory", 
1546                                             "dBCSPwd", "unicodePwd", 
1547                                             "objectSid", 
1548                                             "pwdLastSet", NULL };
1549         const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength", 
1550                                               "maxPwdAge", "minPwdAge", 
1551                                               "minPwdLength", NULL };
1552         NTTIME pwdLastSet;
1553         int64_t minPwdAge;
1554         uint_t minPwdLength, pwdProperties, pwdHistoryLength;
1555         uint_t userAccountControl;
1556         struct samr_Password *sambaLMPwdHistory, *sambaNTPwdHistory, *lmPwdHash, *ntPwdHash;
1557         struct samr_Password local_lmNewHash, local_ntNewHash;
1558         int sambaLMPwdHistory_len, sambaNTPwdHistory_len;
1559         struct dom_sid *domain_sid;
1560         struct ldb_message **res;
1561         bool restrictions;
1562         int count;
1563         time_t now = time(NULL);
1564         NTTIME now_nt;
1565         int i;
1566
1567         /* we need to know the time to compute password age */
1568         unix_to_nt_time(&now_nt, now);
1569
1570         /* pull all the user parameters */
1571         count = gendb_search_dn(ctx, mem_ctx, user_dn, &res, user_attrs);
1572         if (count != 1) {
1573                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1574         }
1575         userAccountControl = samdb_result_uint(res[0],   "userAccountControl", 0);
1576         sambaLMPwdHistory_len =   samdb_result_hashes(mem_ctx, res[0], 
1577                                                  "lmPwdHistory", &sambaLMPwdHistory);
1578         sambaNTPwdHistory_len =   samdb_result_hashes(mem_ctx, res[0], 
1579                                                  "ntPwdHistory", &sambaNTPwdHistory);
1580         lmPwdHash =          samdb_result_hash(mem_ctx, res[0],   "dBCSPwd");
1581         ntPwdHash =          samdb_result_hash(mem_ctx, res[0],   "unicodePwd");
1582         pwdLastSet =         samdb_result_uint64(res[0], "pwdLastSet", 0);
1583
1584         /* Only non-trust accounts have restrictions (possibly this
1585          * test is the wrong way around, but I like to be restrictive
1586          * if possible */
1587         restrictions = !(userAccountControl & (UF_INTERDOMAIN_TRUST_ACCOUNT
1588                                                |UF_WORKSTATION_TRUST_ACCOUNT
1589                                                |UF_SERVER_TRUST_ACCOUNT)); 
1590
1591         if (domain_dn) {
1592                 /* pull the domain parameters */
1593                 count = gendb_search_dn(ctx, mem_ctx, domain_dn, &res, domain_attrs);
1594                 if (count != 1) {
1595                         DEBUG(2, ("samdb_set_password: Domain DN %s is invalid, for user %s\n", 
1596                                   ldb_dn_get_linearized(domain_dn),
1597                                   ldb_dn_get_linearized(user_dn)));
1598                         return NT_STATUS_NO_SUCH_DOMAIN;
1599                 }
1600         } else {
1601                 /* work out the domain sid, and pull the domain from there */
1602                 domain_sid =         samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
1603                 if (domain_sid == NULL) {
1604                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
1605                 }
1606
1607                 count = gendb_search(ctx, mem_ctx, NULL, &res, domain_attrs, 
1608                                      "(objectSid=%s)", 
1609                                      ldap_encode_ndr_dom_sid(mem_ctx, domain_sid));
1610                 if (count != 1) {
1611                         DEBUG(2, ("samdb_set_password: Could not find domain to match SID: %s, for user %s\n", 
1612                                   dom_sid_string(mem_ctx, domain_sid),
1613                                   ldb_dn_get_linearized(user_dn)));
1614                         return NT_STATUS_NO_SUCH_DOMAIN;
1615                 }
1616         }
1617
1618         pwdProperties =    samdb_result_uint(res[0],   "pwdProperties", 0);
1619         pwdHistoryLength = samdb_result_uint(res[0],   "pwdHistoryLength", 0);
1620         minPwdLength =     samdb_result_uint(res[0],   "minPwdLength", 0);
1621         minPwdAge =        samdb_result_int64(res[0],  "minPwdAge", 0);
1622
1623         if (_dominfo) {
1624                 struct samr_DomInfo1 *dominfo;
1625                 /* on failure we need to fill in the reject reasons */
1626                 dominfo = talloc(mem_ctx, struct samr_DomInfo1);
1627                 if (dominfo == NULL) {
1628                         return NT_STATUS_NO_MEMORY;
1629                 }
1630                 dominfo->min_password_length     = minPwdLength;
1631                 dominfo->password_properties     = pwdProperties;
1632                 dominfo->password_history_length = pwdHistoryLength;
1633                 dominfo->max_password_age        = minPwdAge;
1634                 dominfo->min_password_age        = minPwdAge;
1635                 *_dominfo = dominfo;
1636         }
1637
1638         if (restrictions && new_pass) {
1639
1640                 /* check the various password restrictions */
1641                 if (restrictions && minPwdLength > strlen_m(new_pass)) {
1642                         if (reject_reason) {
1643                                 *reject_reason = SAMR_REJECT_TOO_SHORT;
1644                         }
1645                         return NT_STATUS_PASSWORD_RESTRICTION;
1646                 }
1647                 
1648                 /* possibly check password complexity */
1649                 if (restrictions && pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
1650                     !samdb_password_complexity_ok(new_pass)) {
1651                         if (reject_reason) {
1652                                 *reject_reason = SAMR_REJECT_COMPLEXITY;
1653                         }
1654                         return NT_STATUS_PASSWORD_RESTRICTION;
1655                 }
1656                 
1657                 /* compute the new nt and lm hashes */
1658                 if (E_deshash(new_pass, local_lmNewHash.hash)) {
1659                         lmNewHash = &local_lmNewHash;
1660                 }
1661                 if (!E_md4hash(new_pass, local_ntNewHash.hash)) {
1662                         /* If we can't convert this password to UCS2, then we should not accept it */
1663                         if (reject_reason) {
1664                                 *reject_reason = SAMR_REJECT_OTHER;
1665                         }
1666                         return NT_STATUS_PASSWORD_RESTRICTION;
1667                 }
1668                 ntNewHash = &local_ntNewHash;
1669         }
1670
1671         if (user_change) {
1672                 /* are all password changes disallowed? */
1673                 if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
1674                         if (reject_reason) {
1675                                 *reject_reason = SAMR_REJECT_OTHER;
1676                         }
1677                         return NT_STATUS_PASSWORD_RESTRICTION;
1678                 }
1679                 
1680                 /* can this user change password? */
1681                 if (userAccountControl & UF_PASSWD_CANT_CHANGE) {
1682                         if (reject_reason) {
1683                                 *reject_reason = SAMR_REJECT_OTHER;
1684                         }
1685                         return NT_STATUS_PASSWORD_RESTRICTION;
1686                 }
1687                 
1688                 /* yes, this is a minus. The ages are in negative 100nsec units! */
1689                 if (pwdLastSet - minPwdAge > now_nt) {
1690                         if (reject_reason) {
1691                                 *reject_reason = SAMR_REJECT_OTHER;
1692                         }
1693                         return NT_STATUS_PASSWORD_RESTRICTION;
1694                 }
1695
1696                 /* check the immediately past password */
1697                 if (pwdHistoryLength > 0) {
1698                         if (lmNewHash && lmPwdHash && memcmp(lmNewHash->hash, lmPwdHash->hash, 16) == 0) {
1699                                 if (reject_reason) {
1700                                         *reject_reason = SAMR_REJECT_IN_HISTORY;
1701                                 }
1702                                 return NT_STATUS_PASSWORD_RESTRICTION;
1703                         }
1704                         if (ntNewHash && ntPwdHash && memcmp(ntNewHash->hash, ntPwdHash->hash, 16) == 0) {
1705                                 if (reject_reason) {
1706                                         *reject_reason = SAMR_REJECT_IN_HISTORY;
1707                                 }
1708                                 return NT_STATUS_PASSWORD_RESTRICTION;
1709                         }
1710                 }
1711                 
1712                 /* check the password history */
1713                 sambaLMPwdHistory_len = MIN(sambaLMPwdHistory_len, pwdHistoryLength);
1714                 sambaNTPwdHistory_len = MIN(sambaNTPwdHistory_len, pwdHistoryLength);
1715                 
1716                 for (i=0; lmNewHash && i<sambaLMPwdHistory_len;i++) {
1717                         if (memcmp(lmNewHash->hash, sambaLMPwdHistory[i].hash, 16) == 0) {
1718                                 if (reject_reason) {
1719                                         *reject_reason = SAMR_REJECT_IN_HISTORY;
1720                                 }
1721                                 return NT_STATUS_PASSWORD_RESTRICTION;
1722                         }
1723                 }
1724                 for (i=0; ntNewHash && i<sambaNTPwdHistory_len;i++) {
1725                         if (memcmp(ntNewHash->hash, sambaNTPwdHistory[i].hash, 16) == 0) {
1726                                 if (reject_reason) {
1727                                         *reject_reason = SAMR_REJECT_IN_HISTORY;
1728                                 }
1729                                 return NT_STATUS_PASSWORD_RESTRICTION;
1730                         }
1731                 }
1732         }
1733
1734 #define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0)
1735
1736         /* the password is acceptable. Start forming the new fields */
1737         if (new_pass) {
1738                 /* if we know the cleartext, then only set it.
1739                  * Modules in ldb will set all the appropriate
1740                  * hashes */
1741                 CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod, 
1742                                                "sambaPassword", new_pass));
1743         } else {
1744                 /* We don't have the cleartext, so delete the old one
1745                  * and set what we have of the hashes */
1746                 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "sambaPassword"));
1747
1748                 if (lmNewHash) {
1749                         CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "dBCSPwd", lmNewHash));
1750                 } else {
1751                         CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "dBCSPwd"));
1752                 }
1753                 
1754                 if (ntNewHash) {
1755                         CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "unicodePwd", ntNewHash));
1756                 } else {
1757                         CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "unicodePwd"));
1758                 }
1759         }
1760
1761         return NT_STATUS_OK;
1762 }
1763
1764
1765 /*
1766   set the user password using plaintext, obeying any user or domain
1767   password restrictions
1768
1769   This wrapper function takes a SID as input, rather than a user DN,
1770   and actually performs the password change
1771
1772 */
1773 _PUBLIC_ NTSTATUS samdb_set_password_sid(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
1774                                 const struct dom_sid *user_sid,
1775                                 const char *new_pass,
1776                                 struct samr_Password *lmNewHash, 
1777                                 struct samr_Password *ntNewHash,
1778                                 bool user_change,
1779                                 enum samr_RejectReason *reject_reason,
1780                                 struct samr_DomInfo1 **_dominfo) 
1781 {
1782         NTSTATUS nt_status;
1783         struct ldb_dn *user_dn;
1784         struct ldb_message *msg;
1785         int ret;
1786
1787         ret = ldb_transaction_start(ctx);
1788         if (ret) {
1789                 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(ctx)));
1790                 return NT_STATUS_TRANSACTION_ABORTED;
1791         }
1792
1793         user_dn = samdb_search_dn(ctx, mem_ctx, NULL, 
1794                                   "(&(objectSid=%s)(objectClass=user))", 
1795                                   ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
1796         if (!user_dn) {
1797                 ldb_transaction_cancel(ctx);
1798                 DEBUG(3, ("samdb_set_password_sid: SID %s not found in samdb, returning NO_SUCH_USER\n",
1799                           dom_sid_string(mem_ctx, user_sid)));
1800                 return NT_STATUS_NO_SUCH_USER;
1801         }
1802
1803         msg = ldb_msg_new(mem_ctx);
1804         if (msg == NULL) {
1805                 ldb_transaction_cancel(ctx);
1806                 return NT_STATUS_NO_MEMORY;
1807         }
1808
1809         msg->dn = ldb_dn_copy(msg, user_dn);
1810         if (!msg->dn) {
1811                 ldb_transaction_cancel(ctx);
1812                 return NT_STATUS_NO_MEMORY;
1813         }
1814
1815         nt_status = samdb_set_password(ctx, mem_ctx,
1816                                        user_dn, NULL,
1817                                        msg, new_pass, 
1818                                        lmNewHash, ntNewHash,
1819                                        user_change, /* This is a password set, not change */
1820                                        reject_reason, _dominfo);
1821         if (!NT_STATUS_IS_OK(nt_status)) {
1822                 ldb_transaction_cancel(ctx);
1823                 return nt_status;
1824         }
1825         
1826         /* modify the samdb record */
1827         ret = samdb_replace(ctx, mem_ctx, msg);
1828         if (ret != 0) {
1829                 ldb_transaction_cancel(ctx);
1830                 return NT_STATUS_ACCESS_DENIED;
1831         }
1832
1833         ret = ldb_transaction_commit(ctx);
1834         if (ret != 0) {
1835                 DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
1836                          ldb_dn_get_linearized(msg->dn),
1837                          ldb_errstring(ctx)));
1838                 return NT_STATUS_TRANSACTION_ABORTED;
1839         }
1840         return NT_STATUS_OK;
1841 }
1842
1843
1844
1845 NTSTATUS samdb_create_foreign_security_principal(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, 
1846                                                  struct dom_sid *sid, struct ldb_dn **ret_dn) 
1847 {
1848         struct ldb_message *msg;
1849         struct ldb_dn *basedn;
1850         const char *sidstr;
1851         int ret;
1852         
1853         sidstr = dom_sid_string(mem_ctx, sid);
1854         NT_STATUS_HAVE_NO_MEMORY(sidstr);
1855         
1856         /* We might have to create a ForeignSecurityPrincipal, even if this user
1857          * is in our own domain */
1858         
1859         msg = ldb_msg_new(mem_ctx);
1860         if (msg == NULL) {
1861                 return NT_STATUS_NO_MEMORY;
1862         }
1863         
1864         /* TODO: Hmmm. This feels wrong. How do I find the base dn to
1865          * put the ForeignSecurityPrincipals? d_state->domain_dn does
1866          * not work, this is wrong for the Builtin domain, there's no
1867          * cn=For...,cn=Builtin,dc={BASEDN}.  -- vl
1868          */
1869         
1870         basedn = samdb_search_dn(sam_ctx, mem_ctx, NULL,
1871                                  "(&(objectClass=container)(cn=ForeignSecurityPrincipals))");
1872         
1873         if (basedn == NULL) {
1874                 DEBUG(0, ("Failed to find DN for "
1875                           "ForeignSecurityPrincipal container\n"));
1876                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1877         }
1878         
1879         /* add core elements to the ldb_message for the alias */
1880         msg->dn = ldb_dn_copy(mem_ctx, basedn);
1881         if ( ! ldb_dn_add_child_fmt(msg->dn, "CN=%s", sidstr))
1882                 return NT_STATUS_NO_MEMORY;
1883         
1884         samdb_msg_add_string(sam_ctx, mem_ctx, msg,
1885                              "objectClass",
1886                              "foreignSecurityPrincipal");
1887         
1888         /* create the alias */
1889         ret = ldb_add(sam_ctx, msg);
1890         if (ret != 0) {
1891                 DEBUG(0,("Failed to create foreignSecurityPrincipal "
1892                          "record %s: %s\n", 
1893                          ldb_dn_get_linearized(msg->dn),
1894                          ldb_errstring(sam_ctx)));
1895                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1896         }
1897         *ret_dn = msg->dn;
1898         return NT_STATUS_OK;
1899 }
1900
1901
1902 /*
1903   Find the DN of a domain, assuming it to be a dotted.dns name
1904 */
1905
1906 struct ldb_dn *samdb_dns_domain_to_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const char *dns_domain) 
1907 {
1908         int i;
1909         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
1910         const char *binary_encoded;
1911         const char **split_realm;
1912         struct ldb_dn *dn;
1913         
1914         if (!tmp_ctx) {
1915                 return NULL;
1916         }
1917         
1918         split_realm = str_list_make(tmp_ctx, dns_domain, ".");
1919         if (!split_realm) {
1920                 talloc_free(tmp_ctx);
1921                 return NULL;
1922         }
1923         dn = ldb_dn_new(mem_ctx, ldb, NULL);
1924         for (i=0; split_realm[i]; i++) {
1925                 binary_encoded = ldb_binary_encode_string(tmp_ctx, split_realm[i]);
1926                 if (!ldb_dn_add_base_fmt(dn, "dc=%s", binary_encoded)) {
1927                         DEBUG(2, ("Failed to add dc=%s element to DN %s\n",
1928                                   binary_encoded, ldb_dn_get_linearized(dn)));
1929                         talloc_free(tmp_ctx);
1930                         return NULL;
1931                 }
1932         }
1933         if (!ldb_dn_validate(dn)) {
1934                 DEBUG(2, ("Failed to validated DN %s\n",
1935                           ldb_dn_get_linearized(dn)));
1936                 return NULL;
1937         }
1938         return dn;
1939 }
1940 /*
1941   Find the DN of a domain, be it the netbios or DNS name 
1942 */
1943
1944 struct ldb_dn *samdb_domain_to_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, 
1945                                   const char *domain_name) 
1946 {
1947         const char * const domain_ref_attrs[] = {
1948                 "ncName", NULL
1949         };
1950         const char * const domain_ref2_attrs[] = {
1951                 NULL
1952         };
1953         struct ldb_result *res_domain_ref;
1954         char *escaped_domain = ldb_binary_encode_string(mem_ctx, domain_name);
1955         /* find the domain's DN */
1956         int ret_domain = ldb_search_exp_fmt(ldb, mem_ctx, 
1957                                             &res_domain_ref, 
1958                                             samdb_partitions_dn(ldb, mem_ctx), 
1959                                             LDB_SCOPE_ONELEVEL, 
1960                                             domain_ref_attrs,
1961                                             "(&(nETBIOSName=%s)(objectclass=crossRef))", 
1962                                             escaped_domain);
1963         if (ret_domain != 0) {
1964                 return NULL;
1965         }
1966         
1967         if (res_domain_ref->count == 0) {
1968                 ret_domain = ldb_search_exp_fmt(ldb, mem_ctx, 
1969                                                 &res_domain_ref, 
1970                                                 samdb_dns_domain_to_dn(ldb, mem_ctx, domain_name),
1971                                                 LDB_SCOPE_BASE,
1972                                                 domain_ref2_attrs,
1973                                                 "(objectclass=domain)");
1974                 if (ret_domain != 0) {
1975                         return NULL;
1976                 }
1977         
1978                 if (res_domain_ref->count == 1) {
1979                         return res_domain_ref->msgs[0]->dn;
1980                 }
1981                 return NULL;
1982         }
1983         
1984         if (res_domain_ref->count > 1) {
1985                 DEBUG(0,("Found %d records matching domain [%s]\n", 
1986                          ret_domain, domain_name));
1987                 return NULL;
1988         }
1989         
1990         return samdb_result_dn(ldb, mem_ctx, res_domain_ref->msgs[0], "nCName", NULL);
1991
1992 }