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