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