r23937: Use ads_config_path() when we need to know the configration context.
[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 <jmcd@us.ibm.com> 2002
7    Copyright (C) Guenther Deschner 2005
8    Copyright (C) Gerald Carter 2006
9    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 3 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "includes.h"
25
26 #ifdef HAVE_LDAP
27
28 /**
29  * @file ldap.c
30  * @brief basic ldap client-side routines for ads server communications
31  *
32  * The routines contained here should do the necessary ldap calls for
33  * ads setups.
34  * 
35  * Important note: attribute names passed into ads_ routines must
36  * already be in UTF-8 format.  We do not convert them because in almost
37  * all cases, they are just ascii (which is represented with the same
38  * codepoints in UTF-8).  This may have to change at some point
39  **/
40
41
42 #define LDAP_SERVER_TREE_DELETE_OID     "1.2.840.113556.1.4.805"
43
44 static SIG_ATOMIC_T gotalarm;
45                                                                                                                    
46 /***************************************************************
47  Signal function to tell us we timed out.
48 ****************************************************************/
49
50 static void gotalarm_sig(void)
51 {
52         gotalarm = 1;
53 }
54
55  LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
56 {
57         LDAP *ldp = NULL;
58
59         /* Setup timeout */
60         gotalarm = 0;
61         CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
62         alarm(to);
63         /* End setup timeout. */
64
65         ldp = ldap_open(server, port);
66
67         if (ldp == NULL) {
68                 DEBUG(2,("Could not open LDAP connection to %s:%d: %s\n",
69                          server, port, strerror(errno)));
70         }
71
72         /* Teardown timeout. */
73         CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
74         alarm(0);
75
76         return ldp;
77 }
78
79 static int ldap_search_with_timeout(LDAP *ld,
80                                     LDAP_CONST char *base,
81                                     int scope,
82                                     LDAP_CONST char *filter,
83                                     char **attrs,
84                                     int attrsonly,
85                                     LDAPControl **sctrls,
86                                     LDAPControl **cctrls,
87                                     int sizelimit,
88                                     LDAPMessage **res )
89 {
90         struct timeval timeout;
91         int result;
92
93         /* Setup timeout for the ldap_search_ext_s call - local and remote. */
94         timeout.tv_sec = lp_ldap_timeout();
95         timeout.tv_usec = 0;
96
97         /* Setup alarm timeout.... Do we need both of these ? JRA. */
98         gotalarm = 0;
99         CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
100         alarm(lp_ldap_timeout());
101         /* End setup timeout. */
102
103         result = ldap_search_ext_s(ld, base, scope, filter, attrs,
104                                    attrsonly, sctrls, cctrls, &timeout,
105                                    sizelimit, res);
106
107         /* Teardown timeout. */
108         CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
109         alarm(0);
110
111         if (gotalarm != 0)
112                 return LDAP_TIMELIMIT_EXCEEDED;
113
114         return result;
115 }
116
117 /**********************************************
118  Do client and server sitename match ?
119 **********************************************/
120
121 BOOL ads_sitename_match(ADS_STRUCT *ads)
122 {
123         if (ads->config.server_site_name == NULL &&
124             ads->config.client_site_name == NULL ) {
125                 DEBUG(10,("ads_sitename_match: both null\n"));
126                 return True;
127         }
128         if (ads->config.server_site_name &&
129             ads->config.client_site_name &&
130             strequal(ads->config.server_site_name,
131                      ads->config.client_site_name)) {
132                 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
133                 return True;
134         }
135         DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
136                 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
137                 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
138         return False;
139 }
140
141 /**********************************************
142  Is this the closest DC ?
143 **********************************************/
144
145 BOOL ads_closest_dc(ADS_STRUCT *ads)
146 {
147         if (ads->config.flags & ADS_CLOSEST) {
148                 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag set\n"));
149                 return True;
150         }
151
152         /* not sure if this can ever happen */
153         if (ads_sitename_match(ads)) {
154                 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag not set but sites match\n"));
155                 return True;
156         }
157
158         DEBUG(10,("ads_closest_dc: %s is not the closest DC\n", 
159                 ads->config.ldap_server_name));
160
161         return False;
162 }
163
164
165 /*
166   try a connection to a given ldap server, returning True and setting the servers IP
167   in the ads struct if successful
168  */
169 BOOL ads_try_connect(ADS_STRUCT *ads, const char *server )
170 {
171         char *srv;
172         struct cldap_netlogon_reply cldap_reply;
173
174         if (!server || !*server) {
175                 return False;
176         }
177         
178         DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n", 
179                 server, ads->server.realm));
180
181         /* this copes with inet_ntoa brokenness */
182         
183         srv = SMB_STRDUP(server);
184
185         ZERO_STRUCT( cldap_reply );
186
187         if ( !ads_cldap_netlogon( srv, ads->server.realm, &cldap_reply ) ) {
188                 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
189                 SAFE_FREE( srv );
190                 return False;
191         }
192
193         /* Check the CLDAP reply flags */
194
195         if ( !(cldap_reply.flags & ADS_LDAP) ) {
196                 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
197                         srv));
198                 SAFE_FREE( srv );
199                 return False;
200         }
201
202         /* Fill in the ads->config values */
203
204         SAFE_FREE(ads->config.realm);
205         SAFE_FREE(ads->config.bind_path);
206         SAFE_FREE(ads->config.ldap_server_name);
207         SAFE_FREE(ads->config.server_site_name);
208         SAFE_FREE(ads->config.client_site_name);
209         SAFE_FREE(ads->server.workgroup);
210
211         ads->config.flags              = cldap_reply.flags;
212         ads->config.ldap_server_name   = SMB_STRDUP(cldap_reply.hostname);
213         strupper_m(cldap_reply.domain);
214         ads->config.realm              = SMB_STRDUP(cldap_reply.domain);
215         ads->config.bind_path          = ads_build_dn(ads->config.realm);
216         if (*cldap_reply.server_site_name) {
217                 ads->config.server_site_name =
218                         SMB_STRDUP(cldap_reply.server_site_name);
219         }
220         if (*cldap_reply.client_site_name) {
221                 ads->config.client_site_name =
222                         SMB_STRDUP(cldap_reply.client_site_name);
223         }
224                 
225         ads->server.workgroup          = SMB_STRDUP(cldap_reply.netbios_domain);
226
227         ads->ldap.port = LDAP_PORT;
228         ads->ldap.ip = *interpret_addr2(srv);
229         SAFE_FREE(srv);
230         
231         /* Store our site name. */
232         sitename_store( cldap_reply.domain, cldap_reply.client_site_name );
233
234         return True;
235 }
236
237 /**********************************************************************
238  Try to find an AD dc using our internal name resolution routines
239  Try the realm first and then then workgroup name if netbios is not 
240  disabled
241 **********************************************************************/
242
243 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
244 {
245         const char *c_realm;
246         int count, i=0;
247         struct ip_service *ip_list;
248         pstring realm;
249         BOOL got_realm = False;
250         BOOL use_own_domain = False;
251         char *sitename;
252         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
253
254         /* if the realm and workgroup are both empty, assume they are ours */
255
256         /* realm */
257         c_realm = ads->server.realm;
258         
259         if ( !c_realm || !*c_realm ) {
260                 /* special case where no realm and no workgroup means our own */
261                 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
262                         use_own_domain = True;
263                         c_realm = lp_realm();
264                 }
265         }
266         
267         if (c_realm && *c_realm) 
268                 got_realm = True;
269                    
270         /* we need to try once with the realm name and fallback to the 
271            netbios domain name if we fail (if netbios has not been disabled */
272            
273         if ( !got_realm && !lp_disable_netbios() ) {
274                 c_realm = ads->server.workgroup;
275                 if (!c_realm || !*c_realm) {
276                         if ( use_own_domain )
277                                 c_realm = lp_workgroup();
278                 }
279                 
280                 if ( !c_realm || !*c_realm ) {
281                         DEBUG(0,("ads_find_dc: no realm or workgroup!  Don't know what to do\n"));
282                         return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
283                 }
284         }
285         
286         pstrcpy( realm, c_realm );
287
288         sitename = sitename_fetch(realm);
289
290  again:
291
292         DEBUG(6,("ads_find_dc: looking for %s '%s'\n", 
293                 (got_realm ? "realm" : "domain"), realm));
294
295         status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
296         if (!NT_STATUS_IS_OK(status)) {
297                 /* fall back to netbios if we can */
298                 if ( got_realm && !lp_disable_netbios() ) {
299                         got_realm = False;
300                         goto again;
301                 }
302                 
303                 SAFE_FREE(sitename);
304                 return status;
305         }
306
307         /* if we fail this loop, then giveup since all the IP addresses returned were dead */
308         for ( i=0; i<count; i++ ) {
309                 fstring server;
310                 
311                 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
312                 
313                 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
314                         continue;
315
316                 if (!got_realm) {
317                         /* realm in this case is a workgroup name. We need
318                            to ignore any IP addresses in the negative connection
319                            cache that match ip addresses returned in the ad realm
320                            case. It sucks that I have to reproduce the logic above... */
321                         c_realm = ads->server.realm;
322                         if ( !c_realm || !*c_realm ) {
323                                 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
324                                         c_realm = lp_realm();
325                                 }
326                         }
327                         if (c_realm && *c_realm &&
328                                         !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
329                                 /* Ensure we add the workgroup name for this
330                                    IP address as negative too. */
331                                 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
332                                 continue;
333                         }
334                 }
335                         
336                 if ( ads_try_connect(ads, server) ) {
337                         SAFE_FREE(ip_list);
338                         SAFE_FREE(sitename);
339                         return NT_STATUS_OK;
340                 }
341                 
342                 /* keep track of failures */
343                 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
344         }
345
346         SAFE_FREE(ip_list);
347
348         /* In case we failed to contact one of our closest DC on our site we
349          * need to try to find another DC, retry with a site-less SRV DNS query
350          * - Guenther */
351
352         if (sitename) {
353                 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
354                                 "trying to find another DC\n", sitename));
355                 SAFE_FREE(sitename);
356                 namecache_delete(realm, 0x1C);
357                 goto again;
358         }
359
360         return NT_STATUS_NO_LOGON_SERVERS;
361 }
362
363
364 /**
365  * Connect to the LDAP server
366  * @param ads Pointer to an existing ADS_STRUCT
367  * @return status of connection
368  **/
369 ADS_STATUS ads_connect(ADS_STRUCT *ads)
370 {
371         int version = LDAP_VERSION3;
372         ADS_STATUS status;
373         NTSTATUS ntstatus;
374
375         ads->ldap.last_attempt = time(NULL);
376         ads->ldap.ld = NULL;
377
378         /* try with a user specified server */
379
380         if (ads->server.ldap_server && 
381             ads_try_connect(ads, ads->server.ldap_server)) {
382                 goto got_connection;
383         }
384
385         ntstatus = ads_find_dc(ads);
386         if (NT_STATUS_IS_OK(ntstatus)) {
387                 goto got_connection;
388         }
389
390         return ADS_ERROR_NT(ntstatus);
391
392 got_connection:
393         DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap.ip)));
394
395         if (!ads->auth.user_name) {
396                 /* Must use the userPrincipalName value here or sAMAccountName
397                    and not servicePrincipalName; found by Guenther Deschner */
398
399                 asprintf(&ads->auth.user_name, "%s$", global_myname() );
400         }
401
402         if (!ads->auth.realm) {
403                 ads->auth.realm = SMB_STRDUP(ads->config.realm);
404         }
405
406         if (!ads->auth.kdc_server) {
407                 ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap.ip));
408         }
409
410 #if KRB5_DNS_HACK
411         /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
412            to MIT kerberos to work (tridge) */
413         {
414                 char *env;
415                 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
416                 setenv(env, ads->auth.kdc_server, 1);
417                 free(env);
418         }
419 #endif
420
421         /* If the caller() requested no LDAP bind, then we are done */
422         
423         if (ads->auth.flags & ADS_AUTH_NO_BIND) {
424                 return ADS_SUCCESS;
425         }
426         
427         /* Otherwise setup the TCP LDAP session */
428
429         if ( (ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name, 
430                 LDAP_PORT, lp_ldap_timeout())) == NULL )
431         {
432                 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
433         }
434
435         /* cache the successful connection for workgroup and realm */
436         if (ads_closest_dc(ads)) {
437                 saf_store( ads->server.workgroup, inet_ntoa(ads->ldap.ip));
438                 saf_store( ads->server.realm, inet_ntoa(ads->ldap.ip));
439         }
440
441         ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
442
443         status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
444         if (!ADS_ERR_OK(status)) {
445                 return status;
446         }
447
448         /* fill in the current time and offsets */
449         
450         status = ads_current_time( ads );
451         if ( !ADS_ERR_OK(status) ) {
452                 return status;
453         }
454
455         /* Now do the bind */
456         
457         if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
458                 return ADS_ERROR(ldap_simple_bind_s( ads->ldap.ld, NULL, NULL));
459         }
460
461         if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
462                 return ADS_ERROR(ldap_simple_bind_s( ads->ldap.ld, ads->auth.user_name, ads->auth.password));
463         }
464
465         return ads_sasl_bind(ads);
466 }
467
468 /**
469  * Disconnect the LDAP server
470  * @param ads Pointer to an existing ADS_STRUCT
471  **/
472 void ads_disconnect(ADS_STRUCT *ads)
473 {
474         if (ads->ldap.ld) {
475                 ldap_unbind(ads->ldap.ld);
476                 ads->ldap.ld = NULL;
477         }
478 }
479
480 /*
481   Duplicate a struct berval into talloc'ed memory
482  */
483 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
484 {
485         struct berval *value;
486
487         if (!in_val) return NULL;
488
489         value = TALLOC_ZERO_P(ctx, struct berval);
490         if (value == NULL)
491                 return NULL;
492         if (in_val->bv_len == 0) return value;
493
494         value->bv_len = in_val->bv_len;
495         value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
496                                               in_val->bv_len);
497         return value;
498 }
499
500 /*
501   Make a values list out of an array of (struct berval *)
502  */
503 static struct berval **ads_dup_values(TALLOC_CTX *ctx, 
504                                       const struct berval **in_vals)
505 {
506         struct berval **values;
507         int i;
508        
509         if (!in_vals) return NULL;
510         for (i=0; in_vals[i]; i++)
511                 ; /* count values */
512         values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
513         if (!values) return NULL;
514
515         for (i=0; in_vals[i]; i++) {
516                 values[i] = dup_berval(ctx, in_vals[i]);
517         }
518         return values;
519 }
520
521 /*
522   UTF8-encode a values list out of an array of (char *)
523  */
524 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
525 {
526         char **values;
527         int i;
528        
529         if (!in_vals) return NULL;
530         for (i=0; in_vals[i]; i++)
531                 ; /* count values */
532         values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
533         if (!values) return NULL;
534
535         for (i=0; in_vals[i]; i++) {
536                 push_utf8_talloc(ctx, &values[i], in_vals[i]);
537         }
538         return values;
539 }
540
541 /*
542   Pull a (char *) array out of a UTF8-encoded values list
543  */
544 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
545 {
546         char **values;
547         int i;
548        
549         if (!in_vals) return NULL;
550         for (i=0; in_vals[i]; i++)
551                 ; /* count values */
552         values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
553         if (!values) return NULL;
554
555         for (i=0; in_vals[i]; i++) {
556                 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
557         }
558         return values;
559 }
560
561 /**
562  * Do a search with paged results.  cookie must be null on the first
563  *  call, and then returned on each subsequent call.  It will be null
564  *  again when the entire search is complete 
565  * @param ads connection to ads server 
566  * @param bind_path Base dn for the search
567  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
568  * @param expr Search expression - specified in local charset
569  * @param attrs Attributes to retrieve - specified in utf8 or ascii
570  * @param res ** which will contain results - free res* with ads_msgfree()
571  * @param count Number of entries retrieved on this page
572  * @param cookie The paged results cookie to be returned on subsequent calls
573  * @return status of search
574  **/
575 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
576                                            const char *bind_path,
577                                            int scope, const char *expr,
578                                            const char **attrs, void *args,
579                                            LDAPMessage **res, 
580                                            int *count, struct berval **cookie)
581 {
582         int rc, i, version;
583         char *utf8_expr, *utf8_path, **search_attrs;
584         LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
585         BerElement *cookie_be = NULL;
586         struct berval *cookie_bv= NULL;
587         BerElement *ext_be = NULL;
588         struct berval *ext_bv= NULL;
589
590         TALLOC_CTX *ctx;
591         ads_control *external_control = (ads_control *) args;
592
593         *res = NULL;
594
595         if (!(ctx = talloc_init("ads_do_paged_search_args")))
596                 return ADS_ERROR(LDAP_NO_MEMORY);
597
598         /* 0 means the conversion worked but the result was empty 
599            so we only fail if it's -1.  In any case, it always 
600            at least nulls out the dest */
601         if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
602             (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
603                 rc = LDAP_NO_MEMORY;
604                 goto done;
605         }
606
607         if (!attrs || !(*attrs))
608                 search_attrs = NULL;
609         else {
610                 /* This would be the utf8-encoded version...*/
611                 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
612                 if (!(str_list_copy(&search_attrs, attrs))) {
613                         rc = LDAP_NO_MEMORY;
614                         goto done;
615                 }
616         }
617                 
618         /* Paged results only available on ldap v3 or later */
619         ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
620         if (version < LDAP_VERSION3) {
621                 rc =  LDAP_NOT_SUPPORTED;
622                 goto done;
623         }
624
625         cookie_be = ber_alloc_t(LBER_USE_DER);
626         if (*cookie) {
627                 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
628                 ber_bvfree(*cookie); /* don't need it from last time */
629                 *cookie = NULL;
630         } else {
631                 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
632         }
633         ber_flatten(cookie_be, &cookie_bv);
634         PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
635         PagedResults.ldctl_iscritical = (char) 1;
636         PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
637         PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
638
639         NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
640         NoReferrals.ldctl_iscritical = (char) 0;
641         NoReferrals.ldctl_value.bv_len = 0;
642         NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
643
644         if (external_control && 
645             (strequal(external_control->control, ADS_EXTENDED_DN_OID) || 
646              strequal(external_control->control, ADS_SD_FLAGS_OID))) {
647
648                 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
649                 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
650
651                 /* win2k does not accept a ldctl_value beeing passed in */
652
653                 if (external_control->val != 0) {
654
655                         if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
656                                 rc = LDAP_NO_MEMORY;
657                                 goto done;
658                         }
659
660                         if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
661                                 rc = LDAP_NO_MEMORY;
662                                 goto done;
663                         }
664                         if ((ber_flatten(ext_be, &ext_bv)) == -1) {
665                                 rc = LDAP_NO_MEMORY;
666                                 goto done;
667                         }
668
669                         ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
670                         ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
671
672                 } else {
673                         ExternalCtrl.ldctl_value.bv_len = 0;
674                         ExternalCtrl.ldctl_value.bv_val = NULL;
675                 }
676
677                 controls[0] = &NoReferrals;
678                 controls[1] = &PagedResults;
679                 controls[2] = &ExternalCtrl;
680                 controls[3] = NULL;
681
682         } else {
683                 controls[0] = &NoReferrals;
684                 controls[1] = &PagedResults;
685                 controls[2] = NULL;
686         }
687
688         /* we need to disable referrals as the openldap libs don't
689            handle them and paged results at the same time.  Using them
690            together results in the result record containing the server 
691            page control being removed from the result list (tridge/jmcd) 
692         
693            leaving this in despite the control that says don't generate
694            referrals, in case the server doesn't support it (jmcd)
695         */
696         ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
697
698         rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr, 
699                                       search_attrs, 0, controls,
700                                       NULL, LDAP_NO_LIMIT,
701                                       (LDAPMessage **)res);
702
703         ber_free(cookie_be, 1);
704         ber_bvfree(cookie_bv);
705
706         if (rc) {
707                 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
708                          ldap_err2string(rc)));
709                 goto done;
710         }
711
712         rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
713                                         NULL, &rcontrols,  0);
714
715         if (!rcontrols) {
716                 goto done;
717         }
718
719         for (i=0; rcontrols[i]; i++) {
720                 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
721                         cookie_be = ber_init(&rcontrols[i]->ldctl_value);
722                         ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
723                                   &cookie_bv);
724                         /* the berval is the cookie, but must be freed when
725                            it is all done */
726                         if (cookie_bv->bv_len) /* still more to do */
727                                 *cookie=ber_bvdup(cookie_bv);
728                         else
729                                 *cookie=NULL;
730                         ber_bvfree(cookie_bv);
731                         ber_free(cookie_be, 1);
732                         break;
733                 }
734         }
735         ldap_controls_free(rcontrols);
736
737 done:
738         talloc_destroy(ctx);
739
740         if (ext_be) {
741                 ber_free(ext_be, 1);
742         }
743
744         if (ext_bv) {
745                 ber_bvfree(ext_bv);
746         }
747  
748         /* if/when we decide to utf8-encode attrs, take out this next line */
749         str_list_free(&search_attrs);
750
751         return ADS_ERROR(rc);
752 }
753
754 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
755                                       int scope, const char *expr,
756                                       const char **attrs, LDAPMessage **res, 
757                                       int *count, struct berval **cookie)
758 {
759         return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
760 }
761
762
763 /**
764  * Get all results for a search.  This uses ads_do_paged_search() to return 
765  * all entries in a large search.
766  * @param ads connection to ads server 
767  * @param bind_path Base dn for the search
768  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
769  * @param expr Search expression
770  * @param attrs Attributes to retrieve
771  * @param res ** which will contain results - free res* with ads_msgfree()
772  * @return status of search
773  **/
774  ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
775                                    int scope, const char *expr,
776                                    const char **attrs, void *args,
777                                    LDAPMessage **res)
778 {
779         struct berval *cookie = NULL;
780         int count = 0;
781         ADS_STATUS status;
782
783         *res = NULL;
784         status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
785                                      &count, &cookie);
786
787         if (!ADS_ERR_OK(status)) 
788                 return status;
789
790 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
791         while (cookie) {
792                 LDAPMessage *res2 = NULL;
793                 ADS_STATUS status2;
794                 LDAPMessage *msg, *next;
795
796                 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr, 
797                                               attrs, args, &res2, &count, &cookie);
798
799                 if (!ADS_ERR_OK(status2)) break;
800
801                 /* this relies on the way that ldap_add_result_entry() works internally. I hope
802                    that this works on all ldap libs, but I have only tested with openldap */
803                 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
804                         next = ads_next_entry(ads, msg);
805                         ldap_add_result_entry((LDAPMessage **)res, msg);
806                 }
807                 /* note that we do not free res2, as the memory is now
808                    part of the main returned list */
809         }
810 #else
811         DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
812         status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
813 #endif
814
815         return status;
816 }
817
818  ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
819                               int scope, const char *expr,
820                               const char **attrs, LDAPMessage **res)
821 {
822         return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
823 }
824
825  ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
826                                        int scope, const char *expr,
827                                        const char **attrs, uint32 sd_flags, 
828                                        LDAPMessage **res)
829 {
830         ads_control args;
831
832         args.control = ADS_SD_FLAGS_OID;
833         args.val = sd_flags;
834         args.critical = True;
835
836         return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
837 }
838
839
840 /**
841  * Run a function on all results for a search.  Uses ads_do_paged_search() and
842  *  runs the function as each page is returned, using ads_process_results()
843  * @param ads connection to ads server
844  * @param bind_path Base dn for the search
845  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
846  * @param expr Search expression - specified in local charset
847  * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
848  * @param fn Function which takes attr name, values list, and data_area
849  * @param data_area Pointer which is passed to function on each call
850  * @return status of search
851  **/
852 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
853                                 int scope, const char *expr, const char **attrs,
854                                 BOOL(*fn)(ADS_STRUCT *, char *, void **, void *), 
855                                 void *data_area)
856 {
857         struct berval *cookie = NULL;
858         int count = 0;
859         ADS_STATUS status;
860         LDAPMessage *res;
861
862         status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
863                                      &count, &cookie);
864
865         if (!ADS_ERR_OK(status)) return status;
866
867         ads_process_results(ads, res, fn, data_area);
868         ads_msgfree(ads, res);
869
870         while (cookie) {
871                 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
872                                              &res, &count, &cookie);
873
874                 if (!ADS_ERR_OK(status)) break;
875                 
876                 ads_process_results(ads, res, fn, data_area);
877                 ads_msgfree(ads, res);
878         }
879
880         return status;
881 }
882
883 /**
884  * Do a search with a timeout.
885  * @param ads connection to ads server
886  * @param bind_path Base dn for the search
887  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
888  * @param expr Search expression
889  * @param attrs Attributes to retrieve
890  * @param res ** which will contain results - free res* with ads_msgfree()
891  * @return status of search
892  **/
893  ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope, 
894                           const char *expr,
895                           const char **attrs, LDAPMessage **res)
896 {
897         int rc;
898         char *utf8_expr, *utf8_path, **search_attrs = NULL;
899         TALLOC_CTX *ctx;
900
901         *res = NULL;
902         if (!(ctx = talloc_init("ads_do_search"))) {
903                 DEBUG(1,("ads_do_search: talloc_init() failed!"));
904                 return ADS_ERROR(LDAP_NO_MEMORY);
905         }
906
907         /* 0 means the conversion worked but the result was empty 
908            so we only fail if it's negative.  In any case, it always 
909            at least nulls out the dest */
910         if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
911             (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
912                 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
913                 rc = LDAP_NO_MEMORY;
914                 goto done;
915         }
916
917         if (!attrs || !(*attrs))
918                 search_attrs = NULL;
919         else {
920                 /* This would be the utf8-encoded version...*/
921                 /* if (!(search_attrs = ads_push_strvals(ctx, attrs)))  */
922                 if (!(str_list_copy(&search_attrs, attrs)))
923                 {
924                         DEBUG(1,("ads_do_search: str_list_copy() failed!"));
925                         rc = LDAP_NO_MEMORY;
926                         goto done;
927                 }
928         }
929
930         /* see the note in ads_do_paged_search - we *must* disable referrals */
931         ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
932
933         rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
934                                       search_attrs, 0, NULL, NULL, 
935                                       LDAP_NO_LIMIT,
936                                       (LDAPMessage **)res);
937
938         if (rc == LDAP_SIZELIMIT_EXCEEDED) {
939                 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
940                 rc = 0;
941         }
942
943  done:
944         talloc_destroy(ctx);
945         /* if/when we decide to utf8-encode attrs, take out this next line */
946         str_list_free(&search_attrs);
947         return ADS_ERROR(rc);
948 }
949 /**
950  * Do a general ADS search
951  * @param ads connection to ads server
952  * @param res ** which will contain results - free res* with ads_msgfree()
953  * @param expr Search expression
954  * @param attrs Attributes to retrieve
955  * @return status of search
956  **/
957  ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res, 
958                        const char *expr, const char **attrs)
959 {
960         return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE, 
961                              expr, attrs, res);
962 }
963
964 /**
965  * Do a search on a specific DistinguishedName
966  * @param ads connection to ads server
967  * @param res ** which will contain results - free res* with ads_msgfree()
968  * @param dn DistinguishName to search
969  * @param attrs Attributes to retrieve
970  * @return status of search
971  **/
972  ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res, 
973                           const char *dn, const char **attrs)
974 {
975         return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
976                              attrs, res);
977 }
978
979 /**
980  * Free up memory from a ads_search
981  * @param ads connection to ads server
982  * @param msg Search results to free
983  **/
984  void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
985 {
986         if (!msg) return;
987         ldap_msgfree(msg);
988 }
989
990 /**
991  * Free up memory from various ads requests
992  * @param ads connection to ads server
993  * @param mem Area to free
994  **/
995 void ads_memfree(ADS_STRUCT *ads, void *mem)
996 {
997         SAFE_FREE(mem);
998 }
999
1000 /**
1001  * Get a dn from search results
1002  * @param ads connection to ads server
1003  * @param msg Search result
1004  * @return dn string
1005  **/
1006  char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
1007 {
1008         char *utf8_dn, *unix_dn;
1009
1010         utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1011
1012         if (!utf8_dn) {
1013                 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1014                 return NULL;
1015         }
1016
1017         if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
1018                 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1019                         utf8_dn ));
1020                 return NULL;
1021         }
1022         ldap_memfree(utf8_dn);
1023         return unix_dn;
1024 }
1025
1026 /**
1027  * Get the parent from a dn
1028  * @param dn the dn to return the parent from
1029  * @return parent dn string
1030  **/
1031 char *ads_parent_dn(const char *dn)
1032 {
1033         char *p;
1034
1035         if (dn == NULL) {
1036                 return NULL;
1037         }
1038
1039         p = strchr(dn, ',');
1040
1041         if (p == NULL) {
1042                 return NULL;
1043         }
1044
1045         return p+1;
1046 }
1047
1048 /**
1049  * Find a machine account given a hostname
1050  * @param ads connection to ads server
1051  * @param res ** which will contain results - free res* with ads_msgfree()
1052  * @param host Hostname to search for
1053  * @return status of search
1054  **/
1055  ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1056                                   const char *machine)
1057 {
1058         ADS_STATUS status;
1059         char *expr;
1060         const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1061
1062         *res = NULL;
1063
1064         /* the easiest way to find a machine account anywhere in the tree
1065            is to look for hostname$ */
1066         if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1067                 DEBUG(1, ("asprintf failed!\n"));
1068                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1069         }
1070         
1071         status = ads_search(ads, res, expr, attrs);
1072         SAFE_FREE(expr);
1073         return status;
1074 }
1075
1076 /**
1077  * Initialize a list of mods to be used in a modify request
1078  * @param ctx An initialized TALLOC_CTX
1079  * @return allocated ADS_MODLIST
1080  **/
1081 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1082 {
1083 #define ADS_MODLIST_ALLOC_SIZE 10
1084         LDAPMod **mods;
1085         
1086         if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1087                 /* -1 is safety to make sure we don't go over the end.
1088                    need to reset it to NULL before doing ldap modify */
1089                 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1090         
1091         return (ADS_MODLIST)mods;
1092 }
1093
1094
1095 /*
1096   add an attribute to the list, with values list already constructed
1097 */
1098 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
1099                                   int mod_op, const char *name, 
1100                                   const void *_invals)
1101 {
1102         const void **invals = (const void **)_invals;
1103         int curmod;
1104         LDAPMod **modlist = (LDAPMod **) *mods;
1105         struct berval **ber_values = NULL;
1106         char **char_values = NULL;
1107
1108         if (!invals) {
1109                 mod_op = LDAP_MOD_DELETE;
1110         } else {
1111                 if (mod_op & LDAP_MOD_BVALUES)
1112                         ber_values = ads_dup_values(ctx, 
1113                                                 (const struct berval **)invals);
1114                 else
1115                         char_values = ads_push_strvals(ctx, 
1116                                                   (const char **) invals);
1117         }
1118
1119         /* find the first empty slot */
1120         for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1121              curmod++);
1122         if (modlist[curmod] == (LDAPMod *) -1) {
1123                 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1124                                 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1125                         return ADS_ERROR(LDAP_NO_MEMORY);
1126                 memset(&modlist[curmod], 0, 
1127                        ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1128                 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1129                 *mods = (ADS_MODLIST)modlist;
1130         }
1131                 
1132         if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1133                 return ADS_ERROR(LDAP_NO_MEMORY);
1134         modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1135         if (mod_op & LDAP_MOD_BVALUES) {
1136                 modlist[curmod]->mod_bvalues = ber_values;
1137         } else if (mod_op & LDAP_MOD_DELETE) {
1138                 modlist[curmod]->mod_values = NULL;
1139         } else {
1140                 modlist[curmod]->mod_values = char_values;
1141         }
1142
1143         modlist[curmod]->mod_op = mod_op;
1144         return ADS_ERROR(LDAP_SUCCESS);
1145 }
1146
1147 /**
1148  * Add a single string value to a mod list
1149  * @param ctx An initialized TALLOC_CTX
1150  * @param mods An initialized ADS_MODLIST
1151  * @param name The attribute name to add
1152  * @param val The value to add - NULL means DELETE
1153  * @return ADS STATUS indicating success of add
1154  **/
1155 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
1156                        const char *name, const char *val)
1157 {
1158         const char *values[2];
1159
1160         values[0] = val;
1161         values[1] = NULL;
1162
1163         if (!val)
1164                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1165         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1166 }
1167
1168 /**
1169  * Add an array of string values to a mod list
1170  * @param ctx An initialized TALLOC_CTX
1171  * @param mods An initialized ADS_MODLIST
1172  * @param name The attribute name to add
1173  * @param vals The array of string values to add - NULL means DELETE
1174  * @return ADS STATUS indicating success of add
1175  **/
1176 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1177                            const char *name, const char **vals)
1178 {
1179         if (!vals)
1180                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1181         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, 
1182                                name, (const void **) vals);
1183 }
1184
1185 #if 0
1186 /**
1187  * Add a single ber-encoded value to a mod list
1188  * @param ctx An initialized TALLOC_CTX
1189  * @param mods An initialized ADS_MODLIST
1190  * @param name The attribute name to add
1191  * @param val The value to add - NULL means DELETE
1192  * @return ADS STATUS indicating success of add
1193  **/
1194 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
1195                               const char *name, const struct berval *val)
1196 {
1197         const struct berval *values[2];
1198
1199         values[0] = val;
1200         values[1] = NULL;
1201         if (!val)
1202                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1203         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1204                                name, (const void **) values);
1205 }
1206 #endif
1207
1208 /**
1209  * Perform an ldap modify
1210  * @param ads connection to ads server
1211  * @param mod_dn DistinguishedName to modify
1212  * @param mods list of modifications to perform
1213  * @return status of modify
1214  **/
1215 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1216 {
1217         int ret,i;
1218         char *utf8_dn = NULL;
1219         /* 
1220            this control is needed to modify that contains a currently 
1221            non-existent attribute (but allowable for the object) to run
1222         */
1223         LDAPControl PermitModify = {
1224                 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1225                 {0, NULL},
1226                 (char) 1};
1227         LDAPControl *controls[2];
1228
1229         controls[0] = &PermitModify;
1230         controls[1] = NULL;
1231
1232         if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1233                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1234         }
1235
1236         /* find the end of the list, marked by NULL or -1 */
1237         for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1238         /* make sure the end of the list is NULL */
1239         mods[i] = NULL;
1240         ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1241                                 (LDAPMod **) mods, controls, NULL);
1242         SAFE_FREE(utf8_dn);
1243         return ADS_ERROR(ret);
1244 }
1245
1246 /**
1247  * Perform an ldap add
1248  * @param ads connection to ads server
1249  * @param new_dn DistinguishedName to add
1250  * @param mods list of attributes and values for DN
1251  * @return status of add
1252  **/
1253 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1254 {
1255         int ret, i;
1256         char *utf8_dn = NULL;
1257
1258         if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1259                 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1260                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1261         }
1262         
1263         /* find the end of the list, marked by NULL or -1 */
1264         for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1265         /* make sure the end of the list is NULL */
1266         mods[i] = NULL;
1267
1268         ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1269         SAFE_FREE(utf8_dn);
1270         return ADS_ERROR(ret);
1271 }
1272
1273 /**
1274  * Delete a DistinguishedName
1275  * @param ads connection to ads server
1276  * @param new_dn DistinguishedName to delete
1277  * @return status of delete
1278  **/
1279 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1280 {
1281         int ret;
1282         char *utf8_dn = NULL;
1283         if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1284                 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1285                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1286         }
1287         
1288         ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1289         SAFE_FREE(utf8_dn);
1290         return ADS_ERROR(ret);
1291 }
1292
1293 /**
1294  * Build an org unit string
1295  *  if org unit is Computers or blank then assume a container, otherwise
1296  *  assume a / separated list of organisational units.
1297  * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1298  * @param ads connection to ads server
1299  * @param org_unit Organizational unit
1300  * @return org unit string - caller must free
1301  **/
1302 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1303 {
1304         char *ret = NULL;
1305
1306         if (!org_unit || !*org_unit) {
1307
1308                 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1309
1310                 /* samba4 might not yet respond to a wellknownobject-query */
1311                 return ret ? ret : SMB_STRDUP("cn=Computers");
1312         }
1313         
1314         if (strequal(org_unit, "Computers")) {
1315                 return SMB_STRDUP("cn=Computers");
1316         }
1317
1318         /* jmcd: removed "\\" from the separation chars, because it is
1319            needed as an escape for chars like '#' which are valid in an
1320            OU name */
1321         return ads_build_path(org_unit, "/", "ou=", 1);
1322 }
1323
1324 /**
1325  * Get a org unit string for a well-known GUID
1326  * @param ads connection to ads server
1327  * @param wknguid Well known GUID
1328  * @return org unit string - caller must free
1329  **/
1330 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1331 {
1332         ADS_STATUS status;
1333         LDAPMessage *res = NULL;
1334         char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1335                 **bind_dn_exp = NULL;
1336         const char *attrs[] = {"distinguishedName", NULL};
1337         int new_ln, wkn_ln, bind_ln, i;
1338
1339         if (wknguid == NULL) {
1340                 return NULL;
1341         }
1342
1343         if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1344                 DEBUG(1, ("asprintf failed!\n"));
1345                 return NULL;
1346         }
1347
1348         status = ads_search_dn(ads, &res, base, attrs);
1349         if (!ADS_ERR_OK(status)) {
1350                 DEBUG(1,("Failed while searching for: %s\n", base));
1351                 goto out;
1352         }
1353
1354         if (ads_count_replies(ads, res) != 1) {
1355                 goto out;
1356         }
1357
1358         /* substitute the bind-path from the well-known-guid-search result */
1359         wkn_dn = ads_get_dn(ads, res);
1360         if (!wkn_dn) {
1361                 goto out;
1362         }
1363
1364         wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1365         if (!wkn_dn_exp) {
1366                 goto out;
1367         }
1368
1369         bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1370         if (!bind_dn_exp) {
1371                 goto out;
1372         }
1373
1374         for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1375                 ;
1376         for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1377                 ;
1378
1379         new_ln = wkn_ln - bind_ln;
1380
1381         ret = SMB_STRDUP(wkn_dn_exp[0]);
1382         if (!ret) {
1383                 goto out;
1384         }
1385
1386         for (i=1; i < new_ln; i++) {
1387                 char *s = NULL;
1388                 
1389                 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1390                         SAFE_FREE(ret);
1391                         goto out;
1392                 }
1393
1394                 SAFE_FREE(ret);
1395                 ret = SMB_STRDUP(s);
1396                 free(s);
1397                 if (!ret) {
1398                         goto out;
1399                 }
1400         }
1401
1402  out:
1403         SAFE_FREE(base);
1404         ads_msgfree(ads, res);
1405         ads_memfree(ads, wkn_dn);
1406         if (wkn_dn_exp) {
1407                 ldap_value_free(wkn_dn_exp);
1408         }
1409         if (bind_dn_exp) {
1410                 ldap_value_free(bind_dn_exp);
1411         }
1412
1413         return ret;
1414 }
1415
1416 /**
1417  * Adds (appends) an item to an attribute array, rather then
1418  * replacing the whole list
1419  * @param ctx An initialized TALLOC_CTX
1420  * @param mods An initialized ADS_MODLIST
1421  * @param name name of the ldap attribute to append to
1422  * @param vals an array of values to add
1423  * @return status of addition
1424  **/
1425
1426 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1427                                 const char *name, const char **vals)
1428 {
1429         return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1430                                (const void *) vals);
1431 }
1432
1433 /**
1434  * Determines the computer account's current KVNO via an LDAP lookup
1435  * @param ads An initialized ADS_STRUCT
1436  * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1437  * @return the kvno for the computer account, or -1 in case of a failure.
1438  **/
1439
1440 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1441 {
1442         LDAPMessage *res = NULL;
1443         uint32 kvno = (uint32)-1;      /* -1 indicates a failure */
1444         char *filter;
1445         const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1446         char *dn_string = NULL;
1447         ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1448
1449         DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1450         if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1451                 return kvno;
1452         }
1453         ret = ads_search(ads, &res, filter, attrs);
1454         SAFE_FREE(filter);
1455         if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1456                 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1457                 ads_msgfree(ads, res);
1458                 return kvno;
1459         }
1460
1461         dn_string = ads_get_dn(ads, res);
1462         if (!dn_string) {
1463                 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1464                 ads_msgfree(ads, res);
1465                 return kvno;
1466         }
1467         DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1468         ads_memfree(ads, dn_string);
1469
1470         /* ---------------------------------------------------------
1471          * 0 is returned as a default KVNO from this point on...
1472          * This is done because Windows 2000 does not support key
1473          * version numbers.  Chances are that a failure in the next
1474          * step is simply due to Windows 2000 being used for a
1475          * domain controller. */
1476         kvno = 0;
1477
1478         if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1479                 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1480                 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1481                 ads_msgfree(ads, res);
1482                 return kvno;
1483         }
1484
1485         /* Success */
1486         DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1487         ads_msgfree(ads, res);
1488         return kvno;
1489 }
1490
1491 /**
1492  * This clears out all registered spn's for a given hostname
1493  * @param ads An initilaized ADS_STRUCT
1494  * @param machine_name the NetBIOS name of the computer.
1495  * @return 0 upon success, non-zero otherwise.
1496  **/
1497
1498 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1499 {
1500         TALLOC_CTX *ctx;
1501         LDAPMessage *res = NULL;
1502         ADS_MODLIST mods;
1503         const char *servicePrincipalName[1] = {NULL};
1504         ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1505         char *dn_string = NULL;
1506
1507         ret = ads_find_machine_acct(ads, &res, machine_name);
1508         if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1509                 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1510                 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1511                 ads_msgfree(ads, res);
1512                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1513         }
1514
1515         DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1516         ctx = talloc_init("ads_clear_service_principal_names");
1517         if (!ctx) {
1518                 ads_msgfree(ads, res);
1519                 return ADS_ERROR(LDAP_NO_MEMORY);
1520         }
1521
1522         if (!(mods = ads_init_mods(ctx))) {
1523                 talloc_destroy(ctx);
1524                 ads_msgfree(ads, res);
1525                 return ADS_ERROR(LDAP_NO_MEMORY);
1526         }
1527         ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1528         if (!ADS_ERR_OK(ret)) {
1529                 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1530                 ads_msgfree(ads, res);
1531                 talloc_destroy(ctx);
1532                 return ret;
1533         }
1534         dn_string = ads_get_dn(ads, res);
1535         if (!dn_string) {
1536                 talloc_destroy(ctx);
1537                 ads_msgfree(ads, res);
1538                 return ADS_ERROR(LDAP_NO_MEMORY);
1539         }
1540         ret = ads_gen_mod(ads, dn_string, mods);
1541         ads_memfree(ads,dn_string);
1542         if (!ADS_ERR_OK(ret)) {
1543                 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1544                         machine_name));
1545                 ads_msgfree(ads, res);
1546                 talloc_destroy(ctx);
1547                 return ret;
1548         }
1549
1550         ads_msgfree(ads, res);
1551         talloc_destroy(ctx);
1552         return ret;
1553 }
1554
1555 /**
1556  * This adds a service principal name to an existing computer account
1557  * (found by hostname) in AD.
1558  * @param ads An initialized ADS_STRUCT
1559  * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1560  * @param my_fqdn The fully qualified DNS name of the machine
1561  * @param spn A string of the service principal to add, i.e. 'host'
1562  * @return 0 upon sucess, or non-zero if a failure occurs
1563  **/
1564
1565 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, 
1566                                           const char *my_fqdn, const char *spn)
1567 {
1568         ADS_STATUS ret;
1569         TALLOC_CTX *ctx;
1570         LDAPMessage *res = NULL;
1571         char *psp1, *psp2;
1572         ADS_MODLIST mods;
1573         char *dn_string = NULL;
1574         const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1575
1576         ret = ads_find_machine_acct(ads, &res, machine_name);
1577         if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1578                 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1579                         machine_name));
1580                 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1581                         spn, machine_name, ads->config.realm));
1582                 ads_msgfree(ads, res);
1583                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1584         }
1585
1586         DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1587         if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1588                 ads_msgfree(ads, res);
1589                 return ADS_ERROR(LDAP_NO_MEMORY);
1590         }
1591
1592         /* add short name spn */
1593         
1594         if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1595                 talloc_destroy(ctx);
1596                 ads_msgfree(ads, res);
1597                 return ADS_ERROR(LDAP_NO_MEMORY);
1598         }
1599         strupper_m(psp1);
1600         strlower_m(&psp1[strlen(spn)]);
1601         servicePrincipalName[0] = psp1;
1602         
1603         DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", 
1604                 psp1, machine_name));
1605
1606
1607         /* add fully qualified spn */
1608         
1609         if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1610                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1611                 goto out;
1612         }
1613         strupper_m(psp2);
1614         strlower_m(&psp2[strlen(spn)]);
1615         servicePrincipalName[1] = psp2;
1616
1617         DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", 
1618                 psp2, machine_name));
1619
1620         if ( (mods = ads_init_mods(ctx)) == NULL ) {
1621                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1622                 goto out;
1623         }
1624         
1625         ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1626         if (!ADS_ERR_OK(ret)) {
1627                 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1628                 goto out;
1629         }
1630         
1631         if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1632                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1633                 goto out;
1634         }
1635         
1636         ret = ads_gen_mod(ads, dn_string, mods);
1637         ads_memfree(ads,dn_string);
1638         if (!ADS_ERR_OK(ret)) {
1639                 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1640                 goto out;
1641         }
1642
1643  out:
1644         TALLOC_FREE( ctx );
1645         ads_msgfree(ads, res);
1646         return ret;
1647 }
1648
1649 /**
1650  * adds a machine account to the ADS server
1651  * @param ads An intialized ADS_STRUCT
1652  * @param machine_name - the NetBIOS machine name of this account.
1653  * @param account_type A number indicating the type of account to create
1654  * @param org_unit The LDAP path in which to place this account
1655  * @return 0 upon success, or non-zero otherwise
1656 **/
1657
1658 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name, 
1659                                    const char *org_unit)
1660 {
1661         ADS_STATUS ret;
1662         char *samAccountName, *controlstr;
1663         TALLOC_CTX *ctx;
1664         ADS_MODLIST mods;
1665         char *machine_escaped = NULL;
1666         char *new_dn;
1667         const char *objectClass[] = {"top", "person", "organizationalPerson",
1668                                      "user", "computer", NULL};
1669         LDAPMessage *res = NULL;
1670         uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1671                                 UF_DONT_EXPIRE_PASSWD |\
1672                                 UF_ACCOUNTDISABLE );
1673                               
1674         if (!(ctx = talloc_init("ads_add_machine_acct")))
1675                 return ADS_ERROR(LDAP_NO_MEMORY);
1676
1677         ret = ADS_ERROR(LDAP_NO_MEMORY);
1678
1679         machine_escaped = escape_rdn_val_string_alloc(machine_name);
1680         if (!machine_escaped) {
1681                 goto done;
1682         }
1683
1684         new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1685         samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1686
1687         if ( !new_dn || !samAccountName ) {
1688                 goto done;
1689         }
1690         
1691 #ifndef ENCTYPE_ARCFOUR_HMAC
1692         acct_control |= UF_USE_DES_KEY_ONLY;
1693 #endif
1694
1695         if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1696                 goto done;
1697         }
1698
1699         if (!(mods = ads_init_mods(ctx))) {
1700                 goto done;
1701         }
1702         
1703         ads_mod_str(ctx, &mods, "cn", machine_name);
1704         ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1705         ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1706         ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1707
1708         ret = ads_gen_add(ads, new_dn, mods);
1709
1710 done:
1711         SAFE_FREE(machine_escaped);
1712         ads_msgfree(ads, res);
1713         talloc_destroy(ctx);
1714         
1715         return ret;
1716 }
1717
1718 /**
1719  * move a machine account to another OU on the ADS server
1720  * @param ads - An intialized ADS_STRUCT
1721  * @param machine_name - the NetBIOS machine name of this account.
1722  * @param org_unit - The LDAP path in which to place this account
1723  * @param moved - whether we moved the machine account (optional)
1724  * @return 0 upon success, or non-zero otherwise
1725 **/
1726
1727 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name, 
1728                                  const char *org_unit, BOOL *moved)
1729 {
1730         ADS_STATUS rc;
1731         int ldap_status;
1732         LDAPMessage *res = NULL;
1733         char *filter = NULL;
1734         char *computer_dn = NULL;
1735         char *parent_dn;
1736         char *computer_rdn = NULL;
1737         BOOL need_move = False;
1738
1739         if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1740                 rc = ADS_ERROR(LDAP_NO_MEMORY);
1741                 goto done;
1742         }
1743
1744         /* Find pre-existing machine */
1745         rc = ads_search(ads, &res, filter, NULL);
1746         if (!ADS_ERR_OK(rc)) {
1747                 goto done;
1748         }
1749
1750         computer_dn = ads_get_dn(ads, res);
1751         if (!computer_dn) {
1752                 rc = ADS_ERROR(LDAP_NO_MEMORY);
1753                 goto done;
1754         }
1755
1756         parent_dn = ads_parent_dn(computer_dn);
1757         if (strequal(parent_dn, org_unit)) {
1758                 goto done;
1759         }
1760
1761         need_move = True;
1762
1763         if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
1764                 rc = ADS_ERROR(LDAP_NO_MEMORY);
1765                 goto done;
1766         }
1767
1768         ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn, 
1769                                     org_unit, 1, NULL, NULL);
1770         rc = ADS_ERROR(ldap_status);
1771
1772 done:
1773         ads_msgfree(ads, res);
1774         SAFE_FREE(filter);
1775         SAFE_FREE(computer_dn);
1776         SAFE_FREE(computer_rdn);
1777
1778         if (!ADS_ERR_OK(rc)) {
1779                 need_move = False;
1780         }
1781
1782         if (moved) {
1783                 *moved = need_move;
1784         }
1785
1786         return rc;
1787 }
1788
1789 /*
1790   dump a binary result from ldap
1791 */
1792 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
1793 {
1794         int i, j;
1795         for (i=0; values[i]; i++) {
1796                 printf("%s: ", field);
1797                 for (j=0; j<values[i]->bv_len; j++) {
1798                         printf("%02X", (unsigned char)values[i]->bv_val[j]);
1799                 }
1800                 printf("\n");
1801         }
1802 }
1803
1804 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
1805 {
1806         int i;
1807         UUID_FLAT guid;
1808         for (i=0; values[i]; i++) {
1809                 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1810                 printf("%s: %s\n", field, 
1811                        smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1812         }
1813 }
1814
1815 /*
1816   dump a sid result from ldap
1817 */
1818 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
1819 {
1820         int i;
1821         for (i=0; values[i]; i++) {
1822                 DOM_SID sid;
1823                 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1824                 printf("%s: %s\n", field, sid_string_static(&sid));
1825         }
1826 }
1827
1828 /*
1829   dump ntSecurityDescriptor
1830 */
1831 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
1832 {
1833         prs_struct ps;
1834         
1835         SEC_DESC   *psd = 0;
1836         TALLOC_CTX *ctx = 0;
1837
1838         if (!(ctx = talloc_init("sec_io_desc")))
1839                 return;
1840
1841         /* prepare data */
1842         prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1843         prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1844         prs_set_offset(&ps,0);
1845
1846         /* parse secdesc */
1847         if (!sec_io_desc("sd", &psd, &ps, 1)) {
1848                 prs_mem_free(&ps);
1849                 talloc_destroy(ctx);
1850                 return;
1851         }
1852         if (psd) {
1853                 ads_disp_sd(ads, ctx, psd);
1854         }
1855
1856         prs_mem_free(&ps);
1857         talloc_destroy(ctx);
1858 }
1859
1860 /*
1861   dump a string result from ldap
1862 */
1863 static void dump_string(const char *field, char **values)
1864 {
1865         int i;
1866         for (i=0; values[i]; i++) {
1867                 printf("%s: %s\n", field, values[i]);
1868         }
1869 }
1870
1871 /*
1872   dump a field from LDAP on stdout
1873   used for debugging
1874 */
1875
1876 static BOOL ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
1877 {
1878         const struct {
1879                 const char *name;
1880                 BOOL string;
1881                 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
1882         } handlers[] = {
1883                 {"objectGUID", False, dump_guid},
1884                 {"netbootGUID", False, dump_guid},
1885                 {"nTSecurityDescriptor", False, dump_sd},
1886                 {"dnsRecord", False, dump_binary},
1887                 {"objectSid", False, dump_sid},
1888                 {"tokenGroups", False, dump_sid},
1889                 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1890                 {"tokengroupsGlobalandUniversal", False, dump_sid},
1891                 {"mS-DS-CreatorSID", False, dump_sid},
1892                 {NULL, True, NULL}
1893         };
1894         int i;
1895
1896         if (!field) { /* must be end of an entry */
1897                 printf("\n");
1898                 return False;
1899         }
1900
1901         for (i=0; handlers[i].name; i++) {
1902                 if (StrCaseCmp(handlers[i].name, field) == 0) {
1903                         if (!values) /* first time, indicate string or not */
1904                                 return handlers[i].string;
1905                         handlers[i].handler(ads, field, (struct berval **) values);
1906                         break;
1907                 }
1908         }
1909         if (!handlers[i].name) {
1910                 if (!values) /* first time, indicate string conversion */
1911                         return True;
1912                 dump_string(field, (char **)values);
1913         }
1914         return False;
1915 }
1916
1917 /**
1918  * Dump a result from LDAP on stdout
1919  *  used for debugging
1920  * @param ads connection to ads server
1921  * @param res Results to dump
1922  **/
1923
1924  void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
1925 {
1926         ads_process_results(ads, res, ads_dump_field, NULL);
1927 }
1928
1929 /**
1930  * Walk through results, calling a function for each entry found.
1931  *  The function receives a field name, a berval * array of values,
1932  *  and a data area passed through from the start.  The function is
1933  *  called once with null for field and values at the end of each
1934  *  entry.
1935  * @param ads connection to ads server
1936  * @param res Results to process
1937  * @param fn Function for processing each result
1938  * @param data_area user-defined area to pass to function
1939  **/
1940  void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
1941                           BOOL(*fn)(ADS_STRUCT *, char *, void **, void *),
1942                           void *data_area)
1943 {
1944         LDAPMessage *msg;
1945         TALLOC_CTX *ctx;
1946
1947         if (!(ctx = talloc_init("ads_process_results")))
1948                 return;
1949
1950         for (msg = ads_first_entry(ads, res); msg; 
1951              msg = ads_next_entry(ads, msg)) {
1952                 char *utf8_field;
1953                 BerElement *b;
1954         
1955                 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
1956                                                      (LDAPMessage *)msg,&b); 
1957                      utf8_field;
1958                      utf8_field=ldap_next_attribute(ads->ldap.ld,
1959                                                     (LDAPMessage *)msg,b)) {
1960                         struct berval **ber_vals;
1961                         char **str_vals, **utf8_vals;
1962                         char *field;
1963                         BOOL string; 
1964
1965                         pull_utf8_talloc(ctx, &field, utf8_field);
1966                         string = fn(ads, field, NULL, data_area);
1967
1968                         if (string) {
1969                                 utf8_vals = ldap_get_values(ads->ldap.ld,
1970                                                  (LDAPMessage *)msg, field);
1971                                 str_vals = ads_pull_strvals(ctx, 
1972                                                   (const char **) utf8_vals);
1973                                 fn(ads, field, (void **) str_vals, data_area);
1974                                 ldap_value_free(utf8_vals);
1975                         } else {
1976                                 ber_vals = ldap_get_values_len(ads->ldap.ld, 
1977                                                  (LDAPMessage *)msg, field);
1978                                 fn(ads, field, (void **) ber_vals, data_area);
1979
1980                                 ldap_value_free_len(ber_vals);
1981                         }
1982                         ldap_memfree(utf8_field);
1983                 }
1984                 ber_free(b, 0);
1985                 talloc_free_children(ctx);
1986                 fn(ads, NULL, NULL, data_area); /* completed an entry */
1987
1988         }
1989         talloc_destroy(ctx);
1990 }
1991
1992 /**
1993  * count how many replies are in a LDAPMessage
1994  * @param ads connection to ads server
1995  * @param res Results to count
1996  * @return number of replies
1997  **/
1998 int ads_count_replies(ADS_STRUCT *ads, void *res)
1999 {
2000         return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2001 }
2002
2003 /**
2004  * pull the first entry from a ADS result
2005  * @param ads connection to ads server
2006  * @param res Results of search
2007  * @return first entry from result
2008  **/
2009  LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2010 {
2011         return ldap_first_entry(ads->ldap.ld, res);
2012 }
2013
2014 /**
2015  * pull the next entry from a ADS result
2016  * @param ads connection to ads server
2017  * @param res Results of search
2018  * @return next entry from result
2019  **/
2020  LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2021 {
2022         return ldap_next_entry(ads->ldap.ld, res);
2023 }
2024
2025 /**
2026  * pull a single string from a ADS result
2027  * @param ads connection to ads server
2028  * @param mem_ctx TALLOC_CTX to use for allocating result string
2029  * @param msg Results of search
2030  * @param field Attribute to retrieve
2031  * @return Result string in talloc context
2032  **/
2033  char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2034                        const char *field)
2035 {
2036         char **values;
2037         char *ret = NULL;
2038         char *ux_string;
2039         size_t rc;
2040
2041         values = ldap_get_values(ads->ldap.ld, msg, field);
2042         if (!values)
2043                 return NULL;
2044         
2045         if (values[0]) {
2046                 rc = pull_utf8_talloc(mem_ctx, &ux_string, 
2047                                       values[0]);
2048                 if (rc != (size_t)-1)
2049                         ret = ux_string;
2050                 
2051         }
2052         ldap_value_free(values);
2053         return ret;
2054 }
2055
2056 /**
2057  * pull an array of strings from a ADS result
2058  * @param ads connection to ads server
2059  * @param mem_ctx TALLOC_CTX to use for allocating result string
2060  * @param msg Results of search
2061  * @param field Attribute to retrieve
2062  * @return Result strings in talloc context
2063  **/
2064  char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2065                          LDAPMessage *msg, const char *field,
2066                          size_t *num_values)
2067 {
2068         char **values;
2069         char **ret = NULL;
2070         int i;
2071
2072         values = ldap_get_values(ads->ldap.ld, msg, field);
2073         if (!values)
2074                 return NULL;
2075
2076         *num_values = ldap_count_values(values);
2077
2078         ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2079         if (!ret) {
2080                 ldap_value_free(values);
2081                 return NULL;
2082         }
2083
2084         for (i=0;i<*num_values;i++) {
2085                 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2086                         ldap_value_free(values);
2087                         return NULL;
2088                 }
2089         }
2090         ret[i] = NULL;
2091
2092         ldap_value_free(values);
2093         return ret;
2094 }
2095
2096 /**
2097  * pull an array of strings from a ADS result 
2098  *  (handle large multivalue attributes with range retrieval)
2099  * @param ads connection to ads server
2100  * @param mem_ctx TALLOC_CTX to use for allocating result string
2101  * @param msg Results of search
2102  * @param field Attribute to retrieve
2103  * @param current_strings strings returned by a previous call to this function
2104  * @param next_attribute The next query should ask for this attribute
2105  * @param num_values How many values did we get this time?
2106  * @param more_values Are there more values to get?
2107  * @return Result strings in talloc context
2108  **/
2109  char **ads_pull_strings_range(ADS_STRUCT *ads, 
2110                                TALLOC_CTX *mem_ctx,
2111                                LDAPMessage *msg, const char *field,
2112                                char **current_strings,
2113                                const char **next_attribute,
2114                                size_t *num_strings,
2115                                BOOL *more_strings)
2116 {
2117         char *attr;
2118         char *expected_range_attrib, *range_attr;
2119         BerElement *ptr = NULL;
2120         char **strings;
2121         char **new_strings;
2122         size_t num_new_strings;
2123         unsigned long int range_start;
2124         unsigned long int range_end;
2125         
2126         /* we might have been given the whole lot anyway */
2127         if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2128                 *more_strings = False;
2129                 return strings;
2130         }
2131
2132         expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2133
2134         /* look for Range result */
2135         for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr); 
2136              attr; 
2137              attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2138                 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2139                 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2140                         range_attr = attr;
2141                         break;
2142                 }
2143                 ldap_memfree(attr);
2144         }
2145         if (!attr) {
2146                 ber_free(ptr, 0);
2147                 /* nothing here - this field is just empty */
2148                 *more_strings = False;
2149                 return NULL;
2150         }
2151         
2152         if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu", 
2153                    &range_start, &range_end) == 2) {
2154                 *more_strings = True;
2155         } else {
2156                 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*", 
2157                            &range_start) == 1) {
2158                         *more_strings = False;
2159                 } else {
2160                         DEBUG(1, ("ads_pull_strings_range:  Cannot parse Range attriubte (%s)\n", 
2161                                   range_attr));
2162                         ldap_memfree(range_attr);
2163                         *more_strings = False;
2164                         return NULL;
2165                 }
2166         }
2167
2168         if ((*num_strings) != range_start) {
2169                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2170                           " - aborting range retreival\n",
2171                           range_attr, (unsigned int)(*num_strings) + 1, range_start));
2172                 ldap_memfree(range_attr);
2173                 *more_strings = False;
2174                 return NULL;
2175         }
2176
2177         new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2178         
2179         if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2180                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2181                           "strings in this bunch, but we only got %lu - aborting range retreival\n",
2182                           range_attr, (unsigned long int)range_end - range_start + 1, 
2183                           (unsigned long int)num_new_strings));
2184                 ldap_memfree(range_attr);
2185                 *more_strings = False;
2186                 return NULL;
2187         }
2188
2189         strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2190                                  *num_strings + num_new_strings);
2191         
2192         if (strings == NULL) {
2193                 ldap_memfree(range_attr);
2194                 *more_strings = False;
2195                 return NULL;
2196         }
2197         
2198         if (new_strings && num_new_strings) {
2199                 memcpy(&strings[*num_strings], new_strings,
2200                        sizeof(*new_strings) * num_new_strings);
2201         }
2202
2203         (*num_strings) += num_new_strings;
2204
2205         if (*more_strings) {
2206                 *next_attribute = talloc_asprintf(mem_ctx,
2207                                                   "%s;range=%d-*", 
2208                                                   field,
2209                                                   (int)*num_strings);
2210                 
2211                 if (!*next_attribute) {
2212                         DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2213                         ldap_memfree(range_attr);
2214                         *more_strings = False;
2215                         return NULL;
2216                 }
2217         }
2218
2219         ldap_memfree(range_attr);
2220
2221         return strings;
2222 }
2223
2224 /**
2225  * pull a single uint32 from a ADS result
2226  * @param ads connection to ads server
2227  * @param msg Results of search
2228  * @param field Attribute to retrieve
2229  * @param v Pointer to int to store result
2230  * @return boolean inidicating success
2231 */
2232  BOOL ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2233                       uint32 *v)
2234 {
2235         char **values;
2236
2237         values = ldap_get_values(ads->ldap.ld, msg, field);
2238         if (!values)
2239                 return False;
2240         if (!values[0]) {
2241                 ldap_value_free(values);
2242                 return False;
2243         }
2244
2245         *v = atoi(values[0]);
2246         ldap_value_free(values);
2247         return True;
2248 }
2249
2250 /**
2251  * pull a single objectGUID from an ADS result
2252  * @param ads connection to ADS server
2253  * @param msg results of search
2254  * @param guid 37-byte area to receive text guid
2255  * @return boolean indicating success
2256  **/
2257  BOOL ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2258 {
2259         char **values;
2260         UUID_FLAT flat_guid;
2261
2262         values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2263         if (!values)
2264                 return False;
2265         
2266         if (values[0]) {
2267                 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2268                 smb_uuid_unpack(flat_guid, guid);
2269                 ldap_value_free(values);
2270                 return True;
2271         }
2272         ldap_value_free(values);
2273         return False;
2274
2275 }
2276
2277
2278 /**
2279  * pull a single DOM_SID from a ADS result
2280  * @param ads connection to ads server
2281  * @param msg Results of search
2282  * @param field Attribute to retrieve
2283  * @param sid Pointer to sid to store result
2284  * @return boolean inidicating success
2285 */
2286  BOOL ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2287                    DOM_SID *sid)
2288 {
2289         struct berval **values;
2290         BOOL ret = False;
2291
2292         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2293
2294         if (!values)
2295                 return False;
2296
2297         if (values[0])
2298                 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2299         
2300         ldap_value_free_len(values);
2301         return ret;
2302 }
2303
2304 /**
2305  * pull an array of DOM_SIDs from a ADS result
2306  * @param ads connection to ads server
2307  * @param mem_ctx TALLOC_CTX for allocating sid array
2308  * @param msg Results of search
2309  * @param field Attribute to retrieve
2310  * @param sids pointer to sid array to allocate
2311  * @return the count of SIDs pulled
2312  **/
2313  int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2314                    LDAPMessage *msg, const char *field, DOM_SID **sids)
2315 {
2316         struct berval **values;
2317         BOOL ret;
2318         int count, i;
2319
2320         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2321
2322         if (!values)
2323                 return 0;
2324
2325         for (i=0; values[i]; i++)
2326                 /* nop */ ;
2327
2328         if (i) {
2329                 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2330                 if (!(*sids)) {
2331                         ldap_value_free_len(values);
2332                         return 0;
2333                 }
2334         } else {
2335                 (*sids) = NULL;
2336         }
2337
2338         count = 0;
2339         for (i=0; values[i]; i++) {
2340                 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2341                 if (ret) {
2342                         fstring sid;
2343                         DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2344                         count++;
2345                 }
2346         }
2347         
2348         ldap_value_free_len(values);
2349         return count;
2350 }
2351
2352 /**
2353  * pull a SEC_DESC from a ADS result
2354  * @param ads connection to ads server
2355  * @param mem_ctx TALLOC_CTX for allocating sid array
2356  * @param msg Results of search
2357  * @param field Attribute to retrieve
2358  * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2359  * @return boolean inidicating success
2360 */
2361  BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2362                   LDAPMessage *msg, const char *field, SEC_DESC **sd)
2363 {
2364         struct berval **values;
2365         BOOL ret = False;
2366
2367         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2368
2369         if (!values) return False;
2370
2371         if (values[0]) {
2372                 prs_struct ps;
2373                 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2374                 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2375                 prs_set_offset(&ps,0);
2376
2377                 ret = sec_io_desc("sd", sd, &ps, 1);
2378                 prs_mem_free(&ps);
2379         }
2380         
2381         ldap_value_free_len(values);
2382         return ret;
2383 }
2384
2385 /* 
2386  * in order to support usernames longer than 21 characters we need to 
2387  * use both the sAMAccountName and the userPrincipalName attributes 
2388  * It seems that not all users have the userPrincipalName attribute set
2389  *
2390  * @param ads connection to ads server
2391  * @param mem_ctx TALLOC_CTX for allocating sid array
2392  * @param msg Results of search
2393  * @return the username
2394  */
2395  char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2396                          LDAPMessage *msg)
2397 {
2398 #if 0   /* JERRY */
2399         char *ret, *p;
2400
2401         /* lookup_name() only works on the sAMAccountName to 
2402            returning the username portion of userPrincipalName
2403            breaks winbindd_getpwnam() */
2404
2405         ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2406         if (ret && (p = strchr_m(ret, '@'))) {
2407                 *p = 0;
2408                 return ret;
2409         }
2410 #endif
2411         return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2412 }
2413
2414
2415 /**
2416  * find the update serial number - this is the core of the ldap cache
2417  * @param ads connection to ads server
2418  * @param ads connection to ADS server
2419  * @param usn Pointer to retrieved update serial number
2420  * @return status of search
2421  **/
2422 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2423 {
2424         const char *attrs[] = {"highestCommittedUSN", NULL};
2425         ADS_STATUS status;
2426         LDAPMessage *res;
2427
2428         status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2429         if (!ADS_ERR_OK(status)) 
2430                 return status;
2431
2432         if (ads_count_replies(ads, res) != 1) {
2433                 ads_msgfree(ads, res);
2434                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2435         }
2436
2437         if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2438                 ads_msgfree(ads, res);
2439                 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2440         }
2441
2442         ads_msgfree(ads, res);
2443         return ADS_SUCCESS;
2444 }
2445
2446 /* parse a ADS timestring - typical string is
2447    '20020917091222.0Z0' which means 09:12.22 17th September
2448    2002, timezone 0 */
2449 static time_t ads_parse_time(const char *str)
2450 {
2451         struct tm tm;
2452
2453         ZERO_STRUCT(tm);
2454
2455         if (sscanf(str, "%4d%2d%2d%2d%2d%2d", 
2456                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
2457                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2458                 return 0;
2459         }
2460         tm.tm_year -= 1900;
2461         tm.tm_mon -= 1;
2462
2463         return timegm(&tm);
2464 }
2465
2466 /********************************************************************
2467 ********************************************************************/
2468
2469 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2470 {
2471         const char *attrs[] = {"currentTime", NULL};
2472         ADS_STATUS status;
2473         LDAPMessage *res;
2474         char *timestr;
2475         TALLOC_CTX *ctx;
2476         ADS_STRUCT *ads_s = ads;
2477
2478         if (!(ctx = talloc_init("ads_current_time"))) {
2479                 return ADS_ERROR(LDAP_NO_MEMORY);
2480         }
2481
2482         /* establish a new ldap tcp session if necessary */
2483
2484         if ( !ads->ldap.ld ) {
2485                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2486                         ads->server.ldap_server )) == NULL )
2487                 {
2488                         goto done;
2489                 }
2490                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2491                 status = ads_connect( ads_s );
2492                 if ( !ADS_ERR_OK(status))
2493                         goto done;
2494         }
2495
2496         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2497         if (!ADS_ERR_OK(status)) {
2498                 goto done;
2499         }
2500
2501         timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2502         if (!timestr) {
2503                 ads_msgfree(ads_s, res);
2504                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2505                 goto done;
2506         }
2507
2508         /* but save the time and offset in the original ADS_STRUCT */   
2509         
2510         ads->config.current_time = ads_parse_time(timestr);
2511
2512         if (ads->config.current_time != 0) {
2513                 ads->auth.time_offset = ads->config.current_time - time(NULL);
2514                 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2515         }
2516
2517         ads_msgfree(ads, res);
2518
2519         status = ADS_SUCCESS;
2520
2521 done:
2522         /* free any temporary ads connections */
2523         if ( ads_s != ads ) {
2524                 ads_destroy( &ads_s );
2525         }
2526         talloc_destroy(ctx);
2527
2528         return status;
2529 }
2530
2531 /********************************************************************
2532 ********************************************************************/
2533
2534 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2535 {
2536         const char *attrs[] = {"domainFunctionality", NULL};
2537         ADS_STATUS status;
2538         LDAPMessage *res;
2539         ADS_STRUCT *ads_s = ads;
2540         
2541         *val = DS_DOMAIN_FUNCTION_2000;
2542
2543         /* establish a new ldap tcp session if necessary */
2544
2545         if ( !ads->ldap.ld ) {
2546                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2547                         ads->server.ldap_server )) == NULL )
2548                 {
2549                         goto done;
2550                 }
2551                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2552                 status = ads_connect( ads_s );
2553                 if ( !ADS_ERR_OK(status))
2554                         goto done;
2555         }
2556
2557         /* If the attribute does not exist assume it is a Windows 2000 
2558            functional domain */
2559            
2560         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2561         if (!ADS_ERR_OK(status)) {
2562                 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2563                         status = ADS_SUCCESS;
2564                 }
2565                 goto done;
2566         }
2567
2568         if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2569                 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2570         }
2571         DEBUG(3,("ads_domain_func_level: %d\n", *val));
2572
2573         
2574         ads_msgfree(ads, res);
2575
2576 done:
2577         /* free any temporary ads connections */
2578         if ( ads_s != ads ) {
2579                 ads_destroy( &ads_s );
2580         }
2581
2582         return status;
2583 }
2584
2585 /**
2586  * find the domain sid for our domain
2587  * @param ads connection to ads server
2588  * @param sid Pointer to domain sid
2589  * @return status of search
2590  **/
2591 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2592 {
2593         const char *attrs[] = {"objectSid", NULL};
2594         LDAPMessage *res;
2595         ADS_STATUS rc;
2596
2597         rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
2598                            attrs, &res);
2599         if (!ADS_ERR_OK(rc)) return rc;
2600         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2601                 ads_msgfree(ads, res);
2602                 return ADS_ERROR_SYSTEM(ENOENT);
2603         }
2604         ads_msgfree(ads, res);
2605         
2606         return ADS_SUCCESS;
2607 }
2608
2609 /**
2610  * find our site name 
2611  * @param ads connection to ads server
2612  * @param mem_ctx Pointer to talloc context
2613  * @param site_name Pointer to the sitename
2614  * @return status of search
2615  **/
2616 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2617 {
2618         ADS_STATUS status;
2619         LDAPMessage *res;
2620         const char *dn, *service_name;
2621         const char *attrs[] = { "dsServiceName", NULL };
2622
2623         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2624         if (!ADS_ERR_OK(status)) {
2625                 return status;
2626         }
2627
2628         service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2629         if (service_name == NULL) {
2630                 ads_msgfree(ads, res);
2631                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2632         }
2633
2634         ads_msgfree(ads, res);
2635
2636         /* go up three levels */
2637         dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2638         if (dn == NULL) {
2639                 return ADS_ERROR(LDAP_NO_MEMORY);
2640         }
2641
2642         *site_name = talloc_strdup(mem_ctx, dn);
2643         if (*site_name == NULL) {
2644                 return ADS_ERROR(LDAP_NO_MEMORY);
2645         }
2646
2647         return status;
2648         /*
2649         dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2650         */                                               
2651 }
2652
2653 /**
2654  * find the site dn where a machine resides
2655  * @param ads connection to ads server
2656  * @param mem_ctx Pointer to talloc context
2657  * @param computer_name name of the machine
2658  * @param site_name Pointer to the sitename
2659  * @return status of search
2660  **/
2661 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2662 {
2663         ADS_STATUS status;
2664         LDAPMessage *res;
2665         const char *parent, *filter;
2666         char *config_context = NULL;
2667         char *dn;
2668
2669         /* shortcut a query */
2670         if (strequal(computer_name, ads->config.ldap_server_name)) {
2671                 return ads_site_dn(ads, mem_ctx, site_dn);
2672         }
2673
2674         status = ads_config_path(ads, mem_ctx, &config_context);
2675         if (!ADS_ERR_OK(status)) {
2676                 return status;
2677         }
2678
2679         filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2680         if (filter == NULL) {
2681                 return ADS_ERROR(LDAP_NO_MEMORY);
2682         }
2683
2684         status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, 
2685                                filter, NULL, &res);
2686         if (!ADS_ERR_OK(status)) {
2687                 return status;
2688         }
2689
2690         if (ads_count_replies(ads, res) != 1) {
2691                 ads_msgfree(ads, res);
2692                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2693         }
2694
2695         dn = ads_get_dn(ads, res);
2696         if (dn == NULL) {
2697                 ads_msgfree(ads, res);
2698                 return ADS_ERROR(LDAP_NO_MEMORY);
2699         }
2700
2701         /* go up three levels */
2702         parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2703         if (parent == NULL) {
2704                 ads_msgfree(ads, res);
2705                 ads_memfree(ads, dn);
2706                 return ADS_ERROR(LDAP_NO_MEMORY);
2707         }
2708
2709         *site_dn = talloc_strdup(mem_ctx, parent);
2710         if (*site_dn == NULL) {
2711                 ads_msgfree(ads, res);
2712                 ads_memfree(ads, dn);
2713                 ADS_ERROR(LDAP_NO_MEMORY);
2714         }
2715
2716         ads_memfree(ads, dn);
2717         ads_msgfree(ads, res);
2718
2719         return status;
2720 }
2721
2722 /**
2723  * get the upn suffixes for a domain
2724  * @param ads connection to ads server
2725  * @param mem_ctx Pointer to talloc context
2726  * @param suffixes Pointer to an array of suffixes
2727  * @param num_suffixes Pointer to the number of suffixes
2728  * @return status of search
2729  **/
2730 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2731 {
2732         ADS_STATUS status;
2733         LDAPMessage *res;
2734         const char *base;
2735         char *config_context = NULL;
2736         const char *attrs[] = { "uPNSuffixes", NULL };
2737
2738         status = ads_config_path(ads, mem_ctx, &config_context);
2739         if (!ADS_ERR_OK(status)) {
2740                 return status;
2741         }
2742
2743         base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2744         if (base == NULL) {
2745                 return ADS_ERROR(LDAP_NO_MEMORY);
2746         }
2747
2748         status = ads_search_dn(ads, &res, base, attrs);
2749         if (!ADS_ERR_OK(status)) {
2750                 return status;
2751         }
2752
2753         if (ads_count_replies(ads, res) != 1) {
2754                 ads_msgfree(ads, res);
2755                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2756         }
2757
2758         (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2759         if ((*suffixes) == NULL) {
2760                 ads_msgfree(ads, res);
2761                 return ADS_ERROR(LDAP_NO_MEMORY);
2762         }
2763
2764         ads_msgfree(ads, res);
2765
2766         return status;
2767 }
2768
2769 /**
2770  * pull a DOM_SID from an extended dn string
2771  * @param mem_ctx TALLOC_CTX 
2772  * @param extended_dn string
2773  * @param flags string type of extended_dn
2774  * @param sid pointer to a DOM_SID
2775  * @return boolean inidicating success
2776  **/
2777 BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx, 
2778                                   const char *extended_dn, 
2779                                   enum ads_extended_dn_flags flags, 
2780                                   DOM_SID *sid)
2781 {
2782         char *p, *q, *dn;
2783
2784         if (!extended_dn) {
2785                 return False;
2786         }
2787
2788         /* otherwise extended_dn gets stripped off */
2789         if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
2790                 return False;
2791         }
2792         /* 
2793          * ADS_EXTENDED_DN_HEX_STRING:
2794          * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2795          *
2796          * ADS_EXTENDED_DN_STRING (only with w2k3):
2797         <GUID=63198e23-39cb-4b0f-b032-ba0105525a29>;<SID=S-1-5-21-4257769659-666132843-1169174103-1110>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2798          */
2799
2800         p = strchr(dn, ';');
2801         if (!p) {
2802                 return False;
2803         }
2804
2805         if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2806                 return False;
2807         }
2808
2809         p += strlen(";<SID=");
2810
2811         q = strchr(p, '>');
2812         if (!q) {
2813                 return False;
2814         }
2815         
2816         *q = '\0';
2817
2818         DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2819
2820         switch (flags) {
2821         
2822         case ADS_EXTENDED_DN_STRING:
2823                 if (!string_to_sid(sid, p)) {
2824                         return False;
2825                 }
2826                 break;
2827         case ADS_EXTENDED_DN_HEX_STRING: {
2828                 pstring buf;
2829                 size_t buf_len;
2830
2831                 buf_len = strhex_to_str(buf, strlen(p), p);
2832                 if (buf_len == 0) {
2833                         return False;
2834                 }
2835
2836                 if (!sid_parse(buf, buf_len, sid)) {
2837                         DEBUG(10,("failed to parse sid\n"));
2838                         return False;
2839                 }
2840                 break;
2841                 }
2842         default:
2843                 DEBUG(10,("unknown extended dn format\n"));
2844                 return False;
2845         }
2846
2847         return True;
2848 }
2849
2850 /**
2851  * pull an array of DOM_SIDs from a ADS result
2852  * @param ads connection to ads server
2853  * @param mem_ctx TALLOC_CTX for allocating sid array
2854  * @param msg Results of search
2855  * @param field Attribute to retrieve
2856  * @param flags string type of extended_dn
2857  * @param sids pointer to sid array to allocate
2858  * @return the count of SIDs pulled
2859  **/
2860  int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads, 
2861                                    TALLOC_CTX *mem_ctx, 
2862                                    LDAPMessage *msg, 
2863                                    const char *field,
2864                                    enum ads_extended_dn_flags flags,
2865                                    DOM_SID **sids)
2866 {
2867         int i;
2868         size_t dn_count;
2869         char **dn_strings;
2870
2871         if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field, 
2872                                            &dn_count)) == NULL) {
2873                 return 0;
2874         }
2875
2876         (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2877         if (!(*sids)) {
2878                 TALLOC_FREE(dn_strings);
2879                 return 0;
2880         }
2881
2882         for (i=0; i<dn_count; i++) {
2883
2884                 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i], 
2885                                                   flags, &(*sids)[i])) {
2886                         TALLOC_FREE(*sids);
2887                         TALLOC_FREE(dn_strings);
2888                         return 0;
2889                 }
2890         }
2891
2892         TALLOC_FREE(dn_strings);
2893
2894         return dn_count;
2895 }
2896
2897 /********************************************************************
2898 ********************************************************************/
2899
2900 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2901 {
2902         LDAPMessage *res = NULL;
2903         ADS_STATUS status;
2904         int count = 0;
2905         char *name = NULL;
2906         
2907         status = ads_find_machine_acct(ads, &res, global_myname());
2908         if (!ADS_ERR_OK(status)) {
2909                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2910                         global_myname()));
2911                 goto out;
2912         }
2913                 
2914         if ( (count = ads_count_replies(ads, res)) != 1 ) {
2915                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2916                 goto out;
2917         }
2918                 
2919         if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
2920                 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2921         }
2922
2923 out:
2924         ads_msgfree(ads, res);
2925         
2926         return name;
2927 }
2928
2929 /********************************************************************
2930 ********************************************************************/
2931
2932 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2933 {
2934         LDAPMessage *res = NULL;
2935         ADS_STATUS status;
2936         int count = 0;
2937         char *name = NULL;
2938         
2939         status = ads_find_machine_acct(ads, &res, global_myname());
2940         if (!ADS_ERR_OK(status)) {
2941                 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
2942                         global_myname()));
2943                 goto out;
2944         }
2945                 
2946         if ( (count = ads_count_replies(ads, res)) != 1 ) {
2947                 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
2948                 goto out;
2949         }
2950                 
2951         if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
2952                 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
2953         }
2954
2955 out:
2956         ads_msgfree(ads, res);
2957         
2958         return name;
2959 }
2960
2961 /********************************************************************
2962 ********************************************************************/
2963
2964 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2965 {
2966         LDAPMessage *res = NULL;
2967         ADS_STATUS status;
2968         int count = 0;
2969         char *name = NULL;
2970         
2971         status = ads_find_machine_acct(ads, &res, global_myname());
2972         if (!ADS_ERR_OK(status)) {
2973                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2974                         global_myname()));
2975                 goto out;
2976         }
2977                 
2978         if ( (count = ads_count_replies(ads, res)) != 1 ) {
2979                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2980                 goto out;
2981         }
2982                 
2983         if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
2984                 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
2985         }
2986
2987 out:
2988         ads_msgfree(ads, res);
2989         
2990         return name;
2991 }
2992
2993 #if 0
2994
2995    SAVED CODE - we used to join via ldap - remember how we did this. JRA.
2996
2997 /**
2998  * Join a machine to a realm
2999  *  Creates the machine account and sets the machine password
3000  * @param ads connection to ads server
3001  * @param machine name of host to add
3002  * @param org_unit Organizational unit to place machine in
3003  * @return status of join
3004  **/
3005 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3006                         uint32 account_type, const char *org_unit)
3007 {
3008         ADS_STATUS status;
3009         LDAPMessage *res = NULL;
3010         char *machine;
3011
3012         /* machine name must be lowercase */
3013         machine = SMB_STRDUP(machine_name);
3014         strlower_m(machine);
3015
3016         /*
3017         status = ads_find_machine_acct(ads, (void **)&res, machine);
3018         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3019                 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3020                 status = ads_leave_realm(ads, machine);
3021                 if (!ADS_ERR_OK(status)) {
3022                         DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3023                                 machine, ads->config.realm));
3024                         return status;
3025                 }
3026         }
3027         */
3028         status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3029         if (!ADS_ERR_OK(status)) {
3030                 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3031                 SAFE_FREE(machine);
3032                 return status;
3033         }
3034
3035         status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3036         if (!ADS_ERR_OK(status)) {
3037                 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3038                 SAFE_FREE(machine);
3039                 return status;
3040         }
3041
3042         SAFE_FREE(machine);
3043         ads_msgfree(ads, res);
3044
3045         return status;
3046 }
3047 #endif
3048
3049 /**
3050  * Delete a machine from the realm
3051  * @param ads connection to ads server
3052  * @param hostname Machine to remove
3053  * @return status of delete
3054  **/
3055 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3056 {
3057         ADS_STATUS status;
3058         void *msg;
3059         LDAPMessage *res;
3060         char *hostnameDN, *host;
3061         int rc;
3062         LDAPControl ldap_control;
3063         LDAPControl  * pldap_control[2] = {NULL, NULL};
3064
3065         pldap_control[0] = &ldap_control;
3066         memset(&ldap_control, 0, sizeof(LDAPControl));
3067         ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3068
3069         /* hostname must be lowercase */
3070         host = SMB_STRDUP(hostname);
3071         strlower_m(host);
3072
3073         status = ads_find_machine_acct(ads, &res, host);
3074         if (!ADS_ERR_OK(status)) {
3075                 DEBUG(0, ("Host account for %s does not exist.\n", host));
3076                 SAFE_FREE(host);
3077                 return status;
3078         }
3079
3080         msg = ads_first_entry(ads, res);
3081         if (!msg) {
3082                 SAFE_FREE(host);
3083                 return ADS_ERROR_SYSTEM(ENOENT);
3084         }
3085
3086         hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3087
3088         rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3089         if (rc) {
3090                 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3091         }else {
3092                 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3093         }
3094
3095         if (rc != LDAP_SUCCESS) {
3096                 const char *attrs[] = { "cn", NULL };
3097                 LDAPMessage *msg_sub;
3098
3099                 /* we only search with scope ONE, we do not expect any further
3100                  * objects to be created deeper */
3101
3102                 status = ads_do_search_retry(ads, hostnameDN,
3103                                              LDAP_SCOPE_ONELEVEL,
3104                                              "(objectclass=*)", attrs, &res);
3105
3106                 if (!ADS_ERR_OK(status)) {
3107                         SAFE_FREE(host);
3108                         ads_memfree(ads, hostnameDN);
3109                         return status;
3110                 }
3111
3112                 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3113                         msg_sub = ads_next_entry(ads, msg_sub)) {
3114
3115                         char *dn = NULL;
3116
3117                         if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3118                                 SAFE_FREE(host);
3119                                 ads_memfree(ads, hostnameDN);
3120                                 return ADS_ERROR(LDAP_NO_MEMORY);
3121                         }
3122
3123                         status = ads_del_dn(ads, dn);
3124                         if (!ADS_ERR_OK(status)) {
3125                                 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3126                                 SAFE_FREE(host);
3127                                 ads_memfree(ads, dn);
3128                                 ads_memfree(ads, hostnameDN);
3129                                 return status;
3130                         }
3131
3132                         ads_memfree(ads, dn);
3133                 }
3134
3135                 /* there should be no subordinate objects anymore */
3136                 status = ads_do_search_retry(ads, hostnameDN,
3137                                              LDAP_SCOPE_ONELEVEL,
3138                                              "(objectclass=*)", attrs, &res);
3139
3140                 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3141                         SAFE_FREE(host);
3142                         ads_memfree(ads, hostnameDN);
3143                         return status;
3144                 }
3145
3146                 /* delete hostnameDN now */
3147                 status = ads_del_dn(ads, hostnameDN);
3148                 if (!ADS_ERR_OK(status)) {
3149                         SAFE_FREE(host);
3150                         DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3151                         ads_memfree(ads, hostnameDN);
3152                         return status;
3153                 }
3154         }
3155
3156         ads_memfree(ads, hostnameDN);
3157
3158         status = ads_find_machine_acct(ads, &res, host);
3159         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3160                 DEBUG(3, ("Failed to remove host account.\n"));
3161                 SAFE_FREE(host);
3162                 return status;
3163         }
3164
3165         SAFE_FREE(host);
3166         return status;
3167 }
3168
3169 /**
3170  * pull all token-sids from an LDAP dn
3171  * @param ads connection to ads server
3172  * @param mem_ctx TALLOC_CTX for allocating sid array
3173  * @param dn of LDAP object
3174  * @param user_sid pointer to DOM_SID (objectSid)
3175  * @param primary_group_sid pointer to DOM_SID (self composed)
3176  * @param sids pointer to sid array to allocate
3177  * @param num_sids counter of SIDs pulled
3178  * @return status of token query
3179  **/
3180  ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3181                               TALLOC_CTX *mem_ctx,
3182                               const char *dn,
3183                               DOM_SID *user_sid,
3184                               DOM_SID *primary_group_sid,
3185                               DOM_SID **sids,
3186                               size_t *num_sids)
3187 {
3188         ADS_STATUS status;
3189         LDAPMessage *res = NULL;
3190         int count = 0;
3191         size_t tmp_num_sids;
3192         DOM_SID *tmp_sids;
3193         DOM_SID tmp_user_sid;
3194         DOM_SID tmp_primary_group_sid;
3195         uint32 pgid;
3196         const char *attrs[] = {
3197                 "objectSid",
3198                 "tokenGroups",
3199                 "primaryGroupID",
3200                 NULL
3201         };
3202
3203         status = ads_search_retry_dn(ads, &res, dn, attrs);
3204         if (!ADS_ERR_OK(status)) {
3205                 return status;
3206         }
3207
3208         count = ads_count_replies(ads, res);
3209         if (count != 1) {
3210                 ads_msgfree(ads, res);
3211                 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3212         }
3213
3214         if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3215                 ads_msgfree(ads, res);
3216                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3217         }
3218
3219         if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3220                 ads_msgfree(ads, res);
3221                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3222         }
3223
3224         {
3225                 /* hack to compose the primary group sid without knowing the
3226                  * domsid */
3227
3228                 DOM_SID domsid;
3229                 uint32 dummy_rid;
3230
3231                 sid_copy(&domsid, &tmp_user_sid);
3232
3233                 if (!sid_split_rid(&domsid, &dummy_rid)) {
3234                         ads_msgfree(ads, res);
3235                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3236                 }
3237
3238                 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3239                         ads_msgfree(ads, res);
3240                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3241                 }
3242         }
3243
3244         tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3245
3246         if (tmp_num_sids == 0 || !tmp_sids) {
3247                 ads_msgfree(ads, res);
3248                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3249         }
3250
3251         if (num_sids) {
3252                 *num_sids = tmp_num_sids;
3253         }
3254
3255         if (sids) {
3256                 *sids = tmp_sids;
3257         }
3258
3259         if (user_sid) {
3260                 *user_sid = tmp_user_sid;
3261         }
3262
3263         if (primary_group_sid) {
3264                 *primary_group_sid = tmp_primary_group_sid;
3265         }
3266
3267         DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3268
3269         ads_msgfree(ads, res);
3270         return ADS_ERROR_LDAP(LDAP_SUCCESS);
3271 }
3272
3273 /**
3274  * Find a sAMAccoutName in LDAP
3275  * @param ads connection to ads server
3276  * @param mem_ctx TALLOC_CTX for allocating sid array
3277  * @param samaccountname to search
3278  * @param uac_ret uint32 pointer userAccountControl attribute value
3279  * @param dn_ret pointer to dn
3280  * @return status of token query
3281  **/
3282 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3283                                TALLOC_CTX *mem_ctx,
3284                                const char *samaccountname,
3285                                uint32 *uac_ret,
3286                                const char **dn_ret)
3287 {
3288         ADS_STATUS status;
3289         const char *attrs[] = { "userAccountControl", NULL };
3290         const char *filter;
3291         LDAPMessage *res = NULL;
3292         char *dn = NULL;
3293         uint32 uac = 0;
3294
3295         filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3296                 samaccountname);
3297         if (filter == NULL) {
3298                 goto out;
3299         }
3300
3301         status = ads_do_search_all(ads, ads->config.bind_path,
3302                                    LDAP_SCOPE_SUBTREE,
3303                                    filter, attrs, &res);
3304         
3305         if (!ADS_ERR_OK(status)) {
3306                 goto out;
3307         }
3308
3309         if (ads_count_replies(ads, res) != 1) {
3310                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3311                 goto out;
3312         }
3313
3314         dn = ads_get_dn(ads, res);
3315         if (dn == NULL) {
3316                 status = ADS_ERROR(LDAP_NO_MEMORY);
3317                 goto out;
3318         }
3319
3320         if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3321                 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3322                 goto out;
3323         }
3324
3325         if (uac_ret) {
3326                 *uac_ret = uac;
3327         }
3328
3329         if (dn_ret) {
3330                 *dn_ret = talloc_strdup(mem_ctx, dn);
3331                 if (!*dn_ret) {
3332                         status = ADS_ERROR(LDAP_NO_MEMORY);
3333                         goto out;
3334                 }
3335         }
3336  out:
3337         ads_memfree(ads, dn);
3338         ads_msgfree(ads, res);
3339
3340         return status;
3341 }
3342
3343 /**
3344  * find our configuration path 
3345  * @param ads connection to ads server
3346  * @param mem_ctx Pointer to talloc context
3347  * @param config_path Pointer to the config path
3348  * @return status of search
3349  **/
3350 ADS_STATUS ads_config_path(ADS_STRUCT *ads, 
3351                            TALLOC_CTX *mem_ctx, 
3352                            char **config_path)
3353 {
3354         ADS_STATUS status;
3355         LDAPMessage *res = NULL;
3356         const char *config_context = NULL;
3357         const char *attrs[] = { "configurationNamingContext", NULL };
3358
3359         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, 
3360                                "(objectclass=*)", attrs, &res);
3361         if (!ADS_ERR_OK(status)) {
3362                 return status;
3363         }
3364
3365         config_context = ads_pull_string(ads, mem_ctx, res, 
3366                                          "configurationNamingContext");
3367         ads_msgfree(ads, res);
3368         if (!config_context) {
3369                 return ADS_ERROR(LDAP_NO_MEMORY);
3370         }
3371
3372         if (config_path) {
3373                 *config_path = talloc_strdup(mem_ctx, config_context);
3374                 if (!*config_path) {
3375                         return ADS_ERROR(LDAP_NO_MEMORY);
3376                 }
3377         }
3378
3379         return ADS_ERROR(LDAP_SUCCESS);
3380 }
3381
3382 /**
3383  * find the displayName of an extended right 
3384  * @param ads connection to ads server
3385  * @param config_path The config path
3386  * @param mem_ctx Pointer to talloc context
3387  * @param GUID struct of the rightsGUID
3388  * @return status of search
3389  **/
3390 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads, 
3391                                                 const char *config_path, 
3392                                                 TALLOC_CTX *mem_ctx, 
3393                                                 const struct GUID *rights_guid)
3394 {
3395         ADS_STATUS rc;
3396         LDAPMessage *res = NULL;
3397         char *expr = NULL;
3398         const char *attrs[] = { "displayName", NULL };
3399         const char *result = NULL;
3400         const char *path;
3401
3402         if (!ads || !mem_ctx || !rights_guid) {
3403                 goto done;
3404         }
3405
3406         expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)", 
3407                                smb_uuid_string_static(*rights_guid));
3408         if (!expr) {
3409                 goto done;
3410         }
3411
3412         path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3413         if (!path) {
3414                 goto done;
3415         }
3416
3417         rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE, 
3418                                  expr, attrs, &res);
3419         if (!ADS_ERR_OK(rc)) {
3420                 goto done;
3421         }
3422
3423         if (ads_count_replies(ads, res) != 1) {
3424                 goto done;
3425         }
3426
3427         result = ads_pull_string(ads, mem_ctx, res, "displayName");
3428
3429  done:
3430         ads_msgfree(ads, res);
3431         return result;
3432         
3433 }
3434
3435 #endif