Treat maxPwdAge == 0 as passwords never expire.
[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                                     NTTIME default_value)
450 {
451         NTTIME ret = ldb_msg_find_attr_as_uint64(msg, "accountExpires",
452                                                  default_value);
453
454         if (ret == (NTTIME)0)
455                 ret = 0x7FFFFFFFFFFFFFFFULL;
456
457         return ret;
458 }
459
460 /*
461   pull a uint64_t from a result set. 
462 */
463 uint64_t samdb_result_uint64(struct ldb_message *msg, const char *attr, uint64_t default_value)
464 {
465         return ldb_msg_find_attr_as_uint64(msg, attr, default_value);
466 }
467
468
469 /*
470   construct the allow_password_change field from the PwdLastSet attribute and the 
471   domain password settings
472 */
473 NTTIME samdb_result_allow_password_change(struct ldb_context *sam_ldb, 
474                                           TALLOC_CTX *mem_ctx, 
475                                           struct ldb_dn *domain_dn, 
476                                           struct ldb_message *msg, 
477                                           const char *attr)
478 {
479         uint64_t attr_time = samdb_result_uint64(msg, attr, 0);
480         int64_t minPwdAge;
481
482         if (attr_time == 0) {
483                 return 0;
484         }
485
486         minPwdAge = samdb_search_int64(sam_ldb, mem_ctx, 0, domain_dn, "minPwdAge", NULL);
487
488         /* yes, this is a -= not a += as minPwdAge is stored as the negative
489            of the number of 100-nano-seconds */
490         attr_time -= minPwdAge;
491
492         return attr_time;
493 }
494
495 /*
496   construct the force_password_change field from the PwdLastSet
497   attribute, the userAccountControl and the domain password settings
498 */
499 NTTIME samdb_result_force_password_change(struct ldb_context *sam_ldb, 
500                                           TALLOC_CTX *mem_ctx, 
501                                           struct ldb_dn *domain_dn, 
502                                           struct ldb_message *msg)
503 {
504         uint64_t attr_time = samdb_result_uint64(msg, "pwdLastSet", 0);
505         uint32_t userAccountControl = samdb_result_uint64(msg, "userAccountControl", 0);
506         int64_t maxPwdAge;
507
508         /* Machine accounts don't expire, and there is a flag for 'no expiry' */
509         if (!(userAccountControl & UF_NORMAL_ACCOUNT)
510             || (userAccountControl & UF_DONT_EXPIRE_PASSWD)) {
511                 return 0x7FFFFFFFFFFFFFFFULL;
512         }
513
514         if (attr_time == 0) {
515                 return 0;
516         }
517
518         maxPwdAge = samdb_search_int64(sam_ldb, mem_ctx, 0, domain_dn, "maxPwdAge", NULL);
519         if (maxPwdAge == 0) {
520                 return 0x7FFFFFFFFFFFFFFFULL;
521         } else {
522                 attr_time -= maxPwdAge;
523         }
524
525         return attr_time;
526 }
527
528 /*
529   pull a samr_Password structutre from a result set. 
530 */
531 struct samr_Password *samdb_result_hash(TALLOC_CTX *mem_ctx, struct ldb_message *msg, const char *attr)
532 {
533         struct samr_Password *hash = NULL;
534         const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
535         if (val && (val->length >= sizeof(hash->hash))) {
536                 hash = talloc(mem_ctx, struct samr_Password);
537                 memcpy(hash->hash, val->data, MIN(val->length, sizeof(hash->hash)));
538         }
539         return hash;
540 }
541
542 /*
543   pull an array of samr_Password structutres from a result set. 
544 */
545 uint_t samdb_result_hashes(TALLOC_CTX *mem_ctx, struct ldb_message *msg, 
546                            const char *attr, struct samr_Password **hashes)
547 {
548         uint_t count = 0;
549         const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
550         int i;
551
552         *hashes = NULL;
553         if (!val) {
554                 return 0;
555         }
556         count = val->length / 16;
557         if (count == 0) {
558                 return 0;
559         }
560
561         *hashes = talloc_array(mem_ctx, struct samr_Password, count);
562         if (! *hashes) {
563                 return 0;
564         }
565
566         for (i=0;i<count;i++) {
567                 memcpy((*hashes)[i].hash, (i*16)+(char *)val->data, 16);
568         }
569
570         return count;
571 }
572
573 NTSTATUS samdb_result_passwords(TALLOC_CTX *mem_ctx, struct ldb_message *msg, 
574                                 struct samr_Password **lm_pwd, struct samr_Password **nt_pwd) 
575 {
576         struct samr_Password *lmPwdHash, *ntPwdHash;
577         if (nt_pwd) {
578                 int num_nt;
579                 num_nt = samdb_result_hashes(mem_ctx, msg, "unicodePwd", &ntPwdHash);
580                 if (num_nt == 0) {
581                         *nt_pwd = NULL;
582                 } else if (num_nt > 1) {
583                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
584                 } else {
585                         *nt_pwd = &ntPwdHash[0];
586                 }
587         }
588         if (lm_pwd) {
589                 int num_lm;
590                 num_lm = samdb_result_hashes(mem_ctx, msg, "dBCSPwd", &lmPwdHash);
591                 if (num_lm == 0) {
592                         *lm_pwd = NULL;
593                 } else if (num_lm > 1) {
594                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
595                 } else {
596                         *lm_pwd = &lmPwdHash[0];
597                 }
598         }
599         return NT_STATUS_OK;
600 }
601
602 /*
603   pull a samr_LogonHours structutre from a result set. 
604 */
605 struct samr_LogonHours samdb_result_logon_hours(TALLOC_CTX *mem_ctx, struct ldb_message *msg, const char *attr)
606 {
607         struct samr_LogonHours hours;
608         const int units_per_week = 168;
609         const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
610         ZERO_STRUCT(hours);
611         hours.bits = talloc_array(mem_ctx, uint8_t, units_per_week);
612         if (!hours.bits) {
613                 return hours;
614         }
615         hours.units_per_week = units_per_week;
616         memset(hours.bits, 0xFF, units_per_week);
617         if (val) {
618                 memcpy(hours.bits, val->data, MIN(val->length, units_per_week));
619         }
620         return hours;
621 }
622
623 /*
624   pull a set of account_flags from a result set. 
625
626   This requires that the attributes: 
627    pwdLastSet
628    userAccountControl
629   be included in 'msg'
630 */
631 uint32_t samdb_result_acct_flags(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, 
632                                  struct ldb_message *msg, struct ldb_dn *domain_dn)
633 {
634         uint32_t userAccountControl = ldb_msg_find_attr_as_uint(msg, "userAccountControl", 0);
635         uint32_t acct_flags = samdb_uf2acb(userAccountControl); 
636         NTTIME must_change_time;
637         NTTIME now;
638         
639         must_change_time = samdb_result_force_password_change(sam_ctx, mem_ctx, 
640                                                               domain_dn, msg);
641         
642         /* Test account expire time */
643         unix_to_nt_time(&now, time(NULL));
644         /* check for expired password */
645         if (must_change_time < now) {
646                 acct_flags |= ACB_PW_EXPIRED;
647         }
648         return acct_flags;
649 }
650
651
652 /* Find an attribute, with a particular value */
653
654 /* The current callers of this function expect a very specific
655  * behaviour: In particular, objectClass subclass equivilance is not
656  * wanted.  This means that we should not lookup the schema for the
657  * comparison function */
658 struct ldb_message_element *samdb_find_attribute(struct ldb_context *ldb, 
659                                                  const struct ldb_message *msg, 
660                                                  const char *name, const char *value)
661 {
662         int i;
663         struct ldb_message_element *el = ldb_msg_find_element(msg, name);
664
665         if (!el) {
666                 return NULL;
667         }
668
669         for (i=0;i<el->num_values;i++) {
670                 if (ldb_attr_cmp(value, (char *)el->values[i].data) == 0) {
671                         return el;
672                 }
673         }
674
675         return NULL;
676 }
677
678 int samdb_find_or_add_value(struct ldb_context *ldb, struct ldb_message *msg, const char *name, const char *set_value)
679 {
680         if (samdb_find_attribute(ldb, msg, name, set_value) == NULL) {
681                 return samdb_msg_add_string(ldb, msg, msg, name, set_value);
682         }
683         return LDB_SUCCESS;
684 }
685
686 int samdb_find_or_add_attribute(struct ldb_context *ldb, struct ldb_message *msg, const char *name, const char *set_value)
687 {
688         struct ldb_message_element *el;
689
690         el = ldb_msg_find_element(msg, name);
691         if (el) {
692                 return LDB_SUCCESS;
693         }
694                 
695         return samdb_msg_add_string(ldb, msg, msg, name, set_value);
696 }
697
698
699
700 /*
701   add a string element to a message
702 */
703 int samdb_msg_add_string(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
704                          const char *attr_name, const char *str)
705 {
706         char *s = talloc_strdup(mem_ctx, str);
707         char *a = talloc_strdup(mem_ctx, attr_name);
708         if (s == NULL || a == NULL) {
709                 return LDB_ERR_OPERATIONS_ERROR;
710         }
711         return ldb_msg_add_string(msg, a, s);
712 }
713
714 /*
715   add a dom_sid element to a message
716 */
717 int samdb_msg_add_dom_sid(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
718                          const char *attr_name, struct dom_sid *sid)
719 {
720         struct ldb_val v;
721         enum ndr_err_code ndr_err;
722
723         ndr_err = ndr_push_struct_blob(&v, mem_ctx, 
724                                        lp_iconv_convenience(ldb_get_opaque(sam_ldb, "loadparm")),
725                                        sid,
726                                        (ndr_push_flags_fn_t)ndr_push_dom_sid);
727         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
728                 return -1;
729         }
730         return ldb_msg_add_value(msg, attr_name, &v, NULL);
731 }
732
733
734 /*
735   add a delete element operation to a message
736 */
737 int samdb_msg_add_delete(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
738                          const char *attr_name)
739 {
740         /* we use an empty replace rather than a delete, as it allows for 
741            samdb_replace() to be used everywhere */
742         return ldb_msg_add_empty(msg, attr_name, LDB_FLAG_MOD_REPLACE, NULL);
743 }
744
745 /*
746   add a add attribute value to a message
747 */
748 int samdb_msg_add_addval(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
749                          const char *attr_name, const char *value)
750 {
751         struct ldb_message_element *el;
752         char *a, *v;
753         int ret;
754         a = talloc_strdup(mem_ctx, attr_name);
755         if (a == NULL)
756                 return -1;
757         v = talloc_strdup(mem_ctx, value);
758         if (v == NULL)
759                 return -1;
760         ret = ldb_msg_add_string(msg, a, v);
761         if (ret != 0)
762                 return ret;
763         el = ldb_msg_find_element(msg, a);
764         if (el == NULL)
765                 return -1;
766         el->flags = LDB_FLAG_MOD_ADD;
767         return 0;
768 }
769
770 /*
771   add a delete attribute value to a message
772 */
773 int samdb_msg_add_delval(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
774                          const char *attr_name, const char *value)
775 {
776         struct ldb_message_element *el;
777         char *a, *v;
778         int ret;
779         a = talloc_strdup(mem_ctx, attr_name);
780         if (a == NULL)
781                 return -1;
782         v = talloc_strdup(mem_ctx, value);
783         if (v == NULL)
784                 return -1;
785         ret = ldb_msg_add_string(msg, a, v);
786         if (ret != 0)
787                 return ret;
788         el = ldb_msg_find_element(msg, a);
789         if (el == NULL)
790                 return -1;
791         el->flags = LDB_FLAG_MOD_DELETE;
792         return 0;
793 }
794
795 /*
796   add a int element to a message
797 */
798 int samdb_msg_add_int(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
799                        const char *attr_name, int v)
800 {
801         const char *s = talloc_asprintf(mem_ctx, "%d", v);
802         return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
803 }
804
805 /*
806   add a uint_t element to a message
807 */
808 int samdb_msg_add_uint(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
809                        const char *attr_name, uint_t v)
810 {
811         const char *s = talloc_asprintf(mem_ctx, "%u", v);
812         return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
813 }
814
815 /*
816   add a (signed) int64_t element to a message
817 */
818 int samdb_msg_add_int64(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
819                         const char *attr_name, int64_t v)
820 {
821         const char *s = talloc_asprintf(mem_ctx, "%lld", (long long)v);
822         return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
823 }
824
825 /*
826   add a uint64_t element to a message
827 */
828 int samdb_msg_add_uint64(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
829                         const char *attr_name, uint64_t v)
830 {
831         const char *s = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)v);
832         return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
833 }
834
835 /*
836   add a samr_Password element to a message
837 */
838 int samdb_msg_add_hash(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
839                        const char *attr_name, struct samr_Password *hash)
840 {
841         struct ldb_val val;
842         val.data = talloc_memdup(mem_ctx, hash->hash, 16);
843         if (!val.data) {
844                 return -1;
845         }
846         val.length = 16;
847         return ldb_msg_add_value(msg, attr_name, &val, NULL);
848 }
849
850 /*
851   add a samr_Password array to a message
852 */
853 int samdb_msg_add_hashes(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
854                          const char *attr_name, struct samr_Password *hashes, uint_t count)
855 {
856         struct ldb_val val;
857         int i;
858         val.data = talloc_array_size(mem_ctx, 16, count);
859         val.length = count*16;
860         if (!val.data) {
861                 return -1;
862         }
863         for (i=0;i<count;i++) {
864                 memcpy(i*16 + (char *)val.data, hashes[i].hash, 16);
865         }
866         return ldb_msg_add_value(msg, attr_name, &val, NULL);
867 }
868
869 /*
870   add a acct_flags element to a message
871 */
872 int samdb_msg_add_acct_flags(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
873                              const char *attr_name, uint32_t v)
874 {
875         return samdb_msg_add_uint(sam_ldb, mem_ctx, msg, attr_name, samdb_acb2uf(v));
876 }
877
878 /*
879   add a logon_hours element to a message
880 */
881 int samdb_msg_add_logon_hours(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
882                               const char *attr_name, struct samr_LogonHours *hours)
883 {
884         struct ldb_val val;
885         val.length = hours->units_per_week / 8;
886         val.data = hours->bits;
887         return ldb_msg_add_value(msg, attr_name, &val, NULL);
888 }
889
890 /*
891   add a general value element to a message
892 */
893 int samdb_msg_add_value(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
894                               const char *attr_name, const struct ldb_val *val)
895 {
896         return ldb_msg_add_value(msg, attr_name, val, NULL);
897 }
898
899 /*
900   sets a general value element to a message
901 */
902 int samdb_msg_set_value(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
903                         const char *attr_name, const struct ldb_val *val)
904 {
905         struct ldb_message_element *el;
906
907         el = ldb_msg_find_element(msg, attr_name);
908         if (el) {
909                 el->num_values = 0;
910         }
911         return ldb_msg_add_value(msg, attr_name, val, NULL);
912 }
913
914 /*
915   set a string element in a message
916 */
917 int samdb_msg_set_string(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
918                          const char *attr_name, const char *str)
919 {
920         struct ldb_message_element *el;
921
922         el = ldb_msg_find_element(msg, attr_name);
923         if (el) {
924                 el->num_values = 0;
925         }
926         return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, str);
927 }
928
929 /*
930   replace elements in a record
931 */
932 int samdb_replace(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg)
933 {
934         int i;
935
936         /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
937         for (i=0;i<msg->num_elements;i++) {
938                 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
939         }
940
941         /* modify the samdb record */
942         return ldb_modify(sam_ldb, msg);
943 }
944
945 /*
946   return a default security descriptor
947 */
948 struct security_descriptor *samdb_default_security_descriptor(TALLOC_CTX *mem_ctx)
949 {
950         struct security_descriptor *sd;
951
952         sd = security_descriptor_initialise(mem_ctx);
953
954         return sd;
955 }
956
957 struct ldb_dn *samdb_base_dn(struct ldb_context *sam_ctx) 
958 {
959         return ldb_get_default_basedn(sam_ctx);
960 }
961
962 struct ldb_dn *samdb_config_dn(struct ldb_context *sam_ctx) 
963 {
964         return ldb_get_config_basedn(sam_ctx);
965 }
966
967 struct ldb_dn *samdb_schema_dn(struct ldb_context *sam_ctx) 
968 {
969         return ldb_get_schema_basedn(sam_ctx);
970 }
971
972 struct ldb_dn *samdb_root_dn(struct ldb_context *sam_ctx) 
973 {
974         return ldb_get_root_basedn(sam_ctx);
975 }
976
977 struct ldb_dn *samdb_partitions_dn(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx)
978 {
979         struct ldb_dn *new_dn;
980
981         new_dn = ldb_dn_copy(mem_ctx, samdb_config_dn(sam_ctx));
982         if ( ! ldb_dn_add_child_fmt(new_dn, "CN=Partitions")) {
983                 talloc_free(new_dn);
984                 return NULL;
985         }
986         return new_dn;
987 }
988
989 struct ldb_dn *samdb_sites_dn(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx)
990 {
991         struct ldb_dn *new_dn;
992
993         new_dn = ldb_dn_copy(mem_ctx, samdb_config_dn(sam_ctx));
994         if ( ! ldb_dn_add_child_fmt(new_dn, "CN=Sites")) {
995                 talloc_free(new_dn);
996                 return NULL;
997         }
998         return new_dn;
999 }
1000
1001 /*
1002   work out the domain sid for the current open ldb
1003 */
1004 const struct dom_sid *samdb_domain_sid(struct ldb_context *ldb)
1005 {
1006         TALLOC_CTX *tmp_ctx;
1007         struct dom_sid *domain_sid;
1008
1009         /* see if we have a cached copy */
1010         domain_sid = (struct dom_sid *)ldb_get_opaque(ldb, "cache.domain_sid");
1011         if (domain_sid) {
1012                 return domain_sid;
1013         }
1014
1015         tmp_ctx = talloc_new(ldb);
1016         if (tmp_ctx == NULL) {
1017                 goto failed;
1018         }
1019
1020         /* find the domain_sid */
1021         domain_sid = samdb_search_dom_sid(ldb, tmp_ctx, ldb_get_default_basedn(ldb),
1022                                           "objectSid", "objectClass=domainDNS");
1023         if (domain_sid == NULL) {
1024                 goto failed;
1025         }
1026
1027         /* cache the domain_sid in the ldb */
1028         if (ldb_set_opaque(ldb, "cache.domain_sid", domain_sid) != LDB_SUCCESS) {
1029                 goto failed;
1030         }
1031
1032         talloc_steal(ldb, domain_sid);
1033         talloc_free(tmp_ctx);
1034
1035         return domain_sid;
1036
1037 failed:
1038         DEBUG(1,("Failed to find domain_sid for open ldb\n"));
1039         talloc_free(tmp_ctx);
1040         return NULL;
1041 }
1042
1043 bool samdb_set_domain_sid(struct ldb_context *ldb, const struct dom_sid *dom_sid_in)
1044 {
1045         TALLOC_CTX *tmp_ctx;
1046         struct dom_sid *dom_sid_new;
1047         struct dom_sid *dom_sid_old;
1048
1049         /* see if we have a cached copy */
1050         dom_sid_old = talloc_get_type(ldb_get_opaque(ldb, 
1051                                                      "cache.domain_sid"), struct dom_sid);
1052
1053         tmp_ctx = talloc_new(ldb);
1054         if (tmp_ctx == NULL) {
1055                 goto failed;
1056         }
1057
1058         dom_sid_new = dom_sid_dup(tmp_ctx, dom_sid_in);
1059         if (!dom_sid_new) {
1060                 goto failed;
1061         }
1062
1063         /* cache the domain_sid in the ldb */
1064         if (ldb_set_opaque(ldb, "cache.domain_sid", dom_sid_new) != LDB_SUCCESS) {
1065                 goto failed;
1066         }
1067
1068         talloc_steal(ldb, dom_sid_new);
1069         talloc_free(tmp_ctx);
1070         talloc_free(dom_sid_old);
1071
1072         return true;
1073
1074 failed:
1075         DEBUG(1,("Failed to set our own cached domain SID in the ldb!\n"));
1076         talloc_free(tmp_ctx);
1077         return false;
1078 }
1079
1080 /* Obtain the short name of the flexible single master operator
1081  * (FSMO), such as the PDC Emulator */
1082 const char *samdb_result_fsmo_name(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const struct ldb_message *msg, 
1083                              const char *attr)
1084 {
1085         /* Format is cn=NTDS Settings,cn=<NETBIOS name of FSMO>,.... */
1086         struct ldb_dn *fsmo_dn = ldb_msg_find_attr_as_dn(ldb, mem_ctx, msg, attr);
1087         const struct ldb_val *val = ldb_dn_get_component_val(fsmo_dn, 1);
1088         const char *name = ldb_dn_get_component_name(fsmo_dn, 1);
1089
1090         if (!name || (ldb_attr_cmp(name, "cn") != 0)) {
1091                 /* Ensure this matches the format.  This gives us a
1092                  * bit more confidence that a 'cn' value will be a
1093                  * ascii string */
1094                 return NULL;
1095         }
1096         if (val) {
1097                 return (char *)val->data;
1098         }
1099         return NULL;
1100 }
1101
1102 /*
1103   work out the ntds settings dn for the current open ldb
1104 */
1105 struct ldb_dn *samdb_ntds_settings_dn(struct ldb_context *ldb)
1106 {
1107         TALLOC_CTX *tmp_ctx;
1108         const char *root_attrs[] = { "dsServiceName", NULL };
1109         int ret;
1110         struct ldb_result *root_res;
1111         struct ldb_dn *settings_dn;
1112         
1113         /* see if we have a cached copy */
1114         settings_dn = (struct ldb_dn *)ldb_get_opaque(ldb, "cache.settings_dn");
1115         if (settings_dn) {
1116                 return settings_dn;
1117         }
1118
1119         tmp_ctx = talloc_new(ldb);
1120         if (tmp_ctx == NULL) {
1121                 goto failed;
1122         }
1123         
1124
1125         ret = ldb_search(ldb, ldb_dn_new(tmp_ctx, ldb, ""), LDB_SCOPE_BASE, NULL, root_attrs, &root_res);
1126         if (ret) {
1127                 DEBUG(1,("Searching for dsServiceName in rootDSE failed: %s\n", 
1128                          ldb_errstring(ldb)));
1129                 goto failed;
1130         }
1131         talloc_steal(tmp_ctx, root_res);
1132
1133         if (root_res->count != 1) {
1134                 goto failed;
1135         }
1136
1137         settings_dn = ldb_msg_find_attr_as_dn(ldb, tmp_ctx, root_res->msgs[0], "dsServiceName");
1138
1139         /* cache the domain_sid in the ldb */
1140         if (ldb_set_opaque(ldb, "cache.settings_dn", settings_dn) != LDB_SUCCESS) {
1141                 goto failed;
1142         }
1143
1144         talloc_steal(ldb, settings_dn);
1145         talloc_free(tmp_ctx);
1146
1147         return settings_dn;
1148
1149 failed:
1150         DEBUG(1,("Failed to find our own NTDS Settings DN in the ldb!\n"));
1151         talloc_free(tmp_ctx);
1152         return NULL;
1153 }
1154
1155 /*
1156   work out the ntds settings invocationId for the current open ldb
1157 */
1158 const struct GUID *samdb_ntds_invocation_id(struct ldb_context *ldb)
1159 {
1160         TALLOC_CTX *tmp_ctx;
1161         const char *attrs[] = { "invocationId", NULL };
1162         int ret;
1163         struct ldb_result *res;
1164         struct GUID *invocation_id;
1165         
1166         /* see if we have a cached copy */
1167         invocation_id = (struct GUID *)ldb_get_opaque(ldb, "cache.invocation_id");
1168         if (invocation_id) {
1169                 return invocation_id;
1170         }
1171
1172         tmp_ctx = talloc_new(ldb);
1173         if (tmp_ctx == NULL) {
1174                 goto failed;
1175         }
1176
1177         ret = ldb_search(ldb, samdb_ntds_settings_dn(ldb), LDB_SCOPE_BASE, NULL, attrs, &res);
1178         if (ret) {
1179                 goto failed;
1180         }
1181         talloc_steal(tmp_ctx, res);
1182
1183         if (res->count != 1) {
1184                 goto failed;
1185         }
1186
1187         invocation_id = talloc(tmp_ctx, struct GUID);
1188         if (!invocation_id) {
1189                 goto failed;
1190         }
1191
1192         *invocation_id = samdb_result_guid(res->msgs[0], "invocationId");
1193
1194         /* cache the domain_sid in the ldb */
1195         if (ldb_set_opaque(ldb, "cache.invocation_id", invocation_id) != LDB_SUCCESS) {
1196                 goto failed;
1197         }
1198
1199         talloc_steal(ldb, invocation_id);
1200         talloc_free(tmp_ctx);
1201
1202         return invocation_id;
1203
1204 failed:
1205         DEBUG(1,("Failed to find our own NTDS Settings invocationId in the ldb!\n"));
1206         talloc_free(tmp_ctx);
1207         return NULL;
1208 }
1209
1210 bool samdb_set_ntds_invocation_id(struct ldb_context *ldb, const struct GUID *invocation_id_in)
1211 {
1212         TALLOC_CTX *tmp_ctx;
1213         struct GUID *invocation_id_new;
1214         struct GUID *invocation_id_old;
1215
1216         /* see if we have a cached copy */
1217         invocation_id_old = (struct GUID *)ldb_get_opaque(ldb, 
1218                                                          "cache.invocation_id");
1219
1220         tmp_ctx = talloc_new(ldb);
1221         if (tmp_ctx == NULL) {
1222                 goto failed;
1223         }
1224
1225         invocation_id_new = talloc(tmp_ctx, struct GUID);
1226         if (!invocation_id_new) {
1227                 goto failed;
1228         }
1229
1230         *invocation_id_new = *invocation_id_in;
1231
1232         /* cache the domain_sid in the ldb */
1233         if (ldb_set_opaque(ldb, "cache.invocation_id", invocation_id_new) != LDB_SUCCESS) {
1234                 goto failed;
1235         }
1236
1237         talloc_steal(ldb, invocation_id_new);
1238         talloc_free(tmp_ctx);
1239         talloc_free(invocation_id_old);
1240
1241         return true;
1242
1243 failed:
1244         DEBUG(1,("Failed to set our own cached invocationId in the ldb!\n"));
1245         talloc_free(tmp_ctx);
1246         return false;
1247 }
1248
1249 /*
1250   work out the ntds settings objectGUID for the current open ldb
1251 */
1252 const struct GUID *samdb_ntds_objectGUID(struct ldb_context *ldb)
1253 {
1254         TALLOC_CTX *tmp_ctx;
1255         const char *attrs[] = { "objectGUID", NULL };
1256         int ret;
1257         struct ldb_result *res;
1258         struct GUID *ntds_guid;
1259         
1260         /* see if we have a cached copy */
1261         ntds_guid = (struct GUID *)ldb_get_opaque(ldb, "cache.ntds_guid");
1262         if (ntds_guid) {
1263                 return ntds_guid;
1264         }
1265
1266         tmp_ctx = talloc_new(ldb);
1267         if (tmp_ctx == NULL) {
1268                 goto failed;
1269         }
1270
1271         ret = ldb_search(ldb, samdb_ntds_settings_dn(ldb), LDB_SCOPE_BASE, NULL, attrs, &res);
1272         if (ret) {
1273                 goto failed;
1274         }
1275         talloc_steal(tmp_ctx, res);
1276
1277         if (res->count != 1) {
1278                 goto failed;
1279         }
1280
1281         ntds_guid = talloc(tmp_ctx, struct GUID);
1282         if (!ntds_guid) {
1283                 goto failed;
1284         }
1285
1286         *ntds_guid = samdb_result_guid(res->msgs[0], "objectGUID");
1287
1288         /* cache the domain_sid in the ldb */
1289         if (ldb_set_opaque(ldb, "cache.ntds_guid", ntds_guid) != LDB_SUCCESS) {
1290                 goto failed;
1291         }
1292
1293         talloc_steal(ldb, ntds_guid);
1294         talloc_free(tmp_ctx);
1295
1296         return ntds_guid;
1297
1298 failed:
1299         DEBUG(1,("Failed to find our own NTDS Settings objectGUID in the ldb!\n"));
1300         talloc_free(tmp_ctx);
1301         return NULL;
1302 }
1303
1304 bool samdb_set_ntds_objectGUID(struct ldb_context *ldb, const struct GUID *ntds_guid_in)
1305 {
1306         TALLOC_CTX *tmp_ctx;
1307         struct GUID *ntds_guid_new;
1308         struct GUID *ntds_guid_old;
1309         
1310         /* see if we have a cached copy */
1311         ntds_guid_old = (struct GUID *)ldb_get_opaque(ldb, "cache.ntds_guid");
1312
1313         tmp_ctx = talloc_new(ldb);
1314         if (tmp_ctx == NULL) {
1315                 goto failed;
1316         }
1317
1318         ntds_guid_new = talloc(tmp_ctx, struct GUID);
1319         if (!ntds_guid_new) {
1320                 goto failed;
1321         }
1322
1323         *ntds_guid_new = *ntds_guid_in;
1324
1325         /* cache the domain_sid in the ldb */
1326         if (ldb_set_opaque(ldb, "cache.ntds_guid", ntds_guid_new) != LDB_SUCCESS) {
1327                 goto failed;
1328         }
1329
1330         talloc_steal(ldb, ntds_guid_new);
1331         talloc_free(tmp_ctx);
1332         talloc_free(ntds_guid_old);
1333
1334         return true;
1335
1336 failed:
1337         DEBUG(1,("Failed to set our own cached invocationId in the ldb!\n"));
1338         talloc_free(tmp_ctx);
1339         return false;
1340 }
1341
1342 /*
1343   work out the server dn for the current open ldb
1344 */
1345 struct ldb_dn *samdb_server_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx)
1346 {
1347         return ldb_dn_get_parent(mem_ctx, samdb_ntds_settings_dn(ldb));
1348 }
1349
1350 /*
1351   work out the server dn for the current open ldb
1352 */
1353 struct ldb_dn *samdb_server_site_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx)
1354 {
1355         struct ldb_dn *server_dn;
1356         struct ldb_dn *server_site_dn;
1357
1358         server_dn = samdb_server_dn(ldb, mem_ctx);
1359         if (!server_dn) return NULL;
1360
1361         server_site_dn = ldb_dn_get_parent(mem_ctx, server_dn);
1362
1363         talloc_free(server_dn);
1364         return server_site_dn;
1365 }
1366
1367 /*
1368   work out if we are the PDC for the domain of the current open ldb
1369 */
1370 bool samdb_is_pdc(struct ldb_context *ldb)
1371 {
1372         const char *dom_attrs[] = { "fSMORoleOwner", NULL };
1373         int ret;
1374         struct ldb_result *dom_res;
1375         TALLOC_CTX *tmp_ctx;
1376         bool is_pdc;
1377         struct ldb_dn *pdc;
1378
1379         tmp_ctx = talloc_new(ldb);
1380         if (tmp_ctx == NULL) {
1381                 DEBUG(1, ("talloc_new failed in samdb_is_pdc"));
1382                 return false;
1383         }
1384
1385         ret = ldb_search(ldb, ldb_get_default_basedn(ldb), LDB_SCOPE_BASE, NULL, dom_attrs, &dom_res);
1386         if (ret) {
1387                 DEBUG(1,("Searching for fSMORoleOwner in %s failed: %s\n", 
1388                          ldb_dn_get_linearized(ldb_get_default_basedn(ldb)), 
1389                          ldb_errstring(ldb)));
1390                 goto failed;
1391         }
1392         talloc_steal(tmp_ctx, dom_res);
1393         if (dom_res->count != 1) {
1394                 goto failed;
1395         }
1396
1397         pdc = ldb_msg_find_attr_as_dn(ldb, tmp_ctx, dom_res->msgs[0], "fSMORoleOwner");
1398
1399         if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), pdc) == 0) {
1400                 is_pdc = true;
1401         } else {
1402                 is_pdc = false;
1403         }
1404
1405         talloc_free(tmp_ctx);
1406
1407         return is_pdc;
1408
1409 failed:
1410         DEBUG(1,("Failed to find if we are the PDC for this ldb\n"));
1411         talloc_free(tmp_ctx);
1412         return false;
1413 }
1414
1415 /*
1416   work out if we are a Global Catalog server for the domain of the current open ldb
1417 */
1418 bool samdb_is_gc(struct ldb_context *ldb)
1419 {
1420         const char *attrs[] = { "options", NULL };
1421         int ret, options;
1422         struct ldb_result *res;
1423         TALLOC_CTX *tmp_ctx;
1424
1425         tmp_ctx = talloc_new(ldb);
1426         if (tmp_ctx == NULL) {
1427                 DEBUG(1, ("talloc_new failed in samdb_is_pdc"));
1428                 return false;
1429         }
1430
1431         /* Query cn=ntds settings,.... */
1432         ret = ldb_search(ldb, samdb_ntds_settings_dn(ldb), LDB_SCOPE_BASE, NULL, attrs, &res);
1433         if (ret) {
1434                 return false;
1435         }
1436         if (res->count != 1) {
1437                 talloc_free(res);
1438                 return false;
1439         }
1440
1441         options = ldb_msg_find_attr_as_int(res->msgs[0], "options", 0);
1442         talloc_free(res);
1443         talloc_free(tmp_ctx);
1444
1445         /* if options attribute has the 0x00000001 flag set, then enable the global catlog */
1446         if (options & 0x000000001) {
1447                 return true;
1448         }
1449         return false;
1450 }
1451
1452 /* Find a domain object in the parents of a particular DN.  */
1453 int samdb_search_for_parent_domain(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, struct ldb_dn *dn,
1454                                    struct ldb_dn **parent_dn, const char **errstring)
1455 {
1456         TALLOC_CTX *local_ctx;
1457         struct ldb_dn *sdn = dn;
1458         struct ldb_result *res = NULL;
1459         int ret = 0;
1460         const char *attrs[] = { NULL };
1461
1462         local_ctx = talloc_new(mem_ctx);
1463         if (local_ctx == NULL) return LDB_ERR_OPERATIONS_ERROR;
1464         
1465         while ((sdn = ldb_dn_get_parent(local_ctx, sdn))) {
1466                 ret = ldb_search(ldb, sdn, LDB_SCOPE_BASE, 
1467                                  "(|(objectClass=domain)(objectClass=builtinDomain))", attrs, &res);
1468                 if (ret == LDB_SUCCESS) {
1469                         talloc_steal(local_ctx, res);
1470                         if (res->count == 1) {
1471                                 break;
1472                         }
1473                 } else {
1474                         break;
1475                 }
1476         }
1477
1478         if (ret != LDB_SUCCESS) {
1479                 *errstring = talloc_asprintf(mem_ctx, "Error searching for parent domain of %s, failed searching for %s: %s",
1480                                              ldb_dn_get_linearized(dn),
1481                                              ldb_dn_get_linearized(sdn),
1482                                              ldb_errstring(ldb));
1483                 talloc_free(local_ctx);
1484                 return ret;
1485         }
1486         if (res->count != 1) {
1487                 *errstring = talloc_asprintf(mem_ctx, "Invalid dn (%s), not child of a domain object",
1488                                              ldb_dn_get_linearized(dn));
1489                 talloc_free(local_ctx);
1490                 return LDB_ERR_CONSTRAINT_VIOLATION;
1491         }
1492
1493         *parent_dn = talloc_steal(mem_ctx, res->msgs[0]->dn);
1494         talloc_free(local_ctx);
1495         return ret;
1496 }
1497
1498 /*
1499   check that a password is sufficiently complex
1500 */
1501 static bool samdb_password_complexity_ok(const char *pass)
1502 {
1503         return check_password_quality(pass);
1504 }
1505
1506
1507
1508 /*
1509   set the user password using plaintext, obeying any user or domain
1510   password restrictions
1511
1512   note that this function doesn't actually store the result in the
1513   database, it just fills in the "mod" structure with ldb modify
1514   elements to setup the correct change when samdb_replace() is
1515   called. This allows the caller to combine the change with other
1516   changes (as is needed by some of the set user info levels)
1517
1518   The caller should probably have a transaction wrapping this
1519 */
1520 _PUBLIC_ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
1521                             struct ldb_dn *user_dn,
1522                             struct ldb_dn *domain_dn,
1523                             struct ldb_message *mod,
1524                             const char *new_pass,
1525                             struct samr_Password *lmNewHash, 
1526                             struct samr_Password *ntNewHash,
1527                             bool user_change,
1528                             enum samr_RejectReason *reject_reason,
1529                             struct samr_DomInfo1 **_dominfo)
1530 {
1531         const char * const user_attrs[] = { "userAccountControl", "lmPwdHistory", 
1532                                             "ntPwdHistory", 
1533                                             "dBCSPwd", "unicodePwd", 
1534                                             "objectSid", 
1535                                             "pwdLastSet", NULL };
1536         const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength", 
1537                                               "maxPwdAge", "minPwdAge", 
1538                                               "minPwdLength", NULL };
1539         NTTIME pwdLastSet;
1540         int64_t minPwdAge;
1541         uint_t minPwdLength, pwdProperties, pwdHistoryLength;
1542         uint_t userAccountControl;
1543         struct samr_Password *sambaLMPwdHistory, *sambaNTPwdHistory, *lmPwdHash, *ntPwdHash;
1544         struct samr_Password local_lmNewHash, local_ntNewHash;
1545         int sambaLMPwdHistory_len, sambaNTPwdHistory_len;
1546         struct dom_sid *domain_sid;
1547         struct ldb_message **res;
1548         bool restrictions;
1549         int count;
1550         time_t now = time(NULL);
1551         NTTIME now_nt;
1552         int i;
1553
1554         /* we need to know the time to compute password age */
1555         unix_to_nt_time(&now_nt, now);
1556
1557         /* pull all the user parameters */
1558         count = gendb_search_dn(ctx, mem_ctx, user_dn, &res, user_attrs);
1559         if (count != 1) {
1560                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1561         }
1562         userAccountControl = samdb_result_uint(res[0],   "userAccountControl", 0);
1563         sambaLMPwdHistory_len =   samdb_result_hashes(mem_ctx, res[0], 
1564                                                  "lmPwdHistory", &sambaLMPwdHistory);
1565         sambaNTPwdHistory_len =   samdb_result_hashes(mem_ctx, res[0], 
1566                                                  "ntPwdHistory", &sambaNTPwdHistory);
1567         lmPwdHash =          samdb_result_hash(mem_ctx, res[0],   "dBCSPwd");
1568         ntPwdHash =          samdb_result_hash(mem_ctx, res[0],   "unicodePwd");
1569         pwdLastSet =         samdb_result_uint64(res[0], "pwdLastSet", 0);
1570
1571         /* Only non-trust accounts have restrictions (possibly this
1572          * test is the wrong way around, but I like to be restrictive
1573          * if possible */
1574         restrictions = !(userAccountControl & (UF_INTERDOMAIN_TRUST_ACCOUNT
1575                                                |UF_WORKSTATION_TRUST_ACCOUNT
1576                                                |UF_SERVER_TRUST_ACCOUNT)); 
1577
1578         if (domain_dn) {
1579                 /* pull the domain parameters */
1580                 count = gendb_search_dn(ctx, mem_ctx, domain_dn, &res, domain_attrs);
1581                 if (count != 1) {
1582                         DEBUG(2, ("samdb_set_password: Domain DN %s is invalid, for user %s\n", 
1583                                   ldb_dn_get_linearized(domain_dn),
1584                                   ldb_dn_get_linearized(user_dn)));
1585                         return NT_STATUS_NO_SUCH_DOMAIN;
1586                 }
1587         } else {
1588                 /* work out the domain sid, and pull the domain from there */
1589                 domain_sid =         samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
1590                 if (domain_sid == NULL) {
1591                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
1592                 }
1593
1594                 count = gendb_search(ctx, mem_ctx, NULL, &res, domain_attrs, 
1595                                      "(objectSid=%s)", 
1596                                      ldap_encode_ndr_dom_sid(mem_ctx, domain_sid));
1597                 if (count != 1) {
1598                         DEBUG(2, ("samdb_set_password: Could not find domain to match SID: %s, for user %s\n", 
1599                                   dom_sid_string(mem_ctx, domain_sid),
1600                                   ldb_dn_get_linearized(user_dn)));
1601                         return NT_STATUS_NO_SUCH_DOMAIN;
1602                 }
1603         }
1604
1605         pwdProperties =    samdb_result_uint(res[0],   "pwdProperties", 0);
1606         pwdHistoryLength = samdb_result_uint(res[0],   "pwdHistoryLength", 0);
1607         minPwdLength =     samdb_result_uint(res[0],   "minPwdLength", 0);
1608         minPwdAge =        samdb_result_int64(res[0],  "minPwdAge", 0);
1609
1610         if (_dominfo) {
1611                 struct samr_DomInfo1 *dominfo;
1612                 /* on failure we need to fill in the reject reasons */
1613                 dominfo = talloc(mem_ctx, struct samr_DomInfo1);
1614                 if (dominfo == NULL) {
1615                         return NT_STATUS_NO_MEMORY;
1616                 }
1617                 dominfo->min_password_length     = minPwdLength;
1618                 dominfo->password_properties     = pwdProperties;
1619                 dominfo->password_history_length = pwdHistoryLength;
1620                 dominfo->max_password_age        = minPwdAge;
1621                 dominfo->min_password_age        = minPwdAge;
1622                 *_dominfo = dominfo;
1623         }
1624
1625         if (restrictions && new_pass) {
1626
1627                 /* check the various password restrictions */
1628                 if (restrictions && minPwdLength > strlen_m(new_pass)) {
1629                         if (reject_reason) {
1630                                 *reject_reason = SAMR_REJECT_TOO_SHORT;
1631                         }
1632                         return NT_STATUS_PASSWORD_RESTRICTION;
1633                 }
1634                 
1635                 /* possibly check password complexity */
1636                 if (restrictions && pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
1637                     !samdb_password_complexity_ok(new_pass)) {
1638                         if (reject_reason) {
1639                                 *reject_reason = SAMR_REJECT_COMPLEXITY;
1640                         }
1641                         return NT_STATUS_PASSWORD_RESTRICTION;
1642                 }
1643                 
1644                 /* compute the new nt and lm hashes */
1645                 if (E_deshash(new_pass, local_lmNewHash.hash)) {
1646                         lmNewHash = &local_lmNewHash;
1647                 }
1648                 if (!E_md4hash(new_pass, local_ntNewHash.hash)) {
1649                         /* If we can't convert this password to UCS2, then we should not accept it */
1650                         if (reject_reason) {
1651                                 *reject_reason = SAMR_REJECT_OTHER;
1652                         }
1653                         return NT_STATUS_PASSWORD_RESTRICTION;
1654                 }
1655                 ntNewHash = &local_ntNewHash;
1656         }
1657
1658         if (user_change) {
1659                 /* are all password changes disallowed? */
1660                 if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
1661                         if (reject_reason) {
1662                                 *reject_reason = SAMR_REJECT_OTHER;
1663                         }
1664                         return NT_STATUS_PASSWORD_RESTRICTION;
1665                 }
1666                 
1667                 /* can this user change password? */
1668                 if (userAccountControl & UF_PASSWD_CANT_CHANGE) {
1669                         if (reject_reason) {
1670                                 *reject_reason = SAMR_REJECT_OTHER;
1671                         }
1672                         return NT_STATUS_PASSWORD_RESTRICTION;
1673                 }
1674                 
1675                 /* yes, this is a minus. The ages are in negative 100nsec units! */
1676                 if (pwdLastSet - minPwdAge > now_nt) {
1677                         if (reject_reason) {
1678                                 *reject_reason = SAMR_REJECT_OTHER;
1679                         }
1680                         return NT_STATUS_PASSWORD_RESTRICTION;
1681                 }
1682
1683                 /* check the immediately past password */
1684                 if (pwdHistoryLength > 0) {
1685                         if (lmNewHash && lmPwdHash && memcmp(lmNewHash->hash, lmPwdHash->hash, 16) == 0) {
1686                                 if (reject_reason) {
1687                                         *reject_reason = SAMR_REJECT_IN_HISTORY;
1688                                 }
1689                                 return NT_STATUS_PASSWORD_RESTRICTION;
1690                         }
1691                         if (ntNewHash && ntPwdHash && memcmp(ntNewHash->hash, ntPwdHash->hash, 16) == 0) {
1692                                 if (reject_reason) {
1693                                         *reject_reason = SAMR_REJECT_IN_HISTORY;
1694                                 }
1695                                 return NT_STATUS_PASSWORD_RESTRICTION;
1696                         }
1697                 }
1698                 
1699                 /* check the password history */
1700                 sambaLMPwdHistory_len = MIN(sambaLMPwdHistory_len, pwdHistoryLength);
1701                 sambaNTPwdHistory_len = MIN(sambaNTPwdHistory_len, pwdHistoryLength);
1702                 
1703                 for (i=0; lmNewHash && i<sambaLMPwdHistory_len;i++) {
1704                         if (memcmp(lmNewHash->hash, sambaLMPwdHistory[i].hash, 16) == 0) {
1705                                 if (reject_reason) {
1706                                         *reject_reason = SAMR_REJECT_IN_HISTORY;
1707                                 }
1708                                 return NT_STATUS_PASSWORD_RESTRICTION;
1709                         }
1710                 }
1711                 for (i=0; ntNewHash && i<sambaNTPwdHistory_len;i++) {
1712                         if (memcmp(ntNewHash->hash, sambaNTPwdHistory[i].hash, 16) == 0) {
1713                                 if (reject_reason) {
1714                                         *reject_reason = SAMR_REJECT_IN_HISTORY;
1715                                 }
1716                                 return NT_STATUS_PASSWORD_RESTRICTION;
1717                         }
1718                 }
1719         }
1720
1721 #define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0)
1722
1723         /* the password is acceptable. Start forming the new fields */
1724         if (new_pass) {
1725                 /* if we know the cleartext, then only set it.
1726                  * Modules in ldb will set all the appropriate
1727                  * hashes */
1728                 CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod, 
1729                                                "sambaPassword", new_pass));
1730         } else {
1731                 /* We don't have the cleartext, so delete the old one
1732                  * and set what we have of the hashes */
1733                 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "sambaPassword"));
1734
1735                 if (lmNewHash) {
1736                         CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "dBCSPwd", lmNewHash));
1737                 } else {
1738                         CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "dBCSPwd"));
1739                 }
1740                 
1741                 if (ntNewHash) {
1742                         CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "unicodePwd", ntNewHash));
1743                 } else {
1744                         CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "unicodePwd"));
1745                 }
1746         }
1747
1748         return NT_STATUS_OK;
1749 }
1750
1751
1752 /*
1753   set the user password using plaintext, obeying any user or domain
1754   password restrictions
1755
1756   This wrapper function takes a SID as input, rather than a user DN,
1757   and actually performs the password change
1758
1759 */
1760 _PUBLIC_ NTSTATUS samdb_set_password_sid(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
1761                                 const struct dom_sid *user_sid,
1762                                 const char *new_pass,
1763                                 struct samr_Password *lmNewHash, 
1764                                 struct samr_Password *ntNewHash,
1765                                 bool user_change,
1766                                 enum samr_RejectReason *reject_reason,
1767                                 struct samr_DomInfo1 **_dominfo) 
1768 {
1769         NTSTATUS nt_status;
1770         struct ldb_dn *user_dn;
1771         struct ldb_message *msg;
1772         int ret;
1773
1774         ret = ldb_transaction_start(ctx);
1775         if (ret) {
1776                 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(ctx)));
1777                 return NT_STATUS_TRANSACTION_ABORTED;
1778         }
1779
1780         user_dn = samdb_search_dn(ctx, mem_ctx, NULL, 
1781                                   "(&(objectSid=%s)(objectClass=user))", 
1782                                   ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
1783         if (!user_dn) {
1784                 ldb_transaction_cancel(ctx);
1785                 DEBUG(3, ("samdb_set_password_sid: SID %s not found in samdb, returning NO_SUCH_USER\n",
1786                           dom_sid_string(mem_ctx, user_sid)));
1787                 return NT_STATUS_NO_SUCH_USER;
1788         }
1789
1790         msg = ldb_msg_new(mem_ctx);
1791         if (msg == NULL) {
1792                 ldb_transaction_cancel(ctx);
1793                 return NT_STATUS_NO_MEMORY;
1794         }
1795
1796         msg->dn = ldb_dn_copy(msg, user_dn);
1797         if (!msg->dn) {
1798                 ldb_transaction_cancel(ctx);
1799                 return NT_STATUS_NO_MEMORY;
1800         }
1801
1802         nt_status = samdb_set_password(ctx, mem_ctx,
1803                                        user_dn, NULL,
1804                                        msg, new_pass, 
1805                                        lmNewHash, ntNewHash,
1806                                        user_change, /* This is a password set, not change */
1807                                        reject_reason, _dominfo);
1808         if (!NT_STATUS_IS_OK(nt_status)) {
1809                 ldb_transaction_cancel(ctx);
1810                 return nt_status;
1811         }
1812         
1813         /* modify the samdb record */
1814         ret = samdb_replace(ctx, mem_ctx, msg);
1815         if (ret != 0) {
1816                 ldb_transaction_cancel(ctx);
1817                 return NT_STATUS_ACCESS_DENIED;
1818         }
1819
1820         ret = ldb_transaction_commit(ctx);
1821         if (ret != 0) {
1822                 DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
1823                          ldb_dn_get_linearized(msg->dn),
1824                          ldb_errstring(ctx)));
1825                 return NT_STATUS_TRANSACTION_ABORTED;
1826         }
1827         return NT_STATUS_OK;
1828 }
1829
1830
1831
1832 NTSTATUS samdb_create_foreign_security_principal(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, 
1833                                                  struct dom_sid *sid, struct ldb_dn **ret_dn) 
1834 {
1835         struct ldb_message *msg;
1836         struct ldb_dn *basedn;
1837         const char *sidstr;
1838         int ret;
1839         
1840         sidstr = dom_sid_string(mem_ctx, sid);
1841         NT_STATUS_HAVE_NO_MEMORY(sidstr);
1842         
1843         /* We might have to create a ForeignSecurityPrincipal, even if this user
1844          * is in our own domain */
1845         
1846         msg = ldb_msg_new(mem_ctx);
1847         if (msg == NULL) {
1848                 return NT_STATUS_NO_MEMORY;
1849         }
1850         
1851         /* TODO: Hmmm. This feels wrong. How do I find the base dn to
1852          * put the ForeignSecurityPrincipals? d_state->domain_dn does
1853          * not work, this is wrong for the Builtin domain, there's no
1854          * cn=For...,cn=Builtin,dc={BASEDN}.  -- vl
1855          */
1856         
1857         basedn = samdb_search_dn(sam_ctx, mem_ctx, NULL,
1858                                  "(&(objectClass=container)(cn=ForeignSecurityPrincipals))");
1859         
1860         if (basedn == NULL) {
1861                 DEBUG(0, ("Failed to find DN for "
1862                           "ForeignSecurityPrincipal container\n"));
1863                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1864         }
1865         
1866         /* add core elements to the ldb_message for the alias */
1867         msg->dn = ldb_dn_copy(mem_ctx, basedn);
1868         if ( ! ldb_dn_add_child_fmt(msg->dn, "CN=%s", sidstr))
1869                 return NT_STATUS_NO_MEMORY;
1870         
1871         samdb_msg_add_string(sam_ctx, mem_ctx, msg,
1872                              "objectClass",
1873                              "foreignSecurityPrincipal");
1874         
1875         /* create the alias */
1876         ret = ldb_add(sam_ctx, msg);
1877         if (ret != 0) {
1878                 DEBUG(0,("Failed to create foreignSecurityPrincipal "
1879                          "record %s: %s\n", 
1880                          ldb_dn_get_linearized(msg->dn),
1881                          ldb_errstring(sam_ctx)));
1882                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1883         }
1884         *ret_dn = msg->dn;
1885         return NT_STATUS_OK;
1886 }
1887
1888
1889 /*
1890   Find the DN of a domain, assuming it to be a dotted.dns name
1891 */
1892
1893 struct ldb_dn *samdb_dns_domain_to_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const char *dns_domain) 
1894 {
1895         int i;
1896         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
1897         const char *binary_encoded;
1898         const char **split_realm;
1899         struct ldb_dn *dn;
1900         
1901         if (!tmp_ctx) {
1902                 return NULL;
1903         }
1904         
1905         split_realm = str_list_make(tmp_ctx, dns_domain, ".");
1906         if (!split_realm) {
1907                 talloc_free(tmp_ctx);
1908                 return NULL;
1909         }
1910         dn = ldb_dn_new(mem_ctx, ldb, NULL);
1911         for (i=0; split_realm[i]; i++) {
1912                 binary_encoded = ldb_binary_encode_string(tmp_ctx, split_realm[i]);
1913                 if (!ldb_dn_add_base_fmt(dn, "dc=%s", binary_encoded)) {
1914                         DEBUG(2, ("Failed to add dc=%s element to DN %s\n",
1915                                   binary_encoded, ldb_dn_get_linearized(dn)));
1916                         talloc_free(tmp_ctx);
1917                         return NULL;
1918                 }
1919         }
1920         if (!ldb_dn_validate(dn)) {
1921                 DEBUG(2, ("Failed to validated DN %s\n",
1922                           ldb_dn_get_linearized(dn)));
1923                 return NULL;
1924         }
1925         return dn;
1926 }
1927 /*
1928   Find the DN of a domain, be it the netbios or DNS name 
1929 */
1930
1931 struct ldb_dn *samdb_domain_to_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, 
1932                                   const char *domain_name) 
1933 {
1934         const char * const domain_ref_attrs[] = {
1935                 "ncName", NULL
1936         };
1937         const char * const domain_ref2_attrs[] = {
1938                 NULL
1939         };
1940         struct ldb_result *res_domain_ref;
1941         char *escaped_domain = ldb_binary_encode_string(mem_ctx, domain_name);
1942         /* find the domain's DN */
1943         int ret_domain = ldb_search_exp_fmt(ldb, mem_ctx, 
1944                                             &res_domain_ref, 
1945                                             samdb_partitions_dn(ldb, mem_ctx), 
1946                                             LDB_SCOPE_ONELEVEL, 
1947                                             domain_ref_attrs,
1948                                             "(&(nETBIOSName=%s)(objectclass=crossRef))", 
1949                                             escaped_domain);
1950         if (ret_domain != 0) {
1951                 return NULL;
1952         }
1953         
1954         if (res_domain_ref->count == 0) {
1955                 ret_domain = ldb_search_exp_fmt(ldb, mem_ctx, 
1956                                                 &res_domain_ref, 
1957                                                 samdb_dns_domain_to_dn(ldb, mem_ctx, domain_name),
1958                                                 LDB_SCOPE_BASE,
1959                                                 domain_ref2_attrs,
1960                                                 "(objectclass=domain)");
1961                 if (ret_domain != 0) {
1962                         return NULL;
1963                 }
1964         
1965                 if (res_domain_ref->count == 1) {
1966                         return res_domain_ref->msgs[0]->dn;
1967                 }
1968                 return NULL;
1969         }
1970         
1971         if (res_domain_ref->count > 1) {
1972                 DEBUG(0,("Found %d records matching domain [%s]\n", 
1973                          ret_domain, domain_name));
1974                 return NULL;
1975         }
1976         
1977         return samdb_result_dn(ldb, mem_ctx, res_domain_ref->msgs[0], "nCName", NULL);
1978
1979 }