This commit was manufactured by cvs2svn to create branch 'SAMBA_3_0'.(This used to...
[samba.git] / source3 / libads / ldap.c
1 /* 
2    Unix SMB/CIFS implementation.
3    ads (active directory) utility library
4    Copyright (C) Andrew Tridgell 2001
5    Copyright (C) Remus Koos 2001
6    Copyright (C) Jim McDonough 2002
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24
25 #ifdef HAVE_LDAP
26
27 /**
28  * @file ldap.c
29  * @brief basic ldap client-side routines for ads server communications
30  *
31  * The routines contained here should do the necessary ldap calls for
32  * ads setups.
33  * 
34  * Important note: attribute names passed into ads_ routines must
35  * already be in UTF-8 format.  We do not convert them because in almost
36  * all cases, they are just ascii (which is represented with the same
37  * codepoints in UTF-8).  This may have to change at some point
38  **/
39
40
41 /*
42   try a connection to a given ldap server, returning True and setting the servers IP
43   in the ads struct if successful
44  */
45 static BOOL ads_try_connect(ADS_STRUCT *ads, const char *server, unsigned port)
46 {
47         char *srv;
48
49         if (!server || !*server) {
50                 return False;
51         }
52
53         DEBUG(5,("ads_try_connect: trying ldap server '%s' port %u\n", server, port));
54
55         /* this copes with inet_ntoa brokenness */
56         srv = strdup(server);
57
58         ads->ld = ldap_open(srv, port);
59         if (!ads->ld) {
60                 free(srv);
61                 return False;
62         }
63         ads->ldap_port = port;
64         ads->ldap_ip = *interpret_addr2(srv);
65         free(srv);
66
67         return True;
68 }
69
70 /*
71   try a connection to a given ldap server, based on URL, returning True if successful
72  */
73 static BOOL ads_try_connect_uri(ADS_STRUCT *ads)
74 {
75 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
76         DEBUG(5,("ads_try_connect: trying ldap server at URI '%s'\n", 
77                  ads->server.ldap_uri));
78
79         
80         if (ldap_initialize((LDAP**)&(ads->ld), ads->server.ldap_uri) == LDAP_SUCCESS) {
81                 return True;
82         }
83         DEBUG(0, ("ldap_initialize: %s\n", strerror(errno)));
84         
85 #else 
86
87         DEBUG(1, ("no URL support in LDAP libs!\n"));
88 #endif
89
90         return False;
91 }
92
93 /* used by the IP comparison function */
94 struct ldap_ip {
95         struct in_addr ip;
96         unsigned port;
97 };
98
99 /* compare 2 ldap IPs by nearness to our interfaces - used in qsort */
100 static int ldap_ip_compare(struct ldap_ip *ip1, struct ldap_ip *ip2)
101 {
102         return ip_compare(&ip1->ip, &ip2->ip);
103 }
104
105 /* try connecting to a ldap server via DNS */
106 static BOOL ads_try_dns(ADS_STRUCT *ads)
107 {
108         const char *c_realm;
109         const char *ptr;
110         char *realm;
111         char *list = NULL;
112         pstring tok;
113         struct ldap_ip *ip_list;
114         int count, i=0;
115
116         c_realm = ads->server.realm;
117         if (!c_realm || !*c_realm) {
118                 c_realm = lp_realm();
119         }
120         if (!c_realm || !*c_realm) {
121                 c_realm = ads->server.workgroup;
122         }
123         if (!c_realm || !*c_realm) {
124                 c_realm = lp_workgroup();
125         }
126         if (!c_realm) {
127                 return False;
128         }
129         realm = smb_xstrdup(c_realm);
130
131         DEBUG(6,("ads_try_dns: looking for realm '%s'\n", realm));
132         if (ldap_domain2hostlist(realm, &list) != LDAP_SUCCESS) {
133                 SAFE_FREE(realm);
134                 return False;
135         }
136
137         DEBUG(6,("ads_try_dns: ldap realm '%s' host list '%s'\n", realm, list));
138         SAFE_FREE(realm);
139
140         count = count_chars(list, ' ') + 1;
141         ip_list = malloc(count * sizeof(struct ldap_ip));
142         if (!ip_list) {
143                 return False;
144         }
145
146         ptr = list;
147         while (next_token(&ptr, tok, " ", sizeof(tok))) {
148                 unsigned port = LDAP_PORT;
149                 char *p = strchr(tok, ':');
150                 if (p) {
151                         *p = 0;
152                         port = atoi(p+1);
153                 }
154                 ip_list[i].ip = *interpret_addr2(tok);
155                 ip_list[i].port = port;
156                 if (!is_zero_ip(ip_list[i].ip)) {
157                         i++;
158                 }
159         }
160         free(list);
161
162         count = i;
163
164         /* we sort the list of addresses by closeness to our interfaces. This
165            tries to prevent us using a DC on the other side of the country */
166         if (count > 1) {
167                 qsort(ip_list, count, sizeof(struct ldap_ip), 
168                       QSORT_CAST ldap_ip_compare);      
169         }
170
171         for (i=0;i<count;i++) {
172                 if (ads_try_connect(ads, inet_ntoa(ip_list[i].ip), ip_list[i].port)) {
173                         free(ip_list);
174                         return True;
175                 }
176         }
177
178         SAFE_FREE(ip_list);
179         return False;
180 }
181
182 /* try connecting to a ldap server via netbios */
183 static BOOL ads_try_netbios(ADS_STRUCT *ads)
184 {
185         struct in_addr *ip_list, pdc_ip;
186         int count;
187         int i;
188         const char *workgroup = ads->server.workgroup;
189         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         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                                        const char *org_unit)
1028 {
1029         ADS_STATUS ret, status;
1030         char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
1031         char *ou_str;
1032         TALLOC_CTX *ctx;
1033         ADS_MODLIST mods;
1034         const char *objectClass[] = {"top", "person", "organizationalPerson",
1035                                      "user", "computer", NULL};
1036         const char *servicePrincipalName[5] = {NULL, NULL, NULL, NULL, NULL};
1037         char *psp, *psp2;
1038         unsigned acct_control;
1039
1040         if (!(ctx = talloc_init("machine_account")))
1041                 return ADS_ERROR(LDAP_NO_MEMORY);
1042
1043         ret = ADS_ERROR(LDAP_NO_MEMORY);
1044
1045         if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", hostname)))
1046                 goto done;
1047         if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
1048                 goto done;
1049         ou_str = ads_ou_string(org_unit);
1050         if (!ou_str) {
1051                 DEBUG(1, ("ads_ou_string returned NULL (malloc failure?)\n"));
1052                 goto done;
1053         }
1054         new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", hostname, ou_str, 
1055                                  ads->config.bind_path);
1056         servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", hostname);
1057         psp = talloc_asprintf(ctx, "HOST/%s.%s", 
1058                               hostname, 
1059                               ads->config.realm);
1060         strlower(&psp[5]);
1061         servicePrincipalName[1] = psp;
1062         servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", hostname);
1063         psp2 = talloc_asprintf(ctx, "CIFS/%s.%s", 
1064                                hostname, 
1065                                ads->config.realm);
1066         strlower(&psp2[5]);
1067         servicePrincipalName[3] = psp2;
1068
1069         free(ou_str);
1070         if (!new_dn)
1071                 goto done;
1072
1073         if (!(samAccountName = talloc_asprintf(ctx, "%s$", hostname)))
1074                 goto done;
1075
1076         acct_control = UF_WORKSTATION_TRUST_ACCOUNT | UF_DONT_EXPIRE_PASSWD;
1077 #ifndef ENCTYPE_ARCFOUR_HMAC
1078         acct_control |= UF_USE_DES_KEY_ONLY;
1079 #endif
1080         if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control)))
1081                 goto done;
1082
1083         if (!(mods = ads_init_mods(ctx)))
1084                 goto done;
1085         
1086         ads_mod_str(ctx, &mods, "cn", hostname);
1087         ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1088         ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1089         ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1090         ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1091         ads_mod_str(ctx, &mods, "dNSHostName", hostname);
1092         ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1093         ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1094         ads_mod_str(ctx, &mods, "operatingSystemVersion", VERSION);
1095
1096         ret = ads_gen_add(ads, new_dn, mods);
1097
1098         if (!ADS_ERR_OK(ret))
1099                 goto done;
1100
1101         /* Do not fail if we can't set security descriptor
1102          * it shouldn't be mandatory and probably we just 
1103          * don't have enough rights to do it.
1104          */
1105         status = ads_set_machine_sd(ads, hostname, new_dn);
1106
1107         if (!ADS_ERR_OK(status)) {
1108                 DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1109                                 ads_errstr(status)));
1110         }
1111 done:
1112         talloc_destroy(ctx);
1113         return ret;
1114 }
1115
1116 /*
1117   dump a binary result from ldap
1118 */
1119 static void dump_binary(const char *field, struct berval **values)
1120 {
1121         int i, j;
1122         for (i=0; values[i]; i++) {
1123                 printf("%s: ", field);
1124                 for (j=0; j<values[i]->bv_len; j++) {
1125                         printf("%02X", (unsigned char)values[i]->bv_val[j]);
1126                 }
1127                 printf("\n");
1128         }
1129 }
1130
1131 struct uuid {
1132         uint32   i1;
1133         uint16   i2;
1134         uint16   i3;
1135         uint8    s[8];
1136 };
1137
1138 static void dump_guid(const char *field, struct berval **values)
1139 {
1140         int i;
1141         GUID guid;
1142         for (i=0; values[i]; i++) {
1143                 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1144                 printf("%s: %s\n", field, uuid_string_static(guid));
1145         }
1146 }
1147
1148 /*
1149   dump a sid result from ldap
1150 */
1151 static void dump_sid(const char *field, struct berval **values)
1152 {
1153         int i;
1154         for (i=0; values[i]; i++) {
1155                 DOM_SID sid;
1156                 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1157                 printf("%s: %s\n", field, sid_string_static(&sid));
1158         }
1159 }
1160
1161 /*
1162   dump ntSecurityDescriptor
1163 */
1164 static void dump_sd(const char *filed, struct berval **values)
1165 {
1166         prs_struct ps;
1167         
1168         SEC_DESC   *psd = 0;
1169         TALLOC_CTX *ctx = 0;
1170
1171         if (!(ctx = talloc_init("sec_io_desc")))
1172                 return;
1173
1174         /* prepare data */
1175         prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1176         prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1177         prs_set_offset(&ps,0);
1178
1179         /* parse secdesc */
1180         if (!sec_io_desc("sd", &psd, &ps, 1)) {
1181                 prs_mem_free(&ps);
1182                 talloc_destroy(ctx);
1183                 return;
1184         }
1185         if (psd) ads_disp_sd(psd);
1186
1187         prs_mem_free(&ps);
1188         talloc_destroy(ctx);
1189 }
1190
1191 /*
1192   dump a string result from ldap
1193 */
1194 static void dump_string(const char *field, char **values)
1195 {
1196         int i;
1197         for (i=0; values[i]; i++) {
1198                 printf("%s: %s\n", field, values[i]);
1199         }
1200 }
1201
1202 /*
1203   dump a field from LDAP on stdout
1204   used for debugging
1205 */
1206
1207 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1208 {
1209         const struct {
1210                 const char *name;
1211                 BOOL string;
1212                 void (*handler)(const char *, struct berval **);
1213         } handlers[] = {
1214                 {"objectGUID", False, dump_guid},
1215                 {"nTSecurityDescriptor", False, dump_sd},
1216                 {"dnsRecord", False, dump_binary},
1217                 {"objectSid", False, dump_sid},
1218                 {"tokenGroups", False, dump_sid},
1219                 {NULL, True, NULL}
1220         };
1221         int i;
1222
1223         if (!field) { /* must be end of an entry */
1224                 printf("\n");
1225                 return False;
1226         }
1227
1228         for (i=0; handlers[i].name; i++) {
1229                 if (StrCaseCmp(handlers[i].name, field) == 0) {
1230                         if (!values) /* first time, indicate string or not */
1231                                 return handlers[i].string;
1232                         handlers[i].handler(field, (struct berval **) values);
1233                         break;
1234                 }
1235         }
1236         if (!handlers[i].name) {
1237                 if (!values) /* first time, indicate string conversion */
1238                         return True;
1239                 dump_string(field, (char **)values);
1240         }
1241         return False;
1242 }
1243
1244 /**
1245  * Dump a result from LDAP on stdout
1246  *  used for debugging
1247  * @param ads connection to ads server
1248  * @param res Results to dump
1249  **/
1250
1251 void ads_dump(ADS_STRUCT *ads, void *res)
1252 {
1253         ads_process_results(ads, res, ads_dump_field, NULL);
1254 }
1255
1256 /**
1257  * Walk through results, calling a function for each entry found.
1258  *  The function receives a field name, a berval * array of values,
1259  *  and a data area passed through from the start.  The function is
1260  *  called once with null for field and values at the end of each
1261  *  entry.
1262  * @param ads connection to ads server
1263  * @param res Results to process
1264  * @param fn Function for processing each result
1265  * @param data_area user-defined area to pass to function
1266  **/
1267 void ads_process_results(ADS_STRUCT *ads, void *res,
1268                          BOOL(*fn)(char *, void **, void *),
1269                          void *data_area)
1270 {
1271         void *msg;
1272         TALLOC_CTX *ctx;
1273
1274         if (!(ctx = talloc_init("ads_process_results")))
1275                 return;
1276
1277         for (msg = ads_first_entry(ads, res); msg; 
1278              msg = ads_next_entry(ads, msg)) {
1279                 char *utf8_field;
1280                 BerElement *b;
1281         
1282                 for (utf8_field=ldap_first_attribute(ads->ld,
1283                                                      (LDAPMessage *)msg,&b); 
1284                      utf8_field;
1285                      utf8_field=ldap_next_attribute(ads->ld,
1286                                                     (LDAPMessage *)msg,b)) {
1287                         struct berval **ber_vals;
1288                         char **str_vals, **utf8_vals;
1289                         char *field;
1290                         BOOL string; 
1291
1292                         pull_utf8_talloc(ctx, &field, utf8_field);
1293                         string = fn(field, NULL, data_area);
1294
1295                         if (string) {
1296                                 utf8_vals = ldap_get_values(ads->ld,
1297                                                  (LDAPMessage *)msg, field);
1298                                 str_vals = ads_pull_strvals(ctx, 
1299                                                   (const char **) utf8_vals);
1300                                 fn(field, (void **) str_vals, data_area);
1301                                 ldap_value_free(utf8_vals);
1302                         } else {
1303                                 ber_vals = ldap_get_values_len(ads->ld, 
1304                                                  (LDAPMessage *)msg, field);
1305                                 fn(field, (void **) ber_vals, data_area);
1306
1307                                 ldap_value_free_len(ber_vals);
1308                         }
1309                         ldap_memfree(utf8_field);
1310                 }
1311                 ber_free(b, 0);
1312                 talloc_destroy_pool(ctx);
1313                 fn(NULL, NULL, data_area); /* completed an entry */
1314
1315         }
1316         talloc_destroy(ctx);
1317 }
1318
1319 /**
1320  * count how many replies are in a LDAPMessage
1321  * @param ads connection to ads server
1322  * @param res Results to count
1323  * @return number of replies
1324  **/
1325 int ads_count_replies(ADS_STRUCT *ads, void *res)
1326 {
1327         return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1328 }
1329
1330 /**
1331  * Join a machine to a realm
1332  *  Creates the machine account and sets the machine password
1333  * @param ads connection to ads server
1334  * @param hostname name of host to add
1335  * @param org_unit Organizational unit to place machine in
1336  * @return status of join
1337  **/
1338 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname, const char *org_unit)
1339 {
1340         ADS_STATUS status;
1341         LDAPMessage *res;
1342         char *host;
1343
1344         /* hostname must be lowercase */
1345         host = strdup(hostname);
1346         strlower(host);
1347
1348         status = ads_find_machine_acct(ads, (void **)&res, host);
1349         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1350                 DEBUG(0, ("Host account for %s already exists - deleting old account\n", host));
1351                 status = ads_leave_realm(ads, host);
1352                 if (!ADS_ERR_OK(status)) {
1353                         DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n", 
1354                                   host, ads->config.realm));
1355                         return status;
1356                 }
1357         }
1358
1359         status = ads_add_machine_acct(ads, host, org_unit);
1360         if (!ADS_ERR_OK(status)) {
1361                 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
1362                 return status;
1363         }
1364
1365         status = ads_find_machine_acct(ads, (void **)&res, host);
1366         if (!ADS_ERR_OK(status)) {
1367                 DEBUG(0, ("Host account test failed\n"));
1368                 return status;
1369         }
1370
1371         free(host);
1372
1373         return status;
1374 }
1375
1376 /**
1377  * Delete a machine from the realm
1378  * @param ads connection to ads server
1379  * @param hostname Machine to remove
1380  * @return status of delete
1381  **/
1382 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1383 {
1384         ADS_STATUS status;
1385         void *res;
1386         char *hostnameDN, *host; 
1387         int rc;
1388
1389         /* hostname must be lowercase */
1390         host = strdup(hostname);
1391         strlower(host);
1392
1393         status = ads_find_machine_acct(ads, &res, host);
1394         if (!ADS_ERR_OK(status)) {
1395             DEBUG(0, ("Host account for %s does not exist.\n", host));
1396             return status;
1397         }
1398
1399         hostnameDN = ads_get_dn(ads, (LDAPMessage *)res);
1400         rc = ldap_delete_s(ads->ld, hostnameDN);
1401         ads_memfree(ads, hostnameDN);
1402         if (rc != LDAP_SUCCESS) {
1403                 return ADS_ERROR(rc);
1404         }
1405
1406         status = ads_find_machine_acct(ads, &res, host);
1407         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1408                 DEBUG(0, ("Failed to remove host account.\n"));
1409                 return status;
1410         }
1411
1412         free(host);
1413
1414         return status;
1415 }
1416
1417 /**
1418  * add machine account to existing security descriptor 
1419  * @param ads connection to ads server
1420  * @param hostname machine to add
1421  * @param dn DN of security descriptor
1422  * @return status
1423  **/
1424 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1425 {
1426         const char     *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
1427         char           *exp     = 0;
1428         size_t          sd_size = 0;
1429         struct berval   bval = {0, NULL};
1430         prs_struct      ps_wire;
1431         char           *escaped_hostname = escape_ldap_string_alloc(hostname);
1432
1433         LDAPMessage *res  = 0;
1434         LDAPMessage *msg  = 0;
1435         ADS_MODLIST  mods = 0;
1436
1437         NTSTATUS    status;
1438         ADS_STATUS  ret;
1439         DOM_SID     sid;
1440         SEC_DESC   *psd = NULL;
1441         TALLOC_CTX *ctx = NULL; 
1442
1443         /* Avoid segmentation fault in prs_mem_free if
1444          * we have to bail out before prs_init */
1445         ps_wire.is_dynamic = False;
1446
1447         if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1448
1449         ret = ADS_ERROR(LDAP_SUCCESS);
1450
1451         if (!escaped_hostname) {
1452                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1453         }
1454
1455         if (asprintf(&exp, "(samAccountName=%s$)", escaped_hostname) == -1) {
1456                 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1457                 SAFE_FREE(escaped_hostname);
1458                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1459         }
1460
1461         SAFE_FREE(escaped_hostname);
1462
1463         ret = ads_search(ads, (void *) &res, exp, attrs);
1464
1465         if (!ADS_ERR_OK(ret)) return ret;
1466
1467         if ( !(msg = ads_first_entry(ads, res) )) {
1468                 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1469                 goto ads_set_sd_error;
1470         }
1471
1472         if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
1473                 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1474                 goto ads_set_sd_error;
1475         }
1476
1477         if (!(ctx = talloc_init("sec_io_desc"))) {
1478                 ret =  ADS_ERROR(LDAP_NO_MEMORY);
1479                 goto ads_set_sd_error;
1480         }
1481
1482         if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1483                 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1484                 goto ads_set_sd_error;
1485         }
1486
1487         status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1488
1489         if (!NT_STATUS_IS_OK(status)) {
1490                 ret = ADS_ERROR_NT(status);
1491                 goto ads_set_sd_error;
1492         }
1493
1494         if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
1495                 ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1496         }
1497
1498         if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1499                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1500                 goto ads_set_sd_error;
1501         }
1502
1503 #if 0
1504         file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1505 #endif
1506         if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1507
1508         bval.bv_len = prs_offset(&ps_wire);
1509         bval.bv_val = talloc(ctx, bval.bv_len);
1510         if (!bval.bv_val) {
1511                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1512                 goto ads_set_sd_error;
1513         }
1514
1515         prs_set_offset(&ps_wire, 0);
1516
1517         if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
1518                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1519                 goto ads_set_sd_error;          
1520         }
1521
1522         ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
1523         if (ADS_ERR_OK(ret)) {
1524                 ret = ads_gen_mod(ads, dn, mods);
1525         }
1526
1527 ads_set_sd_error:
1528         ads_msgfree(ads, res);
1529         prs_mem_free(&ps_wire);
1530         talloc_destroy(ctx);
1531         return ret;
1532 }
1533
1534 /**
1535  * pull the first entry from a ADS result
1536  * @param ads connection to ads server
1537  * @param res Results of search
1538  * @return first entry from result
1539  **/
1540 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1541 {
1542         return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1543 }
1544
1545 /**
1546  * pull the next entry from a ADS result
1547  * @param ads connection to ads server
1548  * @param res Results of search
1549  * @return next entry from result
1550  **/
1551 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1552 {
1553         return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1554 }
1555
1556 /**
1557  * pull a single string from a ADS result
1558  * @param ads connection to ads server
1559  * @param mem_ctx TALLOC_CTX to use for allocating result string
1560  * @param msg Results of search
1561  * @param field Attribute to retrieve
1562  * @return Result string in talloc context
1563  **/
1564 char *ads_pull_string(ADS_STRUCT *ads, 
1565                       TALLOC_CTX *mem_ctx, void *msg, const char *field)
1566 {
1567         char **values;
1568         char *ret = NULL;
1569         char *ux_string;
1570         size_t rc;
1571
1572         values = ldap_get_values(ads->ld, msg, field);
1573         if (!values)
1574                 return NULL;
1575         
1576         if (values[0]) {
1577                 rc = pull_utf8_talloc(mem_ctx, &ux_string, 
1578                                       values[0]);
1579                 if (rc != (size_t)-1)
1580                         ret = ux_string;
1581                 
1582         }
1583         ldap_value_free(values);
1584         return ret;
1585 }
1586
1587 /**
1588  * pull an array of strings from a ADS result
1589  * @param ads connection to ads server
1590  * @param mem_ctx TALLOC_CTX to use for allocating result string
1591  * @param msg Results of search
1592  * @param field Attribute to retrieve
1593  * @return Result strings in talloc context
1594  **/
1595 char **ads_pull_strings(ADS_STRUCT *ads, 
1596                        TALLOC_CTX *mem_ctx, void *msg, const char *field)
1597 {
1598         char **values;
1599         char **ret = NULL;
1600         int i, n;
1601
1602         values = ldap_get_values(ads->ld, msg, field);
1603         if (!values)
1604                 return NULL;
1605
1606         for (i=0;values[i];i++)
1607                 /* noop */ ;
1608         n = i;
1609
1610         ret = talloc(mem_ctx, sizeof(char *) * (n+1));
1611         if (!ret) {
1612                 ldap_value_free(values);
1613                 return NULL;
1614         }
1615
1616         for (i=0;i<n;i++) {
1617                 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1618                         ldap_value_free(values);
1619                         return NULL;
1620                 }
1621         }
1622         ret[i] = NULL;
1623
1624         ldap_value_free(values);
1625         return ret;
1626 }
1627
1628
1629 /**
1630  * pull a single uint32 from a ADS result
1631  * @param ads connection to ads server
1632  * @param msg Results of search
1633  * @param field Attribute to retrieve
1634  * @param v Pointer to int to store result
1635  * @return boolean inidicating success
1636 */
1637 BOOL ads_pull_uint32(ADS_STRUCT *ads, 
1638                      void *msg, const char *field, uint32 *v)
1639 {
1640         char **values;
1641
1642         values = ldap_get_values(ads->ld, msg, field);
1643         if (!values)
1644                 return False;
1645         if (!values[0]) {
1646                 ldap_value_free(values);
1647                 return False;
1648         }
1649
1650         *v = atoi(values[0]);
1651         ldap_value_free(values);
1652         return True;
1653 }
1654
1655 /**
1656  * pull a single objectGUID from an ADS result
1657  * @param ads connection to ADS server
1658  * @param msg results of search
1659  * @param guid 37-byte area to receive text guid
1660  * @return boolean indicating success
1661  **/
1662 BOOL ads_pull_guid(ADS_STRUCT *ads,
1663                    void *msg, GUID *guid)
1664 {
1665         char **values;
1666
1667         values = ldap_get_values(ads->ld, msg, "objectGUID");
1668         if (!values)
1669                 return False;
1670         
1671         if (values[0]) {
1672                 memcpy(guid, values[0], sizeof(GUID));
1673                 ldap_value_free(values);
1674                 return True;
1675         }
1676         ldap_value_free(values);
1677         return False;
1678
1679 }
1680
1681
1682 /**
1683  * pull a single DOM_SID from a ADS result
1684  * @param ads connection to ads server
1685  * @param msg Results of search
1686  * @param field Attribute to retrieve
1687  * @param sid Pointer to sid to store result
1688  * @return boolean inidicating success
1689 */
1690 BOOL ads_pull_sid(ADS_STRUCT *ads, 
1691                   void *msg, const char *field, DOM_SID *sid)
1692 {
1693         struct berval **values;
1694         BOOL ret = False;
1695
1696         values = ldap_get_values_len(ads->ld, msg, field);
1697
1698         if (!values)
1699                 return False;
1700
1701         if (values[0])
1702                 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
1703         
1704         ldap_value_free_len(values);
1705         return ret;
1706 }
1707
1708 /**
1709  * pull an array of DOM_SIDs from a ADS result
1710  * @param ads connection to ads server
1711  * @param mem_ctx TALLOC_CTX for allocating sid array
1712  * @param msg Results of search
1713  * @param field Attribute to retrieve
1714  * @param sids pointer to sid array to allocate
1715  * @return the count of SIDs pulled
1716  **/
1717 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1718                   void *msg, const char *field, DOM_SID **sids)
1719 {
1720         struct berval **values;
1721         BOOL ret;
1722         int count, i;
1723
1724         values = ldap_get_values_len(ads->ld, msg, field);
1725
1726         if (!values)
1727                 return 0;
1728
1729         for (i=0; values[i]; i++)
1730                 /* nop */ ;
1731
1732         (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
1733         if (!(*sids)) {
1734                 ldap_value_free_len(values);
1735                 return 0;
1736         }
1737
1738         count = 0;
1739         for (i=0; values[i]; i++) {
1740                 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
1741                 if (ret) {
1742                         fstring sid;
1743                         DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
1744                         count++;
1745                 }
1746         }
1747         
1748         ldap_value_free_len(values);
1749         return count;
1750 }
1751
1752 /**
1753  * pull a SEC_DESC from a ADS result
1754  * @param ads connection to ads server
1755  * @param mem_ctx TALLOC_CTX for allocating sid array
1756  * @param msg Results of search
1757  * @param field Attribute to retrieve
1758  * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
1759  * @return boolean inidicating success
1760 */
1761 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1762                   void *msg, const char *field, SEC_DESC **sd)
1763 {
1764         struct berval **values;
1765         prs_struct      ps;
1766         BOOL ret = False;
1767
1768         values = ldap_get_values_len(ads->ld, msg, field);
1769
1770         if (!values) return False;
1771
1772         if (values[0]) {
1773                 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
1774                 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1775                 prs_set_offset(&ps,0);
1776
1777                 ret = sec_io_desc("sd", sd, &ps, 1);
1778         }
1779         
1780         ldap_value_free_len(values);
1781         return ret;
1782 }
1783
1784 /* 
1785  * in order to support usernames longer than 21 characters we need to 
1786  * use both the sAMAccountName and the userPrincipalName attributes 
1787  * It seems that not all users have the userPrincipalName attribute set
1788  *
1789  * @param ads connection to ads server
1790  * @param mem_ctx TALLOC_CTX for allocating sid array
1791  * @param msg Results of search
1792  * @return the username
1793  */
1794 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
1795 {
1796         char *ret, *p;
1797
1798         ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
1799         if (ret && (p = strchr(ret, '@'))) {
1800                 *p = 0;
1801                 return ret;
1802         }
1803         return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
1804 }
1805
1806
1807 /**
1808  * find the update serial number - this is the core of the ldap cache
1809  * @param ads connection to ads server
1810  * @param ads connection to ADS server
1811  * @param usn Pointer to retrieved update serial number
1812  * @return status of search
1813  **/
1814 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
1815 {
1816         const char *attrs[] = {"highestCommittedUSN", NULL};
1817         ADS_STATUS status;
1818         void *res;
1819
1820         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1821         if (!ADS_ERR_OK(status)) return status;
1822
1823         if (ads_count_replies(ads, res) != 1) {
1824                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1825         }
1826
1827         ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
1828         ads_msgfree(ads, res);
1829         return ADS_SUCCESS;
1830 }
1831
1832 /* parse a ADS timestring - typical string is
1833    '20020917091222.0Z0' which means 09:12.22 17th September
1834    2002, timezone 0 */
1835 static time_t ads_parse_time(const char *str)
1836 {
1837         struct tm tm;
1838
1839         ZERO_STRUCT(tm);
1840
1841         if (sscanf(str, "%4d%2d%2d%2d%2d%2d", 
1842                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
1843                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
1844                 return 0;
1845         }
1846         tm.tm_year -= 1900;
1847         tm.tm_mon -= 1;
1848
1849         return timegm(&tm);
1850 }
1851
1852
1853 /**
1854  * Find the servers name and realm - this can be done before authentication 
1855  *  The ldapServiceName field on w2k  looks like this:
1856  *    vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
1857  * @param ads connection to ads server
1858  * @return status of search
1859  **/
1860 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
1861 {
1862         const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
1863         ADS_STATUS status;
1864         void *res;
1865         char *value;
1866         char *p;
1867         char *timestr;
1868         TALLOC_CTX *ctx;
1869
1870         if (!(ctx = talloc_init("ads_server_info"))) {
1871                 return ADS_ERROR(LDAP_NO_MEMORY);
1872         }
1873
1874         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1875         if (!ADS_ERR_OK(status)) return status;
1876
1877         value = ads_pull_string(ads, ctx, res, "ldapServiceName");
1878         if (!value) {
1879                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1880         }
1881
1882         timestr = ads_pull_string(ads, ctx, res, "currentTime");
1883         if (!timestr) {
1884                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1885         }
1886
1887         ldap_msgfree(res);
1888
1889         p = strchr(value, ':');
1890         if (!p) {
1891                 talloc_destroy(ctx);
1892                 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' so was deemed invalid\n"));
1893                 return ADS_ERROR(LDAP_DECODING_ERROR);
1894         }
1895
1896         SAFE_FREE(ads->config.ldap_server_name);
1897
1898         ads->config.ldap_server_name = strdup(p+1);
1899         p = strchr(ads->config.ldap_server_name, '$');
1900         if (!p || p[1] != '@') {
1901                 talloc_destroy(ctx);
1902                 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@' so was deemed invalid\n", ads->config.ldap_server_name));
1903                 SAFE_FREE(ads->config.ldap_server_name);
1904                 return ADS_ERROR(LDAP_DECODING_ERROR);
1905         }
1906
1907         *p = 0;
1908
1909         SAFE_FREE(ads->config.realm);
1910         SAFE_FREE(ads->config.bind_path);
1911
1912         ads->config.realm = strdup(p+2);
1913         ads->config.bind_path = ads_build_dn(ads->config.realm);
1914
1915         DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n", 
1916                  ads->config.ldap_server_name, ads->config.realm,
1917                  ads->config.bind_path));
1918
1919         ads->config.current_time = ads_parse_time(timestr);
1920
1921         if (ads->config.current_time != 0) {
1922                 ads->auth.time_offset = ads->config.current_time - time(NULL);
1923                 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
1924         }
1925
1926         talloc_destroy(ctx);
1927
1928         return ADS_SUCCESS;
1929 }
1930
1931
1932 /**
1933  * find the list of trusted domains
1934  * @param ads connection to ads server
1935  * @param mem_ctx TALLOC_CTX for allocating results
1936  * @param num_trusts pointer to number of trusts
1937  * @param names pointer to trusted domain name list
1938  * @param sids pointer to list of sids of trusted domains
1939  * @return the count of SIDs pulled
1940  **/
1941 ADS_STATUS ads_trusted_domains(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, 
1942                                int *num_trusts, 
1943                                char ***names, 
1944                                char ***alt_names,
1945                                DOM_SID **sids)
1946 {
1947         const char *attrs[] = {"name", "flatname", "securityIdentifier", 
1948                                "trustDirection", NULL};
1949         ADS_STATUS status;
1950         void *res, *msg;
1951         int count, i;
1952
1953         *num_trusts = 0;
1954
1955         status = ads_search(ads, &res, "(objectcategory=trustedDomain)", attrs);
1956         if (!ADS_ERR_OK(status)) return status;
1957
1958         count = ads_count_replies(ads, res);
1959         if (count == 0) {
1960                 ads_msgfree(ads, res);
1961                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1962         }
1963
1964         (*names) = talloc(mem_ctx, sizeof(char *) * count);
1965         (*alt_names) = talloc(mem_ctx, sizeof(char *) * count);
1966         (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * count);
1967         if (! *names || ! *sids) return ADS_ERROR(LDAP_NO_MEMORY);
1968
1969         for (i=0, msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
1970                 uint32 direction;
1971
1972                 /* direction is a 2 bit bitfield, 1 means they trust us 
1973                    but we don't trust them, so we should not list them
1974                    as users from that domain can't login */
1975                 if (ads_pull_uint32(ads, msg, "trustDirection", &direction) &&
1976                     direction == 1) {
1977                         continue;
1978                 }
1979                 
1980                 (*names)[i] = ads_pull_string(ads, mem_ctx, msg, "name");
1981                 (*alt_names)[i] = ads_pull_string(ads, mem_ctx, msg, "flatname");
1982
1983                 if ((*alt_names)[i] && (*alt_names)[i][0]) {
1984                         /* we prefer the flatname as the primary name
1985                            for consistency with RPC */
1986                         char *name = (*alt_names)[i];
1987                         (*alt_names)[i] = (*names)[i];
1988                         (*names)[i] = name;
1989                 }
1990                 if (ads_pull_sid(ads, msg, "securityIdentifier", &(*sids)[i])) {
1991                         i++;
1992                 }
1993         }
1994
1995         ads_msgfree(ads, res);
1996
1997         *num_trusts = i;
1998
1999         return ADS_SUCCESS;
2000 }
2001
2002 /**
2003  * find the domain sid for our domain
2004  * @param ads connection to ads server
2005  * @param sid Pointer to domain sid
2006  * @return status of search
2007  **/
2008 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2009 {
2010         const char *attrs[] = {"objectSid", NULL};
2011         void *res;
2012         ADS_STATUS rc;
2013
2014         rc = ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
2015                            attrs, &res);
2016         if (!ADS_ERR_OK(rc)) return rc;
2017         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2018                 return ADS_ERROR_SYSTEM(ENOENT);
2019         }
2020         ads_msgfree(ads, res);
2021         
2022         return ADS_SUCCESS;
2023 }
2024
2025 /* this is rather complex - we need to find the allternate (netbios) name
2026    for the domain, but there isn't a simple query to do this. Instead
2027    we look for the principle names on the DCs account and find one that has 
2028    the right form, then extract the netbios name of the domain from that
2029
2030    NOTE! better method is this:
2031
2032 bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))'  nETBIOSName 
2033
2034 but you need to force the bind path to match the configurationNamingContext from the rootDSE
2035
2036 */
2037 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **workgroup)
2038 {
2039         char *exp;
2040         ADS_STATUS rc;
2041         char **principles;
2042         char *prefix;
2043         int prefix_length;
2044         int i;
2045         void *res;
2046         const char *attrs[] = {"servicePrincipalName", NULL};
2047
2048         (*workgroup) = NULL;
2049
2050         asprintf(&exp, "(&(objectclass=computer)(dnshostname=%s.%s))", 
2051                  ads->config.ldap_server_name, ads->config.realm);
2052         rc = ads_search(ads, &res, exp, attrs);
2053         free(exp);
2054
2055         if (!ADS_ERR_OK(rc)) {
2056                 return rc;
2057         }
2058
2059         principles = ads_pull_strings(ads, mem_ctx, res, "servicePrincipalName");
2060
2061         ads_msgfree(ads, res);
2062
2063         if (!principles) {
2064                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2065         }
2066
2067         asprintf(&prefix, "HOST/%s.%s/", 
2068                  ads->config.ldap_server_name, 
2069                  ads->config.realm);
2070
2071         prefix_length = strlen(prefix);
2072
2073         for (i=0;principles[i]; i++) {
2074                 if (strncasecmp(principles[i], prefix, prefix_length) == 0 &&
2075                     strcasecmp(ads->config.realm, principles[i]+prefix_length) != 0 &&
2076                     !strchr(principles[i]+prefix_length, '.')) {
2077                         /* found an alternate (short) name for the domain. */
2078                         DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
2079                                  principles[i]+prefix_length, 
2080                                  ads->config.realm));
2081                         (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
2082                         break;
2083                 }
2084         }
2085         free(prefix);
2086
2087         if (!*workgroup) {
2088                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2089         }
2090         
2091         return ADS_SUCCESS;
2092 }
2093
2094 #endif