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