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