don't store cache_readonly in gencache
[jra/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                 fstring tmp;
1851                 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1852                 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
1853         }
1854 }
1855
1856 /*
1857   dump ntSecurityDescriptor
1858 */
1859 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
1860 {
1861         TALLOC_CTX *frame = talloc_stackframe();
1862         struct security_descriptor *psd;
1863         NTSTATUS status;
1864
1865         status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
1866                                      values[0]->bv_len, &psd);
1867         if (!NT_STATUS_IS_OK(status)) {
1868                 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
1869                           nt_errstr(status)));
1870                 TALLOC_FREE(frame);
1871                 return;
1872         }
1873
1874         if (psd) {
1875                 ads_disp_sd(ads, talloc_tos(), psd);
1876         }
1877
1878         TALLOC_FREE(frame);
1879 }
1880
1881 /*
1882   dump a string result from ldap
1883 */
1884 static void dump_string(const char *field, char **values)
1885 {
1886         int i;
1887         for (i=0; values[i]; i++) {
1888                 printf("%s: %s\n", field, values[i]);
1889         }
1890 }
1891
1892 /*
1893   dump a field from LDAP on stdout
1894   used for debugging
1895 */
1896
1897 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
1898 {
1899         const struct {
1900                 const char *name;
1901                 bool string;
1902                 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
1903         } handlers[] = {
1904                 {"objectGUID", False, dump_guid},
1905                 {"netbootGUID", False, dump_guid},
1906                 {"nTSecurityDescriptor", False, dump_sd},
1907                 {"dnsRecord", False, dump_binary},
1908                 {"objectSid", False, dump_sid},
1909                 {"tokenGroups", False, dump_sid},
1910                 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1911                 {"tokengroupsGlobalandUniversal", False, dump_sid},
1912                 {"mS-DS-CreatorSID", False, dump_sid},
1913                 {"msExchMailboxGuid", False, dump_guid},
1914                 {NULL, True, NULL}
1915         };
1916         int i;
1917
1918         if (!field) { /* must be end of an entry */
1919                 printf("\n");
1920                 return False;
1921         }
1922
1923         for (i=0; handlers[i].name; i++) {
1924                 if (StrCaseCmp(handlers[i].name, field) == 0) {
1925                         if (!values) /* first time, indicate string or not */
1926                                 return handlers[i].string;
1927                         handlers[i].handler(ads, field, (struct berval **) values);
1928                         break;
1929                 }
1930         }
1931         if (!handlers[i].name) {
1932                 if (!values) /* first time, indicate string conversion */
1933                         return True;
1934                 dump_string(field, (char **)values);
1935         }
1936         return False;
1937 }
1938
1939 /**
1940  * Dump a result from LDAP on stdout
1941  *  used for debugging
1942  * @param ads connection to ads server
1943  * @param res Results to dump
1944  **/
1945
1946  void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
1947 {
1948         ads_process_results(ads, res, ads_dump_field, NULL);
1949 }
1950
1951 /**
1952  * Walk through results, calling a function for each entry found.
1953  *  The function receives a field name, a berval * array of values,
1954  *  and a data area passed through from the start.  The function is
1955  *  called once with null for field and values at the end of each
1956  *  entry.
1957  * @param ads connection to ads server
1958  * @param res Results to process
1959  * @param fn Function for processing each result
1960  * @param data_area user-defined area to pass to function
1961  **/
1962  void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
1963                           bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1964                           void *data_area)
1965 {
1966         LDAPMessage *msg;
1967         TALLOC_CTX *ctx;
1968
1969         if (!(ctx = talloc_init("ads_process_results")))
1970                 return;
1971
1972         for (msg = ads_first_entry(ads, res); msg; 
1973              msg = ads_next_entry(ads, msg)) {
1974                 char *utf8_field;
1975                 BerElement *b;
1976         
1977                 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
1978                                                      (LDAPMessage *)msg,&b); 
1979                      utf8_field;
1980                      utf8_field=ldap_next_attribute(ads->ldap.ld,
1981                                                     (LDAPMessage *)msg,b)) {
1982                         struct berval **ber_vals;
1983                         char **str_vals, **utf8_vals;
1984                         char *field;
1985                         bool string; 
1986
1987                         pull_utf8_talloc(ctx, &field, utf8_field);
1988                         string = fn(ads, field, NULL, data_area);
1989
1990                         if (string) {
1991                                 utf8_vals = ldap_get_values(ads->ldap.ld,
1992                                                  (LDAPMessage *)msg, field);
1993                                 str_vals = ads_pull_strvals(ctx, 
1994                                                   (const char **) utf8_vals);
1995                                 fn(ads, field, (void **) str_vals, data_area);
1996                                 ldap_value_free(utf8_vals);
1997                         } else {
1998                                 ber_vals = ldap_get_values_len(ads->ldap.ld, 
1999                                                  (LDAPMessage *)msg, field);
2000                                 fn(ads, field, (void **) ber_vals, data_area);
2001
2002                                 ldap_value_free_len(ber_vals);
2003                         }
2004                         ldap_memfree(utf8_field);
2005                 }
2006                 ber_free(b, 0);
2007                 talloc_free_children(ctx);
2008                 fn(ads, NULL, NULL, data_area); /* completed an entry */
2009
2010         }
2011         talloc_destroy(ctx);
2012 }
2013
2014 /**
2015  * count how many replies are in a LDAPMessage
2016  * @param ads connection to ads server
2017  * @param res Results to count
2018  * @return number of replies
2019  **/
2020 int ads_count_replies(ADS_STRUCT *ads, void *res)
2021 {
2022         return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2023 }
2024
2025 /**
2026  * pull the first entry from a ADS result
2027  * @param ads connection to ads server
2028  * @param res Results of search
2029  * @return first entry from result
2030  **/
2031  LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2032 {
2033         return ldap_first_entry(ads->ldap.ld, res);
2034 }
2035
2036 /**
2037  * pull the next entry from a ADS result
2038  * @param ads connection to ads server
2039  * @param res Results of search
2040  * @return next entry from result
2041  **/
2042  LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2043 {
2044         return ldap_next_entry(ads->ldap.ld, res);
2045 }
2046
2047 /**
2048  * pull a single string from a ADS result
2049  * @param ads connection to ads server
2050  * @param mem_ctx TALLOC_CTX to use for allocating result string
2051  * @param msg Results of search
2052  * @param field Attribute to retrieve
2053  * @return Result string in talloc context
2054  **/
2055  char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2056                        const char *field)
2057 {
2058         char **values;
2059         char *ret = NULL;
2060         char *ux_string;
2061         size_t rc;
2062
2063         values = ldap_get_values(ads->ldap.ld, msg, field);
2064         if (!values)
2065                 return NULL;
2066         
2067         if (values[0]) {
2068                 rc = pull_utf8_talloc(mem_ctx, &ux_string, 
2069                                       values[0]);
2070                 if (rc != (size_t)-1)
2071                         ret = ux_string;
2072                 
2073         }
2074         ldap_value_free(values);
2075         return ret;
2076 }
2077
2078 /**
2079  * pull an array of strings from a ADS result
2080  * @param ads connection to ads server
2081  * @param mem_ctx TALLOC_CTX to use for allocating result string
2082  * @param msg Results of search
2083  * @param field Attribute to retrieve
2084  * @return Result strings in talloc context
2085  **/
2086  char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2087                          LDAPMessage *msg, const char *field,
2088                          size_t *num_values)
2089 {
2090         char **values;
2091         char **ret = NULL;
2092         int i;
2093
2094         values = ldap_get_values(ads->ldap.ld, msg, field);
2095         if (!values)
2096                 return NULL;
2097
2098         *num_values = ldap_count_values(values);
2099
2100         ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2101         if (!ret) {
2102                 ldap_value_free(values);
2103                 return NULL;
2104         }
2105
2106         for (i=0;i<*num_values;i++) {
2107                 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2108                         ldap_value_free(values);
2109                         return NULL;
2110                 }
2111         }
2112         ret[i] = NULL;
2113
2114         ldap_value_free(values);
2115         return ret;
2116 }
2117
2118 /**
2119  * pull an array of strings from a ADS result 
2120  *  (handle large multivalue attributes with range retrieval)
2121  * @param ads connection to ads server
2122  * @param mem_ctx TALLOC_CTX to use for allocating result string
2123  * @param msg Results of search
2124  * @param field Attribute to retrieve
2125  * @param current_strings strings returned by a previous call to this function
2126  * @param next_attribute The next query should ask for this attribute
2127  * @param num_values How many values did we get this time?
2128  * @param more_values Are there more values to get?
2129  * @return Result strings in talloc context
2130  **/
2131  char **ads_pull_strings_range(ADS_STRUCT *ads, 
2132                                TALLOC_CTX *mem_ctx,
2133                                LDAPMessage *msg, const char *field,
2134                                char **current_strings,
2135                                const char **next_attribute,
2136                                size_t *num_strings,
2137                                bool *more_strings)
2138 {
2139         char *attr;
2140         char *expected_range_attrib, *range_attr;
2141         BerElement *ptr = NULL;
2142         char **strings;
2143         char **new_strings;
2144         size_t num_new_strings;
2145         unsigned long int range_start;
2146         unsigned long int range_end;
2147         
2148         /* we might have been given the whole lot anyway */
2149         if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2150                 *more_strings = False;
2151                 return strings;
2152         }
2153
2154         expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2155
2156         /* look for Range result */
2157         for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr); 
2158              attr; 
2159              attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2160                 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2161                 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2162                         range_attr = attr;
2163                         break;
2164                 }
2165                 ldap_memfree(attr);
2166         }
2167         if (!attr) {
2168                 ber_free(ptr, 0);
2169                 /* nothing here - this field is just empty */
2170                 *more_strings = False;
2171                 return NULL;
2172         }
2173         
2174         if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu", 
2175                    &range_start, &range_end) == 2) {
2176                 *more_strings = True;
2177         } else {
2178                 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*", 
2179                            &range_start) == 1) {
2180                         *more_strings = False;
2181                 } else {
2182                         DEBUG(1, ("ads_pull_strings_range:  Cannot parse Range attriubte (%s)\n", 
2183                                   range_attr));
2184                         ldap_memfree(range_attr);
2185                         *more_strings = False;
2186                         return NULL;
2187                 }
2188         }
2189
2190         if ((*num_strings) != range_start) {
2191                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2192                           " - aborting range retreival\n",
2193                           range_attr, (unsigned int)(*num_strings) + 1, range_start));
2194                 ldap_memfree(range_attr);
2195                 *more_strings = False;
2196                 return NULL;
2197         }
2198
2199         new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2200         
2201         if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2202                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2203                           "strings in this bunch, but we only got %lu - aborting range retreival\n",
2204                           range_attr, (unsigned long int)range_end - range_start + 1, 
2205                           (unsigned long int)num_new_strings));
2206                 ldap_memfree(range_attr);
2207                 *more_strings = False;
2208                 return NULL;
2209         }
2210
2211         strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2212                                  *num_strings + num_new_strings);
2213         
2214         if (strings == NULL) {
2215                 ldap_memfree(range_attr);
2216                 *more_strings = False;
2217                 return NULL;
2218         }
2219         
2220         if (new_strings && num_new_strings) {
2221                 memcpy(&strings[*num_strings], new_strings,
2222                        sizeof(*new_strings) * num_new_strings);
2223         }
2224
2225         (*num_strings) += num_new_strings;
2226
2227         if (*more_strings) {
2228                 *next_attribute = talloc_asprintf(mem_ctx,
2229                                                   "%s;range=%d-*", 
2230                                                   field,
2231                                                   (int)*num_strings);
2232                 
2233                 if (!*next_attribute) {
2234                         DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2235                         ldap_memfree(range_attr);
2236                         *more_strings = False;
2237                         return NULL;
2238                 }
2239         }
2240
2241         ldap_memfree(range_attr);
2242
2243         return strings;
2244 }
2245
2246 /**
2247  * pull a single uint32 from a ADS result
2248  * @param ads connection to ads server
2249  * @param msg Results of search
2250  * @param field Attribute to retrieve
2251  * @param v Pointer to int to store result
2252  * @return boolean inidicating success
2253 */
2254  bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2255                       uint32 *v)
2256 {
2257         char **values;
2258
2259         values = ldap_get_values(ads->ldap.ld, msg, field);
2260         if (!values)
2261                 return False;
2262         if (!values[0]) {
2263                 ldap_value_free(values);
2264                 return False;
2265         }
2266
2267         *v = atoi(values[0]);
2268         ldap_value_free(values);
2269         return True;
2270 }
2271
2272 /**
2273  * pull a single objectGUID from an ADS result
2274  * @param ads connection to ADS server
2275  * @param msg results of search
2276  * @param guid 37-byte area to receive text guid
2277  * @return boolean indicating success
2278  **/
2279  bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2280 {
2281         char **values;
2282         UUID_FLAT flat_guid;
2283
2284         values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2285         if (!values)
2286                 return False;
2287         
2288         if (values[0]) {
2289                 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2290                 smb_uuid_unpack(flat_guid, guid);
2291                 ldap_value_free(values);
2292                 return True;
2293         }
2294         ldap_value_free(values);
2295         return False;
2296
2297 }
2298
2299
2300 /**
2301  * pull a single DOM_SID from a ADS result
2302  * @param ads connection to ads server
2303  * @param msg Results of search
2304  * @param field Attribute to retrieve
2305  * @param sid Pointer to sid to store result
2306  * @return boolean inidicating success
2307 */
2308  bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2309                    DOM_SID *sid)
2310 {
2311         struct berval **values;
2312         bool ret = False;
2313
2314         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2315
2316         if (!values)
2317                 return False;
2318
2319         if (values[0])
2320                 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2321         
2322         ldap_value_free_len(values);
2323         return ret;
2324 }
2325
2326 /**
2327  * pull an array of DOM_SIDs from a ADS result
2328  * @param ads connection to ads server
2329  * @param mem_ctx TALLOC_CTX for allocating sid array
2330  * @param msg Results of search
2331  * @param field Attribute to retrieve
2332  * @param sids pointer to sid array to allocate
2333  * @return the count of SIDs pulled
2334  **/
2335  int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2336                    LDAPMessage *msg, const char *field, DOM_SID **sids)
2337 {
2338         struct berval **values;
2339         bool ret;
2340         int count, i;
2341
2342         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2343
2344         if (!values)
2345                 return 0;
2346
2347         for (i=0; values[i]; i++)
2348                 /* nop */ ;
2349
2350         if (i) {
2351                 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2352                 if (!(*sids)) {
2353                         ldap_value_free_len(values);
2354                         return 0;
2355                 }
2356         } else {
2357                 (*sids) = NULL;
2358         }
2359
2360         count = 0;
2361         for (i=0; values[i]; i++) {
2362                 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2363                 if (ret) {
2364                         DEBUG(10, ("pulling SID: %s\n",
2365                                    sid_string_dbg(&(*sids)[count])));
2366                         count++;
2367                 }
2368         }
2369         
2370         ldap_value_free_len(values);
2371         return count;
2372 }
2373
2374 /**
2375  * pull a SEC_DESC from a ADS result
2376  * @param ads connection to ads server
2377  * @param mem_ctx TALLOC_CTX for allocating sid array
2378  * @param msg Results of search
2379  * @param field Attribute to retrieve
2380  * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2381  * @return boolean inidicating success
2382 */
2383  bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2384                   LDAPMessage *msg, const char *field, SEC_DESC **sd)
2385 {
2386         struct berval **values;
2387         bool ret = False;
2388
2389         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2390
2391         if (!values) return False;
2392
2393         if (values[0]) {
2394                 prs_struct ps;
2395                 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2396                 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2397                 prs_set_offset(&ps,0);
2398
2399                 ret = sec_io_desc("sd", sd, &ps, 1);
2400                 prs_mem_free(&ps);
2401         }
2402         
2403         ldap_value_free_len(values);
2404         return ret;
2405 }
2406
2407 /* 
2408  * in order to support usernames longer than 21 characters we need to 
2409  * use both the sAMAccountName and the userPrincipalName attributes 
2410  * It seems that not all users have the userPrincipalName attribute set
2411  *
2412  * @param ads connection to ads server
2413  * @param mem_ctx TALLOC_CTX for allocating sid array
2414  * @param msg Results of search
2415  * @return the username
2416  */
2417  char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2418                          LDAPMessage *msg)
2419 {
2420 #if 0   /* JERRY */
2421         char *ret, *p;
2422
2423         /* lookup_name() only works on the sAMAccountName to 
2424            returning the username portion of userPrincipalName
2425            breaks winbindd_getpwnam() */
2426
2427         ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2428         if (ret && (p = strchr_m(ret, '@'))) {
2429                 *p = 0;
2430                 return ret;
2431         }
2432 #endif
2433         return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2434 }
2435
2436
2437 /**
2438  * find the update serial number - this is the core of the ldap cache
2439  * @param ads connection to ads server
2440  * @param ads connection to ADS server
2441  * @param usn Pointer to retrieved update serial number
2442  * @return status of search
2443  **/
2444 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2445 {
2446         const char *attrs[] = {"highestCommittedUSN", NULL};
2447         ADS_STATUS status;
2448         LDAPMessage *res;
2449
2450         status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2451         if (!ADS_ERR_OK(status)) 
2452                 return status;
2453
2454         if (ads_count_replies(ads, res) != 1) {
2455                 ads_msgfree(ads, res);
2456                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2457         }
2458
2459         if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2460                 ads_msgfree(ads, res);
2461                 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2462         }
2463
2464         ads_msgfree(ads, res);
2465         return ADS_SUCCESS;
2466 }
2467
2468 /* parse a ADS timestring - typical string is
2469    '20020917091222.0Z0' which means 09:12.22 17th September
2470    2002, timezone 0 */
2471 static time_t ads_parse_time(const char *str)
2472 {
2473         struct tm tm;
2474
2475         ZERO_STRUCT(tm);
2476
2477         if (sscanf(str, "%4d%2d%2d%2d%2d%2d", 
2478                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
2479                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2480                 return 0;
2481         }
2482         tm.tm_year -= 1900;
2483         tm.tm_mon -= 1;
2484
2485         return timegm(&tm);
2486 }
2487
2488 /********************************************************************
2489 ********************************************************************/
2490
2491 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2492 {
2493         const char *attrs[] = {"currentTime", NULL};
2494         ADS_STATUS status;
2495         LDAPMessage *res;
2496         char *timestr;
2497         TALLOC_CTX *ctx;
2498         ADS_STRUCT *ads_s = ads;
2499
2500         if (!(ctx = talloc_init("ads_current_time"))) {
2501                 return ADS_ERROR(LDAP_NO_MEMORY);
2502         }
2503
2504         /* establish a new ldap tcp session if necessary */
2505
2506         if ( !ads->ldap.ld ) {
2507                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2508                         ads->server.ldap_server )) == NULL )
2509                 {
2510                         goto done;
2511                 }
2512                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2513                 status = ads_connect( ads_s );
2514                 if ( !ADS_ERR_OK(status))
2515                         goto done;
2516         }
2517
2518         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2519         if (!ADS_ERR_OK(status)) {
2520                 goto done;
2521         }
2522
2523         timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2524         if (!timestr) {
2525                 ads_msgfree(ads_s, res);
2526                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2527                 goto done;
2528         }
2529
2530         /* but save the time and offset in the original ADS_STRUCT */   
2531         
2532         ads->config.current_time = ads_parse_time(timestr);
2533
2534         if (ads->config.current_time != 0) {
2535                 ads->auth.time_offset = ads->config.current_time - time(NULL);
2536                 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2537         }
2538
2539         ads_msgfree(ads, res);
2540
2541         status = ADS_SUCCESS;
2542
2543 done:
2544         /* free any temporary ads connections */
2545         if ( ads_s != ads ) {
2546                 ads_destroy( &ads_s );
2547         }
2548         talloc_destroy(ctx);
2549
2550         return status;
2551 }
2552
2553 /********************************************************************
2554 ********************************************************************/
2555
2556 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2557 {
2558         const char *attrs[] = {"domainFunctionality", NULL};
2559         ADS_STATUS status;
2560         LDAPMessage *res;
2561         ADS_STRUCT *ads_s = ads;
2562         
2563         *val = DS_DOMAIN_FUNCTION_2000;
2564
2565         /* establish a new ldap tcp session if necessary */
2566
2567         if ( !ads->ldap.ld ) {
2568                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2569                         ads->server.ldap_server )) == NULL )
2570                 {
2571                         goto done;
2572                 }
2573                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2574                 status = ads_connect( ads_s );
2575                 if ( !ADS_ERR_OK(status))
2576                         goto done;
2577         }
2578
2579         /* If the attribute does not exist assume it is a Windows 2000 
2580            functional domain */
2581            
2582         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2583         if (!ADS_ERR_OK(status)) {
2584                 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2585                         status = ADS_SUCCESS;
2586                 }
2587                 goto done;
2588         }
2589
2590         if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2591                 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2592         }
2593         DEBUG(3,("ads_domain_func_level: %d\n", *val));
2594
2595         
2596         ads_msgfree(ads, res);
2597
2598 done:
2599         /* free any temporary ads connections */
2600         if ( ads_s != ads ) {
2601                 ads_destroy( &ads_s );
2602         }
2603
2604         return status;
2605 }
2606
2607 /**
2608  * find the domain sid for our domain
2609  * @param ads connection to ads server
2610  * @param sid Pointer to domain sid
2611  * @return status of search
2612  **/
2613 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2614 {
2615         const char *attrs[] = {"objectSid", NULL};
2616         LDAPMessage *res;
2617         ADS_STATUS rc;
2618
2619         rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
2620                            attrs, &res);
2621         if (!ADS_ERR_OK(rc)) return rc;
2622         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2623                 ads_msgfree(ads, res);
2624                 return ADS_ERROR_SYSTEM(ENOENT);
2625         }
2626         ads_msgfree(ads, res);
2627         
2628         return ADS_SUCCESS;
2629 }
2630
2631 /**
2632  * find our site name 
2633  * @param ads connection to ads server
2634  * @param mem_ctx Pointer to talloc context
2635  * @param site_name Pointer to the sitename
2636  * @return status of search
2637  **/
2638 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2639 {
2640         ADS_STATUS status;
2641         LDAPMessage *res;
2642         const char *dn, *service_name;
2643         const char *attrs[] = { "dsServiceName", NULL };
2644
2645         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2646         if (!ADS_ERR_OK(status)) {
2647                 return status;
2648         }
2649
2650         service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2651         if (service_name == NULL) {
2652                 ads_msgfree(ads, res);
2653                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2654         }
2655
2656         ads_msgfree(ads, res);
2657
2658         /* go up three levels */
2659         dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2660         if (dn == NULL) {
2661                 return ADS_ERROR(LDAP_NO_MEMORY);
2662         }
2663
2664         *site_name = talloc_strdup(mem_ctx, dn);
2665         if (*site_name == NULL) {
2666                 return ADS_ERROR(LDAP_NO_MEMORY);
2667         }
2668
2669         return status;
2670         /*
2671         dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2672         */                                               
2673 }
2674
2675 /**
2676  * find the site dn where a machine resides
2677  * @param ads connection to ads server
2678  * @param mem_ctx Pointer to talloc context
2679  * @param computer_name name of the machine
2680  * @param site_name Pointer to the sitename
2681  * @return status of search
2682  **/
2683 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2684 {
2685         ADS_STATUS status;
2686         LDAPMessage *res;
2687         const char *parent, *filter;
2688         char *config_context = NULL;
2689         char *dn;
2690
2691         /* shortcut a query */
2692         if (strequal(computer_name, ads->config.ldap_server_name)) {
2693                 return ads_site_dn(ads, mem_ctx, site_dn);
2694         }
2695
2696         status = ads_config_path(ads, mem_ctx, &config_context);
2697         if (!ADS_ERR_OK(status)) {
2698                 return status;
2699         }
2700
2701         filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2702         if (filter == NULL) {
2703                 return ADS_ERROR(LDAP_NO_MEMORY);
2704         }
2705
2706         status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, 
2707                                filter, NULL, &res);
2708         if (!ADS_ERR_OK(status)) {
2709                 return status;
2710         }
2711
2712         if (ads_count_replies(ads, res) != 1) {
2713                 ads_msgfree(ads, res);
2714                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2715         }
2716
2717         dn = ads_get_dn(ads, res);
2718         if (dn == NULL) {
2719                 ads_msgfree(ads, res);
2720                 return ADS_ERROR(LDAP_NO_MEMORY);
2721         }
2722
2723         /* go up three levels */
2724         parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2725         if (parent == NULL) {
2726                 ads_msgfree(ads, res);
2727                 ads_memfree(ads, dn);
2728                 return ADS_ERROR(LDAP_NO_MEMORY);
2729         }
2730
2731         *site_dn = talloc_strdup(mem_ctx, parent);
2732         if (*site_dn == NULL) {
2733                 ads_msgfree(ads, res);
2734                 ads_memfree(ads, dn);
2735                 return ADS_ERROR(LDAP_NO_MEMORY);
2736         }
2737
2738         ads_memfree(ads, dn);
2739         ads_msgfree(ads, res);
2740
2741         return status;
2742 }
2743
2744 /**
2745  * get the upn suffixes for a domain
2746  * @param ads connection to ads server
2747  * @param mem_ctx Pointer to talloc context
2748  * @param suffixes Pointer to an array of suffixes
2749  * @param num_suffixes Pointer to the number of suffixes
2750  * @return status of search
2751  **/
2752 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2753 {
2754         ADS_STATUS status;
2755         LDAPMessage *res;
2756         const char *base;
2757         char *config_context = NULL;
2758         const char *attrs[] = { "uPNSuffixes", NULL };
2759
2760         status = ads_config_path(ads, mem_ctx, &config_context);
2761         if (!ADS_ERR_OK(status)) {
2762                 return status;
2763         }
2764
2765         base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2766         if (base == NULL) {
2767                 return ADS_ERROR(LDAP_NO_MEMORY);
2768         }
2769
2770         status = ads_search_dn(ads, &res, base, attrs);
2771         if (!ADS_ERR_OK(status)) {
2772                 return status;
2773         }
2774
2775         if (ads_count_replies(ads, res) != 1) {
2776                 ads_msgfree(ads, res);
2777                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2778         }
2779
2780         (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2781         if ((*suffixes) == NULL) {
2782                 ads_msgfree(ads, res);
2783                 return ADS_ERROR(LDAP_NO_MEMORY);
2784         }
2785
2786         ads_msgfree(ads, res);
2787
2788         return status;
2789 }
2790
2791 /**
2792  * pull a DOM_SID from an extended dn string
2793  * @param mem_ctx TALLOC_CTX 
2794  * @param extended_dn string
2795  * @param flags string type of extended_dn
2796  * @param sid pointer to a DOM_SID
2797  * @return boolean inidicating success
2798  **/
2799 bool ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx, 
2800                                   const char *extended_dn, 
2801                                   enum ads_extended_dn_flags flags, 
2802                                   DOM_SID *sid)
2803 {
2804         char *p, *q, *dn;
2805
2806         if (!extended_dn) {
2807                 return False;
2808         }
2809
2810         /* otherwise extended_dn gets stripped off */
2811         if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
2812                 return False;
2813         }
2814         /* 
2815          * ADS_EXTENDED_DN_HEX_STRING:
2816          * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2817          *
2818          * ADS_EXTENDED_DN_STRING (only with w2k3):
2819         <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
2820          */
2821
2822         p = strchr(dn, ';');
2823         if (!p) {
2824                 return False;
2825         }
2826
2827         if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2828                 return False;
2829         }
2830
2831         p += strlen(";<SID=");
2832
2833         q = strchr(p, '>');
2834         if (!q) {
2835                 return False;
2836         }
2837         
2838         *q = '\0';
2839
2840         DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2841
2842         switch (flags) {
2843         
2844         case ADS_EXTENDED_DN_STRING:
2845                 if (!string_to_sid(sid, p)) {
2846                         return False;
2847                 }
2848                 break;
2849         case ADS_EXTENDED_DN_HEX_STRING: {
2850                 fstring buf;
2851                 size_t buf_len;
2852
2853                 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
2854                 if (buf_len == 0) {
2855                         return False;
2856                 }
2857
2858                 if (!sid_parse(buf, buf_len, sid)) {
2859                         DEBUG(10,("failed to parse sid\n"));
2860                         return False;
2861                 }
2862                 break;
2863                 }
2864         default:
2865                 DEBUG(10,("unknown extended dn format\n"));
2866                 return False;
2867         }
2868
2869         return True;
2870 }
2871
2872 /**
2873  * pull an array of DOM_SIDs from a ADS result
2874  * @param ads connection to ads server
2875  * @param mem_ctx TALLOC_CTX for allocating sid array
2876  * @param msg Results of search
2877  * @param field Attribute to retrieve
2878  * @param flags string type of extended_dn
2879  * @param sids pointer to sid array to allocate
2880  * @return the count of SIDs pulled
2881  **/
2882  int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads, 
2883                                    TALLOC_CTX *mem_ctx, 
2884                                    LDAPMessage *msg, 
2885                                    const char *field,
2886                                    enum ads_extended_dn_flags flags,
2887                                    DOM_SID **sids)
2888 {
2889         int i;
2890         size_t dn_count;
2891         char **dn_strings;
2892
2893         if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field, 
2894                                            &dn_count)) == NULL) {
2895                 return 0;
2896         }
2897
2898         (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2899         if (!(*sids)) {
2900                 TALLOC_FREE(dn_strings);
2901                 return 0;
2902         }
2903
2904         for (i=0; i<dn_count; i++) {
2905
2906                 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i], 
2907                                                   flags, &(*sids)[i])) {
2908                         TALLOC_FREE(*sids);
2909                         TALLOC_FREE(dn_strings);
2910                         return 0;
2911                 }
2912         }
2913
2914         TALLOC_FREE(dn_strings);
2915
2916         return dn_count;
2917 }
2918
2919 /********************************************************************
2920 ********************************************************************/
2921
2922 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2923 {
2924         LDAPMessage *res = NULL;
2925         ADS_STATUS status;
2926         int count = 0;
2927         char *name = NULL;
2928         
2929         status = ads_find_machine_acct(ads, &res, global_myname());
2930         if (!ADS_ERR_OK(status)) {
2931                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2932                         global_myname()));
2933                 goto out;
2934         }
2935                 
2936         if ( (count = ads_count_replies(ads, res)) != 1 ) {
2937                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2938                 goto out;
2939         }
2940                 
2941         if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
2942                 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2943         }
2944
2945 out:
2946         ads_msgfree(ads, res);
2947         
2948         return name;
2949 }
2950
2951 /********************************************************************
2952 ********************************************************************/
2953
2954 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2955 {
2956         LDAPMessage *res = NULL;
2957         ADS_STATUS status;
2958         int count = 0;
2959         char *name = NULL;
2960         
2961         status = ads_find_machine_acct(ads, &res, global_myname());
2962         if (!ADS_ERR_OK(status)) {
2963                 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
2964                         global_myname()));
2965                 goto out;
2966         }
2967                 
2968         if ( (count = ads_count_replies(ads, res)) != 1 ) {
2969                 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
2970                 goto out;
2971         }
2972                 
2973         if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
2974                 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
2975         }
2976
2977 out:
2978         ads_msgfree(ads, res);
2979         
2980         return name;
2981 }
2982
2983 /********************************************************************
2984 ********************************************************************/
2985
2986 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2987 {
2988         LDAPMessage *res = NULL;
2989         ADS_STATUS status;
2990         int count = 0;
2991         char *name = NULL;
2992         
2993         status = ads_find_machine_acct(ads, &res, global_myname());
2994         if (!ADS_ERR_OK(status)) {
2995                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2996                         global_myname()));
2997                 goto out;
2998         }
2999                 
3000         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3001                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3002                 goto out;
3003         }
3004                 
3005         if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3006                 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3007         }
3008
3009 out:
3010         ads_msgfree(ads, res);
3011         
3012         return name;
3013 }
3014
3015 #if 0
3016
3017    SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3018
3019 /**
3020  * Join a machine to a realm
3021  *  Creates the machine account and sets the machine password
3022  * @param ads connection to ads server
3023  * @param machine name of host to add
3024  * @param org_unit Organizational unit to place machine in
3025  * @return status of join
3026  **/
3027 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3028                         uint32 account_type, const char *org_unit)
3029 {
3030         ADS_STATUS status;
3031         LDAPMessage *res = NULL;
3032         char *machine;
3033
3034         /* machine name must be lowercase */
3035         machine = SMB_STRDUP(machine_name);
3036         strlower_m(machine);
3037
3038         /*
3039         status = ads_find_machine_acct(ads, (void **)&res, machine);
3040         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3041                 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3042                 status = ads_leave_realm(ads, machine);
3043                 if (!ADS_ERR_OK(status)) {
3044                         DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3045                                 machine, ads->config.realm));
3046                         return status;
3047                 }
3048         }
3049         */
3050         status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3051         if (!ADS_ERR_OK(status)) {
3052                 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3053                 SAFE_FREE(machine);
3054                 return status;
3055         }
3056
3057         status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3058         if (!ADS_ERR_OK(status)) {
3059                 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3060                 SAFE_FREE(machine);
3061                 return status;
3062         }
3063
3064         SAFE_FREE(machine);
3065         ads_msgfree(ads, res);
3066
3067         return status;
3068 }
3069 #endif
3070
3071 /**
3072  * Delete a machine from the realm
3073  * @param ads connection to ads server
3074  * @param hostname Machine to remove
3075  * @return status of delete
3076  **/
3077 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3078 {
3079         ADS_STATUS status;
3080         void *msg;
3081         LDAPMessage *res;
3082         char *hostnameDN, *host;
3083         int rc;
3084         LDAPControl ldap_control;
3085         LDAPControl  * pldap_control[2] = {NULL, NULL};
3086
3087         pldap_control[0] = &ldap_control;
3088         memset(&ldap_control, 0, sizeof(LDAPControl));
3089         ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3090
3091         /* hostname must be lowercase */
3092         host = SMB_STRDUP(hostname);
3093         strlower_m(host);
3094
3095         status = ads_find_machine_acct(ads, &res, host);
3096         if (!ADS_ERR_OK(status)) {
3097                 DEBUG(0, ("Host account for %s does not exist.\n", host));
3098                 SAFE_FREE(host);
3099                 return status;
3100         }
3101
3102         msg = ads_first_entry(ads, res);
3103         if (!msg) {
3104                 SAFE_FREE(host);
3105                 return ADS_ERROR_SYSTEM(ENOENT);
3106         }
3107
3108         hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3109
3110         rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3111         if (rc) {
3112                 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3113         }else {
3114                 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3115         }
3116
3117         if (rc != LDAP_SUCCESS) {
3118                 const char *attrs[] = { "cn", NULL };
3119                 LDAPMessage *msg_sub;
3120
3121                 /* we only search with scope ONE, we do not expect any further
3122                  * objects to be created deeper */
3123
3124                 status = ads_do_search_retry(ads, hostnameDN,
3125                                              LDAP_SCOPE_ONELEVEL,
3126                                              "(objectclass=*)", attrs, &res);
3127
3128                 if (!ADS_ERR_OK(status)) {
3129                         SAFE_FREE(host);
3130                         ads_memfree(ads, hostnameDN);
3131                         return status;
3132                 }
3133
3134                 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3135                         msg_sub = ads_next_entry(ads, msg_sub)) {
3136
3137                         char *dn = NULL;
3138
3139                         if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3140                                 SAFE_FREE(host);
3141                                 ads_memfree(ads, hostnameDN);
3142                                 return ADS_ERROR(LDAP_NO_MEMORY);
3143                         }
3144
3145                         status = ads_del_dn(ads, dn);
3146                         if (!ADS_ERR_OK(status)) {
3147                                 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3148                                 SAFE_FREE(host);
3149                                 ads_memfree(ads, dn);
3150                                 ads_memfree(ads, hostnameDN);
3151                                 return status;
3152                         }
3153
3154                         ads_memfree(ads, dn);
3155                 }
3156
3157                 /* there should be no subordinate objects anymore */
3158                 status = ads_do_search_retry(ads, hostnameDN,
3159                                              LDAP_SCOPE_ONELEVEL,
3160                                              "(objectclass=*)", attrs, &res);
3161
3162                 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3163                         SAFE_FREE(host);
3164                         ads_memfree(ads, hostnameDN);
3165                         return status;
3166                 }
3167
3168                 /* delete hostnameDN now */
3169                 status = ads_del_dn(ads, hostnameDN);
3170                 if (!ADS_ERR_OK(status)) {
3171                         SAFE_FREE(host);
3172                         DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3173                         ads_memfree(ads, hostnameDN);
3174                         return status;
3175                 }
3176         }
3177
3178         ads_memfree(ads, hostnameDN);
3179
3180         status = ads_find_machine_acct(ads, &res, host);
3181         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3182                 DEBUG(3, ("Failed to remove host account.\n"));
3183                 SAFE_FREE(host);
3184                 return status;
3185         }
3186
3187         SAFE_FREE(host);
3188         return status;
3189 }
3190
3191 /**
3192  * pull all token-sids from an LDAP dn
3193  * @param ads connection to ads server
3194  * @param mem_ctx TALLOC_CTX for allocating sid array
3195  * @param dn of LDAP object
3196  * @param user_sid pointer to DOM_SID (objectSid)
3197  * @param primary_group_sid pointer to DOM_SID (self composed)
3198  * @param sids pointer to sid array to allocate
3199  * @param num_sids counter of SIDs pulled
3200  * @return status of token query
3201  **/
3202  ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3203                               TALLOC_CTX *mem_ctx,
3204                               const char *dn,
3205                               DOM_SID *user_sid,
3206                               DOM_SID *primary_group_sid,
3207                               DOM_SID **sids,
3208                               size_t *num_sids)
3209 {
3210         ADS_STATUS status;
3211         LDAPMessage *res = NULL;
3212         int count = 0;
3213         size_t tmp_num_sids;
3214         DOM_SID *tmp_sids;
3215         DOM_SID tmp_user_sid;
3216         DOM_SID tmp_primary_group_sid;
3217         uint32 pgid;
3218         const char *attrs[] = {
3219                 "objectSid",
3220                 "tokenGroups",
3221                 "primaryGroupID",
3222                 NULL
3223         };
3224
3225         status = ads_search_retry_dn(ads, &res, dn, attrs);
3226         if (!ADS_ERR_OK(status)) {
3227                 return status;
3228         }
3229
3230         count = ads_count_replies(ads, res);
3231         if (count != 1) {
3232                 ads_msgfree(ads, res);
3233                 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3234         }
3235
3236         if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3237                 ads_msgfree(ads, res);
3238                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3239         }
3240
3241         if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3242                 ads_msgfree(ads, res);
3243                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3244         }
3245
3246         {
3247                 /* hack to compose the primary group sid without knowing the
3248                  * domsid */
3249
3250                 DOM_SID domsid;
3251                 uint32 dummy_rid;
3252
3253                 sid_copy(&domsid, &tmp_user_sid);
3254
3255                 if (!sid_split_rid(&domsid, &dummy_rid)) {
3256                         ads_msgfree(ads, res);
3257                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3258                 }
3259
3260                 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3261                         ads_msgfree(ads, res);
3262                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3263                 }
3264         }
3265
3266         tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3267
3268         if (tmp_num_sids == 0 || !tmp_sids) {
3269                 ads_msgfree(ads, res);
3270                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3271         }
3272
3273         if (num_sids) {
3274                 *num_sids = tmp_num_sids;
3275         }
3276
3277         if (sids) {
3278                 *sids = tmp_sids;
3279         }
3280
3281         if (user_sid) {
3282                 *user_sid = tmp_user_sid;
3283         }
3284
3285         if (primary_group_sid) {
3286                 *primary_group_sid = tmp_primary_group_sid;
3287         }
3288
3289         DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3290
3291         ads_msgfree(ads, res);
3292         return ADS_ERROR_LDAP(LDAP_SUCCESS);
3293 }
3294
3295 /**
3296  * Find a sAMAccoutName in LDAP
3297  * @param ads connection to ads server
3298  * @param mem_ctx TALLOC_CTX for allocating sid array
3299  * @param samaccountname to search
3300  * @param uac_ret uint32 pointer userAccountControl attribute value
3301  * @param dn_ret pointer to dn
3302  * @return status of token query
3303  **/
3304 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3305                                TALLOC_CTX *mem_ctx,
3306                                const char *samaccountname,
3307                                uint32 *uac_ret,
3308                                const char **dn_ret)
3309 {
3310         ADS_STATUS status;
3311         const char *attrs[] = { "userAccountControl", NULL };
3312         const char *filter;
3313         LDAPMessage *res = NULL;
3314         char *dn = NULL;
3315         uint32 uac = 0;
3316
3317         filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3318                 samaccountname);
3319         if (filter == NULL) {
3320                 goto out;
3321         }
3322
3323         status = ads_do_search_all(ads, ads->config.bind_path,
3324                                    LDAP_SCOPE_SUBTREE,
3325                                    filter, attrs, &res);
3326         
3327         if (!ADS_ERR_OK(status)) {
3328                 goto out;
3329         }
3330
3331         if (ads_count_replies(ads, res) != 1) {
3332                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3333                 goto out;
3334         }
3335
3336         dn = ads_get_dn(ads, res);
3337         if (dn == NULL) {
3338                 status = ADS_ERROR(LDAP_NO_MEMORY);
3339                 goto out;
3340         }
3341
3342         if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3343                 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3344                 goto out;
3345         }
3346
3347         if (uac_ret) {
3348                 *uac_ret = uac;
3349         }
3350
3351         if (dn_ret) {
3352                 *dn_ret = talloc_strdup(mem_ctx, dn);
3353                 if (!*dn_ret) {
3354                         status = ADS_ERROR(LDAP_NO_MEMORY);
3355                         goto out;
3356                 }
3357         }
3358  out:
3359         ads_memfree(ads, dn);
3360         ads_msgfree(ads, res);
3361
3362         return status;
3363 }
3364
3365 /**
3366  * find our configuration path 
3367  * @param ads connection to ads server
3368  * @param mem_ctx Pointer to talloc context
3369  * @param config_path Pointer to the config path
3370  * @return status of search
3371  **/
3372 ADS_STATUS ads_config_path(ADS_STRUCT *ads, 
3373                            TALLOC_CTX *mem_ctx, 
3374                            char **config_path)
3375 {
3376         ADS_STATUS status;
3377         LDAPMessage *res = NULL;
3378         const char *config_context = NULL;
3379         const char *attrs[] = { "configurationNamingContext", NULL };
3380
3381         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, 
3382                                "(objectclass=*)", attrs, &res);
3383         if (!ADS_ERR_OK(status)) {
3384                 return status;
3385         }
3386
3387         config_context = ads_pull_string(ads, mem_ctx, res, 
3388                                          "configurationNamingContext");
3389         ads_msgfree(ads, res);
3390         if (!config_context) {
3391                 return ADS_ERROR(LDAP_NO_MEMORY);
3392         }
3393
3394         if (config_path) {
3395                 *config_path = talloc_strdup(mem_ctx, config_context);
3396                 if (!*config_path) {
3397                         return ADS_ERROR(LDAP_NO_MEMORY);
3398                 }
3399         }
3400
3401         return ADS_ERROR(LDAP_SUCCESS);
3402 }
3403
3404 /**
3405  * find the displayName of an extended right 
3406  * @param ads connection to ads server
3407  * @param config_path The config path
3408  * @param mem_ctx Pointer to talloc context
3409  * @param GUID struct of the rightsGUID
3410  * @return status of search
3411  **/
3412 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads, 
3413                                                 const char *config_path, 
3414                                                 TALLOC_CTX *mem_ctx, 
3415                                                 const struct GUID *rights_guid)
3416 {
3417         ADS_STATUS rc;
3418         LDAPMessage *res = NULL;
3419         char *expr = NULL;
3420         const char *attrs[] = { "displayName", NULL };
3421         const char *result = NULL;
3422         const char *path;
3423
3424         if (!ads || !mem_ctx || !rights_guid) {
3425                 goto done;
3426         }
3427
3428         expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)", 
3429                                smb_uuid_string(mem_ctx, *rights_guid));
3430         if (!expr) {
3431                 goto done;
3432         }
3433
3434         path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3435         if (!path) {
3436                 goto done;
3437         }
3438
3439         rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE, 
3440                                  expr, attrs, &res);
3441         if (!ADS_ERR_OK(rc)) {
3442                 goto done;
3443         }
3444
3445         if (ads_count_replies(ads, res) != 1) {
3446                 goto done;
3447         }
3448
3449         result = ads_pull_string(ads, mem_ctx, res, "displayName");
3450
3451  done:
3452         ads_msgfree(ads, res);
3453         return result;
3454         
3455 }
3456
3457 #endif