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