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