Check return values of various join-related functions, and ensure we always
[abartlet/samba.git/.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 -1.  In any case, it always 
430            at least nulls out the dest */
431         if ((push_utf8_talloc(ctx, &utf8_exp, exp) == (size_t)-1) ||
432             (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
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) == (size_t)-1) ||
656             (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
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, status;
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         ret = ads_gen_add(ads, new_dn, mods);
1093
1094         if (!ADS_ERR_OK(ret))
1095                 goto done;
1096
1097         /* Do not fail if we can't set security descriptor
1098          * it shouldn't be mandatory and probably we just 
1099          * don't have enough rights to do it.
1100          */
1101         status = ads_set_machine_sd(ads, hostname, new_dn);
1102
1103         if (!ADS_ERR_OK(status)) {
1104                 DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1105                                 ads_errstr(status)));
1106         }
1107 done:
1108         talloc_destroy(ctx);
1109         return ret;
1110 }
1111
1112 /*
1113   dump a binary result from ldap
1114 */
1115 static void dump_binary(const char *field, struct berval **values)
1116 {
1117         int i, j;
1118         for (i=0; values[i]; i++) {
1119                 printf("%s: ", field);
1120                 for (j=0; j<values[i]->bv_len; j++) {
1121                         printf("%02X", (unsigned char)values[i]->bv_val[j]);
1122                 }
1123                 printf("\n");
1124         }
1125 }
1126
1127 struct uuid {
1128         uint32   i1;
1129         uint16   i2;
1130         uint16   i3;
1131         uint8    s[8];
1132 };
1133
1134 static void dump_guid(const char *field, struct berval **values)
1135 {
1136         int i;
1137         GUID guid;
1138         for (i=0; values[i]; i++) {
1139                 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1140                 printf("%s: %s\n", field, uuid_string_static(guid));
1141         }
1142 }
1143
1144 /*
1145   dump a sid result from ldap
1146 */
1147 static void dump_sid(const char *field, struct berval **values)
1148 {
1149         int i;
1150         for (i=0; values[i]; i++) {
1151                 DOM_SID sid;
1152                 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1153                 printf("%s: %s\n", field, sid_string_static(&sid));
1154         }
1155 }
1156
1157 /*
1158   dump ntSecurityDescriptor
1159 */
1160 static void dump_sd(const char *filed, struct berval **values)
1161 {
1162         prs_struct ps;
1163         
1164         SEC_DESC   *psd = 0;
1165         TALLOC_CTX *ctx = 0;
1166
1167         if (!(ctx = talloc_init("sec_io_desc")))
1168                 return;
1169
1170         /* prepare data */
1171         prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1172         prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1173         prs_set_offset(&ps,0);
1174
1175         /* parse secdesc */
1176         if (!sec_io_desc("sd", &psd, &ps, 1)) {
1177                 prs_mem_free(&ps);
1178                 talloc_destroy(ctx);
1179                 return;
1180         }
1181         if (psd) ads_disp_sd(psd);
1182
1183         prs_mem_free(&ps);
1184         talloc_destroy(ctx);
1185 }
1186
1187 /*
1188   dump a string result from ldap
1189 */
1190 static void dump_string(const char *field, char **values)
1191 {
1192         int i;
1193         for (i=0; values[i]; i++) {
1194                 printf("%s: %s\n", field, values[i]);
1195         }
1196 }
1197
1198 /*
1199   dump a field from LDAP on stdout
1200   used for debugging
1201 */
1202
1203 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1204 {
1205         const struct {
1206                 const char *name;
1207                 BOOL string;
1208                 void (*handler)(const char *, struct berval **);
1209         } handlers[] = {
1210                 {"objectGUID", False, dump_guid},
1211                 {"nTSecurityDescriptor", False, dump_sd},
1212                 {"dnsRecord", False, dump_binary},
1213                 {"objectSid", False, dump_sid},
1214                 {NULL, True, NULL}
1215         };
1216         int i;
1217
1218         if (!field) { /* must be end of an entry */
1219                 printf("\n");
1220                 return False;
1221         }
1222
1223         for (i=0; handlers[i].name; i++) {
1224                 if (StrCaseCmp(handlers[i].name, field) == 0) {
1225                         if (!values) /* first time, indicate string or not */
1226                                 return handlers[i].string;
1227                         handlers[i].handler(field, (struct berval **) values);
1228                         break;
1229                 }
1230         }
1231         if (!handlers[i].name) {
1232                 if (!values) /* first time, indicate string conversion */
1233                         return True;
1234                 dump_string(field, (char **)values);
1235         }
1236         return False;
1237 }
1238
1239 /**
1240  * Dump a result from LDAP on stdout
1241  *  used for debugging
1242  * @param ads connection to ads server
1243  * @param res Results to dump
1244  **/
1245
1246 void ads_dump(ADS_STRUCT *ads, void *res)
1247 {
1248         ads_process_results(ads, res, ads_dump_field, NULL);
1249 }
1250
1251 /**
1252  * Walk through results, calling a function for each entry found.
1253  *  The function receives a field name, a berval * array of values,
1254  *  and a data area passed through from the start.  The function is
1255  *  called once with null for field and values at the end of each
1256  *  entry.
1257  * @param ads connection to ads server
1258  * @param res Results to process
1259  * @param fn Function for processing each result
1260  * @param data_area user-defined area to pass to function
1261  **/
1262 void ads_process_results(ADS_STRUCT *ads, void *res,
1263                          BOOL(*fn)(char *, void **, void *),
1264                          void *data_area)
1265 {
1266         void *msg;
1267         TALLOC_CTX *ctx;
1268
1269         if (!(ctx = talloc_init("ads_process_results")))
1270                 return;
1271
1272         for (msg = ads_first_entry(ads, res); msg; 
1273              msg = ads_next_entry(ads, msg)) {
1274                 char *utf8_field;
1275                 BerElement *b;
1276         
1277                 for (utf8_field=ldap_first_attribute(ads->ld,
1278                                                      (LDAPMessage *)msg,&b); 
1279                      utf8_field;
1280                      utf8_field=ldap_next_attribute(ads->ld,
1281                                                     (LDAPMessage *)msg,b)) {
1282                         struct berval **ber_vals;
1283                         char **str_vals, **utf8_vals;
1284                         char *field;
1285                         BOOL string; 
1286
1287                         pull_utf8_talloc(ctx, &field, utf8_field);
1288                         string = fn(field, NULL, data_area);
1289
1290                         if (string) {
1291                                 utf8_vals = ldap_get_values(ads->ld,
1292                                                  (LDAPMessage *)msg, field);
1293                                 str_vals = ads_pull_strvals(ctx, 
1294                                                   (const char **) utf8_vals);
1295                                 fn(field, (void **) str_vals, data_area);
1296                                 ldap_value_free(utf8_vals);
1297                         } else {
1298                                 ber_vals = ldap_get_values_len(ads->ld, 
1299                                                  (LDAPMessage *)msg, field);
1300                                 fn(field, (void **) ber_vals, data_area);
1301
1302                                 ldap_value_free_len(ber_vals);
1303                         }
1304                         ldap_memfree(utf8_field);
1305                 }
1306                 ber_free(b, 0);
1307                 talloc_destroy_pool(ctx);
1308                 fn(NULL, NULL, data_area); /* completed an entry */
1309
1310         }
1311         talloc_destroy(ctx);
1312 }
1313
1314 /**
1315  * count how many replies are in a LDAPMessage
1316  * @param ads connection to ads server
1317  * @param res Results to count
1318  * @return number of replies
1319  **/
1320 int ads_count_replies(ADS_STRUCT *ads, void *res)
1321 {
1322         return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1323 }
1324
1325 /**
1326  * Join a machine to a realm
1327  *  Creates the machine account and sets the machine password
1328  * @param ads connection to ads server
1329  * @param hostname name of host to add
1330  * @param org_unit Organizational unit to place machine in
1331  * @return status of join
1332  **/
1333 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname, const char *org_unit)
1334 {
1335         ADS_STATUS status;
1336         LDAPMessage *res;
1337         char *host;
1338
1339         /* hostname must be lowercase */
1340         host = strdup(hostname);
1341         strlower(host);
1342
1343         status = ads_find_machine_acct(ads, (void **)&res, host);
1344         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1345                 DEBUG(0, ("Host account for %s already exists - deleting old account\n", host));
1346                 status = ads_leave_realm(ads, host);
1347                 if (!ADS_ERR_OK(status)) {
1348                         DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n", 
1349                                   host, ads->config.realm));
1350                         return status;
1351                 }
1352         }
1353
1354         status = ads_add_machine_acct(ads, host, org_unit);
1355         if (!ADS_ERR_OK(status)) {
1356                 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
1357                 return status;
1358         }
1359
1360         status = ads_find_machine_acct(ads, (void **)&res, host);
1361         if (!ADS_ERR_OK(status)) {
1362                 DEBUG(0, ("Host account test failed\n"));
1363                 return status;
1364         }
1365
1366         free(host);
1367
1368         return status;
1369 }
1370
1371 /**
1372  * Delete a machine from the realm
1373  * @param ads connection to ads server
1374  * @param hostname Machine to remove
1375  * @return status of delete
1376  **/
1377 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1378 {
1379         ADS_STATUS status;
1380         void *res;
1381         char *hostnameDN, *host; 
1382         int rc;
1383
1384         /* hostname must be lowercase */
1385         host = strdup(hostname);
1386         strlower(host);
1387
1388         status = ads_find_machine_acct(ads, &res, host);
1389         if (!ADS_ERR_OK(status)) {
1390             DEBUG(0, ("Host account for %s does not exist.\n", host));
1391             return status;
1392         }
1393
1394         hostnameDN = ads_get_dn(ads, (LDAPMessage *)res);
1395         rc = ldap_delete_s(ads->ld, hostnameDN);
1396         ads_memfree(ads, hostnameDN);
1397         if (rc != LDAP_SUCCESS) {
1398                 return ADS_ERROR(rc);
1399         }
1400
1401         status = ads_find_machine_acct(ads, &res, host);
1402         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1403                 DEBUG(0, ("Failed to remove host account.\n"));
1404                 return status;
1405         }
1406
1407         free(host);
1408
1409         return status;
1410 }
1411
1412 /**
1413  * add machine account to existing security descriptor 
1414  * @param ads connection to ads server
1415  * @param hostname machine to add
1416  * @param dn DN of security descriptor
1417  * @return status
1418  **/
1419 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1420 {
1421         const char     *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
1422         char           *exp     = 0;
1423         size_t          sd_size = 0;
1424         struct berval   bval = {0, NULL};
1425         prs_struct      ps_wire;
1426         char           *escaped_hostname = escape_ldap_string_alloc(hostname);
1427
1428         LDAPMessage *res  = 0;
1429         LDAPMessage *msg  = 0;
1430         ADS_MODLIST  mods = 0;
1431
1432         NTSTATUS    status;
1433         ADS_STATUS  ret;
1434         DOM_SID     sid;
1435         SEC_DESC   *psd = NULL;
1436         TALLOC_CTX *ctx = NULL; 
1437
1438         /* Avoid segmentation fault in prs_mem_free if
1439          * we have to bail out before prs_init */
1440         ps_wire.is_dynamic = False;
1441
1442         if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1443
1444         ret = ADS_ERROR(LDAP_SUCCESS);
1445
1446         if (!escaped_hostname) {
1447                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1448         }
1449
1450         if (asprintf(&exp, "(samAccountName=%s$)", escaped_hostname) == -1) {
1451                 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1452                 SAFE_FREE(escaped_hostname);
1453                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1454         }
1455
1456         SAFE_FREE(escaped_hostname);
1457
1458         ret = ads_search(ads, (void *) &res, exp, attrs);
1459
1460         if (!ADS_ERR_OK(ret)) return ret;
1461
1462         if ( !(msg = ads_first_entry(ads, res) )) {
1463                 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1464                 goto ads_set_sd_error;
1465         }
1466
1467         if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
1468                 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1469                 goto ads_set_sd_error;
1470         }
1471
1472         if (!(ctx = talloc_init("sec_io_desc"))) {
1473                 ret =  ADS_ERROR(LDAP_NO_MEMORY);
1474                 goto ads_set_sd_error;
1475         }
1476
1477         if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1478                 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1479                 goto ads_set_sd_error;
1480         }
1481
1482         status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1483
1484         if (!NT_STATUS_IS_OK(status)) {
1485                 ret = ADS_ERROR_NT(status);
1486                 goto ads_set_sd_error;
1487         }
1488
1489         if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
1490                 ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1491         }
1492
1493         if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1494                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1495                 goto ads_set_sd_error;
1496         }
1497
1498 #if 0
1499         file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1500 #endif
1501         if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1502
1503         bval.bv_len = sd_size;
1504         bval.bv_val = talloc(ctx, sd_size);
1505         if (!bval.bv_val) {
1506                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1507                 goto ads_set_sd_error;
1508         }
1509         prs_copy_all_data_out((char *)&bval.bv_val, &ps_wire);
1510
1511         ads_mod_ber(ctx, &mods, attrs[0], &bval);
1512         ret = ads_gen_mod(ads, dn, mods);
1513
1514 ads_set_sd_error:
1515         ads_msgfree(ads, res);
1516         prs_mem_free(&ps_wire);
1517         talloc_destroy(ctx);
1518         return ret;
1519 }
1520
1521 /**
1522  * pull the first entry from a ADS result
1523  * @param ads connection to ads server
1524  * @param res Results of search
1525  * @return first entry from result
1526  **/
1527 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1528 {
1529         return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1530 }
1531
1532 /**
1533  * pull the next entry from a ADS result
1534  * @param ads connection to ads server
1535  * @param res Results of search
1536  * @return next entry from result
1537  **/
1538 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1539 {
1540         return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1541 }
1542
1543 /**
1544  * pull a single string from a ADS result
1545  * @param ads connection to ads server
1546  * @param mem_ctx TALLOC_CTX to use for allocating result string
1547  * @param msg Results of search
1548  * @param field Attribute to retrieve
1549  * @return Result string in talloc context
1550  **/
1551 char *ads_pull_string(ADS_STRUCT *ads, 
1552                       TALLOC_CTX *mem_ctx, void *msg, const char *field)
1553 {
1554         char **values;
1555         char *ret = NULL;
1556         char *ux_string;
1557         int rc;
1558
1559         values = ldap_get_values(ads->ld, msg, field);
1560         if (!values)
1561                 return NULL;
1562         
1563         if (values[0]) {
1564                 rc = pull_utf8_talloc(mem_ctx, &ux_string, 
1565                                       values[0]);
1566                 if (rc != -1)
1567                         ret = ux_string;
1568                 
1569         }
1570         ldap_value_free(values);
1571         return ret;
1572 }
1573
1574 /**
1575  * pull an array of strings from a ADS result
1576  * @param ads connection to ads server
1577  * @param mem_ctx TALLOC_CTX to use for allocating result string
1578  * @param msg Results of search
1579  * @param field Attribute to retrieve
1580  * @return Result strings in talloc context
1581  **/
1582 char **ads_pull_strings(ADS_STRUCT *ads, 
1583                        TALLOC_CTX *mem_ctx, void *msg, const char *field)
1584 {
1585         char **values;
1586         char **ret = NULL;
1587         int i, n;
1588
1589         values = ldap_get_values(ads->ld, msg, field);
1590         if (!values)
1591                 return NULL;
1592
1593         for (i=0;values[i];i++)
1594                 /* noop */ ;
1595         n = i;
1596
1597         ret = talloc(mem_ctx, sizeof(char *) * (n+1));
1598         if (!ret) {
1599                 ldap_value_free(values);
1600                 return NULL;
1601         }
1602
1603         for (i=0;i<n;i++) {
1604                 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1605                         ldap_value_free(values);
1606                         return NULL;
1607                 }
1608         }
1609         ret[i] = NULL;
1610
1611         ldap_value_free(values);
1612         return ret;
1613 }
1614
1615
1616 /**
1617  * pull a single uint32 from a ADS result
1618  * @param ads connection to ads server
1619  * @param msg Results of search
1620  * @param field Attribute to retrieve
1621  * @param v Pointer to int to store result
1622  * @return boolean inidicating success
1623 */
1624 BOOL ads_pull_uint32(ADS_STRUCT *ads, 
1625                      void *msg, const char *field, uint32 *v)
1626 {
1627         char **values;
1628
1629         values = ldap_get_values(ads->ld, msg, field);
1630         if (!values)
1631                 return False;
1632         if (!values[0]) {
1633                 ldap_value_free(values);
1634                 return False;
1635         }
1636
1637         *v = atoi(values[0]);
1638         ldap_value_free(values);
1639         return True;
1640 }
1641
1642 /**
1643  * pull a single objectGUID from an ADS result
1644  * @param ads connection to ADS server
1645  * @param msg results of search
1646  * @param guid 37-byte area to receive text guid
1647  * @return boolean indicating success
1648  **/
1649 BOOL ads_pull_guid(ADS_STRUCT *ads,
1650                    void *msg, GUID *guid)
1651 {
1652         char **values;
1653
1654         values = ldap_get_values(ads->ld, msg, "objectGUID");
1655         if (!values)
1656                 return False;
1657         
1658         if (values[0]) {
1659                 memcpy(guid, values[0], sizeof(GUID));
1660                 ldap_value_free(values);
1661                 return True;
1662         }
1663         ldap_value_free(values);
1664         return False;
1665
1666 }
1667
1668
1669 /**
1670  * pull a single DOM_SID from a ADS result
1671  * @param ads connection to ads server
1672  * @param msg Results of search
1673  * @param field Attribute to retrieve
1674  * @param sid Pointer to sid to store result
1675  * @return boolean inidicating success
1676 */
1677 BOOL ads_pull_sid(ADS_STRUCT *ads, 
1678                   void *msg, const char *field, DOM_SID *sid)
1679 {
1680         struct berval **values;
1681         BOOL ret = False;
1682
1683         values = ldap_get_values_len(ads->ld, msg, field);
1684
1685         if (!values)
1686                 return False;
1687
1688         if (values[0])
1689                 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
1690         
1691         ldap_value_free_len(values);
1692         return ret;
1693 }
1694
1695 /**
1696  * pull an array of DOM_SIDs from a ADS result
1697  * @param ads connection to ads server
1698  * @param mem_ctx TALLOC_CTX for allocating sid array
1699  * @param msg Results of search
1700  * @param field Attribute to retrieve
1701  * @param sids pointer to sid array to allocate
1702  * @return the count of SIDs pulled
1703  **/
1704 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1705                   void *msg, const char *field, DOM_SID **sids)
1706 {
1707         struct berval **values;
1708         BOOL ret;
1709         int count, i;
1710
1711         values = ldap_get_values_len(ads->ld, msg, field);
1712
1713         if (!values)
1714                 return 0;
1715
1716         for (i=0; values[i]; i++)
1717                 /* nop */ ;
1718
1719         (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
1720         if (!(*sids)) {
1721                 ldap_value_free_len(values);
1722                 return 0;
1723         }
1724
1725         count = 0;
1726         for (i=0; values[i]; i++) {
1727                 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
1728                 if (ret)
1729                         count++;
1730         }
1731         
1732         ldap_value_free_len(values);
1733         return count;
1734 }
1735
1736 /**
1737  * pull a SEC_DESC from a ADS result
1738  * @param ads connection to ads server
1739  * @param mem_ctx TALLOC_CTX for allocating sid array
1740  * @param msg Results of search
1741  * @param field Attribute to retrieve
1742  * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
1743  * @return boolean inidicating success
1744 */
1745 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1746                   void *msg, const char *field, SEC_DESC **sd)
1747 {
1748         struct berval **values;
1749         prs_struct      ps;
1750         BOOL ret = False;
1751
1752         values = ldap_get_values_len(ads->ld, msg, field);
1753
1754         if (!values) return False;
1755
1756         if (values[0]) {
1757                 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
1758                 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1759                 prs_set_offset(&ps,0);
1760
1761                 ret = sec_io_desc("sd", sd, &ps, 1);
1762         }
1763         
1764         ldap_value_free_len(values);
1765         return ret;
1766 }
1767
1768 /* 
1769  * in order to support usernames longer than 21 characters we need to 
1770  * use both the sAMAccountName and the userPrincipalName attributes 
1771  * It seems that not all users have the userPrincipalName attribute set
1772  *
1773  * @param ads connection to ads server
1774  * @param mem_ctx TALLOC_CTX for allocating sid array
1775  * @param msg Results of search
1776  * @return the username
1777  */
1778 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
1779 {
1780         char *ret, *p;
1781
1782         ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
1783         if (ret && (p = strchr(ret, '@'))) {
1784                 *p = 0;
1785                 return ret;
1786         }
1787         return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
1788 }
1789
1790
1791 /**
1792  * find the update serial number - this is the core of the ldap cache
1793  * @param ads connection to ads server
1794  * @param ads connection to ADS server
1795  * @param usn Pointer to retrieved update serial number
1796  * @return status of search
1797  **/
1798 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
1799 {
1800         const char *attrs[] = {"highestCommittedUSN", NULL};
1801         ADS_STATUS status;
1802         void *res;
1803
1804         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1805         if (!ADS_ERR_OK(status)) return status;
1806
1807         if (ads_count_replies(ads, res) != 1) {
1808                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1809         }
1810
1811         ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
1812         ads_msgfree(ads, res);
1813         return ADS_SUCCESS;
1814 }
1815
1816 /* parse a ADS timestring - typical string is
1817    '20020917091222.0Z0' which means 09:12.22 17th September
1818    2002, timezone 0 */
1819 static time_t ads_parse_time(const char *str)
1820 {
1821         struct tm tm;
1822
1823         ZERO_STRUCT(tm);
1824
1825         if (sscanf(str, "%4d%2d%2d%2d%2d%2d", 
1826                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
1827                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
1828                 return 0;
1829         }
1830         tm.tm_year -= 1900;
1831         tm.tm_mon -= 1;
1832
1833         return timegm(&tm);
1834 }
1835
1836
1837 /**
1838  * Find the servers name and realm - this can be done before authentication 
1839  *  The ldapServiceName field on w2k  looks like this:
1840  *    vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
1841  * @param ads connection to ads server
1842  * @return status of search
1843  **/
1844 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
1845 {
1846         const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
1847         ADS_STATUS status;
1848         void *res;
1849         char *value;
1850         char *p;
1851         char *timestr;
1852         TALLOC_CTX *ctx;
1853
1854         if (!(ctx = talloc_init("ads_server_info"))) {
1855                 return ADS_ERROR(LDAP_NO_MEMORY);
1856         }
1857
1858         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1859         if (!ADS_ERR_OK(status)) return status;
1860
1861         value = ads_pull_string(ads, ctx, res, "ldapServiceName");
1862         if (!value) {
1863                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1864         }
1865
1866         timestr = ads_pull_string(ads, ctx, res, "currentTime");
1867         if (!timestr) {
1868                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1869         }
1870
1871         ldap_msgfree(res);
1872
1873         p = strchr(value, ':');
1874         if (!p) {
1875                 talloc_destroy(ctx);
1876                 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' so was deemed invalid\n"));
1877                 return ADS_ERROR(LDAP_DECODING_ERROR);
1878         }
1879
1880         SAFE_FREE(ads->config.ldap_server_name);
1881
1882         ads->config.ldap_server_name = strdup(p+1);
1883         p = strchr(ads->config.ldap_server_name, '$');
1884         if (!p || p[1] != '@') {
1885                 talloc_destroy(ctx);
1886                 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@' so was deemed invalid\n", ads->config.ldap_server_name));
1887                 SAFE_FREE(ads->config.ldap_server_name);
1888                 return ADS_ERROR(LDAP_DECODING_ERROR);
1889         }
1890
1891         *p = 0;
1892
1893         SAFE_FREE(ads->config.realm);
1894         SAFE_FREE(ads->config.bind_path);
1895
1896         ads->config.realm = strdup(p+2);
1897         ads->config.bind_path = ads_build_dn(ads->config.realm);
1898
1899         DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n", 
1900                  ads->config.ldap_server_name, ads->config.realm,
1901                  ads->config.bind_path));
1902
1903         ads->config.current_time = ads_parse_time(timestr);
1904
1905         if (ads->config.current_time != 0) {
1906                 ads->auth.time_offset = ads->config.current_time - time(NULL);
1907                 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
1908         }
1909
1910         talloc_destroy(ctx);
1911
1912         return ADS_SUCCESS;
1913 }
1914
1915
1916 /**
1917  * find the list of trusted domains
1918  * @param ads connection to ads server
1919  * @param mem_ctx TALLOC_CTX for allocating results
1920  * @param num_trusts pointer to number of trusts
1921  * @param names pointer to trusted domain name list
1922  * @param sids pointer to list of sids of trusted domains
1923  * @return the count of SIDs pulled
1924  **/
1925 ADS_STATUS ads_trusted_domains(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, 
1926                                int *num_trusts, 
1927                                char ***names, 
1928                                char ***alt_names,
1929                                DOM_SID **sids)
1930 {
1931         const char *attrs[] = {"name", "flatname", "securityIdentifier", 
1932                                "trustDirection", NULL};
1933         ADS_STATUS status;
1934         void *res, *msg;
1935         int count, i;
1936
1937         *num_trusts = 0;
1938
1939         status = ads_search(ads, &res, "(objectcategory=trustedDomain)", attrs);
1940         if (!ADS_ERR_OK(status)) return status;
1941
1942         count = ads_count_replies(ads, res);
1943         if (count == 0) {
1944                 ads_msgfree(ads, res);
1945                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1946         }
1947
1948         (*names) = talloc(mem_ctx, sizeof(char *) * count);
1949         (*alt_names) = talloc(mem_ctx, sizeof(char *) * count);
1950         (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * count);
1951         if (! *names || ! *sids) return ADS_ERROR(LDAP_NO_MEMORY);
1952
1953         for (i=0, msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
1954                 uint32 direction;
1955
1956                 /* direction is a 2 bit bitfield, 1 means they trust us 
1957                    but we don't trust them, so we should not list them
1958                    as users from that domain can't login */
1959                 if (ads_pull_uint32(ads, msg, "trustDirection", &direction) &&
1960                     direction == 1) {
1961                         continue;
1962                 }
1963                 
1964                 (*names)[i] = ads_pull_string(ads, mem_ctx, msg, "name");
1965                 (*alt_names)[i] = ads_pull_string(ads, mem_ctx, msg, "flatname");
1966
1967                 if ((*alt_names)[i] && (*alt_names)[i][0]) {
1968                         /* we prefer the flatname as the primary name
1969                            for consistency with RPC */
1970                         char *name = (*alt_names)[i];
1971                         (*alt_names)[i] = (*names)[i];
1972                         (*names)[i] = name;
1973                 }
1974                 if (ads_pull_sid(ads, msg, "securityIdentifier", &(*sids)[i])) {
1975                         i++;
1976                 }
1977         }
1978
1979         ads_msgfree(ads, res);
1980
1981         *num_trusts = i;
1982
1983         return ADS_SUCCESS;
1984 }
1985
1986 /**
1987  * find the domain sid for our domain
1988  * @param ads connection to ads server
1989  * @param sid Pointer to domain sid
1990  * @return status of search
1991  **/
1992 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
1993 {
1994         const char *attrs[] = {"objectSid", NULL};
1995         void *res;
1996         ADS_STATUS rc;
1997
1998         rc = ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
1999                            attrs, &res);
2000         if (!ADS_ERR_OK(rc)) return rc;
2001         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2002                 return ADS_ERROR_SYSTEM(ENOENT);
2003         }
2004         ads_msgfree(ads, res);
2005         
2006         return ADS_SUCCESS;
2007 }
2008
2009 /* this is rather complex - we need to find the allternate (netbios) name
2010    for the domain, but there isn't a simple query to do this. Instead
2011    we look for the principle names on the DCs account and find one that has 
2012    the right form, then extract the netbios name of the domain from that
2013
2014    NOTE! better method is this:
2015
2016 bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))'  nETBIOSName 
2017
2018 but you need to force the bind path to match the configurationNamingContext from the rootDSE
2019
2020 */
2021 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **workgroup)
2022 {
2023         char *exp;
2024         ADS_STATUS rc;
2025         char **principles;
2026         char *prefix;
2027         int prefix_length;
2028         int i;
2029         void *res;
2030         const char *attrs[] = {"servicePrincipalName", NULL};
2031
2032         (*workgroup) = NULL;
2033
2034         asprintf(&exp, "(&(objectclass=computer)(dnshostname=%s.%s))", 
2035                  ads->config.ldap_server_name, ads->config.realm);
2036         rc = ads_search(ads, &res, exp, attrs);
2037         free(exp);
2038
2039         if (!ADS_ERR_OK(rc)) {
2040                 return rc;
2041         }
2042
2043         principles = ads_pull_strings(ads, mem_ctx, res, "servicePrincipalName");
2044
2045         ads_msgfree(ads, res);
2046
2047         if (!principles) {
2048                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2049         }
2050
2051         asprintf(&prefix, "HOST/%s.%s/", 
2052                  ads->config.ldap_server_name, 
2053                  ads->config.realm);
2054
2055         prefix_length = strlen(prefix);
2056
2057         for (i=0;principles[i]; i++) {
2058                 if (strncasecmp(principles[i], prefix, prefix_length) == 0 &&
2059                     strcasecmp(ads->config.realm, principles[i]+prefix_length) != 0 &&
2060                     !strchr(principles[i]+prefix_length, '.')) {
2061                         /* found an alternate (short) name for the domain. */
2062                         DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
2063                                  principles[i]+prefix_length, 
2064                                  ads->config.realm));
2065                         (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
2066                         break;
2067                 }
2068         }
2069         free(prefix);
2070
2071         if (!*workgroup) {
2072                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2073         }
2074         
2075         return ADS_SUCCESS;
2076 }
2077
2078 #endif