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