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