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