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