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