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