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