Merge from HEAD - save the type of channel used to contact the DC.
[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 expr 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 *expr,
413                                const char **attrs, void **res, 
414                                int *count, void **cookie)
415 {
416         int rc, i, version;
417         char *utf8_expr, *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_expr, expr) == (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_expr, 
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", expr, 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 expr 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 *expr,
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, expr, 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, expr, 
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 expr 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 *expr, 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, expr, 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, expr, 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 expr 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 *expr,
640                          const char **attrs, void **res)
641 {
642         struct timeval timeout;
643         int rc;
644         char *utf8_expr, *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_expr, expr) == (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_expr,
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 expr 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 *expr, 
707                       const char **attrs)
708 {
709         return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE, 
710                              expr, 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 *expr;
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(&expr, "(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, expr, attrs);
786         free(expr);
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         struct berval **ber_values = NULL;
820         char **char_values = NULL;
821
822         if (!invals) {
823                 mod_op = LDAP_MOD_DELETE;
824         } else {
825                 if (mod_op & LDAP_MOD_BVALUES)
826                         ber_values = ads_dup_values(ctx, 
827                                                 (const struct berval **)invals);
828                 else
829                         char_values = 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 = ber_values;
851         } else if (mod_op & LDAP_MOD_DELETE) {
852                 modlist[curmod]->mod_values = NULL;
853         } else {
854                 modlist[curmod]->mod_values = char_values;
855         }
856
857         modlist[curmod]->mod_op = mod_op;
858         return ADS_ERROR(LDAP_SUCCESS);
859 }
860
861 /**
862  * Add a single string value to a mod list
863  * @param ctx An initialized TALLOC_CTX
864  * @param mods An initialized ADS_MODLIST
865  * @param name The attribute name to add
866  * @param val The value to add - NULL means DELETE
867  * @return ADS STATUS indicating success of add
868  **/
869 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
870                        const char *name, const char *val)
871 {
872         const char *values[2];
873
874         values[0] = val;
875         values[1] = NULL;
876
877         if (!val)
878                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
879         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, 
880                                (const void **) values);
881 }
882
883 /**
884  * Add an array of string values to a mod list
885  * @param ctx An initialized TALLOC_CTX
886  * @param mods An initialized ADS_MODLIST
887  * @param name The attribute name to add
888  * @param vals The array of string values to add - NULL means DELETE
889  * @return ADS STATUS indicating success of add
890  **/
891 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
892                            const char *name, const char **vals)
893 {
894         if (!vals)
895                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
896         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, 
897                                name, (const void **) vals);
898 }
899
900 /**
901  * Add a single ber-encoded value to a mod list
902  * @param ctx An initialized TALLOC_CTX
903  * @param mods An initialized ADS_MODLIST
904  * @param name The attribute name to add
905  * @param val The value to add - NULL means DELETE
906  * @return ADS STATUS indicating success of add
907  **/
908 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
909                               const char *name, const struct berval *val)
910 {
911         const struct berval *values[2];
912
913         values[0] = val;
914         values[1] = NULL;
915         if (!val)
916                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
917         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
918                                name, (const void **) values);
919 }
920
921 /**
922  * Perform an ldap modify
923  * @param ads connection to ads server
924  * @param mod_dn DistinguishedName to modify
925  * @param mods list of modifications to perform
926  * @return status of modify
927  **/
928 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
929 {
930         int ret,i;
931         char *utf8_dn = NULL;
932         /* 
933            this control is needed to modify that contains a currently 
934            non-existent attribute (but allowable for the object) to run
935         */
936         LDAPControl PermitModify = {
937                 ADS_PERMIT_MODIFY_OID,
938                 {0, NULL},
939                 (char) 1};
940         LDAPControl *controls[2];
941
942         controls[0] = &PermitModify;
943         controls[1] = NULL;
944
945         if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
946                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
947         }
948
949         /* find the end of the list, marked by NULL or -1 */
950         for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
951         /* make sure the end of the list is NULL */
952         mods[i] = NULL;
953         ret = ldap_modify_ext_s(ads->ld, utf8_dn,
954                                 (LDAPMod **) mods, controls, NULL);
955         SAFE_FREE(utf8_dn);
956         return ADS_ERROR(ret);
957 }
958
959 /**
960  * Perform an ldap add
961  * @param ads connection to ads server
962  * @param new_dn DistinguishedName to add
963  * @param mods list of attributes and values for DN
964  * @return status of add
965  **/
966 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
967 {
968         int ret, i;
969         char *utf8_dn = NULL;
970
971         if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
972                 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
973                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
974         }
975         
976         /* find the end of the list, marked by NULL or -1 */
977         for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
978         /* make sure the end of the list is NULL */
979         mods[i] = NULL;
980
981         ret = ldap_add_s(ads->ld, utf8_dn, mods);
982         SAFE_FREE(utf8_dn);
983         return ADS_ERROR(ret);
984 }
985
986 /**
987  * Delete a DistinguishedName
988  * @param ads connection to ads server
989  * @param new_dn DistinguishedName to delete
990  * @return status of delete
991  **/
992 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
993 {
994         int ret;
995         char *utf8_dn = NULL;
996         if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
997                 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
998                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
999         }
1000         
1001         ret = ldap_delete(ads->ld, utf8_dn);
1002         return ADS_ERROR(ret);
1003 }
1004
1005 /**
1006  * Build an org unit string
1007  *  if org unit is Computers or blank then assume a container, otherwise
1008  *  assume a \ separated list of organisational units
1009  * @param org_unit Organizational unit
1010  * @return org unit string - caller must free
1011  **/
1012 char *ads_ou_string(const char *org_unit)
1013 {       
1014         if (!org_unit || !*org_unit || strcasecmp(org_unit, "Computers") == 0) {
1015                 return strdup("cn=Computers");
1016         }
1017
1018         return ads_build_path(org_unit, "\\/", "ou=", 1);
1019 }
1020
1021
1022
1023 /*
1024   add a machine account to the ADS server
1025 */
1026 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname, 
1027                                        uint32 account_type,
1028                                        const char *org_unit)
1029 {
1030         ADS_STATUS ret, status;
1031         char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
1032         char *ou_str;
1033         TALLOC_CTX *ctx;
1034         ADS_MODLIST mods;
1035         const char *objectClass[] = {"top", "person", "organizationalPerson",
1036                                      "user", "computer", NULL};
1037         const char *servicePrincipalName[5] = {NULL, NULL, NULL, NULL, NULL};
1038         char *psp, *psp2;
1039         unsigned acct_control;
1040
1041         if (!(ctx = talloc_init("machine_account")))
1042                 return ADS_ERROR(LDAP_NO_MEMORY);
1043
1044         ret = ADS_ERROR(LDAP_NO_MEMORY);
1045
1046         if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", hostname)))
1047                 goto done;
1048         if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
1049                 goto done;
1050         ou_str = ads_ou_string(org_unit);
1051         if (!ou_str) {
1052                 DEBUG(1, ("ads_ou_string returned NULL (malloc failure?)\n"));
1053                 goto done;
1054         }
1055         new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", hostname, ou_str, 
1056                                  ads->config.bind_path);
1057         servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", hostname);
1058         psp = talloc_asprintf(ctx, "HOST/%s.%s", 
1059                               hostname, 
1060                               ads->config.realm);
1061         strlower(&psp[5]);
1062         servicePrincipalName[1] = psp;
1063         servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", hostname);
1064         psp2 = talloc_asprintf(ctx, "CIFS/%s.%s", 
1065                                hostname, 
1066                                ads->config.realm);
1067         strlower(&psp2[5]);
1068         servicePrincipalName[3] = psp2;
1069
1070         free(ou_str);
1071         if (!new_dn)
1072                 goto done;
1073
1074         if (!(samAccountName = talloc_asprintf(ctx, "%s$", hostname)))
1075                 goto done;
1076
1077         acct_control = account_type | UF_DONT_EXPIRE_PASSWD;
1078 #ifndef ENCTYPE_ARCFOUR_HMAC
1079         acct_control |= UF_USE_DES_KEY_ONLY;
1080 #endif
1081         if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control)))
1082                 goto done;
1083
1084         if (!(mods = ads_init_mods(ctx)))
1085                 goto done;
1086         
1087         ads_mod_str(ctx, &mods, "cn", hostname);
1088         ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1089         ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1090         ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1091         ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1092         ads_mod_str(ctx, &mods, "dNSHostName", hostname);
1093         ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1094         ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1095         ads_mod_str(ctx, &mods, "operatingSystemVersion", VERSION);
1096
1097         ret = ads_gen_add(ads, new_dn, mods);
1098
1099         if (!ADS_ERR_OK(ret))
1100                 goto done;
1101
1102         /* Do not fail if we can't set security descriptor
1103          * it shouldn't be mandatory and probably we just 
1104          * don't have enough rights to do it.
1105          */
1106         status = ads_set_machine_sd(ads, hostname, new_dn);
1107
1108         if (!ADS_ERR_OK(status)) {
1109                 DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1110                                 ads_errstr(status)));
1111         }
1112 done:
1113         talloc_destroy(ctx);
1114         return ret;
1115 }
1116
1117 /*
1118   dump a binary result from ldap
1119 */
1120 static void dump_binary(const char *field, struct berval **values)
1121 {
1122         int i, j;
1123         for (i=0; values[i]; i++) {
1124                 printf("%s: ", field);
1125                 for (j=0; j<values[i]->bv_len; j++) {
1126                         printf("%02X", (unsigned char)values[i]->bv_val[j]);
1127                 }
1128                 printf("\n");
1129         }
1130 }
1131
1132 struct uuid {
1133         uint32   i1;
1134         uint16   i2;
1135         uint16   i3;
1136         uint8    s[8];
1137 };
1138
1139 static void dump_guid(const char *field, struct berval **values)
1140 {
1141         int i;
1142         GUID guid;
1143         for (i=0; values[i]; i++) {
1144                 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1145                 printf("%s: %s\n", field, uuid_string_static(guid));
1146         }
1147 }
1148
1149 /*
1150   dump a sid result from ldap
1151 */
1152 static void dump_sid(const char *field, struct berval **values)
1153 {
1154         int i;
1155         for (i=0; values[i]; i++) {
1156                 DOM_SID sid;
1157                 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1158                 printf("%s: %s\n", field, sid_string_static(&sid));
1159         }
1160 }
1161
1162 /*
1163   dump ntSecurityDescriptor
1164 */
1165 static void dump_sd(const char *filed, struct berval **values)
1166 {
1167         prs_struct ps;
1168         
1169         SEC_DESC   *psd = 0;
1170         TALLOC_CTX *ctx = 0;
1171
1172         if (!(ctx = talloc_init("sec_io_desc")))
1173                 return;
1174
1175         /* prepare data */
1176         prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1177         prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1178         prs_set_offset(&ps,0);
1179
1180         /* parse secdesc */
1181         if (!sec_io_desc("sd", &psd, &ps, 1)) {
1182                 prs_mem_free(&ps);
1183                 talloc_destroy(ctx);
1184                 return;
1185         }
1186         if (psd) ads_disp_sd(psd);
1187
1188         prs_mem_free(&ps);
1189         talloc_destroy(ctx);
1190 }
1191
1192 /*
1193   dump a string result from ldap
1194 */
1195 static void dump_string(const char *field, char **values)
1196 {
1197         int i;
1198         for (i=0; values[i]; i++) {
1199                 printf("%s: %s\n", field, values[i]);
1200         }
1201 }
1202
1203 /*
1204   dump a field from LDAP on stdout
1205   used for debugging
1206 */
1207
1208 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1209 {
1210         const struct {
1211                 const char *name;
1212                 BOOL string;
1213                 void (*handler)(const char *, struct berval **);
1214         } handlers[] = {
1215                 {"objectGUID", False, dump_guid},
1216                 {"nTSecurityDescriptor", False, dump_sd},
1217                 {"dnsRecord", False, dump_binary},
1218                 {"objectSid", False, dump_sid},
1219                 {"tokenGroups", False, dump_sid},
1220                 {NULL, True, NULL}
1221         };
1222         int i;
1223
1224         if (!field) { /* must be end of an entry */
1225                 printf("\n");
1226                 return False;
1227         }
1228
1229         for (i=0; handlers[i].name; i++) {
1230                 if (StrCaseCmp(handlers[i].name, field) == 0) {
1231                         if (!values) /* first time, indicate string or not */
1232                                 return handlers[i].string;
1233                         handlers[i].handler(field, (struct berval **) values);
1234                         break;
1235                 }
1236         }
1237         if (!handlers[i].name) {
1238                 if (!values) /* first time, indicate string conversion */
1239                         return True;
1240                 dump_string(field, (char **)values);
1241         }
1242         return False;
1243 }
1244
1245 /**
1246  * Dump a result from LDAP on stdout
1247  *  used for debugging
1248  * @param ads connection to ads server
1249  * @param res Results to dump
1250  **/
1251
1252 void ads_dump(ADS_STRUCT *ads, void *res)
1253 {
1254         ads_process_results(ads, res, ads_dump_field, NULL);
1255 }
1256
1257 /**
1258  * Walk through results, calling a function for each entry found.
1259  *  The function receives a field name, a berval * array of values,
1260  *  and a data area passed through from the start.  The function is
1261  *  called once with null for field and values at the end of each
1262  *  entry.
1263  * @param ads connection to ads server
1264  * @param res Results to process
1265  * @param fn Function for processing each result
1266  * @param data_area user-defined area to pass to function
1267  **/
1268 void ads_process_results(ADS_STRUCT *ads, void *res,
1269                          BOOL(*fn)(char *, void **, void *),
1270                          void *data_area)
1271 {
1272         void *msg;
1273         TALLOC_CTX *ctx;
1274
1275         if (!(ctx = talloc_init("ads_process_results")))
1276                 return;
1277
1278         for (msg = ads_first_entry(ads, res); msg; 
1279              msg = ads_next_entry(ads, msg)) {
1280                 char *utf8_field;
1281                 BerElement *b;
1282         
1283                 for (utf8_field=ldap_first_attribute(ads->ld,
1284                                                      (LDAPMessage *)msg,&b); 
1285                      utf8_field;
1286                      utf8_field=ldap_next_attribute(ads->ld,
1287                                                     (LDAPMessage *)msg,b)) {
1288                         struct berval **ber_vals;
1289                         char **str_vals, **utf8_vals;
1290                         char *field;
1291                         BOOL string; 
1292
1293                         pull_utf8_talloc(ctx, &field, utf8_field);
1294                         string = fn(field, NULL, data_area);
1295
1296                         if (string) {
1297                                 utf8_vals = ldap_get_values(ads->ld,
1298                                                  (LDAPMessage *)msg, field);
1299                                 str_vals = ads_pull_strvals(ctx, 
1300                                                   (const char **) utf8_vals);
1301                                 fn(field, (void **) str_vals, data_area);
1302                                 ldap_value_free(utf8_vals);
1303                         } else {
1304                                 ber_vals = ldap_get_values_len(ads->ld, 
1305                                                  (LDAPMessage *)msg, field);
1306                                 fn(field, (void **) ber_vals, data_area);
1307
1308                                 ldap_value_free_len(ber_vals);
1309                         }
1310                         ldap_memfree(utf8_field);
1311                 }
1312                 ber_free(b, 0);
1313                 talloc_destroy_pool(ctx);
1314                 fn(NULL, NULL, data_area); /* completed an entry */
1315
1316         }
1317         talloc_destroy(ctx);
1318 }
1319
1320 /**
1321  * count how many replies are in a LDAPMessage
1322  * @param ads connection to ads server
1323  * @param res Results to count
1324  * @return number of replies
1325  **/
1326 int ads_count_replies(ADS_STRUCT *ads, void *res)
1327 {
1328         return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1329 }
1330
1331 /**
1332  * Join a machine to a realm
1333  *  Creates the machine account and sets the machine password
1334  * @param ads connection to ads server
1335  * @param hostname name of host to add
1336  * @param org_unit Organizational unit to place machine in
1337  * @return status of join
1338  **/
1339 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname, 
1340                           uint32 account_type, const char *org_unit)
1341 {
1342         ADS_STATUS status;
1343         LDAPMessage *res;
1344         char *host;
1345
1346         /* hostname must be lowercase */
1347         host = strdup(hostname);
1348         strlower(host);
1349
1350         status = ads_find_machine_acct(ads, (void **)&res, host);
1351         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1352                 DEBUG(0, ("Host account for %s already exists - deleting old account\n", host));
1353                 status = ads_leave_realm(ads, host);
1354                 if (!ADS_ERR_OK(status)) {
1355                         DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n", 
1356                                   host, ads->config.realm));
1357                         return status;
1358                 }
1359         }
1360
1361         status = ads_add_machine_acct(ads, host, account_type, org_unit);
1362         if (!ADS_ERR_OK(status)) {
1363                 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
1364                 return status;
1365         }
1366
1367         status = ads_find_machine_acct(ads, (void **)&res, host);
1368         if (!ADS_ERR_OK(status)) {
1369                 DEBUG(0, ("Host account test failed\n"));
1370                 return status;
1371         }
1372
1373         free(host);
1374
1375         return status;
1376 }
1377
1378 /**
1379  * Delete a machine from the realm
1380  * @param ads connection to ads server
1381  * @param hostname Machine to remove
1382  * @return status of delete
1383  **/
1384 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1385 {
1386         ADS_STATUS status;
1387         void *res;
1388         char *hostnameDN, *host; 
1389         int rc;
1390
1391         /* hostname must be lowercase */
1392         host = strdup(hostname);
1393         strlower(host);
1394
1395         status = ads_find_machine_acct(ads, &res, host);
1396         if (!ADS_ERR_OK(status)) {
1397             DEBUG(0, ("Host account for %s does not exist.\n", host));
1398             return status;
1399         }
1400
1401         hostnameDN = ads_get_dn(ads, (LDAPMessage *)res);
1402         rc = ldap_delete_s(ads->ld, hostnameDN);
1403         ads_memfree(ads, hostnameDN);
1404         if (rc != LDAP_SUCCESS) {
1405                 return ADS_ERROR(rc);
1406         }
1407
1408         status = ads_find_machine_acct(ads, &res, host);
1409         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1410                 DEBUG(0, ("Failed to remove host account.\n"));
1411                 return status;
1412         }
1413
1414         free(host);
1415
1416         return status;
1417 }
1418
1419 /**
1420  * add machine account to existing security descriptor 
1421  * @param ads connection to ads server
1422  * @param hostname machine to add
1423  * @param dn DN of security descriptor
1424  * @return status
1425  **/
1426 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1427 {
1428         const char     *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
1429         char           *expr     = 0;
1430         size_t          sd_size = 0;
1431         struct berval   bval = {0, NULL};
1432         prs_struct      ps_wire;
1433         char           *escaped_hostname = escape_ldap_string_alloc(hostname);
1434
1435         LDAPMessage *res  = 0;
1436         LDAPMessage *msg  = 0;
1437         ADS_MODLIST  mods = 0;
1438
1439         NTSTATUS    status;
1440         ADS_STATUS  ret;
1441         DOM_SID     sid;
1442         SEC_DESC   *psd = NULL;
1443         TALLOC_CTX *ctx = NULL; 
1444
1445         /* Avoid segmentation fault in prs_mem_free if
1446          * we have to bail out before prs_init */
1447         ps_wire.is_dynamic = False;
1448
1449         if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1450
1451         ret = ADS_ERROR(LDAP_SUCCESS);
1452
1453         if (!escaped_hostname) {
1454                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1455         }
1456
1457         if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) {
1458                 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1459                 SAFE_FREE(escaped_hostname);
1460                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1461         }
1462
1463         SAFE_FREE(escaped_hostname);
1464
1465         ret = ads_search(ads, (void *) &res, expr, attrs);
1466
1467         if (!ADS_ERR_OK(ret)) return ret;
1468
1469         if ( !(msg = ads_first_entry(ads, res) )) {
1470                 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1471                 goto ads_set_sd_error;
1472         }
1473
1474         if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
1475                 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1476                 goto ads_set_sd_error;
1477         }
1478
1479         if (!(ctx = talloc_init("sec_io_desc"))) {
1480                 ret =  ADS_ERROR(LDAP_NO_MEMORY);
1481                 goto ads_set_sd_error;
1482         }
1483
1484         if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1485                 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1486                 goto ads_set_sd_error;
1487         }
1488
1489         status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1490
1491         if (!NT_STATUS_IS_OK(status)) {
1492                 ret = ADS_ERROR_NT(status);
1493                 goto ads_set_sd_error;
1494         }
1495
1496         if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
1497                 ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1498         }
1499
1500         if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1501                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1502                 goto ads_set_sd_error;
1503         }
1504
1505 #if 0
1506         file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1507 #endif
1508         if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1509
1510         bval.bv_len = prs_offset(&ps_wire);
1511         bval.bv_val = talloc(ctx, bval.bv_len);
1512         if (!bval.bv_val) {
1513                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1514                 goto ads_set_sd_error;
1515         }
1516
1517         prs_set_offset(&ps_wire, 0);
1518
1519         if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
1520                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1521                 goto ads_set_sd_error;          
1522         }
1523
1524         ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
1525         if (ADS_ERR_OK(ret)) {
1526                 ret = ads_gen_mod(ads, dn, mods);
1527         }
1528
1529 ads_set_sd_error:
1530         ads_msgfree(ads, res);
1531         prs_mem_free(&ps_wire);
1532         talloc_destroy(ctx);
1533         return ret;
1534 }
1535
1536 /**
1537  * pull the first entry from a ADS result
1538  * @param ads connection to ads server
1539  * @param res Results of search
1540  * @return first entry from result
1541  **/
1542 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1543 {
1544         return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1545 }
1546
1547 /**
1548  * pull the next entry from a ADS result
1549  * @param ads connection to ads server
1550  * @param res Results of search
1551  * @return next entry from result
1552  **/
1553 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1554 {
1555         return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1556 }
1557
1558 /**
1559  * pull a single string from a ADS result
1560  * @param ads connection to ads server
1561  * @param mem_ctx TALLOC_CTX to use for allocating result string
1562  * @param msg Results of search
1563  * @param field Attribute to retrieve
1564  * @return Result string in talloc context
1565  **/
1566 char *ads_pull_string(ADS_STRUCT *ads, 
1567                       TALLOC_CTX *mem_ctx, void *msg, const char *field)
1568 {
1569         char **values;
1570         char *ret = NULL;
1571         char *ux_string;
1572         size_t rc;
1573
1574         values = ldap_get_values(ads->ld, msg, field);
1575         if (!values)
1576                 return NULL;
1577         
1578         if (values[0]) {
1579                 rc = pull_utf8_talloc(mem_ctx, &ux_string, 
1580                                       values[0]);
1581                 if (rc != (size_t)-1)
1582                         ret = ux_string;
1583                 
1584         }
1585         ldap_value_free(values);
1586         return ret;
1587 }
1588
1589 /**
1590  * pull an array of strings from a ADS result
1591  * @param ads connection to ads server
1592  * @param mem_ctx TALLOC_CTX to use for allocating result string
1593  * @param msg Results of search
1594  * @param field Attribute to retrieve
1595  * @return Result strings in talloc context
1596  **/
1597 char **ads_pull_strings(ADS_STRUCT *ads, 
1598                        TALLOC_CTX *mem_ctx, void *msg, const char *field)
1599 {
1600         char **values;
1601         char **ret = NULL;
1602         int i, n;
1603
1604         values = ldap_get_values(ads->ld, msg, field);
1605         if (!values)
1606                 return NULL;
1607
1608         for (i=0;values[i];i++)
1609                 /* noop */ ;
1610         n = i;
1611
1612         ret = talloc(mem_ctx, sizeof(char *) * (n+1));
1613         if (!ret) {
1614                 ldap_value_free(values);
1615                 return NULL;
1616         }
1617
1618         for (i=0;i<n;i++) {
1619                 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1620                         ldap_value_free(values);
1621                         return NULL;
1622                 }
1623         }
1624         ret[i] = NULL;
1625
1626         ldap_value_free(values);
1627         return ret;
1628 }
1629
1630
1631 /**
1632  * pull a single uint32 from a ADS result
1633  * @param ads connection to ads server
1634  * @param msg Results of search
1635  * @param field Attribute to retrieve
1636  * @param v Pointer to int to store result
1637  * @return boolean inidicating success
1638 */
1639 BOOL ads_pull_uint32(ADS_STRUCT *ads, 
1640                      void *msg, const char *field, uint32 *v)
1641 {
1642         char **values;
1643
1644         values = ldap_get_values(ads->ld, msg, field);
1645         if (!values)
1646                 return False;
1647         if (!values[0]) {
1648                 ldap_value_free(values);
1649                 return False;
1650         }
1651
1652         *v = atoi(values[0]);
1653         ldap_value_free(values);
1654         return True;
1655 }
1656
1657 /**
1658  * pull a single objectGUID from an ADS result
1659  * @param ads connection to ADS server
1660  * @param msg results of search
1661  * @param guid 37-byte area to receive text guid
1662  * @return boolean indicating success
1663  **/
1664 BOOL ads_pull_guid(ADS_STRUCT *ads,
1665                    void *msg, GUID *guid)
1666 {
1667         char **values;
1668
1669         values = ldap_get_values(ads->ld, msg, "objectGUID");
1670         if (!values)
1671                 return False;
1672         
1673         if (values[0]) {
1674                 memcpy(guid, values[0], sizeof(GUID));
1675                 ldap_value_free(values);
1676                 return True;
1677         }
1678         ldap_value_free(values);
1679         return False;
1680
1681 }
1682
1683
1684 /**
1685  * pull a single DOM_SID from a ADS result
1686  * @param ads connection to ads server
1687  * @param msg Results of search
1688  * @param field Attribute to retrieve
1689  * @param sid Pointer to sid to store result
1690  * @return boolean inidicating success
1691 */
1692 BOOL ads_pull_sid(ADS_STRUCT *ads, 
1693                   void *msg, const char *field, DOM_SID *sid)
1694 {
1695         struct berval **values;
1696         BOOL ret = False;
1697
1698         values = ldap_get_values_len(ads->ld, msg, field);
1699
1700         if (!values)
1701                 return False;
1702
1703         if (values[0])
1704                 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
1705         
1706         ldap_value_free_len(values);
1707         return ret;
1708 }
1709
1710 /**
1711  * pull an array of DOM_SIDs from a ADS result
1712  * @param ads connection to ads server
1713  * @param mem_ctx TALLOC_CTX for allocating sid array
1714  * @param msg Results of search
1715  * @param field Attribute to retrieve
1716  * @param sids pointer to sid array to allocate
1717  * @return the count of SIDs pulled
1718  **/
1719 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1720                   void *msg, const char *field, DOM_SID **sids)
1721 {
1722         struct berval **values;
1723         BOOL ret;
1724         int count, i;
1725
1726         values = ldap_get_values_len(ads->ld, msg, field);
1727
1728         if (!values)
1729                 return 0;
1730
1731         for (i=0; values[i]; i++)
1732                 /* nop */ ;
1733
1734         (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
1735         if (!(*sids)) {
1736                 ldap_value_free_len(values);
1737                 return 0;
1738         }
1739
1740         count = 0;
1741         for (i=0; values[i]; i++) {
1742                 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
1743                 if (ret) {
1744                         fstring sid;
1745                         DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
1746                         count++;
1747                 }
1748         }
1749         
1750         ldap_value_free_len(values);
1751         return count;
1752 }
1753
1754 /**
1755  * pull a SEC_DESC from a ADS result
1756  * @param ads connection to ads server
1757  * @param mem_ctx TALLOC_CTX for allocating sid array
1758  * @param msg Results of search
1759  * @param field Attribute to retrieve
1760  * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
1761  * @return boolean inidicating success
1762 */
1763 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1764                   void *msg, const char *field, SEC_DESC **sd)
1765 {
1766         struct berval **values;
1767         prs_struct      ps;
1768         BOOL ret = False;
1769
1770         values = ldap_get_values_len(ads->ld, msg, field);
1771
1772         if (!values) return False;
1773
1774         if (values[0]) {
1775                 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
1776                 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1777                 prs_set_offset(&ps,0);
1778
1779                 ret = sec_io_desc("sd", sd, &ps, 1);
1780         }
1781         
1782         ldap_value_free_len(values);
1783         return ret;
1784 }
1785
1786 /* 
1787  * in order to support usernames longer than 21 characters we need to 
1788  * use both the sAMAccountName and the userPrincipalName attributes 
1789  * It seems that not all users have the userPrincipalName attribute set
1790  *
1791  * @param ads connection to ads server
1792  * @param mem_ctx TALLOC_CTX for allocating sid array
1793  * @param msg Results of search
1794  * @return the username
1795  */
1796 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
1797 {
1798         char *ret, *p;
1799
1800         ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
1801         if (ret && (p = strchr(ret, '@'))) {
1802                 *p = 0;
1803                 return ret;
1804         }
1805         return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
1806 }
1807
1808
1809 /**
1810  * find the update serial number - this is the core of the ldap cache
1811  * @param ads connection to ads server
1812  * @param ads connection to ADS server
1813  * @param usn Pointer to retrieved update serial number
1814  * @return status of search
1815  **/
1816 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
1817 {
1818         const char *attrs[] = {"highestCommittedUSN", NULL};
1819         ADS_STATUS status;
1820         void *res;
1821
1822         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1823         if (!ADS_ERR_OK(status)) return status;
1824
1825         if (ads_count_replies(ads, res) != 1) {
1826                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1827         }
1828
1829         ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
1830         ads_msgfree(ads, res);
1831         return ADS_SUCCESS;
1832 }
1833
1834 /* parse a ADS timestring - typical string is
1835    '20020917091222.0Z0' which means 09:12.22 17th September
1836    2002, timezone 0 */
1837 static time_t ads_parse_time(const char *str)
1838 {
1839         struct tm tm;
1840
1841         ZERO_STRUCT(tm);
1842
1843         if (sscanf(str, "%4d%2d%2d%2d%2d%2d", 
1844                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
1845                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
1846                 return 0;
1847         }
1848         tm.tm_year -= 1900;
1849         tm.tm_mon -= 1;
1850
1851         return timegm(&tm);
1852 }
1853
1854
1855 /**
1856  * Find the servers name and realm - this can be done before authentication 
1857  *  The ldapServiceName field on w2k  looks like this:
1858  *    vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
1859  * @param ads connection to ads server
1860  * @return status of search
1861  **/
1862 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
1863 {
1864         const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
1865         ADS_STATUS status;
1866         void *res;
1867         char *value;
1868         char *p;
1869         char *timestr;
1870         TALLOC_CTX *ctx;
1871
1872         if (!(ctx = talloc_init("ads_server_info"))) {
1873                 return ADS_ERROR(LDAP_NO_MEMORY);
1874         }
1875
1876         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1877         if (!ADS_ERR_OK(status)) return status;
1878
1879         value = ads_pull_string(ads, ctx, res, "ldapServiceName");
1880         if (!value) {
1881                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1882         }
1883
1884         timestr = ads_pull_string(ads, ctx, res, "currentTime");
1885         if (!timestr) {
1886                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1887         }
1888
1889         ldap_msgfree(res);
1890
1891         p = strchr(value, ':');
1892         if (!p) {
1893                 talloc_destroy(ctx);
1894                 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' so was deemed invalid\n"));
1895                 return ADS_ERROR(LDAP_DECODING_ERROR);
1896         }
1897
1898         SAFE_FREE(ads->config.ldap_server_name);
1899
1900         ads->config.ldap_server_name = strdup(p+1);
1901         p = strchr(ads->config.ldap_server_name, '$');
1902         if (!p || p[1] != '@') {
1903                 talloc_destroy(ctx);
1904                 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@' so was deemed invalid\n", ads->config.ldap_server_name));
1905                 SAFE_FREE(ads->config.ldap_server_name);
1906                 return ADS_ERROR(LDAP_DECODING_ERROR);
1907         }
1908
1909         *p = 0;
1910
1911         SAFE_FREE(ads->config.realm);
1912         SAFE_FREE(ads->config.bind_path);
1913
1914         ads->config.realm = strdup(p+2);
1915         ads->config.bind_path = ads_build_dn(ads->config.realm);
1916
1917         DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n", 
1918                  ads->config.ldap_server_name, ads->config.realm,
1919                  ads->config.bind_path));
1920
1921         ads->config.current_time = ads_parse_time(timestr);
1922
1923         if (ads->config.current_time != 0) {
1924                 ads->auth.time_offset = ads->config.current_time - time(NULL);
1925                 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
1926         }
1927
1928         talloc_destroy(ctx);
1929
1930         return ADS_SUCCESS;
1931 }
1932
1933
1934 /**
1935  * find the list of trusted domains
1936  * @param ads connection to ads server
1937  * @param mem_ctx TALLOC_CTX for allocating results
1938  * @param num_trusts pointer to number of trusts
1939  * @param names pointer to trusted domain name list
1940  * @param sids pointer to list of sids of trusted domains
1941  * @return the count of SIDs pulled
1942  **/
1943 ADS_STATUS ads_trusted_domains(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, 
1944                                int *num_trusts, 
1945                                char ***names, 
1946                                char ***alt_names,
1947                                DOM_SID **sids)
1948 {
1949         const char *attrs[] = {"name", "flatname", "securityIdentifier", 
1950                                "trustDirection", NULL};
1951         ADS_STATUS status;
1952         void *res, *msg;
1953         int count, i;
1954
1955         *num_trusts = 0;
1956
1957         status = ads_search(ads, &res, "(objectcategory=trustedDomain)", attrs);
1958         if (!ADS_ERR_OK(status)) return status;
1959
1960         count = ads_count_replies(ads, res);
1961         if (count == 0) {
1962                 ads_msgfree(ads, res);
1963                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1964         }
1965
1966         (*names) = talloc(mem_ctx, sizeof(char *) * count);
1967         (*alt_names) = talloc(mem_ctx, sizeof(char *) * count);
1968         (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * count);
1969         if (! *names || ! *sids) return ADS_ERROR(LDAP_NO_MEMORY);
1970
1971         for (i=0, msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
1972                 uint32 direction;
1973
1974                 /* direction is a 2 bit bitfield, 1 means they trust us 
1975                    but we don't trust them, so we should not list them
1976                    as users from that domain can't login */
1977                 if (ads_pull_uint32(ads, msg, "trustDirection", &direction) &&
1978                     direction == 1) {
1979                         continue;
1980                 }
1981                 
1982                 (*names)[i] = ads_pull_string(ads, mem_ctx, msg, "name");
1983                 (*alt_names)[i] = ads_pull_string(ads, mem_ctx, msg, "flatname");
1984
1985                 if ((*alt_names)[i] && (*alt_names)[i][0]) {
1986                         /* we prefer the flatname as the primary name
1987                            for consistency with RPC */
1988                         char *name = (*alt_names)[i];
1989                         (*alt_names)[i] = (*names)[i];
1990                         (*names)[i] = name;
1991                 }
1992                 if (ads_pull_sid(ads, msg, "securityIdentifier", &(*sids)[i])) {
1993                         i++;
1994                 }
1995         }
1996
1997         ads_msgfree(ads, res);
1998
1999         *num_trusts = i;
2000
2001         return ADS_SUCCESS;
2002 }
2003
2004 /**
2005  * find the domain sid for our domain
2006  * @param ads connection to ads server
2007  * @param sid Pointer to domain sid
2008  * @return status of search
2009  **/
2010 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2011 {
2012         const char *attrs[] = {"objectSid", NULL};
2013         void *res;
2014         ADS_STATUS rc;
2015
2016         rc = ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
2017                            attrs, &res);
2018         if (!ADS_ERR_OK(rc)) return rc;
2019         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2020                 return ADS_ERROR_SYSTEM(ENOENT);
2021         }
2022         ads_msgfree(ads, res);
2023         
2024         return ADS_SUCCESS;
2025 }
2026
2027 /* this is rather complex - we need to find the allternate (netbios) name
2028    for the domain, but there isn't a simple query to do this. Instead
2029    we look for the principle names on the DCs account and find one that has 
2030    the right form, then extract the netbios name of the domain from that
2031
2032    NOTE! better method is this:
2033
2034 bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))'  nETBIOSName 
2035
2036 but you need to force the bind path to match the configurationNamingContext from the rootDSE
2037
2038 */
2039 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **workgroup)
2040 {
2041         char *expr;
2042         ADS_STATUS rc;
2043         char **principles;
2044         char *prefix;
2045         int prefix_length;
2046         int i;
2047         void *res;
2048         const char *attrs[] = {"servicePrincipalName", NULL};
2049
2050         (*workgroup) = NULL;
2051
2052         asprintf(&expr, "(&(objectclass=computer)(dnshostname=%s.%s))", 
2053                  ads->config.ldap_server_name, ads->config.realm);
2054         rc = ads_search(ads, &res, expr, attrs);
2055         free(expr);
2056
2057         if (!ADS_ERR_OK(rc)) {
2058                 return rc;
2059         }
2060
2061         principles = ads_pull_strings(ads, mem_ctx, res, "servicePrincipalName");
2062
2063         ads_msgfree(ads, res);
2064
2065         if (!principles) {
2066                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2067         }
2068
2069         asprintf(&prefix, "HOST/%s.%s/", 
2070                  ads->config.ldap_server_name, 
2071                  ads->config.realm);
2072
2073         prefix_length = strlen(prefix);
2074
2075         for (i=0;principles[i]; i++) {
2076                 if (strncasecmp(principles[i], prefix, prefix_length) == 0 &&
2077                     strcasecmp(ads->config.realm, principles[i]+prefix_length) != 0 &&
2078                     !strchr(principles[i]+prefix_length, '.')) {
2079                         /* found an alternate (short) name for the domain. */
2080                         DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
2081                                  principles[i]+prefix_length, 
2082                                  ads->config.realm));
2083                         (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
2084                         break;
2085                 }
2086         }
2087         free(prefix);
2088
2089         if (!*workgroup) {
2090                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2091         }
2092         
2093         return ADS_SUCCESS;
2094 }
2095
2096 #endif