talloc'ify ads modify functions. Also add more complete berval support.
[kai/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, (char **) 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   free up memory from various ads requests
110 */
111 void ads_memfree(ADS_STRUCT *ads, void *mem)
112 {
113         if (!mem) return;
114         ldap_memfree(mem);
115 }
116
117 /*
118   get a dn from search results
119 */
120 char *ads_get_dn(ADS_STRUCT *ads, void *res)
121 {
122         return ldap_get_dn(ads->ld, res);
123 }
124
125 /*
126   find a machine account given a hostname 
127 */
128 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *host)
129 {
130         ADS_STATUS status;
131         char *exp;
132         const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
133
134         /* the easiest way to find a machine account anywhere in the tree
135            is to look for hostname$ */
136         asprintf(&exp, "(samAccountName=%s$)", host);
137         status = ads_search(ads, res, exp, attrs);
138         free(exp);
139         return status;
140 }
141
142 /*
143   duplicate an already-assembled list of values so that it can be
144   freed as part of the standard msgfree call
145 */
146 static char **ads_dup_values(TALLOC_CTX *ctx, char **values)
147 {
148         char **newvals;
149         int i;
150 #define ADS_MAX_NUM_VALUES 32
151
152         for (i=0; values[i] && i<ADS_MAX_NUM_VALUES; i++);
153         if (!(newvals = talloc_zero(ctx, (i+1)*sizeof(char *))))
154                 return NULL;
155         for (i=0; values[i] && i<ADS_MAX_NUM_VALUES; i++)
156                 newvals[i] = values[i];
157         newvals[i] = NULL;
158         return newvals;
159 }
160
161 /*
162   initialize a list of mods 
163 */
164
165 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
166 {
167 #define ADS_MODLIST_ALLOC_SIZE 10
168         LDAPMod **mods;
169         
170         if ((mods = (LDAPMod **) talloc_zero(ctx, sizeof(LDAPMod *) * 
171                                              (ADS_MODLIST_ALLOC_SIZE + 1))))
172                 /* -1 is safety to make sure we don't go over the end.
173                    need to reset it to NULL before doing ldap modify */
174                 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
175         
176         return mods;
177 }
178
179 /*
180   add an attribute to the list, with values list already constructed
181 */
182 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
183                                   int mod_op, char *name, char **values)
184 {
185         int curmod;
186         LDAPMod **modlist = (LDAPMod **) *mods;
187
188         /* find the first empty slot */
189         for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
190              curmod++);
191         if (modlist[curmod] == (LDAPMod *) -1) {
192                 if (!(modlist = talloc_realloc(ctx, modlist, 
193                         (curmod+ADS_MODLIST_ALLOC_SIZE+1)*sizeof(LDAPMod *))))
194                         return ADS_ERROR(LDAP_NO_MEMORY);
195                 memset(&modlist[curmod], 0, 
196                        ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
197                 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
198                 *mods = modlist;
199         }
200                 
201         if (!(modlist[curmod] = talloc_zero(ctx, sizeof(LDAPMod))))
202                 return ADS_ERROR(LDAP_NO_MEMORY);
203         modlist[curmod]->mod_type = name;
204         if (mod_op & LDAP_MOD_BVALUES)
205                 modlist[curmod]->mod_bvalues = (struct berval **) values;
206         else
207                 modlist[curmod]->mod_values = values;
208         modlist[curmod]->mod_op = mod_op;
209         return ADS_ERROR(LDAP_SUCCESS);
210 }
211
212 ADS_STATUS ads_mod_add_list(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
213                             char *name, char **values)
214 {
215         char **newvals = ads_dup_values(ctx, values);
216         if (newvals)
217                 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name, newvals);
218         else
219                 return ADS_ERROR(LDAP_NO_MEMORY);
220 }
221
222 ADS_STATUS ads_mod_repl_list(TALLOC_CTX *ctx, ADS_MODLIST *mods,
223                              char *name, char **values)
224 {
225         char **newvals;
226         if (values && *values) {
227                 if (!(newvals = ads_dup_values(ctx, values)))
228                         return ADS_ERROR(LDAP_NO_MEMORY);
229                 else
230                         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
231                                                 name, newvals);
232         }
233         else
234                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
235 }
236
237 /*
238   add an attribute to the list, with values list to be built from args
239 */
240 ADS_STATUS ads_mod_add_var(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
241                            int mod_op, char *name, ...)
242 {
243         va_list ap;
244         int num_vals, i, do_op;
245         char *value, **values;
246
247         /* count the number of values */
248         va_start(ap, name);
249         for (num_vals=0; va_arg(ap, char *); num_vals++);
250         va_end(ap);
251
252         if (num_vals) {
253                 if (!(values = talloc_zero(ctx, sizeof(char *)*(num_vals+1))))
254                         return ADS_ERROR(LDAP_NO_MEMORY);
255                 va_start(ap, name);
256                 for (i=0; (value = (char *) va_arg(ap, char *)) &&
257                              i < num_vals; i++)
258                         values[i] = value;
259                 va_end(ap);
260                 values[i] = NULL;
261                 do_op = mod_op;
262         } else {
263                 do_op = LDAP_MOD_DELETE;
264                 values = NULL;
265         }
266         return ads_modlist_add(ctx, mods, do_op, name, values);
267 }
268
269 ADS_STATUS ads_mod_add_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
270                            int mod_op, char *name, ...)
271 {
272         va_list ap;
273         int num_vals, i, do_op;
274         char *value, **values;
275
276         /* count the number of values */
277         va_start(ap, name);
278         for (num_vals=0; va_arg(ap, struct berval *); num_vals++);
279         va_end(ap);
280
281         if (num_vals) {
282                 if (!(values = talloc_zero(ctx, sizeof(struct berval) * 
283                                            (num_vals + 1))))
284                         return ADS_ERROR(LDAP_NO_MEMORY);
285                 va_start(ap, name);
286                 for (i=0; (value = (char *) va_arg(ap, char *)) &&
287                              i < num_vals; i++)
288                         values[i] = value;
289                 va_end(ap);
290                 values[i] = NULL;
291                 do_op = mod_op;
292         } else {
293                 do_op = LDAP_MOD_DELETE;
294                 values = NULL;
295         }
296         do_op |= LDAP_MOD_BVALUES;
297         return ads_modlist_add(ctx, mods, do_op, name, values);
298 }
299
300 ADS_STATUS ads_mod_repl(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
301                         char *name, char *val)
302 {
303         if (val)
304                 return ads_mod_add_var(ctx, mods, LDAP_MOD_REPLACE,
305                                        name, val, NULL);
306         else
307                 return ads_mod_add_var(ctx, mods, LDAP_MOD_DELETE, name, NULL);
308 }
309
310 ADS_STATUS ads_mod_add(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
311                        char *name, char *val)
312 {
313         return ads_mod_add_var(ctx, mods, LDAP_MOD_ADD, name, val, NULL);
314 }
315
316 ADS_STATUS ads_mod_add_len(TALLOC_CTX *ctx, ADS_MODLIST *mods,
317                            char *name, size_t size, char *val)
318 {
319         struct berval *bval = NULL;
320
321         if (!(bval = talloc_zero(ctx, sizeof(struct berval *))))
322                 return ADS_ERROR(LDAP_NO_MEMORY);
323         if (!(bval->bv_val = talloc_zero(ctx, sizeof(char *))))
324                 return ADS_ERROR(LDAP_NO_MEMORY);
325
326         bval->bv_val = val;
327         bval->bv_len = size;
328         return ads_mod_add_ber(ctx, mods, LDAP_MOD_ADD, name, bval, NULL);
329 }
330
331 ADS_STATUS ads_mod_repl_len(TALLOC_CTX *ctx, ADS_MODLIST *mods,
332                            char *name, size_t size, char *val)
333 {
334         struct berval *bval = NULL;
335
336         if (!(bval = talloc_zero(ctx, sizeof(struct berval *))))
337                 return ADS_ERROR(LDAP_NO_MEMORY);
338
339         if (!val)
340                 return ads_mod_add_ber(ctx, mods, LDAP_MOD_DELETE, name, NULL);
341         else {
342                 if (!(bval->bv_val = talloc_zero(ctx, sizeof(char *))))
343                         return ADS_ERROR(LDAP_NO_MEMORY);
344                 bval->bv_val = val;
345                 bval->bv_len = size;
346                 return ads_mod_add_ber(ctx, mods, LDAP_MOD_ADD, name, 
347                                        bval, NULL);
348         }
349 }
350
351 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
352 {
353         int ret,i;
354         /* 
355            this control is needed to modify that contains a currently 
356            non-existent attribute (but allowable for the object) to run
357         */
358         LDAPControl PermitModify = {
359                 "1.2.840.113556.1.4.1413",
360                 {0, NULL},
361                 (char) 1};
362         LDAPControl *controls[2] = {
363                 &PermitModify,
364                 NULL };
365
366         /* find the end of the list, marked by NULL or -1 */
367         for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
368         /* make sure the end of the list is NULL */
369         mods[i] = NULL;
370         ret = ldap_modify_ext_s(ads->ld, mod_dn, (LDAPMod **) mods,
371                                 controls, NULL);
372         return ADS_ERROR(ret);
373 }
374
375 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
376 {
377         int i;
378
379         /* find the end of the list, marked by NULL or -1 */
380         for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
381         /* make sure the end of the list is NULL */
382         mods[i] = NULL;
383
384         return ADS_ERROR(ldap_add_s(ads->ld, new_dn, mods));
385 }
386
387 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
388 {
389         return ADS_ERROR(ldap_delete(ads->ld, del_dn));
390 }
391
392 /*
393   build an org unit string
394   if org unit is Computers or blank then assume a container, otherwise
395   assume a \ separated list of organisational units
396   caller must free
397 */
398 char *ads_ou_string(const char *org_unit)
399 {       
400         if (!org_unit || !*org_unit || strcasecmp(org_unit, "Computers") == 0) {
401                 return strdup("cn=Computers");
402         }
403
404         return ads_build_path(org_unit, "\\/", "ou=", 1);
405 }
406
407
408
409 /*
410   add a machine account to the ADS server
411 */
412 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname, 
413                                        const char *org_unit)
414 {
415         ADS_STATUS ret;
416         char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
417         char *ou_str;
418         TALLOC_CTX *ctx;
419         ADS_MODLIST mods;
420
421         if (!(ctx = talloc_init_named("machine_account")))
422                 return ADS_ERROR(LDAP_NO_MEMORY);
423
424         ret = ADS_ERROR(LDAP_NO_MEMORY);
425
426         if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", hostname)))
427                 goto done;
428         if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->realm)))
429                 goto done;
430         ou_str = ads_ou_string(org_unit);
431         new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", hostname, ou_str, 
432                                  ads->bind_path);
433         free(ou_str);
434         if (!new_dn)
435                 goto done;
436
437         if (!(samAccountName = talloc_asprintf(ctx, "%s$", hostname)))
438                 goto done;
439         if (!(controlstr = talloc_asprintf(ctx, "%u", 
440                    UF_DONT_EXPIRE_PASSWD | UF_WORKSTATION_TRUST_ACCOUNT | 
441                    UF_TRUSTED_FOR_DELEGATION | UF_USE_DES_KEY_ONLY)))
442                 goto done;
443
444         if (!(mods = ads_init_mods(ctx)))
445                 goto done;
446         
447         ads_mod_add(ctx, &mods, "cn", hostname);
448         ads_mod_add(ctx, &mods, "sAMAccountName", samAccountName);
449         ads_mod_add_var(ctx, &mods, LDAP_MOD_ADD, "objectClass",
450                         "top", "person", "organizationalPerson",
451                         "user", "computer", NULL);
452         ads_mod_add(ctx, &mods, "userPrincipalName", host_upn);
453         ads_mod_add(ctx, &mods, "servicePrincipalName", host_spn);
454         ads_mod_add(ctx, &mods, "dNSHostName", hostname);
455         ads_mod_add(ctx, &mods, "userAccountControl", controlstr);
456         ads_mod_add(ctx, &mods, "operatingSystem", "Samba");
457         ads_mod_add(ctx, &mods, "operatingSystemVersion", VERSION);
458
459         ret = ads_gen_add(ads, new_dn, mods);
460
461 done:
462         talloc_destroy(ctx);
463         return ret;
464 }
465
466 /*
467   dump a binary result from ldap
468 */
469 static void dump_binary(const char *field, struct berval **values)
470 {
471         int i, j;
472         for (i=0; values[i]; i++) {
473                 printf("%s: ", field);
474                 for (j=0; j<values[i]->bv_len; j++) {
475                         printf("%02X", (unsigned char)values[i]->bv_val[j]);
476                 }
477                 printf("\n");
478         }
479 }
480
481 /*
482   dump a sid result from ldap
483 */
484 static void dump_sid(const char *field, struct berval **values)
485 {
486         int i;
487         for (i=0; values[i]; i++) {
488                 DOM_SID sid;
489                 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
490                 printf("%s: %s\n", field, sid_string_static(&sid));
491         }
492 }
493
494 /*
495   dump a string result from ldap
496 */
497 static void dump_string(const char *field, struct berval **values)
498 {
499         int i;
500         for (i=0; values[i]; i++) {
501                 printf("%s: %s\n", field, values[i]->bv_val);
502         }
503 }
504
505 /*
506   dump a record from LDAP on stdout
507   used for debugging
508 */
509 void ads_dump(ADS_STRUCT *ads, void *res)
510 {
511         char *field;
512         void *msg;
513         BerElement *b;
514         struct {
515                 char *name;
516                 void (*handler)(const char *, struct berval **);
517         } handlers[] = {
518                 {"objectGUID", dump_binary},
519                 {"nTSecurityDescriptor", dump_binary},
520                 {"objectSid", dump_sid},
521                 {NULL, NULL}
522         };
523     
524         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
525                 for (field = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &b); 
526                      field;
527                      field = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, b)) {
528                         struct berval **values;
529                         int i;
530
531                         values = ldap_get_values_len(ads->ld, (LDAPMessage *)msg, field);
532
533                         for (i=0; handlers[i].name; i++) {
534                                 if (StrCaseCmp(handlers[i].name, field) == 0) {
535                                         handlers[i].handler(field, values);
536                                         break;
537                                 }
538                         }
539                         if (!handlers[i].name) {
540                                 dump_string(field, values);
541                         }
542                         ldap_value_free_len(values);
543                         ldap_memfree(field);
544                 }
545
546                 ber_free(b, 1);
547                 printf("\n");
548         }
549 }
550
551 /*
552   count how many replies are in a LDAPMessage
553 */
554 int ads_count_replies(ADS_STRUCT *ads, void *res)
555 {
556         return ldap_count_entries(ads->ld, (LDAPMessage *)res);
557 }
558
559 /*
560   join a machine to a realm, creating the machine account
561   and setting the machine password
562 */
563 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname, const char *org_unit)
564 {
565         ADS_STATUS status;
566         LDAPMessage *res;
567         char *host;
568
569         /* hostname must be lowercase */
570         host = strdup(hostname);
571         strlower(host);
572
573         status = ads_find_machine_acct(ads, (void **)&res, host);
574         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
575                 DEBUG(0, ("Host account for %s already exists - deleting for readd\n", host));
576                 status = ads_leave_realm(ads, host);
577                 if (!ADS_ERR_OK(status)) {
578                         DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n", 
579                                   host, ads->realm));
580                         return status;
581                 }
582         }
583
584         status = ads_add_machine_acct(ads, host, org_unit);
585         if (!ADS_ERR_OK(status)) {
586                 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
587                 return status;
588         }
589
590         status = ads_find_machine_acct(ads, (void **)&res, host);
591         if (!ADS_ERR_OK(status)) {
592                 DEBUG(0, ("Host account test failed\n"));
593                 return status;
594         }
595
596         free(host);
597
598         return status;
599 }
600
601 /*
602   delete a machine from the realm
603 */
604 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
605 {
606         ADS_STATUS status;
607         void *res;
608         char *hostnameDN, *host; 
609         int rc;
610
611         /* hostname must be lowercase */
612         host = strdup(hostname);
613         strlower(host);
614
615         status = ads_find_machine_acct(ads, &res, host);
616         if (!ADS_ERR_OK(status)) {
617             DEBUG(0, ("Host account for %s does not exist.\n", host));
618             return status;
619         }
620
621         hostnameDN = ads_get_dn(ads, (LDAPMessage *)res);
622         rc = ldap_delete_s(ads->ld, hostnameDN);
623         ads_memfree(ads, hostnameDN);
624         if (rc != LDAP_SUCCESS) {
625                 return ADS_ERROR(rc);
626         }
627
628         status = ads_find_machine_acct(ads, &res, host);
629         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
630                 DEBUG(0, ("Failed to remove host account.\n"));
631                 return status;
632         }
633
634         free(host);
635
636         return status;
637 }
638
639
640 ADS_STATUS ads_set_machine_password(ADS_STRUCT *ads,
641                                     const char *hostname, 
642                                     const char *password)
643 {
644         ADS_STATUS status;
645         char *host = strdup(hostname);
646         char *principal; 
647
648         strlower(host);
649
650         asprintf(&principal, "%s@%s", host, ads->realm);
651         
652         status = krb5_set_password(ads->kdc_server, principal, password);
653         
654         free(host);
655         free(principal);
656
657         return status;
658 }
659
660 /*
661   pull the first entry from a ADS result
662 */
663 void *ads_first_entry(ADS_STRUCT *ads, void *res)
664 {
665         return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
666 }
667
668 /*
669   pull the next entry from a ADS result
670 */
671 void *ads_next_entry(ADS_STRUCT *ads, void *res)
672 {
673         return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
674 }
675
676 /*
677   pull a single string from a ADS result
678 */
679 char *ads_pull_string(ADS_STRUCT *ads, 
680                       TALLOC_CTX *mem_ctx, void *msg, const char *field)
681 {
682         char **values;
683         char *ret = NULL;
684
685         values = ldap_get_values(ads->ld, msg, field);
686         if (!values) return NULL;
687         
688         if (values[0]) {
689                 ret = talloc_strdup(mem_ctx, values[0]);
690         }
691         ldap_value_free(values);
692         return ret;
693 }
694
695 /*
696   pull a single uint32 from a ADS result
697 */
698 BOOL ads_pull_uint32(ADS_STRUCT *ads, 
699                      void *msg, const char *field, uint32 *v)
700 {
701         char **values;
702
703         values = ldap_get_values(ads->ld, msg, field);
704         if (!values) return False;
705         if (!values[0]) {
706                 ldap_value_free(values);
707                 return False;
708         }
709
710         *v = atoi(values[0]);
711         ldap_value_free(values);
712         return True;
713 }
714
715 /*
716   pull a single DOM_SID from a ADS result
717 */
718 BOOL ads_pull_sid(ADS_STRUCT *ads, 
719                   void *msg, const char *field, DOM_SID *sid)
720 {
721         struct berval **values;
722         BOOL ret = False;
723
724         values = ldap_get_values_len(ads->ld, msg, field);
725
726         if (!values) return False;
727
728         if (values[0]) {
729                 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
730         }
731         
732         ldap_value_free_len(values);
733         return ret;
734 }
735
736 /*
737   pull an array of DOM_SIDs from a ADS result
738   return the count of SIDs pulled
739 */
740 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
741                   void *msg, const char *field, DOM_SID **sids)
742 {
743         struct berval **values;
744         BOOL ret;
745         int count, i;
746
747         values = ldap_get_values_len(ads->ld, msg, field);
748
749         if (!values) return 0;
750
751         for (i=0; values[i]; i++) /* nop */ ;
752
753         (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
754
755         count = 0;
756         for (i=0; values[i]; i++) {
757                 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
758                 if (ret) count++;
759         }
760         
761         ldap_value_free_len(values);
762         return count;
763 }
764
765
766 /* find the update serial number - this is the core of the ldap cache */
767 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
768 {
769         const char *attrs[] = {"highestCommittedUSN", NULL};
770         ADS_STATUS status;
771         void *res;
772
773         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
774         if (!ADS_ERR_OK(status)) return status;
775
776         if (ads_count_replies(ads, res) != 1) {
777                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
778         }
779
780         ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
781         ads_msgfree(ads, res);
782         return ADS_SUCCESS;
783 }
784
785
786 /* find the servers name and realm - this can be done before authentication 
787    The ldapServiceName field on w2k  looks like this:
788      vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
789 */
790 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
791 {
792         const char *attrs[] = {"ldapServiceName", NULL};
793         ADS_STATUS status;
794         void *res;
795         char **values;
796         char *p;
797
798         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
799         if (!ADS_ERR_OK(status)) return status;
800
801         values = ldap_get_values(ads->ld, res, "ldapServiceName");
802         if (!values || !values[0]) return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
803
804         p = strchr(values[0], ':');
805         if (!p) {
806                 ldap_value_free(values);
807                 ldap_msgfree(res);
808                 return ADS_ERROR(LDAP_DECODING_ERROR);
809         }
810
811         SAFE_FREE(ads->ldap_server_name);
812
813         ads->ldap_server_name = strdup(p+1);
814         p = strchr(ads->ldap_server_name, '$');
815         if (!p || p[1] != '@') {
816                 ldap_value_free(values);
817                 ldap_msgfree(res);
818                 SAFE_FREE(ads->ldap_server_name);
819                 return ADS_ERROR(LDAP_DECODING_ERROR);
820         }
821
822         *p = 0;
823
824         SAFE_FREE(ads->server_realm);
825         SAFE_FREE(ads->bind_path);
826
827         ads->server_realm = strdup(p+2);
828         ads->bind_path = ads_build_dn(ads->server_realm);
829
830         /* in case the realm isn't configured in smb.conf */
831         if (!ads->realm || !ads->realm[0]) {
832                 SAFE_FREE(ads->realm);
833                 ads->realm = strdup(ads->server_realm);
834         }
835
836         DEBUG(3,("got ldap server name %s@%s\n", 
837                  ads->ldap_server_name, ads->realm));
838
839         return ADS_SUCCESS;
840 }
841
842
843 /* 
844    find the list of trusted domains
845 */
846 ADS_STATUS ads_trusted_domains(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, 
847                                int *num_trusts, char ***names, DOM_SID **sids)
848 {
849         const char *attrs[] = {"flatName", "securityIdentifier", NULL};
850         ADS_STATUS status;
851         void *res, *msg;
852         int count, i;
853
854         *num_trusts = 0;
855
856         status = ads_search(ads, &res, "(objectcategory=trustedDomain)", attrs);
857         if (!ADS_ERR_OK(status)) return status;
858
859         count = ads_count_replies(ads, res);
860         if (count == 0) {
861                 ads_msgfree(ads, res);
862                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
863         }
864
865         (*names) = talloc(mem_ctx, sizeof(char *) * count);
866         (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * count);
867         if (! *names || ! *sids) return ADS_ERROR(LDAP_NO_MEMORY);
868
869         for (i=0, msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
870                 (*names)[i] = ads_pull_string(ads, mem_ctx, msg, "flatName");
871                 if (ads_pull_sid(ads, msg, "securityIdentifier", &(*sids)[i])) {
872                         i++;
873                 }
874         }
875
876         ads_msgfree(ads, res);
877
878         *num_trusts = i;
879
880         return ADS_SUCCESS;
881 }
882
883 /* find the domain sid for our domain */
884 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
885 {
886         const char *attrs[] = {"objectSid", NULL};
887         void *res;
888         ADS_STATUS rc;
889
890         rc = ads_do_search(ads, ads->bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
891                            attrs, &res);
892         if (!ADS_ERR_OK(rc)) return rc;
893         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
894                 return ADS_ERROR_SYSTEM(ENOENT);
895         }
896         ads_msgfree(ads, res);
897         
898         return ADS_SUCCESS;
899 }
900
901 #endif