Fix build errors on non-ldap systems...change function parms from LDAPMod ** to void **
[samba.git] / source3 / libads / ldap.c
1 /* 
2    Unix SMB/CIFS implementation.
3    ads (active directory) utility library
4    Copyright (C) Andrew Tridgell 2001
5    Copyright (C) Remus Koos 2001
6    Copyright (C) Jim McDonough 2002
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24
25 #ifdef HAVE_ADS
26
27 /*
28   connect to the LDAP server
29 */
30 ADS_STATUS ads_connect(ADS_STRUCT *ads)
31 {
32         int version = LDAP_VERSION3;
33         int code;
34         ADS_STATUS status;
35
36         ads->last_attempt = time(NULL);
37
38         ads->ld = ldap_open(ads->ldap_server, ads->ldap_port);
39         if (!ads->ld) {
40                 return ADS_ERROR_SYSTEM(errno)
41         }
42         status = ads_server_info(ads);
43         if (!ADS_ERR_OK(status)) {
44                 DEBUG(1,("Failed to get ldap server info\n"));
45                 return status;
46         }
47
48         ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
49
50         if (ads->password) {
51                 if ((code = ads_kinit_password(ads)))
52                         return ADS_ERROR_KRB5(code);
53         }
54
55         return ads_sasl_bind(ads);
56 }
57
58 /*
59   do a search with a timeout
60 */
61 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope, 
62                          const char *exp,
63                          const char **attrs, void **res)
64 {
65         struct timeval timeout;
66         int rc;
67
68         timeout.tv_sec = ADS_SEARCH_TIMEOUT;
69         timeout.tv_usec = 0;
70         *res = NULL;
71
72         rc = ldap_search_ext_s(ads->ld, 
73                                bind_path, scope,
74                                exp, attrs, 0, NULL, NULL, 
75                                &timeout, LDAP_NO_LIMIT, (LDAPMessage **)res);
76         return ADS_ERROR(rc);
77 }
78 /*
79   do a general ADS search
80 */
81 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res, 
82                       const char *exp, 
83                       const char **attrs)
84 {
85         return ads_do_search(ads, ads->bind_path, LDAP_SCOPE_SUBTREE, 
86                              exp, attrs, res);
87 }
88
89 /*
90   do a search on a specific DistinguishedName
91 */
92 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res, 
93                          const char *dn, 
94                          const char **attrs)
95 {
96         return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
97 }
98
99 /*
100   free up memory from a ads_search
101 */
102 void ads_msgfree(ADS_STRUCT *ads, void *msg)
103 {
104         if (!msg) return;
105         ldap_msgfree(msg);
106 }
107
108 /*
109   find a machine account given a hostname 
110 */
111 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *host)
112 {
113         ADS_STATUS status;
114         char *exp;
115         const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
116
117         /* the easiest way to find a machine account anywhere in the tree
118            is to look for hostname$ */
119         asprintf(&exp, "(samAccountName=%s$)", host);
120         status = ads_search(ads, res, exp, attrs);
121         free(exp);
122         return status;
123 }
124
125 /*
126   initialize a list of mods - returns (void **) instead of (LDAPMod **)
127                               so proto.h works on non-ldap systems
128 */
129
130 void **ads_mod_list_start(int num_mods)
131 {
132         LDAPMod **mods;
133         
134         if (num_mods < 1)
135                 return NULL;
136         
137         mods = malloc(sizeof(LDAPMod *) * (num_mods + 1));
138         memset(mods, 0, sizeof(LDAPMod *) * num_mods);
139         mods[num_mods] = (LDAPMod *) -1;
140         
141         return (void **) mods;
142 }
143
144 /*
145   add an attribute to the list, with values list already constructed
146 */
147 static BOOL ads_mod_list_add(void **mods, int mod_op, char *name, char **values)
148 {
149         int curmod;
150         LDAPMod **modlist = (LDAPMod **) mods;
151
152         /* find the first empty slot */
153         for (curmod=0; modlist[curmod] > 0; curmod++);
154         if (modlist[curmod] == (LDAPMod *) -1)
155                 return False;
156
157         modlist[curmod] = malloc(sizeof(LDAPMod));
158         modlist[curmod]->mod_type = name;
159         modlist[curmod]->mod_values = values;
160         modlist[curmod]->mod_op = mod_op;
161         return True;
162 }
163
164 BOOL ads_mod_add_list(void **mods, char *name, char **values)
165 {
166         return ads_mod_list_add(mods, LDAP_MOD_ADD, name, values);
167 }
168
169 BOOL ads_mod_repl_list(void **mods, char *name, char **values)
170 {
171         if (values && *values)
172                 return ads_mod_list_add(mods, LDAP_MOD_REPLACE, name, values);
173         else
174                 return ads_mod_list_add(mods, LDAP_MOD_DELETE, name, NULL);
175 }
176
177 /*
178   add an attribute to the list, with values list to be built from args
179 */
180 BOOL ads_mod_list_add_var(void **mods, int mod_op, char *name, ...)
181 {
182         va_list ap;
183         int num_vals, i;
184         char *value, **values;
185
186         /* count the number of values */
187         va_start(ap, name);
188         for (num_vals=0; va_arg(ap, char *); num_vals++);
189         va_end(ap);
190
191
192         if (num_vals) {
193                 values = malloc(sizeof(char *) * (num_vals + 1));
194                 va_start(ap, name);
195                 for (i=0; (value = (char *) va_arg(ap, char *)) &&
196                              i < num_vals; i++)
197                         values[i] = value;
198                 va_end(ap);
199                 values[i] = NULL;
200         } else
201                 values = NULL;
202         return ads_mod_list_add(mods, mod_op, name, values);
203 }
204
205 BOOL ads_mod_repl(void **mods, char *name, char *val)
206 {
207   if (val)
208           return ads_mod_list_add_var(mods, LDAP_MOD_REPLACE, name, val);
209   else
210           return ads_mod_list_add_var(mods, LDAP_MOD_DELETE, name, NULL);
211 }
212
213 BOOL ads_mod_add(void **mods, char *name, char *val)
214 {
215   return ads_mod_list_add_var(mods, LDAP_MOD_ADD, name, val);
216 }
217
218 void ads_mod_list_end(void **mods)
219 {
220         int i;
221         LDAPMod **modlist = (LDAPMod **) mods;
222         
223         if (modlist) {
224                 for (i = 0; modlist[i];  i++) {
225                         if (modlist[i]->mod_values) {
226                                 free(modlist[i]->mod_values);
227                         }
228                         free(modlist[i]);
229                 }
230                 free(modlist);
231         }
232 }
233
234 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, void **mods)
235 {
236         int ret,i;
237         
238         /* find the end of the list, marked by NULL or -1 */
239         for(i=0;mods[i]>0;i++);
240         /* make sure the end of the list is NULL */
241         mods[i] = NULL;
242         ret = ldap_add_s(ads->ld, mod_dn, (LDAPMod **) mods);
243         return ADS_ERROR(ret);
244 }
245
246 /*
247   a convenient routine for adding a generic LDAP record 
248 */
249 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ...)
250 {
251         int i;
252         va_list ap;
253         LDAPMod **mods;
254         char *name, *value;
255         int ret;
256 #define MAX_MOD_VALUES 10
257         
258         /* count the number of attributes */
259         va_start(ap, new_dn);
260         for (i=0; va_arg(ap, char *); i++) {
261                 /* skip the values */
262                 while (va_arg(ap, char *)) ;
263         }
264         va_end(ap);
265
266         mods = malloc(sizeof(LDAPMod *) * (i+1));
267
268         va_start(ap, new_dn);
269         for (i=0; (name=va_arg(ap, char *)); i++) {
270                 char **values;
271                 int j;
272                 values = (char **)malloc(sizeof(char *) * (MAX_MOD_VALUES+1));
273                 for (j=0; (value=va_arg(ap, char *)) && j < MAX_MOD_VALUES; j++) {
274                         values[j] = value;
275                 }
276                 values[j] = NULL;
277                 mods[i] = malloc(sizeof(LDAPMod));
278                 mods[i]->mod_type = name;
279                 mods[i]->mod_op = LDAP_MOD_ADD;
280                 mods[i]->mod_values = values;
281         }
282         mods[i] = NULL;
283         va_end(ap);
284
285         ret = ldap_add_s(ads->ld, new_dn, mods);
286
287         for (i=0; mods[i]; i++) {
288                 free(mods[i]->mod_values);
289                 free(mods[i]);
290         }
291         free(mods);
292         
293         return ADS_ERROR(ret);
294 }
295
296 /*
297   build an org unit string
298   if org unit is Computers or blank then assume a container, otherwise
299   assume a \ separated list of organisational units
300   caller must free
301 */
302 char *ads_ou_string(const char *org_unit)
303 {       
304         if (!org_unit || !*org_unit || strcasecmp(org_unit, "Computers") == 0) {
305                 return strdup("cn=Computers");
306         }
307
308         return ads_build_path(org_unit, "\\/", "ou=", 1);
309 }
310
311
312
313 /*
314   add a machine account to the ADS server
315 */
316 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname, 
317                                        const char *org_unit)
318 {
319         ADS_STATUS ret;
320         char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
321         char *ou_str;
322
323         asprintf(&host_spn, "HOST/%s", hostname);
324         asprintf(&host_upn, "%s@%s", host_spn, ads->realm);
325         ou_str = ads_ou_string(org_unit);
326         asprintf(&new_dn, "cn=%s,%s,%s", hostname, ou_str, ads->bind_path);
327         free(ou_str);
328         asprintf(&samAccountName, "%s$", hostname);
329         asprintf(&controlstr, "%u", 
330                 UF_DONT_EXPIRE_PASSWD | UF_WORKSTATION_TRUST_ACCOUNT | 
331                 UF_TRUSTED_FOR_DELEGATION | UF_USE_DES_KEY_ONLY);
332     
333         ret = ads_gen_add(ads, new_dn,
334                            "cn", hostname, NULL,
335                            "sAMAccountName", samAccountName, NULL,
336                            "objectClass", 
337                               "top", "person", "organizationalPerson", 
338                               "user", "computer", NULL,
339                            "userPrincipalName", host_upn, NULL, 
340                            "servicePrincipalName", host_spn, NULL,
341                            "dNSHostName", hostname, NULL,
342                            "userAccountControl", controlstr, NULL,
343                            "operatingSystem", "Samba", NULL,
344                            "operatingSystemVersion", VERSION, NULL,
345                            NULL);
346
347         free(host_spn);
348         free(host_upn);
349         free(new_dn);
350         free(samAccountName);
351         free(controlstr);
352
353         return ret;
354 }
355
356 /*
357   dump a binary result from ldap
358 */
359 static void dump_binary(const char *field, struct berval **values)
360 {
361         int i, j;
362         for (i=0; values[i]; i++) {
363                 printf("%s: ", field);
364                 for (j=0; j<values[i]->bv_len; j++) {
365                         printf("%02X", (unsigned char)values[i]->bv_val[j]);
366                 }
367                 printf("\n");
368         }
369 }
370
371 /*
372   dump a sid result from ldap
373 */
374 static void dump_sid(const char *field, struct berval **values)
375 {
376         int i;
377         for (i=0; values[i]; i++) {
378                 DOM_SID sid;
379                 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
380                 printf("%s: %s\n", field, sid_string_static(&sid));
381         }
382 }
383
384 /*
385   dump a string result from ldap
386 */
387 static void dump_string(const char *field, struct berval **values)
388 {
389         int i;
390         for (i=0; values[i]; i++) {
391                 printf("%s: %s\n", field, values[i]->bv_val);
392         }
393 }
394
395 /*
396   dump a record from LDAP on stdout
397   used for debugging
398 */
399 void ads_dump(ADS_STRUCT *ads, void *res)
400 {
401         char *field;
402         void *msg;
403         BerElement *b;
404         struct {
405                 char *name;
406                 void (*handler)(const char *, struct berval **);
407         } handlers[] = {
408                 {"objectGUID", dump_binary},
409                 {"nTSecurityDescriptor", dump_binary},
410                 {"objectSid", dump_sid},
411                 {NULL, NULL}
412         };
413     
414         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
415                 for (field = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &b); 
416                      field;
417                      field = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, b)) {
418                         struct berval **values;
419                         int i;
420
421                         values = ldap_get_values_len(ads->ld, (LDAPMessage *)msg, field);
422
423                         for (i=0; handlers[i].name; i++) {
424                                 if (StrCaseCmp(handlers[i].name, field) == 0) {
425                                         handlers[i].handler(field, values);
426                                         break;
427                                 }
428                         }
429                         if (!handlers[i].name) {
430                                 dump_string(field, values);
431                         }
432                         ldap_value_free_len(values);
433                         ldap_memfree(field);
434                 }
435
436                 ber_free(b, 1);
437                 printf("\n");
438         }
439 }
440
441 /*
442   count how many replies are in a LDAPMessage
443 */
444 int ads_count_replies(ADS_STRUCT *ads, void *res)
445 {
446         return ldap_count_entries(ads->ld, (LDAPMessage *)res);
447 }
448
449 /*
450   join a machine to a realm, creating the machine account
451   and setting the machine password
452 */
453 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname, const char *org_unit)
454 {
455         ADS_STATUS status;
456         LDAPMessage *res;
457         char *host;
458
459         /* hostname must be lowercase */
460         host = strdup(hostname);
461         strlower(host);
462
463         status = ads_find_machine_acct(ads, (void **)&res, host);
464         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
465                 DEBUG(0, ("Host account for %s already exists - deleting for readd\n", host));
466                 status = ads_leave_realm(ads, host);
467                 if (!ADS_ERR_OK(status)) {
468                         DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n", 
469                                   host, ads->realm));
470                         return status;
471                 }
472         }
473
474         status = ads_add_machine_acct(ads, host, org_unit);
475         if (!ADS_ERR_OK(status)) {
476                 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
477                 return status;
478         }
479
480         status = ads_find_machine_acct(ads, (void **)&res, host);
481         if (!ADS_ERR_OK(status)) {
482                 DEBUG(0, ("Host account test failed\n"));
483                 return status;
484         }
485
486         free(host);
487
488         return status;
489 }
490
491 /*
492   delete a machine from the realm
493 */
494 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
495 {
496         ADS_STATUS status;
497         void *res;
498         char *hostnameDN, *host; 
499         int rc;
500
501         /* hostname must be lowercase */
502         host = strdup(hostname);
503         strlower(host);
504
505         status = ads_find_machine_acct(ads, &res, host);
506         if (!ADS_ERR_OK(status)) {
507             DEBUG(0, ("Host account for %s does not exist.\n", host));
508             return status;
509         }
510
511         hostnameDN = ldap_get_dn(ads->ld, (LDAPMessage *)res);
512         rc = ldap_delete_s(ads->ld, hostnameDN);
513         ldap_memfree(hostnameDN);
514         if (rc != LDAP_SUCCESS) {
515                 return ADS_ERROR(rc);
516         }
517
518         status = ads_find_machine_acct(ads, &res, host);
519         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
520                 DEBUG(0, ("Failed to remove host account.\n"));
521                 return status;
522         }
523
524         free(host);
525
526         return status;
527 }
528
529
530 ADS_STATUS ads_set_machine_password(ADS_STRUCT *ads,
531                                     const char *hostname, 
532                                     const char *password)
533 {
534         ADS_STATUS status;
535         char *host = strdup(hostname);
536         char *principal; 
537
538         strlower(host);
539
540         asprintf(&principal, "%s@%s", host, ads->realm);
541         
542         status = krb5_set_password(ads->kdc_server, principal, password);
543         
544         free(host);
545         free(principal);
546
547         return status;
548 }
549
550 /*
551   pull the first entry from a ADS result
552 */
553 void *ads_first_entry(ADS_STRUCT *ads, void *res)
554 {
555         return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
556 }
557
558 /*
559   pull the next entry from a ADS result
560 */
561 void *ads_next_entry(ADS_STRUCT *ads, void *res)
562 {
563         return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
564 }
565
566 /*
567   pull a single string from a ADS result
568 */
569 char *ads_pull_string(ADS_STRUCT *ads, 
570                       TALLOC_CTX *mem_ctx, void *msg, const char *field)
571 {
572         char **values;
573         char *ret = NULL;
574
575         values = ldap_get_values(ads->ld, msg, field);
576         if (!values) return NULL;
577         
578         if (values[0]) {
579                 ret = talloc_strdup(mem_ctx, values[0]);
580         }
581         ldap_value_free(values);
582         return ret;
583 }
584
585 /*
586   pull a single uint32 from a ADS result
587 */
588 BOOL ads_pull_uint32(ADS_STRUCT *ads, 
589                      void *msg, const char *field, uint32 *v)
590 {
591         char **values;
592
593         values = ldap_get_values(ads->ld, msg, field);
594         if (!values) return False;
595         if (!values[0]) {
596                 ldap_value_free(values);
597                 return False;
598         }
599
600         *v = atoi(values[0]);
601         ldap_value_free(values);
602         return True;
603 }
604
605 /*
606   pull a single DOM_SID from a ADS result
607 */
608 BOOL ads_pull_sid(ADS_STRUCT *ads, 
609                   void *msg, const char *field, DOM_SID *sid)
610 {
611         struct berval **values;
612         BOOL ret = False;
613
614         values = ldap_get_values_len(ads->ld, msg, field);
615
616         if (!values) return False;
617
618         if (values[0]) {
619                 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
620         }
621         
622         ldap_value_free_len(values);
623         return ret;
624 }
625
626 /*
627   pull an array of DOM_SIDs from a ADS result
628   return the count of SIDs pulled
629 */
630 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
631                   void *msg, const char *field, DOM_SID **sids)
632 {
633         struct berval **values;
634         BOOL ret;
635         int count, i;
636
637         values = ldap_get_values_len(ads->ld, msg, field);
638
639         if (!values) return 0;
640
641         for (i=0; values[i]; i++) /* nop */ ;
642
643         (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
644
645         count = 0;
646         for (i=0; values[i]; i++) {
647                 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
648                 if (ret) count++;
649         }
650         
651         ldap_value_free_len(values);
652         return count;
653 }
654
655
656 /* find the update serial number - this is the core of the ldap cache */
657 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
658 {
659         const char *attrs[] = {"highestCommittedUSN", NULL};
660         ADS_STATUS status;
661         void *res;
662
663         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
664         if (!ADS_ERR_OK(status)) return status;
665
666         if (ads_count_replies(ads, res) != 1) {
667                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
668         }
669
670         ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
671         ads_msgfree(ads, res);
672         return ADS_SUCCESS;
673 }
674
675
676 /* find the servers name and realm - this can be done before authentication 
677    The ldapServiceName field on w2k  looks like this:
678      vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
679 */
680 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
681 {
682         const char *attrs[] = {"ldapServiceName", NULL};
683         ADS_STATUS status;
684         void *res;
685         char **values;
686         char *p;
687
688         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
689         if (!ADS_ERR_OK(status)) return status;
690
691         values = ldap_get_values(ads->ld, res, "ldapServiceName");
692         if (!values || !values[0]) return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
693
694         p = strchr(values[0], ':');
695         if (!p) {
696                 ldap_value_free(values);
697                 ldap_msgfree(res);
698                 return ADS_ERROR(LDAP_DECODING_ERROR);
699         }
700
701         SAFE_FREE(ads->ldap_server_name);
702
703         ads->ldap_server_name = strdup(p+1);
704         p = strchr(ads->ldap_server_name, '$');
705         if (!p || p[1] != '@') {
706                 ldap_value_free(values);
707                 ldap_msgfree(res);
708                 SAFE_FREE(ads->ldap_server_name);
709                 return ADS_ERROR(LDAP_DECODING_ERROR);
710         }
711
712         *p = 0;
713
714         SAFE_FREE(ads->server_realm);
715         SAFE_FREE(ads->bind_path);
716
717         ads->server_realm = strdup(p+2);
718         ads->bind_path = ads_build_dn(ads->server_realm);
719
720         /* in case the realm isn't configured in smb.conf */
721         if (!ads->realm || !ads->realm[0]) {
722                 SAFE_FREE(ads->realm);
723                 ads->realm = strdup(ads->server_realm);
724         }
725
726         DEBUG(3,("got ldap server name %s@%s\n", 
727                  ads->ldap_server_name, ads->realm));
728
729         return ADS_SUCCESS;
730 }
731
732
733 /* 
734    find the list of trusted domains
735 */
736 ADS_STATUS ads_trusted_domains(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, 
737                                int *num_trusts, char ***names, DOM_SID **sids)
738 {
739         const char *attrs[] = {"flatName", "securityIdentifier", NULL};
740         ADS_STATUS status;
741         void *res, *msg;
742         int count, i;
743
744         *num_trusts = 0;
745
746         status = ads_search(ads, &res, "(objectcategory=trustedDomain)", attrs);
747         if (!ADS_ERR_OK(status)) return status;
748
749         count = ads_count_replies(ads, res);
750         if (count == 0) {
751                 ads_msgfree(ads, res);
752                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
753         }
754
755         (*names) = talloc(mem_ctx, sizeof(char *) * count);
756         (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * count);
757         if (! *names || ! *sids) return ADS_ERROR(LDAP_NO_MEMORY);
758
759         for (i=0, msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
760                 (*names)[i] = ads_pull_string(ads, mem_ctx, msg, "flatName");
761                 ads_pull_sid(ads, msg, "securityIdentifier", &(*sids)[i]);
762                 i++;
763         }
764
765         ads_msgfree(ads, res);
766
767         *num_trusts = i;
768
769         return ADS_SUCCESS;
770 }
771
772 /* find the domain sid for our domain */
773 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
774 {
775         const char *attrs[] = {"objectSid", NULL};
776         void *res;
777         ADS_STATUS rc;
778
779         rc = ads_do_search(ads, ads->bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
780                            attrs, &res);
781         if (!ADS_ERR_OK(rc)) return rc;
782         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
783                 return ADS_ERROR_SYSTEM(ENOENT);
784         }
785         ads_msgfree(ads, res);
786         
787         return ADS_SUCCESS;
788 }
789
790 #endif