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