a59b78bf13f4350dcd09b4f39f19a9cd30f7ff2c
[sfrench/samba-autobuild/.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_LDAP
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 /*
42   try a connection to a given ldap server, returning True and setting the servers IP
43   in the ads struct if successful
44  */
45 static BOOL ads_try_connect(ADS_STRUCT *ads, const char *server, unsigned port)
46 {
47         char *srv;
48
49         if (!server || !*server) {
50                 return False;
51         }
52
53         DEBUG(5,("ads_try_connect: trying ldap server '%s' port %u\n", server, port));
54
55         /* this copes with inet_ntoa brokenness */
56         srv = strdup(server);
57
58         ads->ld = ldap_open(srv, port);
59         if (!ads->ld) {
60                 free(srv);
61                 return False;
62         }
63         ads->ldap_port = port;
64         ads->ldap_ip = *interpret_addr2(srv);
65         free(srv);
66
67         return True;
68 }
69
70 /*
71   try a connection to a given ldap server, based on URL, returning True if successful
72  */
73 static BOOL ads_try_connect_uri(ADS_STRUCT *ads)
74 {
75 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
76         DEBUG(5,("ads_try_connect: trying ldap server at URI '%s'\n", 
77                  ads->server.ldap_uri));
78
79         
80         if (ldap_initialize((LDAP**)&(ads->ld), ads->server.ldap_uri) == LDAP_SUCCESS) {
81                 return True;
82         }
83         DEBUG(0, ("ldap_initialize: %s\n", strerror(errno)));
84         
85 #else 
86
87         DEBUG(1, ("no URL support in LDAP libs!\n"));
88 #endif
89
90         return False;
91 }
92
93 /* used by the IP comparison function */
94 struct ldap_ip {
95         struct in_addr ip;
96         unsigned port;
97 };
98
99 /* compare 2 ldap IPs by nearness to our interfaces - used in qsort */
100 static int ldap_ip_compare(struct ldap_ip *ip1, struct ldap_ip *ip2)
101 {
102         return ip_compare(&ip1->ip, &ip2->ip);
103 }
104
105 /* try connecting to a ldap server via DNS */
106 static BOOL ads_try_dns(ADS_STRUCT *ads)
107 {
108         char *realm, *ptr;
109         char *list = NULL;
110         pstring tok;
111         struct ldap_ip *ip_list;
112         int count, i=0;
113
114         realm = ads->server.realm;
115         if (!realm || !*realm) {
116                 realm = lp_realm();
117         }
118         if (!realm || !*realm) {
119                 realm = ads->server.workgroup;
120         }
121         if (!realm || !*realm) {
122                 realm = lp_workgroup();
123         }
124         if (!realm) {
125                 return False;
126         }
127         realm = smb_xstrdup(realm);
128
129         DEBUG(6,("ads_try_dns: looking for realm '%s'\n", realm));
130         if (ldap_domain2hostlist(realm, &list) != LDAP_SUCCESS) {
131                 SAFE_FREE(realm);
132                 return False;
133         }
134
135         DEBUG(6,("ads_try_dns: ldap realm '%s' host list '%s'\n", realm, list));
136         SAFE_FREE(realm);
137
138         count = count_chars(list, ' ') + 1;
139         ip_list = malloc(count * sizeof(struct ldap_ip));
140         if (!ip_list) {
141                 return False;
142         }
143
144         ptr = list;
145         while (next_token(&ptr, tok, " ", sizeof(tok))) {
146                 unsigned port = LDAP_PORT;
147                 char *p = strchr(tok, ':');
148                 if (p) {
149                         *p = 0;
150                         port = atoi(p+1);
151                 }
152                 ip_list[i].ip = *interpret_addr2(tok);
153                 ip_list[i].port = port;
154                 if (!is_zero_ip(ip_list[i].ip)) {
155                         i++;
156                 }
157         }
158         free(list);
159
160         count = i;
161
162         /* we sort the list of addresses by closeness to our interfaces. This
163            tries to prevent us using a DC on the other side of the country */
164         if (count > 1) {
165                 qsort(ip_list, count, sizeof(struct ldap_ip), 
166                       QSORT_CAST ldap_ip_compare);      
167         }
168
169         for (i=0;i<count;i++) {
170                 if (ads_try_connect(ads, inet_ntoa(ip_list[i].ip), ip_list[i].port)) {
171                         free(ip_list);
172                         return True;
173                 }
174         }
175
176         SAFE_FREE(ip_list);
177         return False;
178 }
179
180 /* try connecting to a ldap server via netbios */
181 static BOOL ads_try_netbios(ADS_STRUCT *ads)
182 {
183         struct in_addr *ip_list, pdc_ip;
184         int count;
185         int i;
186         char *workgroup = ads->server.workgroup;
187
188         if (!workgroup) {
189                 workgroup = lp_workgroup();
190         }
191
192         DEBUG(6,("ads_try_netbios: looking for workgroup '%s'\n", workgroup));
193
194         /* try the PDC first */
195         if (get_pdc_ip(workgroup, &pdc_ip)) { 
196                 DEBUG(6,("ads_try_netbios: trying server '%s'\n", 
197                          inet_ntoa(pdc_ip)));
198                 if (ads_try_connect(ads, inet_ntoa(pdc_ip), LDAP_PORT))
199                         return True;
200         }
201
202         /* now any DC, including backups */
203         if (get_dc_list(workgroup, &ip_list, &count)) { 
204                 for (i=0;i<count;i++) {
205                         DEBUG(6,("ads_try_netbios: trying server '%s'\n", 
206                                  inet_ntoa(ip_list[i])));
207                         if (ads_try_connect(ads, inet_ntoa(ip_list[i]), LDAP_PORT)) {
208                                 free(ip_list);
209                                 return True;
210                         }
211                 }
212                 free(ip_list);
213         }
214
215         return False;
216 }
217
218 /**
219  * Connect to the LDAP server
220  * @param ads Pointer to an existing ADS_STRUCT
221  * @return status of connection
222  **/
223 ADS_STATUS ads_connect(ADS_STRUCT *ads)
224 {
225         int version = LDAP_VERSION3;
226         ADS_STATUS status;
227
228         ads->last_attempt = time(NULL);
229         ads->ld = NULL;
230
231         /* try with a URL based server */
232
233         if (ads->server.ldap_uri &&
234             ads_try_connect_uri(ads)) {
235                 goto got_connection;
236         }
237
238         /* try with a user specified server */
239         if (ads->server.ldap_server && 
240             ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
241                 goto got_connection;
242         }
243
244         /* try with a smb.conf ads server setting if we are connecting
245            to the primary workgroup or realm */
246         if (!ads->server.foreign &&
247             ads_try_connect(ads, lp_ads_server(), LDAP_PORT)) {
248                 goto got_connection;
249         }
250
251         /* try via DNS */
252         if (ads_try_dns(ads)) {
253                 goto got_connection;
254         }
255
256         /* try via netbios lookups */
257         if (!lp_disable_netbios() && ads_try_netbios(ads)) {
258                 goto got_connection;
259         }
260
261         return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
262
263 got_connection:
264         DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
265
266         status = ads_server_info(ads);
267         if (!ADS_ERR_OK(status)) {
268                 DEBUG(1,("Failed to get ldap server info\n"));
269                 return status;
270         }
271
272         ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
273
274         if (!ads->auth.user_name) {
275                 /* by default use the machine account */
276                 extern pstring global_myname;
277                 fstring myname;
278                 fstrcpy(myname, global_myname);
279                 strlower(myname);
280                 asprintf(&ads->auth.user_name, "HOST/%s", myname);
281         }
282
283         if (!ads->auth.realm) {
284                 ads->auth.realm = strdup(ads->config.realm);
285         }
286
287         if (!ads->auth.kdc_server) {
288                 ads->auth.kdc_server = strdup(inet_ntoa(ads->ldap_ip));
289         }
290
291 #if KRB5_DNS_HACK
292         /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
293            to MIT kerberos to work (tridge) */
294         {
295                 char *env;
296                 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
297                 setenv(env, ads->auth.kdc_server, 1);
298                 free(env);
299         }
300 #endif
301
302         if (ads->auth.flags & ADS_AUTH_NO_BIND) {
303                 return ADS_SUCCESS;
304         }
305
306         if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
307                 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
308         }
309
310         if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
311                 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
312         }
313
314         return ads_sasl_bind(ads);
315 }
316
317 /*
318   Duplicate a struct berval into talloc'ed memory
319  */
320 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
321 {
322         struct berval *value;
323
324         if (!in_val) return NULL;
325
326         value = talloc_zero(ctx, sizeof(struct berval));
327         if (in_val->bv_len == 0) return value;
328
329         value->bv_len = in_val->bv_len;
330         value->bv_val = talloc_memdup(ctx, in_val->bv_val, in_val->bv_len);
331         return value;
332 }
333
334 /*
335   Make a values list out of an array of (struct berval *)
336  */
337 static struct berval **ads_dup_values(TALLOC_CTX *ctx, 
338                                       const struct berval **in_vals)
339 {
340         struct berval **values;
341         int i;
342        
343         if (!in_vals) return NULL;
344         for (i=0; in_vals[i]; i++); /* count values */
345         values = (struct berval **) talloc_zero(ctx, 
346                                                 (i+1)*sizeof(struct berval *));
347         if (!values) return NULL;
348
349         for (i=0; in_vals[i]; i++) {
350                 values[i] = dup_berval(ctx, in_vals[i]);
351         }
352         return values;
353 }
354
355 /*
356   UTF8-encode a values list out of an array of (char *)
357  */
358 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
359 {
360         char **values;
361         int i;
362        
363         if (!in_vals) return NULL;
364         for (i=0; in_vals[i]; i++); /* count values */
365         values = (char ** ) talloc_zero(ctx, (i+1)*sizeof(char *));
366         if (!values) return NULL;
367
368         for (i=0; in_vals[i]; i++) {
369                 push_utf8_talloc(ctx, &values[i], in_vals[i]);
370         }
371         return values;
372 }
373
374 /*
375   Pull a (char *) array out of a UTF8-encoded values list
376  */
377 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
378 {
379         char **values;
380         int i;
381        
382         if (!in_vals) return NULL;
383         for (i=0; in_vals[i]; i++); /* count values */
384         values = (char **) talloc_zero(ctx, (i+1)*sizeof(char *));
385         if (!values) return NULL;
386
387         for (i=0; in_vals[i]; i++) {
388                 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
389         }
390         return values;
391 }
392
393 /**
394  * Do a search with paged results.  cookie must be null on the first
395  *  call, and then returned on each subsequent call.  It will be null
396  *  again when the entire search is complete 
397  * @param ads connection to ads server 
398  * @param bind_path Base dn for the search
399  * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
400  * @param exp Search expression - specified in local charset
401  * @param attrs Attributes to retrieve - specified in utf8 or ascii
402  * @param res ** which will contain results - free res* with ads_msgfree()
403  * @param count Number of entries retrieved on this page
404  * @param cookie The paged results cookie to be returned on subsequent calls
405  * @return status of search
406  **/
407 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
408                                int scope, const char *exp,
409                                const char **attrs, void **res, 
410                                int *count, void **cookie)
411 {
412         int rc, i, version;
413         char *utf8_exp, *utf8_path, **search_attrs;
414         LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols; 
415         BerElement *cookie_be = NULL;
416         struct berval *cookie_bv= NULL;
417         TALLOC_CTX *ctx;
418
419         *res = NULL;
420
421         if (!(ctx = talloc_init()))
422                 return ADS_ERROR(LDAP_NO_MEMORY);
423
424         /* 0 means the conversion worked but the result was empty 
425            so we only fail if it's negative.  In any case, it always 
426            at least nulls out the dest */
427         if ((push_utf8_talloc(ctx, &utf8_exp, exp) < 0) ||
428             (push_utf8_talloc(ctx, &utf8_path, bind_path) < 0)) {
429                 rc = LDAP_NO_MEMORY;
430                 goto done;
431         }
432
433         if (!attrs || !(*attrs))
434                 search_attrs = NULL;
435         else {
436                 /* This would be the utf8-encoded version...*/
437                 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
438                 if (!(str_list_copy(&search_attrs, attrs)))
439                 {
440                         rc = LDAP_NO_MEMORY;
441                         goto done;
442                 }
443         }
444                 
445                 
446         /* Paged results only available on ldap v3 or later */
447         ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
448         if (version < LDAP_VERSION3) {
449                 rc =  LDAP_NOT_SUPPORTED;
450                 goto done;
451         }
452
453         cookie_be = ber_alloc_t(LBER_USE_DER);
454         if (cookie && *cookie) {
455                 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
456                 ber_bvfree(*cookie); /* don't need it from last time */
457                 *cookie = NULL;
458         } else {
459                 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
460         }
461         ber_flatten(cookie_be, &cookie_bv);
462         PagedResults.ldctl_oid = ADS_PAGE_CTL_OID;
463         PagedResults.ldctl_iscritical = (char) 1;
464         PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
465         PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
466
467         NoReferrals.ldctl_oid = ADS_NO_REFERRALS_OID;
468         NoReferrals.ldctl_iscritical = (char) 0;
469         NoReferrals.ldctl_value.bv_len = 0;
470         NoReferrals.ldctl_value.bv_val = "";
471
472
473         controls[0] = &NoReferrals;
474         controls[1] = &PagedResults;
475         controls[2] = NULL;
476
477         *res = NULL;
478
479         /* we need to disable referrals as the openldap libs don't
480            handle them and paged results at the same time.  Using them
481            together results in the result record containing the server 
482            page control being removed from the result list (tridge/jmcd) 
483         
484            leaving this in despite the control that says don't generate
485            referrals, in case the server doesn't support it (jmcd)
486         */
487         ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
488
489         rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_exp, 
490                                search_attrs, 0, controls,
491                                NULL, NULL, LDAP_NO_LIMIT, (LDAPMessage **)res);
492
493         ber_free(cookie_be, 1);
494         ber_bvfree(cookie_bv);
495
496         if (rc) {
497                 DEBUG(3,("ldap_search_ext_s(%s) -> %s\n", exp, ldap_err2string(rc)));
498                 goto done;
499         }
500
501         rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
502                                         NULL, &rcontrols,  0);
503
504         if (!rcontrols) {
505                 goto done;
506         }
507
508         for (i=0; rcontrols[i]; i++) {
509                 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
510                         cookie_be = ber_init(&rcontrols[i]->ldctl_value);
511                         ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
512                                   &cookie_bv);
513                         /* the berval is the cookie, but must be freed when
514                            it is all done */
515                         if (cookie_bv->bv_len) /* still more to do */
516                                 *cookie=ber_bvdup(cookie_bv);
517                         else
518                                 *cookie=NULL;
519                         ber_bvfree(cookie_bv);
520                         ber_free(cookie_be, 1);
521                         break;
522                 }
523         }
524         ldap_controls_free(rcontrols);
525
526 done:
527         talloc_destroy(ctx);
528         /* if/when we decide to utf8-encode attrs, take out this next line */
529         str_list_free(&search_attrs);
530
531         return ADS_ERROR(rc);
532 }
533
534
535 /**
536  * Get all results for a search.  This uses ads_do_paged_search() to return 
537  * all entries in a large search.
538  * @param ads connection to ads server 
539  * @param bind_path Base dn for the search
540  * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
541  * @param exp Search expression
542  * @param attrs Attributes to retrieve
543  * @param res ** which will contain results - free res* with ads_msgfree()
544  * @return status of search
545  **/
546 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
547                              int scope, const char *exp,
548                              const char **attrs, void **res)
549 {
550         void *cookie = NULL;
551         int count = 0;
552         ADS_STATUS status;
553
554         status = ads_do_paged_search(ads, bind_path, scope, exp, attrs, res,
555                                      &count, &cookie);
556
557         if (!ADS_ERR_OK(status)) return status;
558
559         while (cookie) {
560                 void *res2 = NULL;
561                 ADS_STATUS status2;
562                 LDAPMessage *msg, *next;
563
564                 status2 = ads_do_paged_search(ads, bind_path, scope, exp, 
565                                               attrs, &res2, &count, &cookie);
566
567                 if (!ADS_ERR_OK(status2)) break;
568
569                 /* this relies on the way that ldap_add_result_entry() works internally. I hope
570                    that this works on all ldap libs, but I have only tested with openldap */
571                 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
572                         next = ads_next_entry(ads, msg);
573                         ldap_add_result_entry((LDAPMessage **)res, msg);
574                 }
575                 /* note that we do not free res2, as the memory is now
576                    part of the main returned list */
577         }
578
579         return status;
580 }
581
582 /**
583  * Run a function on all results for a search.  Uses ads_do_paged_search() and
584  *  runs the function as each page is returned, using ads_process_results()
585  * @param ads connection to ads server
586  * @param bind_path Base dn for the search
587  * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
588  * @param exp Search expression - specified in local charset
589  * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
590  * @param fn Function which takes attr name, values list, and data_area
591  * @param data_area Pointer which is passed to function on each call
592  * @return status of search
593  **/
594 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
595                                 int scope, const char *exp, const char **attrs,
596                                 BOOL(*fn)(char *, void **, void *), 
597                                 void *data_area)
598 {
599         void *cookie = NULL;
600         int count = 0;
601         ADS_STATUS status;
602         void *res;
603
604         status = ads_do_paged_search(ads, bind_path, scope, exp, attrs, &res,
605                                      &count, &cookie);
606
607         if (!ADS_ERR_OK(status)) return status;
608
609         ads_process_results(ads, res, fn, data_area);
610         ads_msgfree(ads, res);
611
612         while (cookie) {
613                 status = ads_do_paged_search(ads, bind_path, scope, exp, attrs,
614                                              &res, &count, &cookie);
615
616                 if (!ADS_ERR_OK(status)) break;
617                 
618                 ads_process_results(ads, res, fn, data_area);
619                 ads_msgfree(ads, res);
620         }
621
622         return status;
623 }
624
625 /**
626  * Do a search with a timeout.
627  * @param ads connection to ads server
628  * @param bind_path Base dn for the search
629  * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
630  * @param exp Search expression
631  * @param attrs Attributes to retrieve
632  * @param res ** which will contain results - free res* with ads_msgfree()
633  * @return status of search
634  **/
635 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope, 
636                          const char *exp,
637                          const char **attrs, void **res)
638 {
639         struct timeval timeout;
640         int rc;
641         char *utf8_exp, *utf8_path, **search_attrs = NULL;
642         TALLOC_CTX *ctx;
643
644         if (!(ctx = talloc_init())) {
645                 DEBUG(1,("ads_do_search: talloc_init() failed!"));
646                 return ADS_ERROR(LDAP_NO_MEMORY);
647         }
648
649         /* 0 means the conversion worked but the result was empty 
650            so we only fail if it's negative.  In any case, it always 
651            at least nulls out the dest */
652         if ((push_utf8_talloc(ctx, &utf8_exp, exp) < 0) ||
653             (push_utf8_talloc(ctx, &utf8_path, bind_path) < 0)) {
654                 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
655                 rc = LDAP_NO_MEMORY;
656                 goto done;
657         }
658
659         if (!attrs || !(*attrs))
660                 search_attrs = NULL;
661         else {
662                 /* This would be the utf8-encoded version...*/
663                 /* if (!(search_attrs = ads_push_strvals(ctx, attrs)))  */
664                 if (!(str_list_copy(&search_attrs, attrs)))
665                 {
666                         DEBUG(1,("ads_do_search: str_list_copy() failed!"));
667                         rc = LDAP_NO_MEMORY;
668                         goto done;
669                 }
670         }
671
672         timeout.tv_sec = ADS_SEARCH_TIMEOUT;
673         timeout.tv_usec = 0;
674         *res = NULL;
675
676         /* see the note in ads_do_paged_search - we *must* disable referrals */
677         ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
678
679         rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_exp,
680                                search_attrs, 0, NULL, NULL, 
681                                &timeout, LDAP_NO_LIMIT, (LDAPMessage **)res);
682
683         if (rc == LDAP_SIZELIMIT_EXCEEDED) {
684                 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
685                 rc = 0;
686         }
687
688  done:
689         talloc_destroy(ctx);
690         /* if/when we decide to utf8-encode attrs, take out this next line */
691         str_list_free(&search_attrs);
692         return ADS_ERROR(rc);
693 }
694 /**
695  * Do a general ADS search
696  * @param ads connection to ads server
697  * @param res ** which will contain results - free res* with ads_msgfree()
698  * @param exp Search expression
699  * @param attrs Attributes to retrieve
700  * @return status of search
701  **/
702 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res, 
703                       const char *exp, 
704                       const char **attrs)
705 {
706         return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE, 
707                              exp, attrs, res);
708 }
709
710 /**
711  * Do a search on a specific DistinguishedName
712  * @param ads connection to ads server
713  * @param res ** which will contain results - free res* with ads_msgfree()
714  * @param dn DistinguishName to search
715  * @param attrs Attributes to retrieve
716  * @return status of search
717  **/
718 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res, 
719                          const char *dn, 
720                          const char **attrs)
721 {
722         return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
723 }
724
725 /**
726  * Free up memory from a ads_search
727  * @param ads connection to ads server
728  * @param msg Search results to free
729  **/
730 void ads_msgfree(ADS_STRUCT *ads, void *msg)
731 {
732         if (!msg) return;
733         ldap_msgfree(msg);
734 }
735
736 /**
737  * Free up memory from various ads requests
738  * @param ads connection to ads server
739  * @param mem Area to free
740  **/
741 void ads_memfree(ADS_STRUCT *ads, void *mem)
742 {
743         SAFE_FREE(mem);
744 }
745
746 /**
747  * Get a dn from search results
748  * @param ads connection to ads server
749  * @param res Search results
750  * @return dn string
751  **/
752 char *ads_get_dn(ADS_STRUCT *ads, void *res)
753 {
754         char *utf8_dn, *unix_dn;
755
756         utf8_dn = ldap_get_dn(ads->ld, res);
757         pull_utf8_allocate((void **) &unix_dn, utf8_dn);
758         ldap_memfree(utf8_dn);
759         return unix_dn;
760 }
761
762 /**
763  * Find a machine account given a hostname
764  * @param ads connection to ads server
765  * @param res ** which will contain results - free res* with ads_msgfree()
766  * @param host Hostname to search for
767  * @return status of search
768  **/
769 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *host)
770 {
771         ADS_STATUS status;
772         char *exp;
773         const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
774
775         /* the easiest way to find a machine account anywhere in the tree
776            is to look for hostname$ */
777         if (asprintf(&exp, "(samAccountName=%s$)", host) == -1) {
778                 DEBUG(1, ("asprintf failed!\n"));
779                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
780         }
781         
782         status = ads_search(ads, res, exp, attrs);
783         free(exp);
784         return status;
785 }
786
787 /**
788  * Initialize a list of mods to be used in a modify request
789  * @param ctx An initialized TALLOC_CTX
790  * @return allocated ADS_MODLIST
791  **/
792 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
793 {
794 #define ADS_MODLIST_ALLOC_SIZE 10
795         LDAPMod **mods;
796         
797         if ((mods = (LDAPMod **) talloc_zero(ctx, sizeof(LDAPMod *) * 
798                                              (ADS_MODLIST_ALLOC_SIZE + 1))))
799                 /* -1 is safety to make sure we don't go over the end.
800                    need to reset it to NULL before doing ldap modify */
801                 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
802         
803         return mods;
804 }
805
806
807 /*
808   add an attribute to the list, with values list already constructed
809 */
810 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
811                                   int mod_op, const char *name, 
812                                   const void **invals)
813 {
814         int curmod;
815         LDAPMod **modlist = (LDAPMod **) *mods;
816         void **values;
817
818         if (!invals) {
819                 values = NULL;
820                 mod_op = LDAP_MOD_DELETE;
821         } else {
822                 if (mod_op & LDAP_MOD_BVALUES)
823                         values = (void **) ads_dup_values(ctx, 
824                                            (const struct berval **)invals);
825                 else
826                         values = (void **) ads_push_strvals(ctx, 
827                                                    (const char **) invals);
828         }
829
830         /* find the first empty slot */
831         for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
832              curmod++);
833         if (modlist[curmod] == (LDAPMod *) -1) {
834                 if (!(modlist = talloc_realloc(ctx, modlist, 
835                         (curmod+ADS_MODLIST_ALLOC_SIZE+1)*sizeof(LDAPMod *))))
836                         return ADS_ERROR(LDAP_NO_MEMORY);
837                 memset(&modlist[curmod], 0, 
838                        ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
839                 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
840                 *mods = modlist;
841         }
842                 
843         if (!(modlist[curmod] = talloc_zero(ctx, sizeof(LDAPMod))))
844                 return ADS_ERROR(LDAP_NO_MEMORY);
845         modlist[curmod]->mod_type = talloc_strdup(ctx, name);
846         if (mod_op & LDAP_MOD_BVALUES)
847                 modlist[curmod]->mod_bvalues = (struct berval **) values;
848         else
849                 modlist[curmod]->mod_values = (char **) values;
850         modlist[curmod]->mod_op = mod_op;
851         return ADS_ERROR(LDAP_SUCCESS);
852 }
853
854 /**
855  * Add a single string value to a mod list
856  * @param ctx An initialized TALLOC_CTX
857  * @param mods An initialized ADS_MODLIST
858  * @param name The attribute name to add
859  * @param val The value to add - NULL means DELETE
860  * @return ADS STATUS indicating success of add
861  **/
862 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
863                        const char *name, const char *val)
864 {
865         const char *values[2];
866
867         values[0] = val;
868         values[1] = NULL;
869
870         if (!val)
871                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
872         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, 
873                                (const void **) values);
874 }
875
876 /**
877  * Add an array of string values to a mod list
878  * @param ctx An initialized TALLOC_CTX
879  * @param mods An initialized ADS_MODLIST
880  * @param name The attribute name to add
881  * @param vals The array of string values to add - NULL means DELETE
882  * @return ADS STATUS indicating success of add
883  **/
884 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
885                            const char *name, const char **vals)
886 {
887         if (!vals)
888                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
889         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, 
890                                name, (const void **) vals);
891 }
892
893 /**
894  * Add a single ber-encoded value to a mod list
895  * @param ctx An initialized TALLOC_CTX
896  * @param mods An initialized ADS_MODLIST
897  * @param name The attribute name to add
898  * @param val The value to add - NULL means DELETE
899  * @return ADS STATUS indicating success of add
900  **/
901 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
902                               const char *name, const struct berval *val)
903 {
904         const struct berval *values[2];
905
906         values[0] = val;
907         values[1] = NULL;
908         if (!val)
909                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
910         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
911                                name, (const void **) values);
912 }
913
914 /**
915  * Perform an ldap modify
916  * @param ads connection to ads server
917  * @param mod_dn DistinguishedName to modify
918  * @param mods list of modifications to perform
919  * @return status of modify
920  **/
921 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
922 {
923         int ret,i;
924         char *utf8_dn = NULL;
925         /* 
926            this control is needed to modify that contains a currently 
927            non-existent attribute (but allowable for the object) to run
928         */
929         LDAPControl PermitModify = {
930                 ADS_PERMIT_MODIFY_OID,
931                 {0, NULL},
932                 (char) 1};
933         LDAPControl *controls[2];
934
935         controls[0] = &PermitModify;
936         controls[1] = NULL;
937
938         if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
939                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
940         }
941
942         /* find the end of the list, marked by NULL or -1 */
943         for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
944         /* make sure the end of the list is NULL */
945         mods[i] = NULL;
946         ret = ldap_modify_ext_s(ads->ld, utf8_dn,
947                                 (LDAPMod **) mods, controls, NULL);
948         SAFE_FREE(utf8_dn);
949         return ADS_ERROR(ret);
950 }
951
952 /**
953  * Perform an ldap add
954  * @param ads connection to ads server
955  * @param new_dn DistinguishedName to add
956  * @param mods list of attributes and values for DN
957  * @return status of add
958  **/
959 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
960 {
961         int ret, i;
962         char *utf8_dn = NULL;
963
964         if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
965                 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
966                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
967         }
968         
969         /* find the end of the list, marked by NULL or -1 */
970         for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
971         /* make sure the end of the list is NULL */
972         mods[i] = NULL;
973
974         ret = ldap_add_s(ads->ld, utf8_dn ? utf8_dn : new_dn, mods);
975         SAFE_FREE(utf8_dn);
976         return ADS_ERROR(ret);
977 }
978
979 /**
980  * Delete a DistinguishedName
981  * @param ads connection to ads server
982  * @param new_dn DistinguishedName to delete
983  * @return status of delete
984  **/
985 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
986 {
987         int ret;
988         char *utf8_dn = NULL;
989         if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
990                 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
991                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
992         }
993         
994         ret = ldap_delete(ads->ld, utf8_dn ? utf8_dn : del_dn);
995         return ADS_ERROR(ret);
996 }
997
998 /**
999  * Build an org unit string
1000  *  if org unit is Computers or blank then assume a container, otherwise
1001  *  assume a \ separated list of organisational units
1002  * @param org_unit Organizational unit
1003  * @return org unit string - caller must free
1004  **/
1005 char *ads_ou_string(const char *org_unit)
1006 {       
1007         if (!org_unit || !*org_unit || strcasecmp(org_unit, "Computers") == 0) {
1008                 return strdup("cn=Computers");
1009         }
1010
1011         return ads_build_path(org_unit, "\\/", "ou=", 1);
1012 }
1013
1014
1015
1016 /*
1017   add a machine account to the ADS server
1018 */
1019 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname, 
1020                                        const char *org_unit)
1021 {
1022         ADS_STATUS ret;
1023         char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
1024         char *ou_str;
1025         TALLOC_CTX *ctx;
1026         ADS_MODLIST mods;
1027         const char *objectClass[] = {"top", "person", "organizationalPerson",
1028                                      "user", "computer", NULL};
1029         char *servicePrincipalName[3] = {NULL, NULL, NULL};
1030         unsigned acct_control;
1031
1032         if (!(ctx = talloc_init_named("machine_account")))
1033                 return ADS_ERROR(LDAP_NO_MEMORY);
1034
1035         ret = ADS_ERROR(LDAP_NO_MEMORY);
1036
1037         if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", hostname)))
1038                 goto done;
1039         if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
1040                 goto done;
1041         ou_str = ads_ou_string(org_unit);
1042         if (!ou_str) {
1043                 DEBUG(1, ("ads_ou_string returned NULL (malloc failure?)\n"));
1044                 goto done;
1045         }
1046         new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", hostname, ou_str, 
1047                                  ads->config.bind_path);
1048         servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", hostname);
1049         servicePrincipalName[1] = talloc_asprintf(ctx, "HOST/%s.%s", 
1050                                                   hostname, 
1051                                                   ads->config.realm);
1052         strlower(&servicePrincipalName[1][5]);
1053
1054         free(ou_str);
1055         if (!new_dn)
1056                 goto done;
1057
1058         if (!(samAccountName = talloc_asprintf(ctx, "%s$", hostname)))
1059                 goto done;
1060
1061         acct_control = UF_WORKSTATION_TRUST_ACCOUNT | UF_DONT_EXPIRE_PASSWD;
1062 #ifndef ENCTYPE_ARCFOUR_HMAC
1063         acct_control |= UF_USE_DES_KEY_ONLY;
1064 #endif
1065         if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control)))
1066                 goto done;
1067
1068         if (!(mods = ads_init_mods(ctx)))
1069                 goto done;
1070         
1071         ads_mod_str(ctx, &mods, "cn", hostname);
1072         ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1073         ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1074         ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1075         ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1076         ads_mod_str(ctx, &mods, "dNSHostName", hostname);
1077         ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1078         ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1079         ads_mod_str(ctx, &mods, "operatingSystemVersion", VERSION);
1080
1081         ads_gen_add(ads, new_dn, mods);
1082         ret = ads_set_machine_sd(ads, hostname, new_dn);
1083
1084 done:
1085         talloc_destroy(ctx);
1086         return ret;
1087 }
1088
1089 /*
1090   dump a binary result from ldap
1091 */
1092 static void dump_binary(const char *field, struct berval **values)
1093 {
1094         int i, j;
1095         for (i=0; values[i]; i++) {
1096                 printf("%s: ", field);
1097                 for (j=0; j<values[i]->bv_len; j++) {
1098                         printf("%02X", (unsigned char)values[i]->bv_val[j]);
1099                 }
1100                 printf("\n");
1101         }
1102 }
1103
1104 struct uuid {
1105         uint32   i1;
1106         uint16   i2;
1107         uint16   i3;
1108         uint8    s[8];
1109 };
1110
1111 static void dump_guid(const char *field, struct berval **values)
1112 {
1113         int i;
1114         GUID guid;
1115         for (i=0; values[i]; i++) {
1116                 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1117                 printf("%s: %s\n", field, uuid_string_static(guid));
1118         }
1119 }
1120
1121 /*
1122   dump a sid result from ldap
1123 */
1124 static void dump_sid(const char *field, struct berval **values)
1125 {
1126         int i;
1127         for (i=0; values[i]; i++) {
1128                 DOM_SID sid;
1129                 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1130                 printf("%s: %s\n", field, sid_string_static(&sid));
1131         }
1132 }
1133
1134 /*
1135   dump ntSecurityDescriptor
1136 */
1137 static void dump_sd(const char *filed, struct berval **values)
1138 {
1139         prs_struct ps;
1140         
1141         SEC_DESC   *psd = 0;
1142         TALLOC_CTX *ctx = 0;
1143
1144         if (!(ctx = talloc_init_named("sec_io_desc")))
1145                 return;
1146
1147         /* prepare data */
1148         prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1149         prs_append_data(&ps, values[0]->bv_val, values[0]->bv_len);
1150         ps.data_offset = 0;
1151
1152         /* parse secdesc */
1153         if (!sec_io_desc("sd", &psd, &ps, 1)) {
1154                 prs_mem_free(&ps);
1155                 talloc_destroy(ctx);
1156                 return;
1157         }
1158         if (psd) ads_disp_sd(psd);
1159
1160         prs_mem_free(&ps);
1161         talloc_destroy(ctx);
1162 }
1163
1164 /*
1165   dump a string result from ldap
1166 */
1167 static void dump_string(const char *field, char **values)
1168 {
1169         int i;
1170         for (i=0; values[i]; i++) {
1171                 printf("%s: %s\n", field, values[i]);
1172         }
1173 }
1174
1175 /*
1176   dump a field from LDAP on stdout
1177   used for debugging
1178 */
1179
1180 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1181 {
1182         struct {
1183                 char *name;
1184                 BOOL string;
1185                 void (*handler)(const char *, struct berval **);
1186         } handlers[] = {
1187                 {"objectGUID", False, dump_guid},
1188                 {"nTSecurityDescriptor", False, dump_sd},
1189                 {"dnsRecord", False, dump_binary},
1190                 {"objectSid", False, dump_sid},
1191                 {NULL, True, NULL}
1192         };
1193         int i;
1194
1195         if (!field) { /* must be end of an entry */
1196                 printf("\n");
1197                 return False;
1198         }
1199
1200         for (i=0; handlers[i].name; i++) {
1201                 if (StrCaseCmp(handlers[i].name, field) == 0) {
1202                         if (!values) /* first time, indicate string or not */
1203                                 return handlers[i].string;
1204                         handlers[i].handler(field, (struct berval **) values);
1205                         break;
1206                 }
1207         }
1208         if (!handlers[i].name) {
1209                 if (!values) /* first time, indicate string conversion */
1210                         return True;
1211                 dump_string(field, (char **)values);
1212         }
1213         return False;
1214 }
1215
1216 /**
1217  * Dump a result from LDAP on stdout
1218  *  used for debugging
1219  * @param ads connection to ads server
1220  * @param res Results to dump
1221  **/
1222
1223 void ads_dump(ADS_STRUCT *ads, void *res)
1224 {
1225         ads_process_results(ads, res, ads_dump_field, NULL);
1226 }
1227
1228 /**
1229  * Walk through results, calling a function for each entry found.
1230  *  The function receives a field name, a berval * array of values,
1231  *  and a data area passed through from the start.  The function is
1232  *  called once with null for field and values at the end of each
1233  *  entry.
1234  * @param ads connection to ads server
1235  * @param res Results to process
1236  * @param fn Function for processing each result
1237  * @param data_area user-defined area to pass to function
1238  **/
1239 void ads_process_results(ADS_STRUCT *ads, void *res,
1240                          BOOL(*fn)(char *, void **, void *),
1241                          void *data_area)
1242 {
1243         void *msg;
1244         TALLOC_CTX *ctx;
1245
1246         if (!(ctx = talloc_init()))
1247                 return;
1248
1249         for (msg = ads_first_entry(ads, res); msg; 
1250              msg = ads_next_entry(ads, msg)) {
1251                 char *utf8_field;
1252                 BerElement *b;
1253         
1254                 for (utf8_field=ldap_first_attribute(ads->ld,
1255                                                      (LDAPMessage *)msg,&b); 
1256                      utf8_field;
1257                      utf8_field=ldap_next_attribute(ads->ld,
1258                                                     (LDAPMessage *)msg,b)) {
1259                         struct berval **ber_vals;
1260                         char **str_vals, **utf8_vals;
1261                         char *field;
1262                         BOOL string; 
1263
1264                         pull_utf8_talloc(ctx, &field, utf8_field);
1265                         string = fn(field, NULL, data_area);
1266
1267                         if (string) {
1268                                 utf8_vals = ldap_get_values(ads->ld,
1269                                                  (LDAPMessage *)msg, field);
1270                                 str_vals = ads_pull_strvals(ctx, 
1271                                                   (const char **) utf8_vals);
1272                                 fn(field, (void **) str_vals, data_area);
1273                                 ldap_value_free(utf8_vals);
1274                         } else {
1275                                 ber_vals = ldap_get_values_len(ads->ld, 
1276                                                  (LDAPMessage *)msg, field);
1277                                 fn(field, (void **) ber_vals, data_area);
1278
1279                                 ldap_value_free_len(ber_vals);
1280                         }
1281                         ldap_memfree(utf8_field);
1282                 }
1283                 ber_free(b, 0);
1284                 talloc_destroy_pool(ctx);
1285                 fn(NULL, NULL, data_area); /* completed an entry */
1286
1287         }
1288         talloc_destroy(ctx);
1289 }
1290
1291 /**
1292  * count how many replies are in a LDAPMessage
1293  * @param ads connection to ads server
1294  * @param res Results to count
1295  * @return number of replies
1296  **/
1297 int ads_count_replies(ADS_STRUCT *ads, void *res)
1298 {
1299         return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1300 }
1301
1302 /**
1303  * Join a machine to a realm
1304  *  Creates the machine account and sets the machine password
1305  * @param ads connection to ads server
1306  * @param hostname name of host to add
1307  * @param org_unit Organizational unit to place machine in
1308  * @return status of join
1309  **/
1310 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname, const char *org_unit)
1311 {
1312         ADS_STATUS status;
1313         LDAPMessage *res;
1314         char *host;
1315
1316         /* hostname must be lowercase */
1317         host = strdup(hostname);
1318         strlower(host);
1319
1320         status = ads_find_machine_acct(ads, (void **)&res, host);
1321         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1322                 DEBUG(0, ("Host account for %s already exists - deleting old account\n", host));
1323                 status = ads_leave_realm(ads, host);
1324                 if (!ADS_ERR_OK(status)) {
1325                         DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n", 
1326                                   host, ads->config.realm));
1327                         return status;
1328                 }
1329         }
1330
1331         status = ads_add_machine_acct(ads, host, org_unit);
1332         if (!ADS_ERR_OK(status)) {
1333                 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
1334                 return status;
1335         }
1336
1337         status = ads_find_machine_acct(ads, (void **)&res, host);
1338         if (!ADS_ERR_OK(status)) {
1339                 DEBUG(0, ("Host account test failed\n"));
1340                 return status;
1341         }
1342
1343         free(host);
1344
1345         return status;
1346 }
1347
1348 /**
1349  * Delete a machine from the realm
1350  * @param ads connection to ads server
1351  * @param hostname Machine to remove
1352  * @return status of delete
1353  **/
1354 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1355 {
1356         ADS_STATUS status;
1357         void *res;
1358         char *hostnameDN, *host; 
1359         int rc;
1360
1361         /* hostname must be lowercase */
1362         host = strdup(hostname);
1363         strlower(host);
1364
1365         status = ads_find_machine_acct(ads, &res, host);
1366         if (!ADS_ERR_OK(status)) {
1367             DEBUG(0, ("Host account for %s does not exist.\n", host));
1368             return status;
1369         }
1370
1371         hostnameDN = ads_get_dn(ads, (LDAPMessage *)res);
1372         rc = ldap_delete_s(ads->ld, hostnameDN);
1373         ads_memfree(ads, hostnameDN);
1374         if (rc != LDAP_SUCCESS) {
1375                 return ADS_ERROR(rc);
1376         }
1377
1378         status = ads_find_machine_acct(ads, &res, host);
1379         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1380                 DEBUG(0, ("Failed to remove host account.\n"));
1381                 return status;
1382         }
1383
1384         free(host);
1385
1386         return status;
1387 }
1388
1389 /**
1390  * add machine account to existing security descriptor 
1391  * @param ads connection to ads server
1392  * @param hostname machine to add
1393  * @param dn DN of security descriptor
1394  * @return status
1395  **/
1396 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1397 {
1398         const char     *attrs[] = {"ntSecurityDescriptor", "objectSid", 0};
1399         char           *exp     = 0;
1400         size_t          sd_size = 0;
1401         struct berval   bval = {0, NULL};
1402         prs_struct      ps_wire;
1403
1404         LDAPMessage *res  = 0;
1405         LDAPMessage *msg  = 0;
1406         ADS_MODLIST  mods = 0;
1407
1408         NTSTATUS    status;
1409         ADS_STATUS  ret;
1410         DOM_SID     sid;
1411         SEC_DESC   *psd = 0;
1412         TALLOC_CTX *ctx = 0;    
1413
1414         if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1415
1416         ret = ADS_ERROR(LDAP_SUCCESS);
1417
1418         if (asprintf(&exp, "(samAccountName=%s$)", hostname) == -1) {
1419                 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1420                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1421         }
1422
1423         ret = ads_search(ads, (void *) &res, exp, attrs);
1424
1425         if (!ADS_ERR_OK(ret)) return ret;
1426
1427         msg   = ads_first_entry(ads, res);
1428         ads_pull_sid(ads, msg, attrs[1], &sid); 
1429         if (!(ctx = talloc_init_named("sec_io_desc"))) {
1430                 ret =  ADS_ERROR(LDAP_NO_MEMORY);
1431                 goto ads_set_sd_error;
1432         }
1433
1434         if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1435                 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1436                 goto ads_set_sd_error;
1437         }
1438
1439         status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1440
1441         if (!NT_STATUS_IS_OK(status)) {
1442                 ret = ADS_ERROR_NT(status);
1443                 goto ads_set_sd_error;
1444         }
1445
1446         prs_init(&ps_wire, sd_size, ctx, MARSHALL);
1447         if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1448                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1449                 goto ads_set_sd_error;
1450         }
1451
1452 #if 0
1453         file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1454 #endif
1455         if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1456
1457         bval.bv_len = sd_size;
1458         bval.bv_val = prs_data_p(&ps_wire);
1459         ads_mod_ber(ctx, &mods, attrs[0], &bval);
1460         ret = ads_gen_mod(ads, dn, mods);
1461
1462 ads_set_sd_error:
1463         ads_msgfree(ads, res);
1464         prs_mem_free(&ps_wire);
1465         talloc_destroy(ctx);
1466         return ret;
1467 }
1468
1469 /**
1470  * pull the first entry from a ADS result
1471  * @param ads connection to ads server
1472  * @param res Results of search
1473  * @return first entry from result
1474  **/
1475 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1476 {
1477         return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1478 }
1479
1480 /**
1481  * pull the next entry from a ADS result
1482  * @param ads connection to ads server
1483  * @param res Results of search
1484  * @return next entry from result
1485  **/
1486 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1487 {
1488         return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1489 }
1490
1491 /**
1492  * pull a single string from a ADS result
1493  * @param ads connection to ads server
1494  * @param mem_ctx TALLOC_CTX to use for allocating result string
1495  * @param msg Results of search
1496  * @param field Attribute to retrieve
1497  * @return Result string in talloc context
1498  **/
1499 char *ads_pull_string(ADS_STRUCT *ads, 
1500                       TALLOC_CTX *mem_ctx, void *msg, const char *field)
1501 {
1502         char **values;
1503         char *ret = NULL;
1504         char *ux_string;
1505         int rc;
1506
1507         values = ldap_get_values(ads->ld, msg, field);
1508         if (!values) return NULL;
1509         
1510         if (values[0]) {
1511                 rc = pull_utf8_talloc(mem_ctx, &ux_string, 
1512                                       values[0]);
1513                 if (rc != -1)
1514                         ret = ux_string;
1515                 
1516         }
1517         ldap_value_free(values);
1518         return ret;
1519 }
1520
1521 /**
1522  * pull an array of strings from a ADS result
1523  * @param ads connection to ads server
1524  * @param mem_ctx TALLOC_CTX to use for allocating result string
1525  * @param msg Results of search
1526  * @param field Attribute to retrieve
1527  * @return Result strings in talloc context
1528  **/
1529 char **ads_pull_strings(ADS_STRUCT *ads, 
1530                        TALLOC_CTX *mem_ctx, void *msg, const char *field)
1531 {
1532         char **values;
1533         char **ret = NULL;
1534         int i, n;
1535
1536         values = ldap_get_values(ads->ld, msg, field);
1537         if (!values) return NULL;
1538
1539         for (i=0;values[i];i++) /* noop */ ;
1540         n = i;
1541
1542         ret = talloc(mem_ctx, sizeof(char *) * (n+1));
1543
1544         for (i=0;i<n;i++) {
1545                 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1546                         return NULL;
1547                 }
1548         }
1549         ret[i] = NULL;
1550
1551         ldap_value_free(values);
1552         return ret;
1553 }
1554
1555
1556 /**
1557  * pull a single uint32 from a ADS result
1558  * @param ads connection to ads server
1559  * @param msg Results of search
1560  * @param field Attribute to retrieve
1561  * @param v Pointer to int to store result
1562  * @return boolean inidicating success
1563 */
1564 BOOL ads_pull_uint32(ADS_STRUCT *ads, 
1565                      void *msg, const char *field, uint32 *v)
1566 {
1567         char **values;
1568
1569         values = ldap_get_values(ads->ld, msg, field);
1570         if (!values) return False;
1571         if (!values[0]) {
1572                 ldap_value_free(values);
1573                 return False;
1574         }
1575
1576         *v = atoi(values[0]);
1577         ldap_value_free(values);
1578         return True;
1579 }
1580
1581 /**
1582  * pull a single DOM_SID from a ADS result
1583  * @param ads connection to ads server
1584  * @param msg Results of search
1585  * @param field Attribute to retrieve
1586  * @param sid Pointer to sid to store result
1587  * @return boolean inidicating success
1588 */
1589 BOOL ads_pull_sid(ADS_STRUCT *ads, 
1590                   void *msg, const char *field, DOM_SID *sid)
1591 {
1592         struct berval **values;
1593         BOOL ret = False;
1594
1595         values = ldap_get_values_len(ads->ld, msg, field);
1596
1597         if (!values) return False;
1598
1599         if (values[0]) {
1600                 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
1601         }
1602         
1603         ldap_value_free_len(values);
1604         return ret;
1605 }
1606
1607 /**
1608  * pull an array of DOM_SIDs from a ADS result
1609  * @param ads connection to ads server
1610  * @param mem_ctx TALLOC_CTX for allocating sid array
1611  * @param msg Results of search
1612  * @param field Attribute to retrieve
1613  * @param sids pointer to sid array to allocate
1614  * @return the count of SIDs pulled
1615  **/
1616 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1617                   void *msg, const char *field, DOM_SID **sids)
1618 {
1619         struct berval **values;
1620         BOOL ret;
1621         int count, i;
1622
1623         values = ldap_get_values_len(ads->ld, msg, field);
1624
1625         if (!values) return 0;
1626
1627         for (i=0; values[i]; i++) /* nop */ ;
1628
1629         (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
1630
1631         count = 0;
1632         for (i=0; values[i]; i++) {
1633                 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
1634                 if (ret) count++;
1635         }
1636         
1637         ldap_value_free_len(values);
1638         return count;
1639 }
1640
1641 /**
1642  * pull a SEC_DESC from a ADS result
1643  * @param ads connection to ads server
1644  * @param mem_ctx TALLOC_CTX for allocating sid array
1645  * @param msg Results of search
1646  * @param field Attribute to retrieve
1647  * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
1648  * @return boolean inidicating success
1649 */
1650 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1651                   void *msg, const char *field, SEC_DESC **sd)
1652 {
1653         struct berval **values;
1654         prs_struct      ps;
1655         BOOL ret = False;
1656
1657         values = ldap_get_values_len(ads->ld, msg, field);
1658
1659         if (!values) return False;
1660
1661         if (values[0]) {
1662                 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
1663                 prs_append_data(&ps, values[0]->bv_val, values[0]->bv_len);
1664                 ps.data_offset = 0;
1665
1666                 ret = sec_io_desc("sd", sd, &ps, 1);
1667         }
1668         
1669         ldap_value_free_len(values);
1670         return ret;
1671 }
1672
1673 /* 
1674  * in order to support usernames longer than 21 characters we need to 
1675  * use both the sAMAccountName and the userPrincipalName attributes 
1676  * It seems that not all users have the userPrincipalName attribute set
1677  *
1678  * @param ads connection to ads server
1679  * @param mem_ctx TALLOC_CTX for allocating sid array
1680  * @param msg Results of search
1681  * @return the username
1682  */
1683 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
1684 {
1685         char *ret, *p;
1686
1687         ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
1688         if (ret && (p = strchr(ret, '@'))) {
1689                 *p = 0;
1690                 return ret;
1691         }
1692         return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
1693 }
1694
1695
1696 /**
1697  * find the update serial number - this is the core of the ldap cache
1698  * @param ads connection to ads server
1699  * @param ads connection to ADS server
1700  * @param usn Pointer to retrieved update serial number
1701  * @return status of search
1702  **/
1703 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
1704 {
1705         const char *attrs[] = {"highestCommittedUSN", NULL};
1706         ADS_STATUS status;
1707         void *res;
1708
1709         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1710         if (!ADS_ERR_OK(status)) return status;
1711
1712         if (ads_count_replies(ads, res) != 1) {
1713                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1714         }
1715
1716         ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
1717         ads_msgfree(ads, res);
1718         return ADS_SUCCESS;
1719 }
1720
1721 /* parse a ADS timestring - typical string is
1722    '20020917091222.0Z0' which means 09:12.22 17th September
1723    2002, timezone 0 */
1724 static time_t ads_parse_time(const char *str)
1725 {
1726         struct tm tm;
1727
1728         ZERO_STRUCT(tm);
1729
1730         if (sscanf(str, "%4d%2d%2d%2d%2d%2d", 
1731                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
1732                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
1733                 return 0;
1734         }
1735         tm.tm_year -= 1900;
1736         tm.tm_mon -= 1;
1737
1738         return timegm(&tm);
1739 }
1740
1741
1742 /**
1743  * Find the servers name and realm - this can be done before authentication 
1744  *  The ldapServiceName field on w2k  looks like this:
1745  *    vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
1746  * @param ads connection to ads server
1747  * @return status of search
1748  **/
1749 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
1750 {
1751         const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
1752         ADS_STATUS status;
1753         void *res;
1754         char *value;
1755         char *p;
1756         char *timestr;
1757         TALLOC_CTX *ctx;
1758
1759         if (!(ctx = talloc_init())) {
1760                 return ADS_ERROR(LDAP_NO_MEMORY);
1761         }
1762
1763         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1764         if (!ADS_ERR_OK(status)) return status;
1765
1766         value = ads_pull_string(ads, ctx, res, "ldapServiceName");
1767         if (!value) {
1768                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1769         }
1770
1771         timestr = ads_pull_string(ads, ctx, res, "currentTime");
1772         if (!timestr) {
1773                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1774         }
1775
1776         ldap_msgfree(res);
1777
1778         p = strchr(value, ':');
1779         if (!p) {
1780                 talloc_destroy(ctx);
1781                 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' so was deemed invalid\n"));
1782                 return ADS_ERROR(LDAP_DECODING_ERROR);
1783         }
1784
1785         SAFE_FREE(ads->config.ldap_server_name);
1786
1787         ads->config.ldap_server_name = strdup(p+1);
1788         p = strchr(ads->config.ldap_server_name, '$');
1789         if (!p || p[1] != '@') {
1790                 talloc_destroy(ctx);
1791                 SAFE_FREE(ads->config.ldap_server_name);
1792                 DEBUG(1, ("ads_server_info: returned ldap server name did not contain '$@' so was deemed invalid\n"));
1793                 return ADS_ERROR(LDAP_DECODING_ERROR);
1794         }
1795
1796         *p = 0;
1797
1798         SAFE_FREE(ads->config.realm);
1799         SAFE_FREE(ads->config.bind_path);
1800
1801         ads->config.realm = strdup(p+2);
1802         ads->config.bind_path = ads_build_dn(ads->config.realm);
1803
1804         DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n", 
1805                  ads->config.ldap_server_name, ads->config.realm,
1806                  ads->config.bind_path));
1807
1808         ads->config.current_time = ads_parse_time(timestr);
1809
1810         if (ads->config.current_time != 0) {
1811                 ads->auth.time_offset = ads->config.current_time - time(NULL);
1812                 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
1813         }
1814
1815         talloc_destroy(ctx);
1816
1817         return ADS_SUCCESS;
1818 }
1819
1820
1821 /**
1822  * find the list of trusted domains
1823  * @param ads connection to ads server
1824  * @param mem_ctx TALLOC_CTX for allocating results
1825  * @param num_trusts pointer to number of trusts
1826  * @param names pointer to trusted domain name list
1827  * @param sids pointer to list of sids of trusted domains
1828  * @return the count of SIDs pulled
1829  **/
1830 ADS_STATUS ads_trusted_domains(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, 
1831                                int *num_trusts, 
1832                                char ***names, 
1833                                char ***alt_names,
1834                                DOM_SID **sids)
1835 {
1836         const char *attrs[] = {"name", "flatname", "securityIdentifier", 
1837                                "trustDirection", NULL};
1838         ADS_STATUS status;
1839         void *res, *msg;
1840         int count, i;
1841
1842         *num_trusts = 0;
1843
1844         status = ads_search(ads, &res, "(objectcategory=trustedDomain)", attrs);
1845         if (!ADS_ERR_OK(status)) return status;
1846
1847         count = ads_count_replies(ads, res);
1848         if (count == 0) {
1849                 ads_msgfree(ads, res);
1850                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1851         }
1852
1853         (*names) = talloc(mem_ctx, sizeof(char *) * count);
1854         (*alt_names) = talloc(mem_ctx, sizeof(char *) * count);
1855         (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * count);
1856         if (! *names || ! *sids) return ADS_ERROR(LDAP_NO_MEMORY);
1857
1858         for (i=0, msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
1859                 uint32 direction;
1860
1861                 /* direction is a 2 bit bitfield, 1 means they trust us 
1862                    but we don't trust them, so we should not list them
1863                    as users from that domain can't login */
1864                 if (ads_pull_uint32(ads, msg, "trustDirection", &direction) &&
1865                     direction == 1) {
1866                         continue;
1867                 }
1868                 
1869                 (*names)[i] = ads_pull_string(ads, mem_ctx, msg, "name");
1870                 (*alt_names)[i] = ads_pull_string(ads, mem_ctx, msg, "flatname");
1871
1872                 if ((*alt_names)[i] && (*alt_names)[i][0]) {
1873                         /* we prefer the flatname as the primary name
1874                            for consistency with RPC */
1875                         char *name = (*alt_names)[i];
1876                         (*alt_names)[i] = (*names)[i];
1877                         (*names)[i] = name;
1878                 }
1879                 if (ads_pull_sid(ads, msg, "securityIdentifier", &(*sids)[i])) {
1880                         i++;
1881                 }
1882         }
1883
1884         ads_msgfree(ads, res);
1885
1886         *num_trusts = i;
1887
1888         return ADS_SUCCESS;
1889 }
1890
1891 /**
1892  * find the domain sid for our domain
1893  * @param ads connection to ads server
1894  * @param sid Pointer to domain sid
1895  * @return status of search
1896  **/
1897 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
1898 {
1899         const char *attrs[] = {"objectSid", NULL};
1900         void *res;
1901         ADS_STATUS rc;
1902
1903         rc = ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
1904                            attrs, &res);
1905         if (!ADS_ERR_OK(rc)) return rc;
1906         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
1907                 return ADS_ERROR_SYSTEM(ENOENT);
1908         }
1909         ads_msgfree(ads, res);
1910         
1911         return ADS_SUCCESS;
1912 }
1913
1914 /* this is rather complex - we need to find the allternate (netbios) name
1915    for the domain, but there isn't a simple query to do this. Instead
1916    we look for the principle names on the DCs account and find one that has 
1917    the right form, then extract the netbios name of the domain from that
1918 */
1919 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **workgroup)
1920 {
1921         char *exp;
1922         ADS_STATUS rc;
1923         char **principles;
1924         char *prefix;
1925         int prefix_length;
1926         int i;
1927         void *res;
1928         const char *attrs[] = {"servicePrincipalName", NULL};
1929
1930         (*workgroup) = NULL;
1931
1932         asprintf(&exp, "(&(objectclass=computer)(dnshostname=%s.%s))", 
1933                  ads->config.ldap_server_name, ads->config.realm);
1934         rc = ads_search(ads, &res, exp, attrs);
1935         free(exp);
1936
1937         if (!ADS_ERR_OK(rc)) {
1938                 return rc;
1939         }
1940
1941         principles = ads_pull_strings(ads, mem_ctx, res, "servicePrincipalName");
1942
1943         ads_msgfree(ads, res);
1944
1945         if (!principles) {
1946                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1947         }
1948
1949         asprintf(&prefix, "HOST/%s.%s/", 
1950                  ads->config.ldap_server_name, 
1951                  ads->config.realm);
1952
1953         prefix_length = strlen(prefix);
1954
1955         for (i=0;principles[i]; i++) {
1956                 if (strncasecmp(principles[i], prefix, prefix_length) == 0 &&
1957                     strcasecmp(ads->config.realm, principles[i]+prefix_length) != 0 &&
1958                     !strchr(principles[i]+prefix_length, '.')) {
1959                         /* found an alternate (short) name for the domain. */
1960                         DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
1961                                  principles[i]+prefix_length, 
1962                                  ads->config.realm));
1963                         (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
1964                         break;
1965                 }
1966         }
1967         free(prefix);
1968
1969         if (!*workgroup) {
1970                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1971         }
1972         
1973         return ADS_SUCCESS;
1974 }
1975
1976 #endif