r22459: Adding ads_get_dn_from_extended_dn(), in preparation of making ranged LDAP
[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         BOOL ret = False;
2262
2263         values = ldap_get_values_len(ads->ld, msg, field);
2264
2265         if (!values) return False;
2266
2267         if (values[0]) {
2268                 prs_struct ps;
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                 prs_mem_free(&ps);
2275         }
2276         
2277         ldap_value_free_len(values);
2278         return ret;
2279 }
2280
2281 /* 
2282  * in order to support usernames longer than 21 characters we need to 
2283  * use both the sAMAccountName and the userPrincipalName attributes 
2284  * It seems that not all users have the userPrincipalName attribute set
2285  *
2286  * @param ads connection to ads server
2287  * @param mem_ctx TALLOC_CTX for allocating sid array
2288  * @param msg Results of search
2289  * @return the username
2290  */
2291  char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2292                          LDAPMessage *msg)
2293 {
2294 #if 0   /* JERRY */
2295         char *ret, *p;
2296
2297         /* lookup_name() only works on the sAMAccountName to 
2298            returning the username portion of userPrincipalName
2299            breaks winbindd_getpwnam() */
2300
2301         ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2302         if (ret && (p = strchr_m(ret, '@'))) {
2303                 *p = 0;
2304                 return ret;
2305         }
2306 #endif
2307         return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2308 }
2309
2310
2311 /**
2312  * find the update serial number - this is the core of the ldap cache
2313  * @param ads connection to ads server
2314  * @param ads connection to ADS server
2315  * @param usn Pointer to retrieved update serial number
2316  * @return status of search
2317  **/
2318 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2319 {
2320         const char *attrs[] = {"highestCommittedUSN", NULL};
2321         ADS_STATUS status;
2322         LDAPMessage *res;
2323
2324         status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2325         if (!ADS_ERR_OK(status)) 
2326                 return status;
2327
2328         if (ads_count_replies(ads, res) != 1) {
2329                 ads_msgfree(ads, res);
2330                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2331         }
2332
2333         if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2334                 ads_msgfree(ads, res);
2335                 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2336         }
2337
2338         ads_msgfree(ads, res);
2339         return ADS_SUCCESS;
2340 }
2341
2342 /* parse a ADS timestring - typical string is
2343    '20020917091222.0Z0' which means 09:12.22 17th September
2344    2002, timezone 0 */
2345 static time_t ads_parse_time(const char *str)
2346 {
2347         struct tm tm;
2348
2349         ZERO_STRUCT(tm);
2350
2351         if (sscanf(str, "%4d%2d%2d%2d%2d%2d", 
2352                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
2353                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2354                 return 0;
2355         }
2356         tm.tm_year -= 1900;
2357         tm.tm_mon -= 1;
2358
2359         return timegm(&tm);
2360 }
2361
2362 /********************************************************************
2363 ********************************************************************/
2364
2365 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2366 {
2367         const char *attrs[] = {"currentTime", NULL};
2368         ADS_STATUS status;
2369         LDAPMessage *res;
2370         char *timestr;
2371         TALLOC_CTX *ctx;
2372         ADS_STRUCT *ads_s = ads;
2373
2374         if (!(ctx = talloc_init("ads_current_time"))) {
2375                 return ADS_ERROR(LDAP_NO_MEMORY);
2376         }
2377
2378         /* establish a new ldap tcp session if necessary */
2379
2380         if ( !ads->ld ) {
2381                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2382                         ads->server.ldap_server )) == NULL )
2383                 {
2384                         goto done;
2385                 }
2386                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2387                 status = ads_connect( ads_s );
2388                 if ( !ADS_ERR_OK(status))
2389                         goto done;
2390         }
2391
2392         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2393         if (!ADS_ERR_OK(status)) {
2394                 goto done;
2395         }
2396
2397         timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2398         if (!timestr) {
2399                 ads_msgfree(ads_s, res);
2400                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2401                 goto done;
2402         }
2403
2404         /* but save the time and offset in the original ADS_STRUCT */   
2405         
2406         ads->config.current_time = ads_parse_time(timestr);
2407
2408         if (ads->config.current_time != 0) {
2409                 ads->auth.time_offset = ads->config.current_time - time(NULL);
2410                 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2411         }
2412
2413         ads_msgfree(ads, res);
2414
2415         status = ADS_SUCCESS;
2416
2417 done:
2418         /* free any temporary ads connections */
2419         if ( ads_s != ads ) {
2420                 ads_destroy( &ads_s );
2421         }
2422         talloc_destroy(ctx);
2423
2424         return status;
2425 }
2426
2427 /********************************************************************
2428 ********************************************************************/
2429
2430 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2431 {
2432         const char *attrs[] = {"domainFunctionality", NULL};
2433         ADS_STATUS status;
2434         LDAPMessage *res;
2435         ADS_STRUCT *ads_s = ads;
2436         
2437         *val = DS_DOMAIN_FUNCTION_2000;
2438
2439         /* establish a new ldap tcp session if necessary */
2440
2441         if ( !ads->ld ) {
2442                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2443                         ads->server.ldap_server )) == NULL )
2444                 {
2445                         goto done;
2446                 }
2447                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2448                 status = ads_connect( ads_s );
2449                 if ( !ADS_ERR_OK(status))
2450                         goto done;
2451         }
2452
2453         /* If the attribute does not exist assume it is a Windows 2000 
2454            functional domain */
2455            
2456         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2457         if (!ADS_ERR_OK(status)) {
2458                 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2459                         status = ADS_SUCCESS;
2460                 }
2461                 goto done;
2462         }
2463
2464         if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2465                 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2466         }
2467         DEBUG(3,("ads_domain_func_level: %d\n", *val));
2468
2469         
2470         ads_msgfree(ads, res);
2471
2472 done:
2473         /* free any temporary ads connections */
2474         if ( ads_s != ads ) {
2475                 ads_destroy( &ads_s );
2476         }
2477
2478         return status;
2479 }
2480
2481 /**
2482  * find the domain sid for our domain
2483  * @param ads connection to ads server
2484  * @param sid Pointer to domain sid
2485  * @return status of search
2486  **/
2487 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2488 {
2489         const char *attrs[] = {"objectSid", NULL};
2490         LDAPMessage *res;
2491         ADS_STATUS rc;
2492
2493         rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
2494                            attrs, &res);
2495         if (!ADS_ERR_OK(rc)) return rc;
2496         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2497                 ads_msgfree(ads, res);
2498                 return ADS_ERROR_SYSTEM(ENOENT);
2499         }
2500         ads_msgfree(ads, res);
2501         
2502         return ADS_SUCCESS;
2503 }
2504
2505 /**
2506  * find our site name 
2507  * @param ads connection to ads server
2508  * @param mem_ctx Pointer to talloc context
2509  * @param site_name Pointer to the sitename
2510  * @return status of search
2511  **/
2512 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2513 {
2514         ADS_STATUS status;
2515         LDAPMessage *res;
2516         const char *dn, *service_name;
2517         const char *attrs[] = { "dsServiceName", NULL };
2518
2519         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2520         if (!ADS_ERR_OK(status)) {
2521                 return status;
2522         }
2523
2524         service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2525         if (service_name == NULL) {
2526                 ads_msgfree(ads, res);
2527                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2528         }
2529
2530         ads_msgfree(ads, res);
2531
2532         /* go up three levels */
2533         dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2534         if (dn == NULL) {
2535                 return ADS_ERROR(LDAP_NO_MEMORY);
2536         }
2537
2538         *site_name = talloc_strdup(mem_ctx, dn);
2539         if (*site_name == NULL) {
2540                 return ADS_ERROR(LDAP_NO_MEMORY);
2541         }
2542
2543         return status;
2544         /*
2545         dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2546         */                                               
2547 }
2548
2549 /**
2550  * find the site dn where a machine resides
2551  * @param ads connection to ads server
2552  * @param mem_ctx Pointer to talloc context
2553  * @param computer_name name of the machine
2554  * @param site_name Pointer to the sitename
2555  * @return status of search
2556  **/
2557 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2558 {
2559         ADS_STATUS status;
2560         LDAPMessage *res;
2561         const char *parent, *config_context, *filter;
2562         const char *attrs[] = { "configurationNamingContext", NULL };
2563         char *dn;
2564
2565         /* shortcut a query */
2566         if (strequal(computer_name, ads->config.ldap_server_name)) {
2567                 return ads_site_dn(ads, mem_ctx, site_dn);
2568         }
2569
2570         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2571         if (!ADS_ERR_OK(status)) {
2572                 return status;
2573         }
2574
2575         config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2576         if (config_context == NULL) {
2577                 ads_msgfree(ads, res);
2578                 return ADS_ERROR(LDAP_NO_MEMORY);
2579         }
2580
2581         filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2582         if (filter == NULL) {
2583                 ads_msgfree(ads, res);
2584                 return ADS_ERROR(LDAP_NO_MEMORY);
2585         }
2586
2587         ads_msgfree(ads, res);
2588
2589         status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, filter, NULL, &res);
2590         if (!ADS_ERR_OK(status)) {
2591                 return status;
2592         }
2593
2594         if (ads_count_replies(ads, res) != 1) {
2595                 ads_msgfree(ads, res);
2596                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2597         }
2598
2599         dn = ads_get_dn(ads, res);
2600         if (dn == NULL) {
2601                 ads_msgfree(ads, res);
2602                 return ADS_ERROR(LDAP_NO_MEMORY);
2603         }
2604
2605         /* go up three levels */
2606         parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2607         if (parent == NULL) {
2608                 ads_msgfree(ads, res);
2609                 ads_memfree(ads, dn);
2610                 return ADS_ERROR(LDAP_NO_MEMORY);
2611         }
2612
2613         *site_dn = talloc_strdup(mem_ctx, parent);
2614         if (*site_dn == NULL) {
2615                 ads_msgfree(ads, res);
2616                 ads_memfree(ads, dn);
2617                 ADS_ERROR(LDAP_NO_MEMORY);
2618         }
2619
2620         ads_memfree(ads, dn);
2621         ads_msgfree(ads, res);
2622
2623         return status;
2624 }
2625
2626 /**
2627  * get the upn suffixes for a domain
2628  * @param ads connection to ads server
2629  * @param mem_ctx Pointer to talloc context
2630  * @param suffixes Pointer to an array of suffixes
2631  * @param num_suffixes Pointer to the number of suffixes
2632  * @return status of search
2633  **/
2634 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2635 {
2636         ADS_STATUS status;
2637         LDAPMessage *res;
2638         const char *config_context, *base;
2639         const char *attrs[] = { "configurationNamingContext", NULL };
2640         const char *attrs2[] = { "uPNSuffixes", NULL };
2641
2642         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2643         if (!ADS_ERR_OK(status)) {
2644                 return status;
2645         }
2646
2647         config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2648         if (config_context == NULL) {
2649                 ads_msgfree(ads, res);
2650                 return ADS_ERROR(LDAP_NO_MEMORY);
2651         }
2652
2653         ads_msgfree(ads, res);
2654
2655         base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2656         if (base == NULL) {
2657                 return ADS_ERROR(LDAP_NO_MEMORY);
2658         }
2659
2660         status = ads_search_dn(ads, &res, base, attrs2); 
2661         if (!ADS_ERR_OK(status)) {
2662                 return status;
2663         }
2664
2665         if (ads_count_replies(ads, res) != 1) {
2666                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2667         }
2668
2669         (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2670         if ((*suffixes) == NULL) {
2671                 ads_msgfree(ads, res);
2672                 return ADS_ERROR(LDAP_NO_MEMORY);
2673         }
2674
2675         ads_msgfree(ads, res);
2676
2677         return status;
2678 }
2679
2680 /**
2681  * pull a dn from an extended dn string
2682  * @param mem_ctx TALLOC_CTX 
2683  * @param extended_dn string
2684  * @param dn pointer to the dn
2685  * @return boolean inidicating success
2686  **/
2687 BOOL ads_get_dn_from_extended_dn(TALLOC_CTX *mem_ctx, 
2688                                  const char *extended_dn,
2689                                  char **dn)
2690 {
2691         char *p;
2692         pstring tok;
2693
2694         if (!extended_dn) {
2695                 return False;
2696         }
2697
2698         while (next_token(&extended_dn, tok, ";", sizeof(tok))) {
2699                 p = tok;
2700         }
2701
2702         if ((*dn = talloc_strdup(mem_ctx, p)) == NULL) {
2703                 return False;
2704         }
2705
2706         return True;
2707 }
2708
2709 /**
2710  * pull a DOM_SID from an extended dn string
2711  * @param mem_ctx TALLOC_CTX 
2712  * @param extended_dn string
2713  * @param flags string type of extended_dn
2714  * @param sid pointer to a DOM_SID
2715  * @return boolean inidicating success
2716  **/
2717 BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx, 
2718                                   const char *extended_dn, 
2719                                   enum ads_extended_dn_flags flags, 
2720                                   DOM_SID *sid)
2721 {
2722         char *p, *q, *dn;
2723
2724         if (!extended_dn) {
2725                 return False;
2726         }
2727
2728         /* otherwise extended_dn gets stripped off */
2729         if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
2730                 return False;
2731         }
2732         /* 
2733          * ADS_EXTENDED_DN_HEX_STRING:
2734          * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2735          *
2736          * ADS_EXTENDED_DN_STRING (only with w2k3):
2737         <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
2738          */
2739
2740         p = strchr(dn, ';');
2741         if (!p) {
2742                 return False;
2743         }
2744
2745         if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2746                 return False;
2747         }
2748
2749         p += strlen(";<SID=");
2750
2751         q = strchr(p, '>');
2752         if (!q) {
2753                 return False;
2754         }
2755         
2756         *q = '\0';
2757
2758         DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2759
2760         switch (flags) {
2761         
2762         case ADS_EXTENDED_DN_STRING:
2763                 if (!string_to_sid(sid, p)) {
2764                         return False;
2765                 }
2766                 break;
2767         case ADS_EXTENDED_DN_HEX_STRING: {
2768                 pstring buf;
2769                 size_t buf_len;
2770
2771                 buf_len = strhex_to_str(buf, strlen(p), p);
2772                 if (buf_len == 0) {
2773                         return False;
2774                 }
2775
2776                 if (!sid_parse(buf, buf_len, sid)) {
2777                         DEBUG(10,("failed to parse sid\n"));
2778                         return False;
2779                 }
2780                 break;
2781                 }
2782         default:
2783                 DEBUG(10,("unknown extended dn format\n"));
2784                 return False;
2785         }
2786
2787         return True;
2788 }
2789
2790 /**
2791  * pull an array of DOM_SIDs from a ADS result
2792  * @param ads connection to ads server
2793  * @param mem_ctx TALLOC_CTX for allocating sid array
2794  * @param msg Results of search
2795  * @param field Attribute to retrieve
2796  * @param flags string type of extended_dn
2797  * @param sids pointer to sid array to allocate
2798  * @return the count of SIDs pulled
2799  **/
2800  int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads, 
2801                                    TALLOC_CTX *mem_ctx, 
2802                                    LDAPMessage *msg, 
2803                                    const char *field,
2804                                    enum ads_extended_dn_flags flags,
2805                                    DOM_SID **sids)
2806 {
2807         int i;
2808         size_t dn_count;
2809         char **dn_strings;
2810
2811         if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field, 
2812                                            &dn_count)) == NULL) {
2813                 return 0;
2814         }
2815
2816         (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2817         if (!(*sids)) {
2818                 TALLOC_FREE(dn_strings);
2819                 return 0;
2820         }
2821
2822         for (i=0; i<dn_count; i++) {
2823
2824                 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i], 
2825                                                   flags, &(*sids)[i])) {
2826                         TALLOC_FREE(*sids);
2827                         TALLOC_FREE(dn_strings);
2828                         return 0;
2829                 }
2830         }
2831
2832         TALLOC_FREE(dn_strings);
2833
2834         return dn_count;
2835 }
2836
2837 /********************************************************************
2838 ********************************************************************/
2839
2840 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2841 {
2842         LDAPMessage *res = NULL;
2843         ADS_STATUS status;
2844         int count = 0;
2845         char *name = NULL;
2846         
2847         status = ads_find_machine_acct(ads, &res, global_myname());
2848         if (!ADS_ERR_OK(status)) {
2849                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2850                         global_myname()));
2851                 goto out;
2852         }
2853                 
2854         if ( (count = ads_count_replies(ads, res)) != 1 ) {
2855                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2856                 goto out;
2857         }
2858                 
2859         if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
2860                 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2861         }
2862
2863 out:
2864         ads_msgfree(ads, res);
2865         
2866         return name;
2867 }
2868
2869 /********************************************************************
2870 ********************************************************************/
2871
2872 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2873 {
2874         LDAPMessage *res = NULL;
2875         ADS_STATUS status;
2876         int count = 0;
2877         char *name = NULL;
2878         
2879         status = ads_find_machine_acct(ads, &res, global_myname());
2880         if (!ADS_ERR_OK(status)) {
2881                 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
2882                         global_myname()));
2883                 goto out;
2884         }
2885                 
2886         if ( (count = ads_count_replies(ads, res)) != 1 ) {
2887                 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
2888                 goto out;
2889         }
2890                 
2891         if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
2892                 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
2893         }
2894
2895 out:
2896         ads_msgfree(ads, res);
2897         
2898         return name;
2899 }
2900
2901 /********************************************************************
2902 ********************************************************************/
2903
2904 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2905 {
2906         LDAPMessage *res = NULL;
2907         ADS_STATUS status;
2908         int count = 0;
2909         char *name = NULL;
2910         
2911         status = ads_find_machine_acct(ads, &res, global_myname());
2912         if (!ADS_ERR_OK(status)) {
2913                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2914                         global_myname()));
2915                 goto out;
2916         }
2917                 
2918         if ( (count = ads_count_replies(ads, res)) != 1 ) {
2919                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2920                 goto out;
2921         }
2922                 
2923         if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
2924                 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
2925         }
2926
2927 out:
2928         ads_msgfree(ads, res);
2929         
2930         return name;
2931 }
2932
2933 #if 0
2934
2935    SAVED CODE - we used to join via ldap - remember how we did this. JRA.
2936
2937 /**
2938  * Join a machine to a realm
2939  *  Creates the machine account and sets the machine password
2940  * @param ads connection to ads server
2941  * @param machine name of host to add
2942  * @param org_unit Organizational unit to place machine in
2943  * @return status of join
2944  **/
2945 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
2946                         uint32 account_type, const char *org_unit)
2947 {
2948         ADS_STATUS status;
2949         LDAPMessage *res = NULL;
2950         char *machine;
2951
2952         /* machine name must be lowercase */
2953         machine = SMB_STRDUP(machine_name);
2954         strlower_m(machine);
2955
2956         /*
2957         status = ads_find_machine_acct(ads, (void **)&res, machine);
2958         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
2959                 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
2960                 status = ads_leave_realm(ads, machine);
2961                 if (!ADS_ERR_OK(status)) {
2962                         DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
2963                                 machine, ads->config.realm));
2964                         return status;
2965                 }
2966         }
2967         */
2968         status = ads_add_machine_acct(ads, machine, account_type, org_unit);
2969         if (!ADS_ERR_OK(status)) {
2970                 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
2971                 SAFE_FREE(machine);
2972                 return status;
2973         }
2974
2975         status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
2976         if (!ADS_ERR_OK(status)) {
2977                 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
2978                 SAFE_FREE(machine);
2979                 return status;
2980         }
2981
2982         SAFE_FREE(machine);
2983         ads_msgfree(ads, res);
2984
2985         return status;
2986 }
2987 #endif
2988
2989 /**
2990  * Delete a machine from the realm
2991  * @param ads connection to ads server
2992  * @param hostname Machine to remove
2993  * @return status of delete
2994  **/
2995 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
2996 {
2997         ADS_STATUS status;
2998         void *msg;
2999         LDAPMessage *res;
3000         char *hostnameDN, *host;
3001         int rc;
3002         LDAPControl ldap_control;
3003         LDAPControl  * pldap_control[2] = {NULL, NULL};
3004
3005         pldap_control[0] = &ldap_control;
3006         memset(&ldap_control, 0, sizeof(LDAPControl));
3007         ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3008
3009         /* hostname must be lowercase */
3010         host = SMB_STRDUP(hostname);
3011         strlower_m(host);
3012
3013         status = ads_find_machine_acct(ads, &res, host);
3014         if (!ADS_ERR_OK(status)) {
3015                 DEBUG(0, ("Host account for %s does not exist.\n", host));
3016                 SAFE_FREE(host);
3017                 return status;
3018         }
3019
3020         msg = ads_first_entry(ads, res);
3021         if (!msg) {
3022                 SAFE_FREE(host);
3023                 return ADS_ERROR_SYSTEM(ENOENT);
3024         }
3025
3026         hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3027
3028         rc = ldap_delete_ext_s(ads->ld, hostnameDN, pldap_control, NULL);
3029         if (rc) {
3030                 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3031         }else {
3032                 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3033         }
3034
3035         if (rc != LDAP_SUCCESS) {
3036                 const char *attrs[] = { "cn", NULL };
3037                 LDAPMessage *msg_sub;
3038
3039                 /* we only search with scope ONE, we do not expect any further
3040                  * objects to be created deeper */
3041
3042                 status = ads_do_search_retry(ads, hostnameDN,
3043                                              LDAP_SCOPE_ONELEVEL,
3044                                              "(objectclass=*)", attrs, &res);
3045
3046                 if (!ADS_ERR_OK(status)) {
3047                         SAFE_FREE(host);
3048                         ads_memfree(ads, hostnameDN);
3049                         return status;
3050                 }
3051
3052                 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3053                         msg_sub = ads_next_entry(ads, msg_sub)) {
3054
3055                         char *dn = NULL;
3056
3057                         if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3058                                 SAFE_FREE(host);
3059                                 ads_memfree(ads, hostnameDN);
3060                                 return ADS_ERROR(LDAP_NO_MEMORY);
3061                         }
3062
3063                         status = ads_del_dn(ads, dn);
3064                         if (!ADS_ERR_OK(status)) {
3065                                 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3066                                 SAFE_FREE(host);
3067                                 ads_memfree(ads, dn);
3068                                 ads_memfree(ads, hostnameDN);
3069                                 return status;
3070                         }
3071
3072                         ads_memfree(ads, dn);
3073                 }
3074
3075                 /* there should be no subordinate objects anymore */
3076                 status = ads_do_search_retry(ads, hostnameDN,
3077                                              LDAP_SCOPE_ONELEVEL,
3078                                              "(objectclass=*)", attrs, &res);
3079
3080                 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3081                         SAFE_FREE(host);
3082                         ads_memfree(ads, hostnameDN);
3083                         return status;
3084                 }
3085
3086                 /* delete hostnameDN now */
3087                 status = ads_del_dn(ads, hostnameDN);
3088                 if (!ADS_ERR_OK(status)) {
3089                         SAFE_FREE(host);
3090                         DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3091                         ads_memfree(ads, hostnameDN);
3092                         return status;
3093                 }
3094         }
3095
3096         ads_memfree(ads, hostnameDN);
3097
3098         status = ads_find_machine_acct(ads, &res, host);
3099         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3100                 DEBUG(3, ("Failed to remove host account.\n"));
3101                 SAFE_FREE(host);
3102                 return status;
3103         }
3104
3105         SAFE_FREE(host);
3106         return status;
3107 }
3108
3109 #endif