This is meant to be accessed via the helper fn, not directly.
[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  * @file ldap.c
29  * @brief basic ldap client-side routines for ads server communications
30  *
31  * The routines contained here should do the necessary ldap calls for
32  * ads setups.
33  **/
34
35 /**
36  * Connect to the LDAP server
37  * @param ads Pointer to an existing ADS_STRUCT
38  * @return status of connection
39  **/
40 ADS_STATUS ads_connect(ADS_STRUCT *ads)
41 {
42         int version = LDAP_VERSION3;
43         int code;
44         ADS_STATUS status;
45
46         ads->last_attempt = time(NULL);
47
48         ads->ld = NULL;
49
50         if (ads->ldap_server) {
51                 ads->ld = ldap_open(ads->ldap_server, ads->ldap_port);
52         }
53
54         /* if that failed then try each of the BDC's in turn */
55         if (!ads->ld) {
56                 struct in_addr *ip_list;
57                 int count;
58
59                 if (get_dc_list(False, ads->workgroup, &ip_list, &count)) {
60                         int i;
61                         for (i=0;i<count;i++) {
62                                 ads->ld = ldap_open(inet_ntoa(ip_list[i]),
63                                                     ads->ldap_port);
64                                 if (ads->ld) break;
65                         }
66                         if (ads->ld) {
67                                 SAFE_FREE(ads->ldap_server);
68                                 ads->ldap_server = strdup(inet_ntoa(ip_list[i]));
69                         }
70                         free(ip_list);
71                 }
72         }
73
74         if (!ads->ld) {
75                 return ADS_ERROR_SYSTEM(errno);
76         }
77
78         DEBUG(3,("Connected to LDAP server %s\n", ads->ldap_server));
79
80         status = ads_server_info(ads);
81         if (!ADS_ERR_OK(status)) {
82                 DEBUG(1,("Failed to get ldap server info\n"));
83                 return status;
84         }
85
86         ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
87
88 #if KRB5_DNS_HACK
89         /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
90            to MIT kerberos to work (tridge) */
91         {
92                 char *env;
93                 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->server_realm);
94                 setenv(env, inet_ntoa(*interpret_addr2(ads->ldap_server)), 1);
95                 free(env);
96         }
97 #endif
98
99         if (ads->password) {
100                 if ((code = ads_kinit_password(ads)))
101                         return ADS_ERROR_KRB5(code);
102         }
103
104         return ads_sasl_bind(ads);
105 }
106
107
108 /**
109  * Do a search with paged results.  cookie must be null on the first
110  *  call, and then returned on each subsequent call.  It will be null
111  *  again when the entire search is complete 
112  * @param ads connection to ads server 
113  * @param bind_path Base dn for the search
114  * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
115  * @param exp Search expression
116  * @param attrs Attributes to retrieve
117  * @param res ** which will contain results - free res* with ads_msgfree()
118  * @param count Number of entries retrieved on this page
119  * @param cookie The paged results cookie to be returned on subsequent calls
120  * @return status of search
121  **/
122 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
123                                int scope, const char *exp,
124                                const char **attrs, void **res, 
125                                int *count, void **cookie)
126 {
127         int rc;
128         int version;
129         LDAPControl PagedResults; 
130         LDAPControl NoReferrals;
131         BerElement *cookie_be = NULL;
132         struct berval *cookie_bv= NULL;
133         LDAPControl *controls[3];
134         LDAPControl **rcontrols;
135         int i;
136
137         *res = NULL;
138
139         ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
140
141                 /* Paged results only available on ldap v3 or later, so check
142                    version first before using, since at connect time we're
143                    only v2.  Not sure exactly why... */
144         if (version < LDAP_VERSION3) 
145                 return ADS_ERROR(LDAP_NOT_SUPPORTED);
146
147         cookie_be = ber_alloc_t(LBER_USE_DER);
148         if (cookie && *cookie) {
149                 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
150                 ber_bvfree(*cookie); /* don't need it from last time */
151                 *cookie = NULL;
152         } else {
153                 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
154         }
155         ber_flatten(cookie_be, &cookie_bv);
156         PagedResults.ldctl_oid = ADS_PAGE_CTL_OID;
157         PagedResults.ldctl_iscritical = (char) 1;
158         PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
159         PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
160
161         NoReferrals.ldctl_oid = ADS_NO_REFERRALS_OID;
162         NoReferrals.ldctl_iscritical = (char) 0;
163         NoReferrals.ldctl_value.bv_len = 0;
164         NoReferrals.ldctl_value.bv_val = "";
165
166
167         controls[0] = &NoReferrals;
168         controls[1] = &PagedResults;
169         controls[2] = NULL;
170
171         *res = NULL;
172
173         /* we need to disable referrals as the openldap libs don't
174            seem to handle them correctly. They result in the result
175            record containing the server control being removed from the
176            result list (tridge) 
177         
178            leaving this in despite the control that says don't generate
179            referrals, in case the server doesn't support it (jmcd)
180         */
181         ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
182
183         rc = ldap_search_ext_s(ads->ld, bind_path, scope, exp, 
184                                (char **) attrs, 0, controls, NULL,
185                                NULL, LDAP_NO_LIMIT,
186                                (LDAPMessage **)res);
187
188         ber_free(cookie_be, 1);
189         ber_bvfree(cookie_bv);
190
191         if (rc) {
192                 DEBUG(3,("ldap_search_ext_s(%s) -> %s\n", exp, ldap_err2string(rc)));
193                 return ADS_ERROR(rc);
194         }
195
196         rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
197                                         NULL, &rcontrols,  0);
198
199         if (!rcontrols) {
200                 return ADS_ERROR(rc);
201         }
202
203         for (i=0; rcontrols[i]; i++) {
204                 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
205                         cookie_be = ber_init(&rcontrols[i]->ldctl_value);
206                         ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
207                                   &cookie_bv);
208                         /* the berval is the cookie, but must be freed when
209                            it is all done */
210                         if (cookie_bv->bv_len) /* still more to do */
211                                 *cookie=ber_bvdup(cookie_bv);
212                         else
213                                 *cookie=NULL;
214                         ber_bvfree(cookie_bv);
215                         ber_free(cookie_be, 1);
216                         break;
217                 }
218         }
219         ldap_controls_free(rcontrols);
220                         
221         return ADS_ERROR(rc);
222 }
223
224
225 /**
226  * Get all results for a search.  This uses ads_do_paged_search() to return 
227  * all entries in a large search.
228  * @param ads connection to ads server 
229  * @param bind_path Base dn for the search
230  * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
231  * @param exp Search expression
232  * @param attrs Attributes to retrieve
233  * @param res ** which will contain results - free res* with ads_msgfree()
234  * @return status of search
235  **/
236 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
237                              int scope, const char *exp,
238                              const char **attrs, void **res)
239 {
240         void *cookie = NULL;
241         int count = 0;
242         ADS_STATUS status;
243
244         status = ads_do_paged_search(ads, bind_path, scope, exp, attrs, res,
245                                      &count, &cookie);
246
247         if (!ADS_ERR_OK(status)) return status;
248
249         while (cookie) {
250                 void *res2 = NULL;
251                 ADS_STATUS status2;
252                 LDAPMessage *msg, *next;
253
254                 status2 = ads_do_paged_search(ads, bind_path, scope, exp, 
255                                               attrs, &res2, &count, &cookie);
256
257                 if (!ADS_ERR_OK(status2)) break;
258
259                 /* this relies on the way that ldap_add_result_entry() works internally. I hope
260                    that this works on all ldap libs, but I have only tested with openldap */
261                 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
262                         next = ads_next_entry(ads, msg);
263                         ldap_add_result_entry((LDAPMessage **)res, msg);
264                 }
265
266                 /* note that we do not free res2, as the memory is now
267                    part of the main returned list */
268         }
269
270         return status;
271 }
272
273 /**
274  * Run a function on all results for a search.  Uses ads_do_paged_search() and
275  *  runs the function as each page is returned, using ads_process_results()
276  * @param ads connection to ads server
277  * @param bind_path Base dn for the search
278  * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
279  * @param exp Search expression
280  * @param attrs Attributes to retrieve
281  * @param fn Function which takes attr name, values list, and data_area
282  * @param data_area Pointer which is passed to function on each call
283  * @return status of search
284  **/
285 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
286                                 int scope, const char *exp, const char **attrs,
287                                 void(*fn)(char *, void **, void *), 
288                                 void *data_area)
289 {
290         void *cookie = NULL;
291         int count = 0;
292         ADS_STATUS status;
293         void *res;
294
295         status = ads_do_paged_search(ads, bind_path, scope, exp, attrs, &res,
296                                      &count, &cookie);
297
298         if (!ADS_ERR_OK(status)) return status;
299
300         ads_process_results(ads, res, fn, data_area);
301         ads_msgfree(ads, res);
302
303         while (cookie) {
304                 status = ads_do_paged_search(ads, bind_path, scope, exp, attrs,
305                                              &res, &count, &cookie);
306
307                 if (!ADS_ERR_OK(status)) break;
308                 
309                 ads_process_results(ads, res, fn, data_area);
310                 ads_msgfree(ads, res);
311         }
312
313         return status;
314 }
315
316 /**
317  * Do a search with a timeout.
318  * @param ads connection to ads server
319  * @param bind_path Base dn for the search
320  * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
321  * @param exp Search expression
322  * @param attrs Attributes to retrieve
323  * @param res ** which will contain results - free res* with ads_msgfree()
324  * @return status of search
325  **/
326 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope, 
327                          const char *exp,
328                          const char **attrs, void **res)
329 {
330         struct timeval timeout;
331         int rc;
332
333         timeout.tv_sec = ADS_SEARCH_TIMEOUT;
334         timeout.tv_usec = 0;
335         *res = NULL;
336
337         rc = ldap_search_ext_s(ads->ld, 
338                                bind_path, scope,
339                                exp, (char **) attrs, 0, NULL, NULL, 
340                                &timeout, LDAP_NO_LIMIT, (LDAPMessage **)res);
341
342         if (rc == LDAP_SIZELIMIT_EXCEEDED) {
343                 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
344                 rc = 0;
345         }
346
347         return ADS_ERROR(rc);
348 }
349 /**
350  * Do a general ADS search
351  * @param ads connection to ads server
352  * @param res ** which will contain results - free res* with ads_msgfree()
353  * @param exp Search expression
354  * @param attrs Attributes to retrieve
355  * @return status of search
356  **/
357 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res, 
358                       const char *exp, 
359                       const char **attrs)
360 {
361         return ads_do_search(ads, ads->bind_path, LDAP_SCOPE_SUBTREE, 
362                              exp, attrs, res);
363 }
364
365 /**
366  * Do a search on a specific DistinguishedName
367  * @param ads connection to ads server
368  * @param res ** which will contain results - free res* with ads_msgfree()
369  * @param dn DistinguishName to search
370  * @param attrs Attributes to retrieve
371  * @return status of search
372  **/
373 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res, 
374                          const char *dn, 
375                          const char **attrs)
376 {
377         return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
378 }
379
380 /**
381  * Free up memory from a ads_search
382  * @param ads connection to ads server
383  * @param msg Search results to free
384  **/
385 void ads_msgfree(ADS_STRUCT *ads, void *msg)
386 {
387         if (!msg) return;
388         ldap_msgfree(msg);
389 }
390
391 /**
392  * Free up memory from various ads requests
393  * @param ads connection to ads server
394  * @param mem Area to free
395  **/
396 void ads_memfree(ADS_STRUCT *ads, void *mem)
397 {
398         if (!mem) return;
399         ldap_memfree(mem);
400 }
401
402 /**
403  * Get a dn from search results
404  * @param ads connection to ads server
405  * @param res Search results
406  * @return dn string
407  **/
408 char *ads_get_dn(ADS_STRUCT *ads, void *res)
409 {
410         return ldap_get_dn(ads->ld, res);
411 }
412
413 /**
414  * Find a machine account given a hostname
415  * @param ads connection to ads server
416  * @param res ** which will contain results - free res* with ads_msgfree()
417  * @param host Hostname to search for
418  * @return status of search
419  **/
420 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *host)
421 {
422         ADS_STATUS status;
423         char *exp;
424         const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
425
426         /* the easiest way to find a machine account anywhere in the tree
427            is to look for hostname$ */
428         asprintf(&exp, "(samAccountName=%s$)", host);
429         status = ads_search(ads, res, exp, attrs);
430         free(exp);
431         return status;
432 }
433
434 /*
435   duplicate an already-assembled list of values so that it can be
436   freed as part of the standard msgfree call
437 */
438 static char **ads_dup_values(TALLOC_CTX *ctx, char **values)
439 {
440         char **newvals;
441         int i;
442 #define ADS_MAX_NUM_VALUES 32
443
444         for (i=0; values[i] && i<ADS_MAX_NUM_VALUES; i++);
445         if (!(newvals = talloc_zero(ctx, (i+1)*sizeof(char *))))
446                 return NULL;
447         for (i=0; values[i] && i<ADS_MAX_NUM_VALUES; i++)
448                 newvals[i] = values[i];
449         newvals[i] = NULL;
450         return newvals;
451 }
452
453 /**
454  * Initialize a list of mods to be used in a modify request
455  * @param ctx An initialized TALLOC_CTX
456  * @return allocated ADS_MODLIST
457  **/
458 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
459 {
460 #define ADS_MODLIST_ALLOC_SIZE 10
461         LDAPMod **mods;
462         
463         if ((mods = (LDAPMod **) talloc_zero(ctx, sizeof(LDAPMod *) * 
464                                              (ADS_MODLIST_ALLOC_SIZE + 1))))
465                 /* -1 is safety to make sure we don't go over the end.
466                    need to reset it to NULL before doing ldap modify */
467                 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
468         
469         return mods;
470 }
471
472 /*
473   add an attribute to the list, with values list already constructed
474 */
475 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
476                                   int mod_op, const char *name, char **values)
477 {
478         int curmod;
479         LDAPMod **modlist = (LDAPMod **) *mods;
480
481         /* find the first empty slot */
482         for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
483              curmod++);
484         if (modlist[curmod] == (LDAPMod *) -1) {
485                 if (!(modlist = talloc_realloc(ctx, modlist, 
486                         (curmod+ADS_MODLIST_ALLOC_SIZE+1)*sizeof(LDAPMod *))))
487                         return ADS_ERROR(LDAP_NO_MEMORY);
488                 memset(&modlist[curmod], 0, 
489                        ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
490                 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
491                 *mods = modlist;
492         }
493                 
494         if (!(modlist[curmod] = talloc_zero(ctx, sizeof(LDAPMod))))
495                 return ADS_ERROR(LDAP_NO_MEMORY);
496         modlist[curmod]->mod_type = name;
497         if (mod_op & LDAP_MOD_BVALUES)
498                 modlist[curmod]->mod_bvalues = (struct berval **) values;
499         else
500                 modlist[curmod]->mod_values = values;
501         modlist[curmod]->mod_op = mod_op;
502         return ADS_ERROR(LDAP_SUCCESS);
503 }
504
505 /**
506  * Add an already-constructed list of values to a mod list for an ADD
507  * @param ctx An initialized TALLOC_CTX
508  * @param mods An initialized ADS_MODLIST
509  * @param name The attribute name to add
510  * @param values Constructed values list to add
511  * @return ADS STATUS indicating success of add
512  **/
513 ADS_STATUS ads_mod_add_list(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
514                             char *name, char **values)
515 {
516         char **newvals = ads_dup_values(ctx, values);
517         if (newvals)
518                 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name, newvals);
519         else
520                 return ADS_ERROR(LDAP_NO_MEMORY);
521 }
522
523 /**
524  * Add an already-constructed list of values to a mod list for a REPLACE
525  * @param ctx An initialized TALLOC_CTX
526  * @param mods An initialized ADS_MODLIST
527  * @param name The attribute name to add
528  * @param values Constructed values list to add
529  * @return ADS STATUS indicating success of add
530  **/
531 ADS_STATUS ads_mod_repl_list(TALLOC_CTX *ctx, ADS_MODLIST *mods,
532                              char *name, char **values)
533 {
534         char **newvals;
535         if (values && *values) {
536                 if (!(newvals = ads_dup_values(ctx, values)))
537                         return ADS_ERROR(LDAP_NO_MEMORY);
538                 else
539                         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
540                                                 name, newvals);
541         }
542         else
543                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
544 }
545
546 /**
547  * Add any number of string values to a mod list - for ADD or REPLACE
548  * @param ctx An initialized TALLOC_CTX
549  * @param mods An initialized ADS_MODLIST
550  * @param mod_op Operation to perform (LDAP_MOD_ADD | LDAP_MOD_REPLACE)
551  * @param name The attribute name to add
552  * @param ... Any number of values, in (char *) form
553  * @return ADS STATUS indicating success of add
554  **/
555 ADS_STATUS ads_mod_add_var(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
556                            int mod_op, const char *name, ...)
557 {
558         va_list ap;
559         int num_vals, i, do_op;
560         char *value, **values;
561
562         /* count the number of values */
563         va_start(ap, name);
564         for (num_vals=0; va_arg(ap, char *); num_vals++);
565         va_end(ap);
566
567         if (num_vals) {
568                 if (!(values = talloc_zero(ctx, sizeof(char *)*(num_vals+1))))
569                         return ADS_ERROR(LDAP_NO_MEMORY);
570                 va_start(ap, name);
571                 for (i=0; (value = (char *) va_arg(ap, char *)) &&
572                              i < num_vals; i++)
573                         values[i] = value;
574                 va_end(ap);
575                 values[i] = NULL;
576                 do_op = mod_op;
577         } else {
578                 do_op = LDAP_MOD_DELETE;
579                 values = NULL;
580         }
581         return ads_modlist_add(ctx, mods, do_op, name, values);
582 }
583
584 /**
585  * Add any number of ber values to a mod list - for ADD or REPLACE
586  * @param ctx An initialized TALLOC_CTX
587  * @param mods An initialized ADS_MODLIST
588  * @param mod_op Operation to perform (LDAP_MOD_ADD | LDAP_MOD_REPLACE)
589  * @param name The attribute name to add
590  * @param ... Any number of values, in (struct berval *) form
591  * @return ADS STATUS indicating success of add
592  **/
593 ADS_STATUS ads_mod_add_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
594                            int mod_op, const char *name, ...)
595 {
596         va_list ap;
597         int num_vals, i, do_op;
598         char *value, **values;
599
600         /* count the number of values */
601         va_start(ap, name);
602         for (num_vals=0; va_arg(ap, struct berval *); num_vals++);
603         va_end(ap);
604
605         if (num_vals) {
606                 if (!(values = talloc_zero(ctx, sizeof(struct berval) * 
607                                            (num_vals + 1))))
608                         return ADS_ERROR(LDAP_NO_MEMORY);
609                 va_start(ap, name);
610                 for (i=0; (value = (char *) va_arg(ap, char *)) &&
611                              i < num_vals; i++)
612                         values[i] = value;
613                 va_end(ap);
614                 values[i] = NULL;
615                 do_op = mod_op;
616         } else {
617                 do_op = LDAP_MOD_DELETE;
618                 values = NULL;
619         }
620         do_op |= LDAP_MOD_BVALUES;
621         return ads_modlist_add(ctx, mods, do_op, name, values);
622 }
623
624 /**
625  * Add a single string value to a mod list - for REPLACE
626  * @param ctx An initialized TALLOC_CTX
627  * @param mods An initialized ADS_MODLIST
628  * @param name The attribute name to replace
629  * @param val The value to add
630  * @return ADS STATUS indicating success of add
631  **/
632 ADS_STATUS ads_mod_repl(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
633                         char *name, char *val)
634 {
635         if (val)
636                 return ads_mod_add_var(ctx, mods, LDAP_MOD_REPLACE,
637                                        name, val, NULL);
638         else
639                 return ads_mod_add_var(ctx, mods, LDAP_MOD_DELETE, name, NULL);
640 }
641
642 /**
643  * Add a single string value to a mod list - for ADD
644  * @param ctx An initialized TALLOC_CTX
645  * @param mods An initialized ADS_MODLIST
646  * @param name The attribute name to add
647  * @param val The value to add
648  * @return ADS STATUS indicating success of add
649  **/
650 ADS_STATUS ads_mod_add(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
651                        const char *name, const char *val)
652 {
653         return ads_mod_add_var(ctx, mods, LDAP_MOD_ADD, name, val, NULL);
654 }
655
656 /**
657  * Add a single berval value to a mod list - for ADD
658  * @param ctx An initialized TALLOC_CTX
659  * @param mods An initialized ADS_MODLIST
660  * @param name The attribute name to add
661  * @param size The size of of the value
662  * @param val The value to add
663  * @return ADS STATUS indicating success of add
664  **/
665 ADS_STATUS ads_mod_add_len(TALLOC_CTX *ctx, ADS_MODLIST *mods,
666                            char *name, size_t size, char *val)
667 {
668         struct berval *bval = NULL;
669
670         if (!(bval = talloc_zero(ctx, sizeof(struct berval *))))
671                 return ADS_ERROR(LDAP_NO_MEMORY);
672         if (!(bval->bv_val = talloc_zero(ctx, sizeof(char *))))
673                 return ADS_ERROR(LDAP_NO_MEMORY);
674
675         bval->bv_val = val;
676         bval->bv_len = size;
677         return ads_mod_add_ber(ctx, mods, LDAP_MOD_ADD, name, bval, NULL);
678 }
679
680 /**
681  * Add a single berval value to a mod list - for REPLACE
682  * @param ctx An initialized TALLOC_CTX
683  * @param mods An initialized ADS_MODLIST
684  * @param name The attribute name to replace
685  * @param size The size of of the value
686  * @param val The value to add
687  * @return ADS STATUS indicating success of add
688  **/
689 ADS_STATUS ads_mod_repl_len(TALLOC_CTX *ctx, ADS_MODLIST *mods,
690                             const char *name, size_t size, char *val)
691 {
692         struct berval *bval = NULL;
693
694         if (!(bval = talloc_zero(ctx, sizeof(struct berval *))))
695                 return ADS_ERROR(LDAP_NO_MEMORY);
696
697         if (!val)
698                 return ads_mod_add_ber(ctx, mods, LDAP_MOD_DELETE, name, NULL);
699         else {
700                 if (!(bval->bv_val = talloc_zero(ctx, sizeof(char *))))
701                         return ADS_ERROR(LDAP_NO_MEMORY);
702                 bval->bv_val = val;
703                 bval->bv_len = size;
704                 return ads_mod_add_ber(ctx, mods, LDAP_MOD_REPLACE, name, 
705                                        bval, NULL);
706         }
707 }
708
709 /**
710  * Perform an ldap modify
711  * @param ads connection to ads server
712  * @param mod_dn DistinguishedName to modify
713  * @param mods list of modifications to perform
714  * @return status of modify
715  **/
716 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
717 {
718         int ret,i;
719         /* 
720            this control is needed to modify that contains a currently 
721            non-existent attribute (but allowable for the object) to run
722         */
723         LDAPControl PermitModify = {
724                 "1.2.840.113556.1.4.1413",
725                 {0, NULL},
726                 (char) 1};
727         LDAPControl *controls[2];
728
729         controls[0] = &PermitModify;
730         controls[1] = NULL;
731
732         /* find the end of the list, marked by NULL or -1 */
733         for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
734         /* make sure the end of the list is NULL */
735         mods[i] = NULL;
736         ret = ldap_modify_ext_s(ads->ld, mod_dn, (LDAPMod **) mods,
737                                 controls, NULL);
738         return ADS_ERROR(ret);
739 }
740
741 /**
742  * Perform an ldap add
743  * @param ads connection to ads server
744  * @param new_dn DistinguishedName to add
745  * @param mods list of attributes and values for DN
746  * @return status of add
747  **/
748 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
749 {
750         int i;
751
752         /* find the end of the list, marked by NULL or -1 */
753         for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
754         /* make sure the end of the list is NULL */
755         mods[i] = NULL;
756
757         return ADS_ERROR(ldap_add_s(ads->ld, new_dn, mods));
758 }
759
760 /**
761  * Delete a DistinguishedName
762  * @param ads connection to ads server
763  * @param new_dn DistinguishedName to delete
764  * @return status of delete
765  **/
766 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
767 {
768         return ADS_ERROR(ldap_delete(ads->ld, del_dn));
769 }
770
771 /**
772  * Build an org unit string
773  *  if org unit is Computers or blank then assume a container, otherwise
774  *  assume a \ separated list of organisational units
775  * @param org_unit Organizational unit
776  * @return org unit string - caller must free
777  **/
778 char *ads_ou_string(const char *org_unit)
779 {       
780         if (!org_unit || !*org_unit || strcasecmp(org_unit, "Computers") == 0) {
781                 return strdup("cn=Computers");
782         }
783
784         return ads_build_path(org_unit, "\\/", "ou=", 1);
785 }
786
787
788
789 /*
790   add a machine account to the ADS server
791 */
792 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname, 
793                                        const char *org_unit)
794 {
795         ADS_STATUS ret;
796         char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
797         char *ou_str;
798         TALLOC_CTX *ctx;
799         ADS_MODLIST mods;
800
801         if (!(ctx = talloc_init_named("machine_account")))
802                 return ADS_ERROR(LDAP_NO_MEMORY);
803
804         ret = ADS_ERROR(LDAP_NO_MEMORY);
805
806         if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", hostname)))
807                 goto done;
808         if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->realm)))
809                 goto done;
810         ou_str = ads_ou_string(org_unit);
811         new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", hostname, ou_str, 
812                                  ads->bind_path);
813         free(ou_str);
814         if (!new_dn)
815                 goto done;
816
817         if (!(samAccountName = talloc_asprintf(ctx, "%s$", hostname)))
818                 goto done;
819         if (!(controlstr = talloc_asprintf(ctx, "%u", 
820                    UF_DONT_EXPIRE_PASSWD | UF_WORKSTATION_TRUST_ACCOUNT | 
821                    UF_TRUSTED_FOR_DELEGATION | UF_USE_DES_KEY_ONLY)))
822                 goto done;
823
824         if (!(mods = ads_init_mods(ctx)))
825                 goto done;
826         
827         ads_mod_add(ctx, &mods, "cn", hostname);
828         ads_mod_add(ctx, &mods, "sAMAccountName", samAccountName);
829         ads_mod_add_var(ctx, &mods, LDAP_MOD_ADD, "objectClass",
830                         "top", "person", "organizationalPerson",
831                         "user", "computer", NULL);
832         ads_mod_add(ctx, &mods, "userPrincipalName", host_upn);
833         ads_mod_add(ctx, &mods, "servicePrincipalName", host_spn);
834         ads_mod_add(ctx, &mods, "dNSHostName", hostname);
835         ads_mod_add(ctx, &mods, "userAccountControl", controlstr);
836         ads_mod_add(ctx, &mods, "operatingSystem", "Samba");
837         ads_mod_add(ctx, &mods, "operatingSystemVersion", VERSION);
838
839         ads_gen_add(ads, new_dn, mods);
840         ret = ads_set_machine_sd(ads, hostname, new_dn);
841
842 done:
843         talloc_destroy(ctx);
844         return ret;
845 }
846
847 /*
848   dump a binary result from ldap
849 */
850 static void dump_binary(const char *field, struct berval **values)
851 {
852         int i, j;
853         for (i=0; values[i]; i++) {
854                 printf("%s: ", field);
855                 for (j=0; j<values[i]->bv_len; j++) {
856                         printf("%02X", (unsigned char)values[i]->bv_val[j]);
857                 }
858                 printf("\n");
859         }
860 }
861
862 /*
863   dump a sid result from ldap
864 */
865 static void dump_sid(const char *field, struct berval **values)
866 {
867         int i;
868         for (i=0; values[i]; i++) {
869                 DOM_SID sid;
870                 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
871                 printf("%s: %s\n", field, sid_string_static(&sid));
872         }
873 }
874
875 /*
876   dump ntSecurityDescriptor
877 */
878 static void dump_sd(const char *filed, struct berval **values)
879 {
880         prs_struct ps;
881         
882         SEC_DESC   *psd = 0;
883         TALLOC_CTX *ctx = 0;
884
885         if (!(ctx = talloc_init_named("sec_io_desc")))
886                 return;
887
888         /* prepare data */
889         prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
890         prs_append_data(&ps, values[0]->bv_val, values[0]->bv_len);
891         ps.data_offset = 0;
892
893         /* parse secdesc */
894         if (!sec_io_desc("sd", &psd, &ps, 1)) {
895                 prs_mem_free(&ps);
896                 talloc_destroy(ctx);
897                 return;
898         }
899         if (psd) ads_disp_sd(psd);
900
901         prs_mem_free(&ps);
902         talloc_destroy(ctx);
903 }
904
905 /*
906   dump a string result from ldap
907 */
908 static void dump_string(const char *field, struct berval **values)
909 {
910         int i;
911         for (i=0; values[i]; i++) {
912                 printf("%s: %s\n", field, values[i]->bv_val);
913         }
914 }
915
916 /*
917   dump a field from LDAP on stdout
918   used for debugging
919 */
920
921 static void ads_dump_field(char *field, void **values, void *data_area)
922 {
923         struct {
924                 char *name;
925                 void (*handler)(const char *, struct berval **);
926         } handlers[] = {
927                 {"objectGUID", dump_binary},
928                 {"nTSecurityDescriptor", dump_sd},
929                 {"objectSid", dump_sid},
930                 {NULL, NULL}
931         };
932         int i;
933
934         if (!field) { /* must be end of an entry */
935                 printf("\n");
936                 return;
937         }
938
939         for (i=0; handlers[i].name; i++) {
940                 if (StrCaseCmp(handlers[i].name, field) == 0) {
941                         handlers[i].handler(field, (struct berval **) values);
942                         break;
943                 }
944         }
945         if (!handlers[i].name) {
946                 dump_string(field, (struct berval **) values);
947         }
948 }
949
950 /**
951  * Dump a result from LDAP on stdout
952  *  used for debugging
953  * @param ads connection to ads server
954  * @param res Results to dump
955  **/
956
957 void ads_dump(ADS_STRUCT *ads, void *res)
958 {
959         ads_process_results(ads, res, ads_dump_field, NULL);
960 }
961
962 /**
963  * Walk through results, calling a function for each entry found.
964  *  The function receives a field name, a berval * array of values,
965  *  and a data area passed through from the start.  The function is
966  *  called once with null for field and values at the end of each
967  *  entry.
968  * @param ads connection to ads server
969  * @param res Results to process
970  * @param fn Function for processing each result
971  * @param data_area user-defined area to pass to function
972  **/
973 void ads_process_results(ADS_STRUCT *ads, void *res,
974                          void(*fn)(char *, void **, void *),
975                          void *data_area)
976 {
977         void *msg;
978
979         for (msg = ads_first_entry(ads, res); msg; 
980              msg = ads_next_entry(ads, msg)) {
981                 char *field;
982                 BerElement *b;
983         
984                 for (field = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &b); 
985                      field;
986                      field = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, b)) {
987                         struct berval **values;
988                         
989                         values = ldap_get_values_len(ads->ld, (LDAPMessage *)msg, field);
990                         fn(field, (void **) values, data_area);
991
992                         ldap_value_free_len(values);
993                         ldap_memfree(field);
994                 }
995                 ber_free(b, 0);
996                 fn(NULL, NULL, data_area); /* completed an entry */
997
998         }
999 }
1000
1001 /**
1002  * Walk through an entry, calling a function for each attribute found.
1003  *  The function receives a field name, a berval * array of values,
1004  *  and a data area passed through from the start.
1005  * @param ads connection to ads server
1006  * @param res Results to process
1007  * @param fn Function for processing each result
1008  * @param data_area user-defined area to pass to function
1009  **/
1010 void ads_process_entry(ADS_STRUCT *ads, void *msg,
1011                        void(*fn)(ADS_STRUCT *, char *, void **, void *),
1012                        void *data_area)
1013 {
1014         char *field;
1015         BerElement *b;
1016         
1017         for (field = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &b); 
1018              field;
1019              field = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, b)) {
1020                 struct berval **values;
1021
1022                 values = ldap_get_values_len(ads->ld, (LDAPMessage *)msg, field);
1023                 fn(ads, field, (void **) values, data_area);
1024
1025                 ldap_value_free_len(values);
1026                 ldap_memfree(field);
1027         }
1028         ber_free(b, 0);
1029 }
1030 /**
1031  * count how many replies are in a LDAPMessage
1032  * @param ads connection to ads server
1033  * @param res Results to count
1034  * @return number of replies
1035  **/
1036 int ads_count_replies(ADS_STRUCT *ads, void *res)
1037 {
1038         return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1039 }
1040
1041 /**
1042  * Join a machine to a realm
1043  *  Creates the machine account and sets the machine password
1044  * @param ads connection to ads server
1045  * @param hostname name of host to add
1046  * @param org_unit Organizational unit to place machine in
1047  * @return status of join
1048  **/
1049 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname, const char *org_unit)
1050 {
1051         ADS_STATUS status;
1052         LDAPMessage *res;
1053         char *host;
1054
1055         /* hostname must be lowercase */
1056         host = strdup(hostname);
1057         strlower(host);
1058
1059         status = ads_find_machine_acct(ads, (void **)&res, host);
1060         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1061                 DEBUG(0, ("Host account for %s already exists - deleting old account\n", host));
1062                 status = ads_leave_realm(ads, host);
1063                 if (!ADS_ERR_OK(status)) {
1064                         DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n", 
1065                                   host, ads->realm));
1066                         return status;
1067                 }
1068         }
1069
1070         status = ads_add_machine_acct(ads, host, org_unit);
1071         if (!ADS_ERR_OK(status)) {
1072                 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
1073                 return status;
1074         }
1075
1076         status = ads_find_machine_acct(ads, (void **)&res, host);
1077         if (!ADS_ERR_OK(status)) {
1078                 DEBUG(0, ("Host account test failed\n"));
1079                 return status;
1080         }
1081
1082         free(host);
1083
1084         return status;
1085 }
1086
1087 /**
1088  * Delete a machine from the realm
1089  * @param ads connection to ads server
1090  * @param hostname Machine to remove
1091  * @return status of delete
1092  **/
1093 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1094 {
1095         ADS_STATUS status;
1096         void *res;
1097         char *hostnameDN, *host; 
1098         int rc;
1099
1100         /* hostname must be lowercase */
1101         host = strdup(hostname);
1102         strlower(host);
1103
1104         status = ads_find_machine_acct(ads, &res, host);
1105         if (!ADS_ERR_OK(status)) {
1106             DEBUG(0, ("Host account for %s does not exist.\n", host));
1107             return status;
1108         }
1109
1110         hostnameDN = ads_get_dn(ads, (LDAPMessage *)res);
1111         rc = ldap_delete_s(ads->ld, hostnameDN);
1112         ads_memfree(ads, hostnameDN);
1113         if (rc != LDAP_SUCCESS) {
1114                 return ADS_ERROR(rc);
1115         }
1116
1117         status = ads_find_machine_acct(ads, &res, host);
1118         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1119                 DEBUG(0, ("Failed to remove host account.\n"));
1120                 return status;
1121         }
1122
1123         free(host);
1124
1125         return status;
1126 }
1127
1128 /**
1129  * add machine account to existing security descriptor 
1130  * @param ads connection to ads server
1131  * @param hostname machine to add
1132  * @param dn DN of security descriptor
1133  * @return status
1134  **/
1135 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1136 {
1137         const char     *attrs[] = {"ntSecurityDescriptor", "objectSid", 0};
1138         char           *exp     = 0;
1139         size_t          sd_size = 0;
1140         struct berval **bvals   = 0;
1141         prs_struct      ps;
1142         prs_struct      ps_wire;
1143
1144         LDAPMessage *res  = 0;
1145         LDAPMessage *msg  = 0;
1146         ADS_MODLIST  mods = 0;
1147
1148         NTSTATUS    status;
1149         ADS_STATUS  ret;
1150         DOM_SID     sid;
1151         SEC_DESC   *psd = 0;
1152         TALLOC_CTX *ctx = 0;    
1153
1154         if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1155
1156         ret = ADS_ERROR(LDAP_SUCCESS);
1157
1158         asprintf(&exp, "(samAccountName=%s$)", hostname);
1159         ret = ads_search(ads, (void *) &res, exp, attrs);
1160
1161         if (!ADS_ERR_OK(ret)) return ret;
1162
1163         msg   = ads_first_entry(ads, res);
1164         bvals = ldap_get_values_len(ads->ld, msg, attrs[0]);
1165         ads_pull_sid(ads, msg, attrs[1], &sid); 
1166         ads_msgfree(ads, res);
1167 #if 0
1168         file_save("/tmp/sec_desc.old", bvals[0]->bv_val, bvals[0]->bv_len);
1169 #endif
1170         if (!(ctx = talloc_init_named("sec_io_desc")))
1171                 return ADS_ERROR(LDAP_NO_MEMORY);
1172
1173         prs_init(&ps, bvals[0]->bv_len, ctx, UNMARSHALL);
1174         prs_append_data(&ps, bvals[0]->bv_val, bvals[0]->bv_len);
1175         ps.data_offset = 0;
1176         ldap_value_free_len(bvals);
1177
1178         if (!sec_io_desc("sd", &psd, &ps, 1))
1179                 goto ads_set_sd_error;
1180
1181         status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1182
1183         if (!NT_STATUS_IS_OK(status))
1184                 goto ads_set_sd_error;
1185
1186         prs_init(&ps_wire, sd_size, ctx, MARSHALL);
1187         if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1))
1188                 goto ads_set_sd_error;
1189
1190 #if 0
1191         file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1192 #endif
1193         if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1194
1195         ads_mod_repl_len(ctx, &mods, attrs[0], sd_size, prs_data_p(&ps_wire));
1196         ret = ads_gen_mod(ads, dn, mods);
1197
1198         prs_mem_free(&ps);
1199         prs_mem_free(&ps_wire);
1200         talloc_destroy(ctx);
1201         return ret;
1202
1203 ads_set_sd_error:
1204         prs_mem_free(&ps);
1205         prs_mem_free(&ps_wire);
1206         talloc_destroy(ctx);
1207         return ADS_ERROR(LDAP_NO_MEMORY);
1208 }
1209
1210 /**
1211  * Set the machine account password
1212  * @param ads connection to ads server
1213  * @param hostname machine whose password is being set
1214  * @param password new password
1215  * @return status of password change
1216  **/
1217 ADS_STATUS ads_set_machine_password(ADS_STRUCT *ads,
1218                                     const char *hostname, 
1219                                     const char *password)
1220 {
1221         ADS_STATUS status;
1222         char *host = strdup(hostname);
1223         char *principal; 
1224
1225         if (!ads->kdc_server) {
1226                 DEBUG(0, ("Unable to find KDC server\n"));
1227                 return ADS_ERROR(LDAP_SERVER_DOWN);
1228         }
1229
1230         strlower(host);
1231
1232         asprintf(&principal, "%s@%s", host, ads->realm);
1233         
1234         status = krb5_set_password(ads->kdc_server, principal, password);
1235         
1236         free(host);
1237         free(principal);
1238
1239         return status;
1240 }
1241
1242 /**
1243  * pull the first entry from a ADS result
1244  * @param ads connection to ads server
1245  * @param res Results of search
1246  * @return first entry from result
1247  **/
1248 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1249 {
1250         return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1251 }
1252
1253 /**
1254  * pull the next entry from a ADS result
1255  * @param ads connection to ads server
1256  * @param res Results of search
1257  * @return next entry from result
1258  **/
1259 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1260 {
1261         return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1262 }
1263
1264 /**
1265  * pull a single string from a ADS result
1266  * @param ads connection to ads server
1267  * @param mem_ctx TALLOC_CTX to use for allocating result string
1268  * @param msg Results of search
1269  * @param field Attribute to retrieve
1270  * @return Result string in talloc context
1271  **/
1272 char *ads_pull_string(ADS_STRUCT *ads, 
1273                       TALLOC_CTX *mem_ctx, void *msg, const char *field)
1274 {
1275         char **values;
1276         char *ret = NULL;
1277
1278         values = ldap_get_values(ads->ld, msg, field);
1279         if (!values) return NULL;
1280         
1281         if (values[0]) {
1282                 ret = talloc_strdup(mem_ctx, values[0]);
1283         }
1284         ldap_value_free(values);
1285         return ret;
1286 }
1287
1288 /**
1289  * pull a single uint32 from a ADS result
1290  * @param ads connection to ads server
1291  * @param msg Results of search
1292  * @param field Attribute to retrieve
1293  * @param v Pointer to int to store result
1294  * @return boolean inidicating success
1295 */
1296 BOOL ads_pull_uint32(ADS_STRUCT *ads, 
1297                      void *msg, const char *field, uint32 *v)
1298 {
1299         char **values;
1300
1301         values = ldap_get_values(ads->ld, msg, field);
1302         if (!values) return False;
1303         if (!values[0]) {
1304                 ldap_value_free(values);
1305                 return False;
1306         }
1307
1308         *v = atoi(values[0]);
1309         ldap_value_free(values);
1310         return True;
1311 }
1312
1313 /**
1314  * pull a single DOM_SID from a ADS result
1315  * @param ads connection to ads server
1316  * @param msg Results of search
1317  * @param field Attribute to retrieve
1318  * @param sid Pointer to sid to store result
1319  * @return boolean inidicating success
1320 */
1321 BOOL ads_pull_sid(ADS_STRUCT *ads, 
1322                   void *msg, const char *field, DOM_SID *sid)
1323 {
1324         struct berval **values;
1325         BOOL ret = False;
1326
1327         values = ldap_get_values_len(ads->ld, msg, field);
1328
1329         if (!values) return False;
1330
1331         if (values[0]) {
1332                 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
1333         }
1334         
1335         ldap_value_free_len(values);
1336         return ret;
1337 }
1338
1339 /**
1340  * pull an array of DOM_SIDs from a ADS result
1341  * @param ads connection to ads server
1342  * @param mem_ctx TALLOC_CTX for allocating sid array
1343  * @param msg Results of search
1344  * @param field Attribute to retrieve
1345  * @param sids pointer to sid array to allocate
1346  * @return the count of SIDs pulled
1347  **/
1348 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1349                   void *msg, const char *field, DOM_SID **sids)
1350 {
1351         struct berval **values;
1352         BOOL ret;
1353         int count, i;
1354
1355         values = ldap_get_values_len(ads->ld, msg, field);
1356
1357         if (!values) return 0;
1358
1359         for (i=0; values[i]; i++) /* nop */ ;
1360
1361         (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
1362
1363         count = 0;
1364         for (i=0; values[i]; i++) {
1365                 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
1366                 if (ret) count++;
1367         }
1368         
1369         ldap_value_free_len(values);
1370         return count;
1371 }
1372
1373
1374 /**
1375  * find the update serial number - this is the core of the ldap cache
1376  * @param ads connection to ads server
1377  * @param ads connection to ADS server
1378  * @param usn Pointer to retrieved update serial number
1379  * @return status of search
1380  **/
1381 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
1382 {
1383         const char *attrs[] = {"highestCommittedUSN", NULL};
1384         ADS_STATUS status;
1385         void *res;
1386
1387         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1388         if (!ADS_ERR_OK(status)) return status;
1389
1390         if (ads_count_replies(ads, res) != 1) {
1391                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1392         }
1393
1394         ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
1395         ads_msgfree(ads, res);
1396         return ADS_SUCCESS;
1397 }
1398
1399
1400 /**
1401  * Find the servers name and realm - this can be done before authentication 
1402  *  The ldapServiceName field on w2k  looks like this:
1403  *    vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
1404  * @param ads connection to ads server
1405  * @return status of search
1406  **/
1407 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
1408 {
1409         const char *attrs[] = {"ldapServiceName", NULL};
1410         ADS_STATUS status;
1411         void *res;
1412         char **values;
1413         char *p;
1414
1415         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1416         if (!ADS_ERR_OK(status)) return status;
1417
1418         values = ldap_get_values(ads->ld, res, "ldapServiceName");
1419         if (!values || !values[0]) return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1420
1421         p = strchr(values[0], ':');
1422         if (!p) {
1423                 ldap_value_free(values);
1424                 ldap_msgfree(res);
1425                 return ADS_ERROR(LDAP_DECODING_ERROR);
1426         }
1427
1428         SAFE_FREE(ads->ldap_server_name);
1429
1430         ads->ldap_server_name = strdup(p+1);
1431         p = strchr(ads->ldap_server_name, '$');
1432         if (!p || p[1] != '@') {
1433                 ldap_value_free(values);
1434                 ldap_msgfree(res);
1435                 SAFE_FREE(ads->ldap_server_name);
1436                 return ADS_ERROR(LDAP_DECODING_ERROR);
1437         }
1438
1439         *p = 0;
1440
1441         SAFE_FREE(ads->server_realm);
1442         SAFE_FREE(ads->bind_path);
1443
1444         ads->server_realm = strdup(p+2);
1445         ads->bind_path = ads_build_dn(ads->server_realm);
1446
1447         /* in case the realm isn't configured in smb.conf */
1448         if (!ads->realm || !ads->realm[0]) {
1449                 SAFE_FREE(ads->realm);
1450                 ads->realm = strdup(ads->server_realm);
1451         }
1452
1453         DEBUG(3,("got ldap server name %s@%s\n", 
1454                  ads->ldap_server_name, ads->realm));
1455
1456         return ADS_SUCCESS;
1457 }
1458
1459
1460 /**
1461  * find the list of trusted domains
1462  * @param ads connection to ads server
1463  * @param mem_ctx TALLOC_CTX for allocating results
1464  * @param num_trusts pointer to number of trusts
1465  * @param names pointer to trusted domain name list
1466  * @param sids pointer to list of sids of trusted domains
1467  * @return the count of SIDs pulled
1468  **/
1469 ADS_STATUS ads_trusted_domains(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, 
1470                                int *num_trusts, char ***names, DOM_SID **sids)
1471 {
1472         const char *attrs[] = {"flatName", "securityIdentifier", NULL};
1473         ADS_STATUS status;
1474         void *res, *msg;
1475         int count, i;
1476
1477         *num_trusts = 0;
1478
1479         status = ads_search(ads, &res, "(objectcategory=trustedDomain)", attrs);
1480         if (!ADS_ERR_OK(status)) return status;
1481
1482         count = ads_count_replies(ads, res);
1483         if (count == 0) {
1484                 ads_msgfree(ads, res);
1485                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1486         }
1487
1488         (*names) = talloc(mem_ctx, sizeof(char *) * count);
1489         (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * count);
1490         if (! *names || ! *sids) return ADS_ERROR(LDAP_NO_MEMORY);
1491
1492         for (i=0, msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
1493                 (*names)[i] = ads_pull_string(ads, mem_ctx, msg, "flatName");
1494                 if (ads_pull_sid(ads, msg, "securityIdentifier", &(*sids)[i])) {
1495                         i++;
1496                 }
1497         }
1498
1499         ads_msgfree(ads, res);
1500
1501         *num_trusts = i;
1502
1503         return ADS_SUCCESS;
1504 }
1505
1506 /**
1507  * find the domain sid for our domain
1508  * @param ads connection to ads server
1509  * @param sid Pointer to domain sid
1510  * @return status of search
1511  **/
1512 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
1513 {
1514         const char *attrs[] = {"objectSid", NULL};
1515         void *res;
1516         ADS_STATUS rc;
1517
1518         rc = ads_do_search(ads, ads->bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
1519                            attrs, &res);
1520         if (!ADS_ERR_OK(rc)) return rc;
1521         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
1522                 return ADS_ERROR_SYSTEM(ENOENT);
1523         }
1524         ads_msgfree(ads, res);
1525         
1526         return ADS_SUCCESS;
1527 }
1528
1529 #endif