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