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