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