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