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