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