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