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