Removed global_myworkgroup, global_myname, global_myscope. Added liberal
[kai/samba.git] / source3 / libads / ldap.c
1 /* 
2    Unix SMB/CIFS implementation.
3    ads (active directory) utility library
4    Copyright (C) Andrew Tridgell 2001
5    Copyright (C) Remus Koos 2001
6    Copyright (C) Jim McDonough 2002
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24
25 #ifdef HAVE_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         const char *c_realm;
109         const char *ptr;
110         char *realm;
111         char *list = NULL;
112         pstring tok;
113         struct ldap_ip *ip_list;
114         int count, i=0;
115
116         c_realm = ads->server.realm;
117         if (!c_realm || !*c_realm) {
118                 c_realm = lp_realm();
119         }
120         if (!c_realm || !*c_realm) {
121                 c_realm = ads->server.workgroup;
122         }
123         if (!c_realm || !*c_realm) {
124                 c_realm = lp_workgroup();
125         }
126         if (!c_realm) {
127                 return False;
128         }
129         realm = smb_xstrdup(c_realm);
130
131         DEBUG(6,("ads_try_dns: looking for realm '%s'\n", realm));
132         if (ldap_domain2hostlist(realm, &list) != LDAP_SUCCESS) {
133                 SAFE_FREE(realm);
134                 return False;
135         }
136
137         DEBUG(6,("ads_try_dns: ldap realm '%s' host list '%s'\n", realm, list));
138         SAFE_FREE(realm);
139
140         count = count_chars(list, ' ') + 1;
141         ip_list = malloc(count * sizeof(struct ldap_ip));
142         if (!ip_list) {
143                 return False;
144         }
145
146         ptr = list;
147         while (next_token(&ptr, tok, " ", sizeof(tok))) {
148                 unsigned port = LDAP_PORT;
149                 char *p = strchr(tok, ':');
150                 if (p) {
151                         *p = 0;
152                         port = atoi(p+1);
153                 }
154                 ip_list[i].ip = *interpret_addr2(tok);
155                 ip_list[i].port = port;
156                 if (!is_zero_ip(ip_list[i].ip)) {
157                         i++;
158                 }
159         }
160         free(list);
161
162         count = i;
163
164         /* we sort the list of addresses by closeness to our interfaces. This
165            tries to prevent us using a DC on the other side of the country */
166         if (count > 1) {
167                 qsort(ip_list, count, sizeof(struct ldap_ip), 
168                       QSORT_CAST ldap_ip_compare);      
169         }
170
171         for (i=0;i<count;i++) {
172                 if (ads_try_connect(ads, inet_ntoa(ip_list[i].ip), ip_list[i].port)) {
173                         free(ip_list);
174                         return True;
175                 }
176         }
177
178         SAFE_FREE(ip_list);
179         return False;
180 }
181
182 /* try connecting to a ldap server via netbios */
183 static BOOL ads_try_netbios(ADS_STRUCT *ads)
184 {
185         struct in_addr *ip_list, pdc_ip;
186         int count;
187         int i;
188         const char *workgroup = ads->server.workgroup;
189
190         if (!workgroup) {
191                 workgroup = lp_workgroup();
192         }
193
194         DEBUG(6,("ads_try_netbios: looking for workgroup '%s'\n", workgroup));
195
196         /* try the PDC first */
197         if (get_pdc_ip(workgroup, &pdc_ip)) { 
198                 DEBUG(6,("ads_try_netbios: trying server '%s'\n", 
199                          inet_ntoa(pdc_ip)));
200                 if (ads_try_connect(ads, inet_ntoa(pdc_ip), LDAP_PORT))
201                         return True;
202         }
203
204         /* now any DC, including backups */
205         if (get_dc_list(workgroup, &ip_list, &count)) { 
206                 for (i=0;i<count;i++) {
207                         DEBUG(6,("ads_try_netbios: trying server '%s'\n", 
208                                  inet_ntoa(ip_list[i])));
209                         if (ads_try_connect(ads, inet_ntoa(ip_list[i]), LDAP_PORT)) {
210                                 free(ip_list);
211                                 return True;
212                         }
213                 }
214                 free(ip_list);
215         }
216
217         return False;
218 }
219
220 /**
221  * Connect to the LDAP server
222  * @param ads Pointer to an existing ADS_STRUCT
223  * @return status of connection
224  **/
225 ADS_STATUS ads_connect(ADS_STRUCT *ads)
226 {
227         int version = LDAP_VERSION3;
228         ADS_STATUS status;
229
230         ads->last_attempt = time(NULL);
231         ads->ld = NULL;
232
233         /* try with a URL based server */
234
235         if (ads->server.ldap_uri &&
236             ads_try_connect_uri(ads)) {
237                 goto got_connection;
238         }
239
240         /* try with a user specified server */
241         if (ads->server.ldap_server && 
242             ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
243                 goto got_connection;
244         }
245
246         /* try with a smb.conf ads server setting if we are connecting
247            to the primary workgroup or realm */
248         if (!ads->server.foreign &&
249             ads_try_connect(ads, lp_ads_server(), LDAP_PORT)) {
250                 goto got_connection;
251         }
252
253         /* try via DNS */
254         if (ads_try_dns(ads)) {
255                 goto got_connection;
256         }
257
258         /* try via netbios lookups */
259         if (!lp_disable_netbios() && ads_try_netbios(ads)) {
260                 goto got_connection;
261         }
262
263         return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
264
265 got_connection:
266         DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
267
268         status = ads_server_info(ads);
269         if (!ADS_ERR_OK(status)) {
270                 DEBUG(1,("Failed to get ldap server info\n"));
271                 return status;
272         }
273
274         ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
275
276         if (!ads->auth.user_name) {
277                 /* by default use the machine account */
278                 fstring myname;
279                 fstrcpy(myname, global_myname());
280                 strlower(myname);
281                 asprintf(&ads->auth.user_name, "HOST/%s", myname);
282         }
283
284         if (!ads->auth.realm) {
285                 ads->auth.realm = strdup(ads->config.realm);
286         }
287
288         if (!ads->auth.kdc_server) {
289                 ads->auth.kdc_server = strdup(inet_ntoa(ads->ldap_ip));
290         }
291
292 #if KRB5_DNS_HACK
293         /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
294            to MIT kerberos to work (tridge) */
295         {
296                 char *env;
297                 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
298                 setenv(env, ads->auth.kdc_server, 1);
299                 free(env);
300         }
301 #endif
302
303         if (ads->auth.flags & ADS_AUTH_NO_BIND) {
304                 return ADS_SUCCESS;
305         }
306
307         if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
308                 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
309         }
310
311         if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
312                 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
313         }
314
315         return ads_sasl_bind(ads);
316 }
317
318 /*
319   Duplicate a struct berval into talloc'ed memory
320  */
321 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
322 {
323         struct berval *value;
324
325         if (!in_val) return NULL;
326
327         value = talloc_zero(ctx, sizeof(struct berval));
328         if (in_val->bv_len == 0) return value;
329
330         value->bv_len = in_val->bv_len;
331         value->bv_val = talloc_memdup(ctx, in_val->bv_val, in_val->bv_len);
332         return value;
333 }
334
335 /*
336   Make a values list out of an array of (struct berval *)
337  */
338 static struct berval **ads_dup_values(TALLOC_CTX *ctx, 
339                                       const struct berval **in_vals)
340 {
341         struct berval **values;
342         int i;
343        
344         if (!in_vals) return NULL;
345         for (i=0; in_vals[i]; i++); /* count values */
346         values = (struct berval **) talloc_zero(ctx, 
347                                                 (i+1)*sizeof(struct berval *));
348         if (!values) return NULL;
349
350         for (i=0; in_vals[i]; i++) {
351                 values[i] = dup_berval(ctx, in_vals[i]);
352         }
353         return values;
354 }
355
356 /*
357   UTF8-encode a values list out of an array of (char *)
358  */
359 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
360 {
361         char **values;
362         int i;
363        
364         if (!in_vals) return NULL;
365         for (i=0; in_vals[i]; i++); /* count values */
366         values = (char ** ) talloc_zero(ctx, (i+1)*sizeof(char *));
367         if (!values) return NULL;
368
369         for (i=0; in_vals[i]; i++) {
370                 push_utf8_talloc(ctx, &values[i], in_vals[i]);
371         }
372         return values;
373 }
374
375 /*
376   Pull a (char *) array out of a UTF8-encoded values list
377  */
378 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
379 {
380         char **values;
381         int i;
382        
383         if (!in_vals) return NULL;
384         for (i=0; in_vals[i]; i++); /* count values */
385         values = (char **) talloc_zero(ctx, (i+1)*sizeof(char *));
386         if (!values) return NULL;
387
388         for (i=0; in_vals[i]; i++) {
389                 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
390         }
391         return values;
392 }
393
394 /**
395  * Do a search with paged results.  cookie must be null on the first
396  *  call, and then returned on each subsequent call.  It will be null
397  *  again when the entire search is complete 
398  * @param ads connection to ads server 
399  * @param bind_path Base dn for the search
400  * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
401  * @param exp Search expression - specified in local charset
402  * @param attrs Attributes to retrieve - specified in utf8 or ascii
403  * @param res ** which will contain results - free res* with ads_msgfree()
404  * @param count Number of entries retrieved on this page
405  * @param cookie The paged results cookie to be returned on subsequent calls
406  * @return status of search
407  **/
408 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
409                                int scope, const char *exp,
410                                const char **attrs, void **res, 
411                                int *count, void **cookie)
412 {
413         int rc, i, version;
414         char *utf8_exp, *utf8_path, **search_attrs;
415         LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols; 
416         BerElement *cookie_be = NULL;
417         struct berval *cookie_bv= NULL;
418         TALLOC_CTX *ctx;
419
420         *res = NULL;
421
422         if (!(ctx = talloc_init()))
423                 return ADS_ERROR(LDAP_NO_MEMORY);
424
425         /* 0 means the conversion worked but the result was empty 
426            so we only fail if it's negative.  In any case, it always 
427            at least nulls out the dest */
428         if ((push_utf8_talloc(ctx, &utf8_exp, exp) < 0) ||
429             (push_utf8_talloc(ctx, &utf8_path, bind_path) < 0)) {
430                 rc = LDAP_NO_MEMORY;
431                 goto done;
432         }
433
434         if (!attrs || !(*attrs))
435                 search_attrs = NULL;
436         else {
437                 /* This would be the utf8-encoded version...*/
438                 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
439                 if (!(str_list_copy(&search_attrs, attrs))) {
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         const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1030         char *psp;
1031         unsigned acct_control;
1032
1033         if (!(ctx = talloc_init_named("machine_account")))
1034                 return ADS_ERROR(LDAP_NO_MEMORY);
1035
1036         ret = ADS_ERROR(LDAP_NO_MEMORY);
1037
1038         if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", hostname)))
1039                 goto done;
1040         if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
1041                 goto done;
1042         ou_str = ads_ou_string(org_unit);
1043         if (!ou_str) {
1044                 DEBUG(1, ("ads_ou_string returned NULL (malloc failure?)\n"));
1045                 goto done;
1046         }
1047         new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", hostname, ou_str, 
1048                                  ads->config.bind_path);
1049         servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", hostname);
1050         psp = talloc_asprintf(ctx, "HOST/%s.%s", 
1051                                                   hostname, 
1052                                                   ads->config.realm);
1053         strlower(&psp[5]);
1054         servicePrincipalName[1] = psp;
1055
1056         free(ou_str);
1057         if (!new_dn)
1058                 goto done;
1059
1060         if (!(samAccountName = talloc_asprintf(ctx, "%s$", hostname)))
1061                 goto done;
1062
1063         acct_control = UF_WORKSTATION_TRUST_ACCOUNT | UF_DONT_EXPIRE_PASSWD;
1064 #ifndef ENCTYPE_ARCFOUR_HMAC
1065         acct_control |= UF_USE_DES_KEY_ONLY;
1066 #endif
1067         if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control)))
1068                 goto done;
1069
1070         if (!(mods = ads_init_mods(ctx)))
1071                 goto done;
1072         
1073         ads_mod_str(ctx, &mods, "cn", hostname);
1074         ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1075         ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1076         ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1077         ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1078         ads_mod_str(ctx, &mods, "dNSHostName", hostname);
1079         ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1080         ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1081         ads_mod_str(ctx, &mods, "operatingSystemVersion", VERSION);
1082
1083         ads_gen_add(ads, new_dn, mods);
1084         ret = ads_set_machine_sd(ads, hostname, new_dn);
1085
1086 done:
1087         talloc_destroy(ctx);
1088         return ret;
1089 }
1090
1091 /*
1092   dump a binary result from ldap
1093 */
1094 static void dump_binary(const char *field, struct berval **values)
1095 {
1096         int i, j;
1097         for (i=0; values[i]; i++) {
1098                 printf("%s: ", field);
1099                 for (j=0; j<values[i]->bv_len; j++) {
1100                         printf("%02X", (unsigned char)values[i]->bv_val[j]);
1101                 }
1102                 printf("\n");
1103         }
1104 }
1105
1106 struct uuid {
1107         uint32   i1;
1108         uint16   i2;
1109         uint16   i3;
1110         uint8    s[8];
1111 };
1112
1113 static void dump_guid(const char *field, struct berval **values)
1114 {
1115         int i;
1116         GUID guid;
1117         for (i=0; values[i]; i++) {
1118                 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1119                 printf("%s: %s\n", field, uuid_string_static(guid));
1120         }
1121 }
1122
1123 /*
1124   dump a sid result from ldap
1125 */
1126 static void dump_sid(const char *field, struct berval **values)
1127 {
1128         int i;
1129         for (i=0; values[i]; i++) {
1130                 DOM_SID sid;
1131                 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1132                 printf("%s: %s\n", field, sid_string_static(&sid));
1133         }
1134 }
1135
1136 /*
1137   dump ntSecurityDescriptor
1138 */
1139 static void dump_sd(const char *filed, struct berval **values)
1140 {
1141         prs_struct ps;
1142         
1143         SEC_DESC   *psd = 0;
1144         TALLOC_CTX *ctx = 0;
1145
1146         if (!(ctx = talloc_init_named("sec_io_desc")))
1147                 return;
1148
1149         /* prepare data */
1150         prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1151         prs_append_data(&ps, values[0]->bv_val, values[0]->bv_len);
1152         ps.data_offset = 0;
1153
1154         /* parse secdesc */
1155         if (!sec_io_desc("sd", &psd, &ps, 1)) {
1156                 prs_mem_free(&ps);
1157                 talloc_destroy(ctx);
1158                 return;
1159         }
1160         if (psd) ads_disp_sd(psd);
1161
1162         prs_mem_free(&ps);
1163         talloc_destroy(ctx);
1164 }
1165
1166 /*
1167   dump a string result from ldap
1168 */
1169 static void dump_string(const char *field, char **values)
1170 {
1171         int i;
1172         for (i=0; values[i]; i++) {
1173                 printf("%s: %s\n", field, values[i]);
1174         }
1175 }
1176
1177 /*
1178   dump a field from LDAP on stdout
1179   used for debugging
1180 */
1181
1182 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1183 {
1184         struct {
1185                 char *name;
1186                 BOOL string;
1187                 void (*handler)(const char *, struct berval **);
1188         } handlers[] = {
1189                 {"objectGUID", False, dump_guid},
1190                 {"nTSecurityDescriptor", False, dump_sd},
1191                 {"dnsRecord", False, dump_binary},
1192                 {"objectSid", False, dump_sid},
1193                 {NULL, True, NULL}
1194         };
1195         int i;
1196
1197         if (!field) { /* must be end of an entry */
1198                 printf("\n");
1199                 return False;
1200         }
1201
1202         for (i=0; handlers[i].name; i++) {
1203                 if (StrCaseCmp(handlers[i].name, field) == 0) {
1204                         if (!values) /* first time, indicate string or not */
1205                                 return handlers[i].string;
1206                         handlers[i].handler(field, (struct berval **) values);
1207                         break;
1208                 }
1209         }
1210         if (!handlers[i].name) {
1211                 if (!values) /* first time, indicate string conversion */
1212                         return True;
1213                 dump_string(field, (char **)values);
1214         }
1215         return False;
1216 }
1217
1218 /**
1219  * Dump a result from LDAP on stdout
1220  *  used for debugging
1221  * @param ads connection to ads server
1222  * @param res Results to dump
1223  **/
1224
1225 void ads_dump(ADS_STRUCT *ads, void *res)
1226 {
1227         ads_process_results(ads, res, ads_dump_field, NULL);
1228 }
1229
1230 /**
1231  * Walk through results, calling a function for each entry found.
1232  *  The function receives a field name, a berval * array of values,
1233  *  and a data area passed through from the start.  The function is
1234  *  called once with null for field and values at the end of each
1235  *  entry.
1236  * @param ads connection to ads server
1237  * @param res Results to process
1238  * @param fn Function for processing each result
1239  * @param data_area user-defined area to pass to function
1240  **/
1241 void ads_process_results(ADS_STRUCT *ads, void *res,
1242                          BOOL(*fn)(char *, void **, void *),
1243                          void *data_area)
1244 {
1245         void *msg;
1246         TALLOC_CTX *ctx;
1247
1248         if (!(ctx = talloc_init()))
1249                 return;
1250
1251         for (msg = ads_first_entry(ads, res); msg; 
1252              msg = ads_next_entry(ads, msg)) {
1253                 char *utf8_field;
1254                 BerElement *b;
1255         
1256                 for (utf8_field=ldap_first_attribute(ads->ld,
1257                                                      (LDAPMessage *)msg,&b); 
1258                      utf8_field;
1259                      utf8_field=ldap_next_attribute(ads->ld,
1260                                                     (LDAPMessage *)msg,b)) {
1261                         struct berval **ber_vals;
1262                         char **str_vals, **utf8_vals;
1263                         char *field;
1264                         BOOL string; 
1265
1266                         pull_utf8_talloc(ctx, &field, utf8_field);
1267                         string = fn(field, NULL, data_area);
1268
1269                         if (string) {
1270                                 utf8_vals = ldap_get_values(ads->ld,
1271                                                  (LDAPMessage *)msg, field);
1272                                 str_vals = ads_pull_strvals(ctx, 
1273                                                   (const char **) utf8_vals);
1274                                 fn(field, (void **) str_vals, data_area);
1275                                 ldap_value_free(utf8_vals);
1276                         } else {
1277                                 ber_vals = ldap_get_values_len(ads->ld, 
1278                                                  (LDAPMessage *)msg, field);
1279                                 fn(field, (void **) ber_vals, data_area);
1280
1281                                 ldap_value_free_len(ber_vals);
1282                         }
1283                         ldap_memfree(utf8_field);
1284                 }
1285                 ber_free(b, 0);
1286                 talloc_destroy_pool(ctx);
1287                 fn(NULL, NULL, data_area); /* completed an entry */
1288
1289         }
1290         talloc_destroy(ctx);
1291 }
1292
1293 /**
1294  * count how many replies are in a LDAPMessage
1295  * @param ads connection to ads server
1296  * @param res Results to count
1297  * @return number of replies
1298  **/
1299 int ads_count_replies(ADS_STRUCT *ads, void *res)
1300 {
1301         return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1302 }
1303
1304 /**
1305  * Join a machine to a realm
1306  *  Creates the machine account and sets the machine password
1307  * @param ads connection to ads server
1308  * @param hostname name of host to add
1309  * @param org_unit Organizational unit to place machine in
1310  * @return status of join
1311  **/
1312 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname, const char *org_unit)
1313 {
1314         ADS_STATUS status;
1315         LDAPMessage *res;
1316         char *host;
1317
1318         /* hostname must be lowercase */
1319         host = strdup(hostname);
1320         strlower(host);
1321
1322         status = ads_find_machine_acct(ads, (void **)&res, host);
1323         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1324                 DEBUG(0, ("Host account for %s already exists - deleting old account\n", host));
1325                 status = ads_leave_realm(ads, host);
1326                 if (!ADS_ERR_OK(status)) {
1327                         DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n", 
1328                                   host, ads->config.realm));
1329                         return status;
1330                 }
1331         }
1332
1333         status = ads_add_machine_acct(ads, host, org_unit);
1334         if (!ADS_ERR_OK(status)) {
1335                 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
1336                 return status;
1337         }
1338
1339         status = ads_find_machine_acct(ads, (void **)&res, host);
1340         if (!ADS_ERR_OK(status)) {
1341                 DEBUG(0, ("Host account test failed\n"));
1342                 return status;
1343         }
1344
1345         free(host);
1346
1347         return status;
1348 }
1349
1350 /**
1351  * Delete a machine from the realm
1352  * @param ads connection to ads server
1353  * @param hostname Machine to remove
1354  * @return status of delete
1355  **/
1356 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1357 {
1358         ADS_STATUS status;
1359         void *res;
1360         char *hostnameDN, *host; 
1361         int rc;
1362
1363         /* hostname must be lowercase */
1364         host = strdup(hostname);
1365         strlower(host);
1366
1367         status = ads_find_machine_acct(ads, &res, host);
1368         if (!ADS_ERR_OK(status)) {
1369             DEBUG(0, ("Host account for %s does not exist.\n", host));
1370             return status;
1371         }
1372
1373         hostnameDN = ads_get_dn(ads, (LDAPMessage *)res);
1374         rc = ldap_delete_s(ads->ld, hostnameDN);
1375         ads_memfree(ads, hostnameDN);
1376         if (rc != LDAP_SUCCESS) {
1377                 return ADS_ERROR(rc);
1378         }
1379
1380         status = ads_find_machine_acct(ads, &res, host);
1381         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1382                 DEBUG(0, ("Failed to remove host account.\n"));
1383                 return status;
1384         }
1385
1386         free(host);
1387
1388         return status;
1389 }
1390
1391 /**
1392  * add machine account to existing security descriptor 
1393  * @param ads connection to ads server
1394  * @param hostname machine to add
1395  * @param dn DN of security descriptor
1396  * @return status
1397  **/
1398 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1399 {
1400         const char     *attrs[] = {"ntSecurityDescriptor", "objectSid", 0};
1401         char           *exp     = 0;
1402         size_t          sd_size = 0;
1403         struct berval   bval = {0, NULL};
1404         prs_struct      ps_wire;
1405
1406         LDAPMessage *res  = 0;
1407         LDAPMessage *msg  = 0;
1408         ADS_MODLIST  mods = 0;
1409
1410         NTSTATUS    status;
1411         ADS_STATUS  ret;
1412         DOM_SID     sid;
1413         SEC_DESC   *psd = 0;
1414         TALLOC_CTX *ctx = 0;    
1415
1416         if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1417
1418         ret = ADS_ERROR(LDAP_SUCCESS);
1419
1420         if (asprintf(&exp, "(samAccountName=%s$)", hostname) == -1) {
1421                 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1422                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1423         }
1424
1425         ret = ads_search(ads, (void *) &res, exp, attrs);
1426
1427         if (!ADS_ERR_OK(ret)) return ret;
1428
1429         msg   = ads_first_entry(ads, res);
1430         ads_pull_sid(ads, msg, attrs[1], &sid); 
1431         if (!(ctx = talloc_init_named("sec_io_desc"))) {
1432                 ret =  ADS_ERROR(LDAP_NO_MEMORY);
1433                 goto ads_set_sd_error;
1434         }
1435
1436         if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1437                 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1438                 goto ads_set_sd_error;
1439         }
1440
1441         status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1442
1443         if (!NT_STATUS_IS_OK(status)) {
1444                 ret = ADS_ERROR_NT(status);
1445                 goto ads_set_sd_error;
1446         }
1447
1448         prs_init(&ps_wire, sd_size, ctx, MARSHALL);
1449         if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1450                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1451                 goto ads_set_sd_error;
1452         }
1453
1454 #if 0
1455         file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1456 #endif
1457         if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1458
1459         bval.bv_len = sd_size;
1460         bval.bv_val = prs_data_p(&ps_wire);
1461         ads_mod_ber(ctx, &mods, attrs[0], &bval);
1462         ret = ads_gen_mod(ads, dn, mods);
1463
1464 ads_set_sd_error:
1465         ads_msgfree(ads, res);
1466         prs_mem_free(&ps_wire);
1467         talloc_destroy(ctx);
1468         return ret;
1469 }
1470
1471 /**
1472  * pull the first entry from a ADS result
1473  * @param ads connection to ads server
1474  * @param res Results of search
1475  * @return first entry from result
1476  **/
1477 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1478 {
1479         return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1480 }
1481
1482 /**
1483  * pull the next entry from a ADS result
1484  * @param ads connection to ads server
1485  * @param res Results of search
1486  * @return next entry from result
1487  **/
1488 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1489 {
1490         return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1491 }
1492
1493 /**
1494  * pull a single string from a ADS result
1495  * @param ads connection to ads server
1496  * @param mem_ctx TALLOC_CTX to use for allocating result string
1497  * @param msg Results of search
1498  * @param field Attribute to retrieve
1499  * @return Result string in talloc context
1500  **/
1501 char *ads_pull_string(ADS_STRUCT *ads, 
1502                       TALLOC_CTX *mem_ctx, void *msg, const char *field)
1503 {
1504         char **values;
1505         char *ret = NULL;
1506         char *ux_string;
1507         int rc;
1508
1509         values = ldap_get_values(ads->ld, msg, field);
1510         if (!values) return NULL;
1511         
1512         if (values[0]) {
1513                 rc = pull_utf8_talloc(mem_ctx, &ux_string, 
1514                                       values[0]);
1515                 if (rc != -1)
1516                         ret = ux_string;
1517                 
1518         }
1519         ldap_value_free(values);
1520         return ret;
1521 }
1522
1523 /**
1524  * pull an array of strings from a ADS result
1525  * @param ads connection to ads server
1526  * @param mem_ctx TALLOC_CTX to use for allocating result string
1527  * @param msg Results of search
1528  * @param field Attribute to retrieve
1529  * @return Result strings in talloc context
1530  **/
1531 char **ads_pull_strings(ADS_STRUCT *ads, 
1532                        TALLOC_CTX *mem_ctx, void *msg, const char *field)
1533 {
1534         char **values;
1535         char **ret = NULL;
1536         int i, n;
1537
1538         values = ldap_get_values(ads->ld, msg, field);
1539         if (!values) return NULL;
1540
1541         for (i=0;values[i];i++) /* noop */ ;
1542         n = i;
1543
1544         ret = talloc(mem_ctx, sizeof(char *) * (n+1));
1545
1546         for (i=0;i<n;i++) {
1547                 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1548                         return NULL;
1549                 }
1550         }
1551         ret[i] = NULL;
1552
1553         ldap_value_free(values);
1554         return ret;
1555 }
1556
1557
1558 /**
1559  * pull a single uint32 from a ADS result
1560  * @param ads connection to ads server
1561  * @param msg Results of search
1562  * @param field Attribute to retrieve
1563  * @param v Pointer to int to store result
1564  * @return boolean inidicating success
1565 */
1566 BOOL ads_pull_uint32(ADS_STRUCT *ads, 
1567                      void *msg, const char *field, uint32 *v)
1568 {
1569         char **values;
1570
1571         values = ldap_get_values(ads->ld, msg, field);
1572         if (!values) return False;
1573         if (!values[0]) {
1574                 ldap_value_free(values);
1575                 return False;
1576         }
1577
1578         *v = atoi(values[0]);
1579         ldap_value_free(values);
1580         return True;
1581 }
1582
1583 /**
1584  * pull a single DOM_SID from a ADS result
1585  * @param ads connection to ads server
1586  * @param msg Results of search
1587  * @param field Attribute to retrieve
1588  * @param sid Pointer to sid to store result
1589  * @return boolean inidicating success
1590 */
1591 BOOL ads_pull_sid(ADS_STRUCT *ads, 
1592                   void *msg, const char *field, DOM_SID *sid)
1593 {
1594         struct berval **values;
1595         BOOL ret = False;
1596
1597         values = ldap_get_values_len(ads->ld, msg, field);
1598
1599         if (!values) return False;
1600
1601         if (values[0]) {
1602                 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
1603         }
1604         
1605         ldap_value_free_len(values);
1606         return ret;
1607 }
1608
1609 /**
1610  * pull an array of DOM_SIDs from a ADS result
1611  * @param ads connection to ads server
1612  * @param mem_ctx TALLOC_CTX for allocating sid array
1613  * @param msg Results of search
1614  * @param field Attribute to retrieve
1615  * @param sids pointer to sid array to allocate
1616  * @return the count of SIDs pulled
1617  **/
1618 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1619                   void *msg, const char *field, DOM_SID **sids)
1620 {
1621         struct berval **values;
1622         BOOL ret;
1623         int count, i;
1624
1625         values = ldap_get_values_len(ads->ld, msg, field);
1626
1627         if (!values) return 0;
1628
1629         for (i=0; values[i]; i++) /* nop */ ;
1630
1631         (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
1632
1633         count = 0;
1634         for (i=0; values[i]; i++) {
1635                 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
1636                 if (ret) count++;
1637         }
1638         
1639         ldap_value_free_len(values);
1640         return count;
1641 }
1642
1643 /**
1644  * pull a SEC_DESC from a ADS result
1645  * @param ads connection to ads server
1646  * @param mem_ctx TALLOC_CTX for allocating sid array
1647  * @param msg Results of search
1648  * @param field Attribute to retrieve
1649  * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
1650  * @return boolean inidicating success
1651 */
1652 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1653                   void *msg, const char *field, SEC_DESC **sd)
1654 {
1655         struct berval **values;
1656         prs_struct      ps;
1657         BOOL ret = False;
1658
1659         values = ldap_get_values_len(ads->ld, msg, field);
1660
1661         if (!values) return False;
1662
1663         if (values[0]) {
1664                 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
1665                 prs_append_data(&ps, values[0]->bv_val, values[0]->bv_len);
1666                 ps.data_offset = 0;
1667
1668                 ret = sec_io_desc("sd", sd, &ps, 1);
1669         }
1670         
1671         ldap_value_free_len(values);
1672         return ret;
1673 }
1674
1675 /* 
1676  * in order to support usernames longer than 21 characters we need to 
1677  * use both the sAMAccountName and the userPrincipalName attributes 
1678  * It seems that not all users have the userPrincipalName attribute set
1679  *
1680  * @param ads connection to ads server
1681  * @param mem_ctx TALLOC_CTX for allocating sid array
1682  * @param msg Results of search
1683  * @return the username
1684  */
1685 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
1686 {
1687         char *ret, *p;
1688
1689         ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
1690         if (ret && (p = strchr(ret, '@'))) {
1691                 *p = 0;
1692                 return ret;
1693         }
1694         return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
1695 }
1696
1697
1698 /**
1699  * find the update serial number - this is the core of the ldap cache
1700  * @param ads connection to ads server
1701  * @param ads connection to ADS server
1702  * @param usn Pointer to retrieved update serial number
1703  * @return status of search
1704  **/
1705 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
1706 {
1707         const char *attrs[] = {"highestCommittedUSN", NULL};
1708         ADS_STATUS status;
1709         void *res;
1710
1711         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1712         if (!ADS_ERR_OK(status)) return status;
1713
1714         if (ads_count_replies(ads, res) != 1) {
1715                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1716         }
1717
1718         ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
1719         ads_msgfree(ads, res);
1720         return ADS_SUCCESS;
1721 }
1722
1723 /* parse a ADS timestring - typical string is
1724    '20020917091222.0Z0' which means 09:12.22 17th September
1725    2002, timezone 0 */
1726 static time_t ads_parse_time(const char *str)
1727 {
1728         struct tm tm;
1729
1730         ZERO_STRUCT(tm);
1731
1732         if (sscanf(str, "%4d%2d%2d%2d%2d%2d", 
1733                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
1734                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
1735                 return 0;
1736         }
1737         tm.tm_year -= 1900;
1738         tm.tm_mon -= 1;
1739
1740         return timegm(&tm);
1741 }
1742
1743
1744 /**
1745  * Find the servers name and realm - this can be done before authentication 
1746  *  The ldapServiceName field on w2k  looks like this:
1747  *    vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
1748  * @param ads connection to ads server
1749  * @return status of search
1750  **/
1751 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
1752 {
1753         const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
1754         ADS_STATUS status;
1755         void *res;
1756         char *value;
1757         char *p;
1758         char *timestr;
1759         TALLOC_CTX *ctx;
1760
1761         if (!(ctx = talloc_init())) {
1762                 return ADS_ERROR(LDAP_NO_MEMORY);
1763         }
1764
1765         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1766         if (!ADS_ERR_OK(status)) return status;
1767
1768         value = ads_pull_string(ads, ctx, res, "ldapServiceName");
1769         if (!value) {
1770                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1771         }
1772
1773         timestr = ads_pull_string(ads, ctx, res, "currentTime");
1774         if (!timestr) {
1775                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1776         }
1777
1778         ldap_msgfree(res);
1779
1780         p = strchr(value, ':');
1781         if (!p) {
1782                 talloc_destroy(ctx);
1783                 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' so was deemed invalid\n"));
1784                 return ADS_ERROR(LDAP_DECODING_ERROR);
1785         }
1786
1787         SAFE_FREE(ads->config.ldap_server_name);
1788
1789         ads->config.ldap_server_name = strdup(p+1);
1790         p = strchr(ads->config.ldap_server_name, '$');
1791         if (!p || p[1] != '@') {
1792                 talloc_destroy(ctx);
1793                 SAFE_FREE(ads->config.ldap_server_name);
1794                 DEBUG(1, ("ads_server_info: returned ldap server name did not contain '$@' so was deemed invalid\n"));
1795                 return ADS_ERROR(LDAP_DECODING_ERROR);
1796         }
1797
1798         *p = 0;
1799
1800         SAFE_FREE(ads->config.realm);
1801         SAFE_FREE(ads->config.bind_path);
1802
1803         ads->config.realm = strdup(p+2);
1804         ads->config.bind_path = ads_build_dn(ads->config.realm);
1805
1806         DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n", 
1807                  ads->config.ldap_server_name, ads->config.realm,
1808                  ads->config.bind_path));
1809
1810         ads->config.current_time = ads_parse_time(timestr);
1811
1812         if (ads->config.current_time != 0) {
1813                 ads->auth.time_offset = ads->config.current_time - time(NULL);
1814                 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
1815         }
1816
1817         talloc_destroy(ctx);
1818
1819         return ADS_SUCCESS;
1820 }
1821
1822
1823 /**
1824  * find the list of trusted domains
1825  * @param ads connection to ads server
1826  * @param mem_ctx TALLOC_CTX for allocating results
1827  * @param num_trusts pointer to number of trusts
1828  * @param names pointer to trusted domain name list
1829  * @param sids pointer to list of sids of trusted domains
1830  * @return the count of SIDs pulled
1831  **/
1832 ADS_STATUS ads_trusted_domains(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, 
1833                                int *num_trusts, 
1834                                char ***names, 
1835                                char ***alt_names,
1836                                DOM_SID **sids)
1837 {
1838         const char *attrs[] = {"name", "flatname", "securityIdentifier", 
1839                                "trustDirection", NULL};
1840         ADS_STATUS status;
1841         void *res, *msg;
1842         int count, i;
1843
1844         *num_trusts = 0;
1845
1846         status = ads_search(ads, &res, "(objectcategory=trustedDomain)", attrs);
1847         if (!ADS_ERR_OK(status)) return status;
1848
1849         count = ads_count_replies(ads, res);
1850         if (count == 0) {
1851                 ads_msgfree(ads, res);
1852                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1853         }
1854
1855         (*names) = talloc(mem_ctx, sizeof(char *) * count);
1856         (*alt_names) = talloc(mem_ctx, sizeof(char *) * count);
1857         (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * count);
1858         if (! *names || ! *sids) return ADS_ERROR(LDAP_NO_MEMORY);
1859
1860         for (i=0, msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
1861                 uint32 direction;
1862
1863                 /* direction is a 2 bit bitfield, 1 means they trust us 
1864                    but we don't trust them, so we should not list them
1865                    as users from that domain can't login */
1866                 if (ads_pull_uint32(ads, msg, "trustDirection", &direction) &&
1867                     direction == 1) {
1868                         continue;
1869                 }
1870                 
1871                 (*names)[i] = ads_pull_string(ads, mem_ctx, msg, "name");
1872                 (*alt_names)[i] = ads_pull_string(ads, mem_ctx, msg, "flatname");
1873
1874                 if ((*alt_names)[i] && (*alt_names)[i][0]) {
1875                         /* we prefer the flatname as the primary name
1876                            for consistency with RPC */
1877                         char *name = (*alt_names)[i];
1878                         (*alt_names)[i] = (*names)[i];
1879                         (*names)[i] = name;
1880                 }
1881                 if (ads_pull_sid(ads, msg, "securityIdentifier", &(*sids)[i])) {
1882                         i++;
1883                 }
1884         }
1885
1886         ads_msgfree(ads, res);
1887
1888         *num_trusts = i;
1889
1890         return ADS_SUCCESS;
1891 }
1892
1893 /**
1894  * find the domain sid for our domain
1895  * @param ads connection to ads server
1896  * @param sid Pointer to domain sid
1897  * @return status of search
1898  **/
1899 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
1900 {
1901         const char *attrs[] = {"objectSid", NULL};
1902         void *res;
1903         ADS_STATUS rc;
1904
1905         rc = ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
1906                            attrs, &res);
1907         if (!ADS_ERR_OK(rc)) return rc;
1908         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
1909                 return ADS_ERROR_SYSTEM(ENOENT);
1910         }
1911         ads_msgfree(ads, res);
1912         
1913         return ADS_SUCCESS;
1914 }
1915
1916 /* this is rather complex - we need to find the allternate (netbios) name
1917    for the domain, but there isn't a simple query to do this. Instead
1918    we look for the principle names on the DCs account and find one that has 
1919    the right form, then extract the netbios name of the domain from that
1920 */
1921 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **workgroup)
1922 {
1923         char *exp;
1924         ADS_STATUS rc;
1925         char **principles;
1926         char *prefix;
1927         int prefix_length;
1928         int i;
1929         void *res;
1930         const char *attrs[] = {"servicePrincipalName", NULL};
1931
1932         (*workgroup) = NULL;
1933
1934         asprintf(&exp, "(&(objectclass=computer)(dnshostname=%s.%s))", 
1935                  ads->config.ldap_server_name, ads->config.realm);
1936         rc = ads_search(ads, &res, exp, attrs);
1937         free(exp);
1938
1939         if (!ADS_ERR_OK(rc)) {
1940                 return rc;
1941         }
1942
1943         principles = ads_pull_strings(ads, mem_ctx, res, "servicePrincipalName");
1944
1945         ads_msgfree(ads, res);
1946
1947         if (!principles) {
1948                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1949         }
1950
1951         asprintf(&prefix, "HOST/%s.%s/", 
1952                  ads->config.ldap_server_name, 
1953                  ads->config.realm);
1954
1955         prefix_length = strlen(prefix);
1956
1957         for (i=0;principles[i]; i++) {
1958                 if (strncasecmp(principles[i], prefix, prefix_length) == 0 &&
1959                     strcasecmp(ads->config.realm, principles[i]+prefix_length) != 0 &&
1960                     !strchr(principles[i]+prefix_length, '.')) {
1961                         /* found an alternate (short) name for the domain. */
1962                         DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
1963                                  principles[i]+prefix_length, 
1964                                  ads->config.realm));
1965                         (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
1966                         break;
1967                 }
1968         }
1969         free(prefix);
1970
1971         if (!*workgroup) {
1972                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1973         }
1974         
1975         return ADS_SUCCESS;
1976 }
1977
1978 #endif