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