r24166: Fix Coverity ID 391
[jra/samba/.git] / source / libads / ldap.c
1 /* 
2    Unix SMB/CIFS implementation.
3    ads (active directory) utility library
4    Copyright (C) Andrew Tridgell 2001
5    Copyright (C) Remus Koos 2001
6    Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
7    Copyright (C) Guenther Deschner 2005
8    Copyright (C) Gerald Carter 2006
9    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 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                 {NULL, True, NULL}
1906         };
1907         int i;
1908
1909         if (!field) { /* must be end of an entry */
1910                 printf("\n");
1911                 return False;
1912         }
1913
1914         for (i=0; handlers[i].name; i++) {
1915                 if (StrCaseCmp(handlers[i].name, field) == 0) {
1916                         if (!values) /* first time, indicate string or not */
1917                                 return handlers[i].string;
1918                         handlers[i].handler(ads, field, (struct berval **) values);
1919                         break;
1920                 }
1921         }
1922         if (!handlers[i].name) {
1923                 if (!values) /* first time, indicate string conversion */
1924                         return True;
1925                 dump_string(field, (char **)values);
1926         }
1927         return False;
1928 }
1929
1930 /**
1931  * Dump a result from LDAP on stdout
1932  *  used for debugging
1933  * @param ads connection to ads server
1934  * @param res Results to dump
1935  **/
1936
1937  void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
1938 {
1939         ads_process_results(ads, res, ads_dump_field, NULL);
1940 }
1941
1942 /**
1943  * Walk through results, calling a function for each entry found.
1944  *  The function receives a field name, a berval * array of values,
1945  *  and a data area passed through from the start.  The function is
1946  *  called once with null for field and values at the end of each
1947  *  entry.
1948  * @param ads connection to ads server
1949  * @param res Results to process
1950  * @param fn Function for processing each result
1951  * @param data_area user-defined area to pass to function
1952  **/
1953  void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
1954                           BOOL(*fn)(ADS_STRUCT *, char *, void **, void *),
1955                           void *data_area)
1956 {
1957         LDAPMessage *msg;
1958         TALLOC_CTX *ctx;
1959
1960         if (!(ctx = talloc_init("ads_process_results")))
1961                 return;
1962
1963         for (msg = ads_first_entry(ads, res); msg; 
1964              msg = ads_next_entry(ads, msg)) {
1965                 char *utf8_field;
1966                 BerElement *b;
1967         
1968                 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
1969                                                      (LDAPMessage *)msg,&b); 
1970                      utf8_field;
1971                      utf8_field=ldap_next_attribute(ads->ldap.ld,
1972                                                     (LDAPMessage *)msg,b)) {
1973                         struct berval **ber_vals;
1974                         char **str_vals, **utf8_vals;
1975                         char *field;
1976                         BOOL string; 
1977
1978                         pull_utf8_talloc(ctx, &field, utf8_field);
1979                         string = fn(ads, field, NULL, data_area);
1980
1981                         if (string) {
1982                                 utf8_vals = ldap_get_values(ads->ldap.ld,
1983                                                  (LDAPMessage *)msg, field);
1984                                 str_vals = ads_pull_strvals(ctx, 
1985                                                   (const char **) utf8_vals);
1986                                 fn(ads, field, (void **) str_vals, data_area);
1987                                 ldap_value_free(utf8_vals);
1988                         } else {
1989                                 ber_vals = ldap_get_values_len(ads->ldap.ld, 
1990                                                  (LDAPMessage *)msg, field);
1991                                 fn(ads, field, (void **) ber_vals, data_area);
1992
1993                                 ldap_value_free_len(ber_vals);
1994                         }
1995                         ldap_memfree(utf8_field);
1996                 }
1997                 ber_free(b, 0);
1998                 talloc_free_children(ctx);
1999                 fn(ads, NULL, NULL, data_area); /* completed an entry */
2000
2001         }
2002         talloc_destroy(ctx);
2003 }
2004
2005 /**
2006  * count how many replies are in a LDAPMessage
2007  * @param ads connection to ads server
2008  * @param res Results to count
2009  * @return number of replies
2010  **/
2011 int ads_count_replies(ADS_STRUCT *ads, void *res)
2012 {
2013         return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2014 }
2015
2016 /**
2017  * pull the first entry from a ADS result
2018  * @param ads connection to ads server
2019  * @param res Results of search
2020  * @return first entry from result
2021  **/
2022  LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2023 {
2024         return ldap_first_entry(ads->ldap.ld, res);
2025 }
2026
2027 /**
2028  * pull the next entry from a ADS result
2029  * @param ads connection to ads server
2030  * @param res Results of search
2031  * @return next entry from result
2032  **/
2033  LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2034 {
2035         return ldap_next_entry(ads->ldap.ld, res);
2036 }
2037
2038 /**
2039  * pull a single string from a ADS result
2040  * @param ads connection to ads server
2041  * @param mem_ctx TALLOC_CTX to use for allocating result string
2042  * @param msg Results of search
2043  * @param field Attribute to retrieve
2044  * @return Result string in talloc context
2045  **/
2046  char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2047                        const char *field)
2048 {
2049         char **values;
2050         char *ret = NULL;
2051         char *ux_string;
2052         size_t rc;
2053
2054         values = ldap_get_values(ads->ldap.ld, msg, field);
2055         if (!values)
2056                 return NULL;
2057         
2058         if (values[0]) {
2059                 rc = pull_utf8_talloc(mem_ctx, &ux_string, 
2060                                       values[0]);
2061                 if (rc != (size_t)-1)
2062                         ret = ux_string;
2063                 
2064         }
2065         ldap_value_free(values);
2066         return ret;
2067 }
2068
2069 /**
2070  * pull an array of strings from a ADS result
2071  * @param ads connection to ads server
2072  * @param mem_ctx TALLOC_CTX to use for allocating result string
2073  * @param msg Results of search
2074  * @param field Attribute to retrieve
2075  * @return Result strings in talloc context
2076  **/
2077  char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2078                          LDAPMessage *msg, const char *field,
2079                          size_t *num_values)
2080 {
2081         char **values;
2082         char **ret = NULL;
2083         int i;
2084
2085         values = ldap_get_values(ads->ldap.ld, msg, field);
2086         if (!values)
2087                 return NULL;
2088
2089         *num_values = ldap_count_values(values);
2090
2091         ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2092         if (!ret) {
2093                 ldap_value_free(values);
2094                 return NULL;
2095         }
2096
2097         for (i=0;i<*num_values;i++) {
2098                 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2099                         ldap_value_free(values);
2100                         return NULL;
2101                 }
2102         }
2103         ret[i] = NULL;
2104
2105         ldap_value_free(values);
2106         return ret;
2107 }
2108
2109 /**
2110  * pull an array of strings from a ADS result 
2111  *  (handle large multivalue attributes with range retrieval)
2112  * @param ads connection to ads server
2113  * @param mem_ctx TALLOC_CTX to use for allocating result string
2114  * @param msg Results of search
2115  * @param field Attribute to retrieve
2116  * @param current_strings strings returned by a previous call to this function
2117  * @param next_attribute The next query should ask for this attribute
2118  * @param num_values How many values did we get this time?
2119  * @param more_values Are there more values to get?
2120  * @return Result strings in talloc context
2121  **/
2122  char **ads_pull_strings_range(ADS_STRUCT *ads, 
2123                                TALLOC_CTX *mem_ctx,
2124                                LDAPMessage *msg, const char *field,
2125                                char **current_strings,
2126                                const char **next_attribute,
2127                                size_t *num_strings,
2128                                BOOL *more_strings)
2129 {
2130         char *attr;
2131         char *expected_range_attrib, *range_attr;
2132         BerElement *ptr = NULL;
2133         char **strings;
2134         char **new_strings;
2135         size_t num_new_strings;
2136         unsigned long int range_start;
2137         unsigned long int range_end;
2138         
2139         /* we might have been given the whole lot anyway */
2140         if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2141                 *more_strings = False;
2142                 return strings;
2143         }
2144
2145         expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2146
2147         /* look for Range result */
2148         for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr); 
2149              attr; 
2150              attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2151                 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2152                 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2153                         range_attr = attr;
2154                         break;
2155                 }
2156                 ldap_memfree(attr);
2157         }
2158         if (!attr) {
2159                 ber_free(ptr, 0);
2160                 /* nothing here - this field is just empty */
2161                 *more_strings = False;
2162                 return NULL;
2163         }
2164         
2165         if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu", 
2166                    &range_start, &range_end) == 2) {
2167                 *more_strings = True;
2168         } else {
2169                 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*", 
2170                            &range_start) == 1) {
2171                         *more_strings = False;
2172                 } else {
2173                         DEBUG(1, ("ads_pull_strings_range:  Cannot parse Range attriubte (%s)\n", 
2174                                   range_attr));
2175                         ldap_memfree(range_attr);
2176                         *more_strings = False;
2177                         return NULL;
2178                 }
2179         }
2180
2181         if ((*num_strings) != range_start) {
2182                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2183                           " - aborting range retreival\n",
2184                           range_attr, (unsigned int)(*num_strings) + 1, range_start));
2185                 ldap_memfree(range_attr);
2186                 *more_strings = False;
2187                 return NULL;
2188         }
2189
2190         new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2191         
2192         if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2193                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2194                           "strings in this bunch, but we only got %lu - aborting range retreival\n",
2195                           range_attr, (unsigned long int)range_end - range_start + 1, 
2196                           (unsigned long int)num_new_strings));
2197                 ldap_memfree(range_attr);
2198                 *more_strings = False;
2199                 return NULL;
2200         }
2201
2202         strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2203                                  *num_strings + num_new_strings);
2204         
2205         if (strings == NULL) {
2206                 ldap_memfree(range_attr);
2207                 *more_strings = False;
2208                 return NULL;
2209         }
2210         
2211         if (new_strings && num_new_strings) {
2212                 memcpy(&strings[*num_strings], new_strings,
2213                        sizeof(*new_strings) * num_new_strings);
2214         }
2215
2216         (*num_strings) += num_new_strings;
2217
2218         if (*more_strings) {
2219                 *next_attribute = talloc_asprintf(mem_ctx,
2220                                                   "%s;range=%d-*", 
2221                                                   field,
2222                                                   (int)*num_strings);
2223                 
2224                 if (!*next_attribute) {
2225                         DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2226                         ldap_memfree(range_attr);
2227                         *more_strings = False;
2228                         return NULL;
2229                 }
2230         }
2231
2232         ldap_memfree(range_attr);
2233
2234         return strings;
2235 }
2236
2237 /**
2238  * pull a single uint32 from a ADS result
2239  * @param ads connection to ads server
2240  * @param msg Results of search
2241  * @param field Attribute to retrieve
2242  * @param v Pointer to int to store result
2243  * @return boolean inidicating success
2244 */
2245  BOOL ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2246                       uint32 *v)
2247 {
2248         char **values;
2249
2250         values = ldap_get_values(ads->ldap.ld, msg, field);
2251         if (!values)
2252                 return False;
2253         if (!values[0]) {
2254                 ldap_value_free(values);
2255                 return False;
2256         }
2257
2258         *v = atoi(values[0]);
2259         ldap_value_free(values);
2260         return True;
2261 }
2262
2263 /**
2264  * pull a single objectGUID from an ADS result
2265  * @param ads connection to ADS server
2266  * @param msg results of search
2267  * @param guid 37-byte area to receive text guid
2268  * @return boolean indicating success
2269  **/
2270  BOOL ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2271 {
2272         char **values;
2273         UUID_FLAT flat_guid;
2274
2275         values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2276         if (!values)
2277                 return False;
2278         
2279         if (values[0]) {
2280                 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2281                 smb_uuid_unpack(flat_guid, guid);
2282                 ldap_value_free(values);
2283                 return True;
2284         }
2285         ldap_value_free(values);
2286         return False;
2287
2288 }
2289
2290
2291 /**
2292  * pull a single DOM_SID from a ADS result
2293  * @param ads connection to ads server
2294  * @param msg Results of search
2295  * @param field Attribute to retrieve
2296  * @param sid Pointer to sid to store result
2297  * @return boolean inidicating success
2298 */
2299  BOOL ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2300                    DOM_SID *sid)
2301 {
2302         struct berval **values;
2303         BOOL ret = False;
2304
2305         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2306
2307         if (!values)
2308                 return False;
2309
2310         if (values[0])
2311                 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2312         
2313         ldap_value_free_len(values);
2314         return ret;
2315 }
2316
2317 /**
2318  * pull an array of DOM_SIDs from a ADS result
2319  * @param ads connection to ads server
2320  * @param mem_ctx TALLOC_CTX for allocating sid array
2321  * @param msg Results of search
2322  * @param field Attribute to retrieve
2323  * @param sids pointer to sid array to allocate
2324  * @return the count of SIDs pulled
2325  **/
2326  int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2327                    LDAPMessage *msg, const char *field, DOM_SID **sids)
2328 {
2329         struct berval **values;
2330         BOOL ret;
2331         int count, i;
2332
2333         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2334
2335         if (!values)
2336                 return 0;
2337
2338         for (i=0; values[i]; i++)
2339                 /* nop */ ;
2340
2341         if (i) {
2342                 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2343                 if (!(*sids)) {
2344                         ldap_value_free_len(values);
2345                         return 0;
2346                 }
2347         } else {
2348                 (*sids) = NULL;
2349         }
2350
2351         count = 0;
2352         for (i=0; values[i]; i++) {
2353                 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2354                 if (ret) {
2355                         fstring sid;
2356                         DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2357                         count++;
2358                 }
2359         }
2360         
2361         ldap_value_free_len(values);
2362         return count;
2363 }
2364
2365 /**
2366  * pull a SEC_DESC from a ADS result
2367  * @param ads connection to ads server
2368  * @param mem_ctx TALLOC_CTX for allocating sid array
2369  * @param msg Results of search
2370  * @param field Attribute to retrieve
2371  * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2372  * @return boolean inidicating success
2373 */
2374  BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2375                   LDAPMessage *msg, const char *field, SEC_DESC **sd)
2376 {
2377         struct berval **values;
2378         BOOL ret = False;
2379
2380         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2381
2382         if (!values) return False;
2383
2384         if (values[0]) {
2385                 prs_struct ps;
2386                 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2387                 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2388                 prs_set_offset(&ps,0);
2389
2390                 ret = sec_io_desc("sd", sd, &ps, 1);
2391                 prs_mem_free(&ps);
2392         }
2393         
2394         ldap_value_free_len(values);
2395         return ret;
2396 }
2397
2398 /* 
2399  * in order to support usernames longer than 21 characters we need to 
2400  * use both the sAMAccountName and the userPrincipalName attributes 
2401  * It seems that not all users have the userPrincipalName attribute set
2402  *
2403  * @param ads connection to ads server
2404  * @param mem_ctx TALLOC_CTX for allocating sid array
2405  * @param msg Results of search
2406  * @return the username
2407  */
2408  char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2409                          LDAPMessage *msg)
2410 {
2411 #if 0   /* JERRY */
2412         char *ret, *p;
2413
2414         /* lookup_name() only works on the sAMAccountName to 
2415            returning the username portion of userPrincipalName
2416            breaks winbindd_getpwnam() */
2417
2418         ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2419         if (ret && (p = strchr_m(ret, '@'))) {
2420                 *p = 0;
2421                 return ret;
2422         }
2423 #endif
2424         return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2425 }
2426
2427
2428 /**
2429  * find the update serial number - this is the core of the ldap cache
2430  * @param ads connection to ads server
2431  * @param ads connection to ADS server
2432  * @param usn Pointer to retrieved update serial number
2433  * @return status of search
2434  **/
2435 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2436 {
2437         const char *attrs[] = {"highestCommittedUSN", NULL};
2438         ADS_STATUS status;
2439         LDAPMessage *res;
2440
2441         status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2442         if (!ADS_ERR_OK(status)) 
2443                 return status;
2444
2445         if (ads_count_replies(ads, res) != 1) {
2446                 ads_msgfree(ads, res);
2447                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2448         }
2449
2450         if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2451                 ads_msgfree(ads, res);
2452                 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2453         }
2454
2455         ads_msgfree(ads, res);
2456         return ADS_SUCCESS;
2457 }
2458
2459 /* parse a ADS timestring - typical string is
2460    '20020917091222.0Z0' which means 09:12.22 17th September
2461    2002, timezone 0 */
2462 static time_t ads_parse_time(const char *str)
2463 {
2464         struct tm tm;
2465
2466         ZERO_STRUCT(tm);
2467
2468         if (sscanf(str, "%4d%2d%2d%2d%2d%2d", 
2469                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
2470                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2471                 return 0;
2472         }
2473         tm.tm_year -= 1900;
2474         tm.tm_mon -= 1;
2475
2476         return timegm(&tm);
2477 }
2478
2479 /********************************************************************
2480 ********************************************************************/
2481
2482 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2483 {
2484         const char *attrs[] = {"currentTime", NULL};
2485         ADS_STATUS status;
2486         LDAPMessage *res;
2487         char *timestr;
2488         TALLOC_CTX *ctx;
2489         ADS_STRUCT *ads_s = ads;
2490
2491         if (!(ctx = talloc_init("ads_current_time"))) {
2492                 return ADS_ERROR(LDAP_NO_MEMORY);
2493         }
2494
2495         /* establish a new ldap tcp session if necessary */
2496
2497         if ( !ads->ldap.ld ) {
2498                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2499                         ads->server.ldap_server )) == NULL )
2500                 {
2501                         goto done;
2502                 }
2503                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2504                 status = ads_connect( ads_s );
2505                 if ( !ADS_ERR_OK(status))
2506                         goto done;
2507         }
2508
2509         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2510         if (!ADS_ERR_OK(status)) {
2511                 goto done;
2512         }
2513
2514         timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2515         if (!timestr) {
2516                 ads_msgfree(ads_s, res);
2517                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2518                 goto done;
2519         }
2520
2521         /* but save the time and offset in the original ADS_STRUCT */   
2522         
2523         ads->config.current_time = ads_parse_time(timestr);
2524
2525         if (ads->config.current_time != 0) {
2526                 ads->auth.time_offset = ads->config.current_time - time(NULL);
2527                 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2528         }
2529
2530         ads_msgfree(ads, res);
2531
2532         status = ADS_SUCCESS;
2533
2534 done:
2535         /* free any temporary ads connections */
2536         if ( ads_s != ads ) {
2537                 ads_destroy( &ads_s );
2538         }
2539         talloc_destroy(ctx);
2540
2541         return status;
2542 }
2543
2544 /********************************************************************
2545 ********************************************************************/
2546
2547 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2548 {
2549         const char *attrs[] = {"domainFunctionality", NULL};
2550         ADS_STATUS status;
2551         LDAPMessage *res;
2552         ADS_STRUCT *ads_s = ads;
2553         
2554         *val = DS_DOMAIN_FUNCTION_2000;
2555
2556         /* establish a new ldap tcp session if necessary */
2557
2558         if ( !ads->ldap.ld ) {
2559                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2560                         ads->server.ldap_server )) == NULL )
2561                 {
2562                         goto done;
2563                 }
2564                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2565                 status = ads_connect( ads_s );
2566                 if ( !ADS_ERR_OK(status))
2567                         goto done;
2568         }
2569
2570         /* If the attribute does not exist assume it is a Windows 2000 
2571            functional domain */
2572            
2573         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2574         if (!ADS_ERR_OK(status)) {
2575                 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2576                         status = ADS_SUCCESS;
2577                 }
2578                 goto done;
2579         }
2580
2581         if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2582                 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2583         }
2584         DEBUG(3,("ads_domain_func_level: %d\n", *val));
2585
2586         
2587         ads_msgfree(ads, res);
2588
2589 done:
2590         /* free any temporary ads connections */
2591         if ( ads_s != ads ) {
2592                 ads_destroy( &ads_s );
2593         }
2594
2595         return status;
2596 }
2597
2598 /**
2599  * find the domain sid for our domain
2600  * @param ads connection to ads server
2601  * @param sid Pointer to domain sid
2602  * @return status of search
2603  **/
2604 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2605 {
2606         const char *attrs[] = {"objectSid", NULL};
2607         LDAPMessage *res;
2608         ADS_STATUS rc;
2609
2610         rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
2611                            attrs, &res);
2612         if (!ADS_ERR_OK(rc)) return rc;
2613         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2614                 ads_msgfree(ads, res);
2615                 return ADS_ERROR_SYSTEM(ENOENT);
2616         }
2617         ads_msgfree(ads, res);
2618         
2619         return ADS_SUCCESS;
2620 }
2621
2622 /**
2623  * find our site name 
2624  * @param ads connection to ads server
2625  * @param mem_ctx Pointer to talloc context
2626  * @param site_name Pointer to the sitename
2627  * @return status of search
2628  **/
2629 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2630 {
2631         ADS_STATUS status;
2632         LDAPMessage *res;
2633         const char *dn, *service_name;
2634         const char *attrs[] = { "dsServiceName", NULL };
2635
2636         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2637         if (!ADS_ERR_OK(status)) {
2638                 return status;
2639         }
2640
2641         service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2642         if (service_name == NULL) {
2643                 ads_msgfree(ads, res);
2644                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2645         }
2646
2647         ads_msgfree(ads, res);
2648
2649         /* go up three levels */
2650         dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2651         if (dn == NULL) {
2652                 return ADS_ERROR(LDAP_NO_MEMORY);
2653         }
2654
2655         *site_name = talloc_strdup(mem_ctx, dn);
2656         if (*site_name == NULL) {
2657                 return ADS_ERROR(LDAP_NO_MEMORY);
2658         }
2659
2660         return status;
2661         /*
2662         dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2663         */                                               
2664 }
2665
2666 /**
2667  * find the site dn where a machine resides
2668  * @param ads connection to ads server
2669  * @param mem_ctx Pointer to talloc context
2670  * @param computer_name name of the machine
2671  * @param site_name Pointer to the sitename
2672  * @return status of search
2673  **/
2674 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2675 {
2676         ADS_STATUS status;
2677         LDAPMessage *res;
2678         const char *parent, *filter;
2679         char *config_context = NULL;
2680         char *dn;
2681
2682         /* shortcut a query */
2683         if (strequal(computer_name, ads->config.ldap_server_name)) {
2684                 return ads_site_dn(ads, mem_ctx, site_dn);
2685         }
2686
2687         status = ads_config_path(ads, mem_ctx, &config_context);
2688         if (!ADS_ERR_OK(status)) {
2689                 return status;
2690         }
2691
2692         filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2693         if (filter == NULL) {
2694                 return ADS_ERROR(LDAP_NO_MEMORY);
2695         }
2696
2697         status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, 
2698                                filter, NULL, &res);
2699         if (!ADS_ERR_OK(status)) {
2700                 return status;
2701         }
2702
2703         if (ads_count_replies(ads, res) != 1) {
2704                 ads_msgfree(ads, res);
2705                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2706         }
2707
2708         dn = ads_get_dn(ads, res);
2709         if (dn == NULL) {
2710                 ads_msgfree(ads, res);
2711                 return ADS_ERROR(LDAP_NO_MEMORY);
2712         }
2713
2714         /* go up three levels */
2715         parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2716         if (parent == NULL) {
2717                 ads_msgfree(ads, res);
2718                 ads_memfree(ads, dn);
2719                 return ADS_ERROR(LDAP_NO_MEMORY);
2720         }
2721
2722         *site_dn = talloc_strdup(mem_ctx, parent);
2723         if (*site_dn == NULL) {
2724                 ads_msgfree(ads, res);
2725                 ads_memfree(ads, dn);
2726                 return ADS_ERROR(LDAP_NO_MEMORY);
2727         }
2728
2729         ads_memfree(ads, dn);
2730         ads_msgfree(ads, res);
2731
2732         return status;
2733 }
2734
2735 /**
2736  * get the upn suffixes for a domain
2737  * @param ads connection to ads server
2738  * @param mem_ctx Pointer to talloc context
2739  * @param suffixes Pointer to an array of suffixes
2740  * @param num_suffixes Pointer to the number of suffixes
2741  * @return status of search
2742  **/
2743 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2744 {
2745         ADS_STATUS status;
2746         LDAPMessage *res;
2747         const char *base;
2748         char *config_context = NULL;
2749         const char *attrs[] = { "uPNSuffixes", NULL };
2750
2751         status = ads_config_path(ads, mem_ctx, &config_context);
2752         if (!ADS_ERR_OK(status)) {
2753                 return status;
2754         }
2755
2756         base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2757         if (base == NULL) {
2758                 return ADS_ERROR(LDAP_NO_MEMORY);
2759         }
2760
2761         status = ads_search_dn(ads, &res, base, attrs);
2762         if (!ADS_ERR_OK(status)) {
2763                 return status;
2764         }
2765
2766         if (ads_count_replies(ads, res) != 1) {
2767                 ads_msgfree(ads, res);
2768                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2769         }
2770
2771         (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2772         if ((*suffixes) == NULL) {
2773                 ads_msgfree(ads, res);
2774                 return ADS_ERROR(LDAP_NO_MEMORY);
2775         }
2776
2777         ads_msgfree(ads, res);
2778
2779         return status;
2780 }
2781
2782 /**
2783  * pull a DOM_SID from an extended dn string
2784  * @param mem_ctx TALLOC_CTX 
2785  * @param extended_dn string
2786  * @param flags string type of extended_dn
2787  * @param sid pointer to a DOM_SID
2788  * @return boolean inidicating success
2789  **/
2790 BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx, 
2791                                   const char *extended_dn, 
2792                                   enum ads_extended_dn_flags flags, 
2793                                   DOM_SID *sid)
2794 {
2795         char *p, *q, *dn;
2796
2797         if (!extended_dn) {
2798                 return False;
2799         }
2800
2801         /* otherwise extended_dn gets stripped off */
2802         if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
2803                 return False;
2804         }
2805         /* 
2806          * ADS_EXTENDED_DN_HEX_STRING:
2807          * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2808          *
2809          * ADS_EXTENDED_DN_STRING (only with w2k3):
2810         <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
2811          */
2812
2813         p = strchr(dn, ';');
2814         if (!p) {
2815                 return False;
2816         }
2817
2818         if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2819                 return False;
2820         }
2821
2822         p += strlen(";<SID=");
2823
2824         q = strchr(p, '>');
2825         if (!q) {
2826                 return False;
2827         }
2828         
2829         *q = '\0';
2830
2831         DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2832
2833         switch (flags) {
2834         
2835         case ADS_EXTENDED_DN_STRING:
2836                 if (!string_to_sid(sid, p)) {
2837                         return False;
2838                 }
2839                 break;
2840         case ADS_EXTENDED_DN_HEX_STRING: {
2841                 pstring buf;
2842                 size_t buf_len;
2843
2844                 buf_len = strhex_to_str(buf, strlen(p), p);
2845                 if (buf_len == 0) {
2846                         return False;
2847                 }
2848
2849                 if (!sid_parse(buf, buf_len, sid)) {
2850                         DEBUG(10,("failed to parse sid\n"));
2851                         return False;
2852                 }
2853                 break;
2854                 }
2855         default:
2856                 DEBUG(10,("unknown extended dn format\n"));
2857                 return False;
2858         }
2859
2860         return True;
2861 }
2862
2863 /**
2864  * pull an array of DOM_SIDs from a ADS result
2865  * @param ads connection to ads server
2866  * @param mem_ctx TALLOC_CTX for allocating sid array
2867  * @param msg Results of search
2868  * @param field Attribute to retrieve
2869  * @param flags string type of extended_dn
2870  * @param sids pointer to sid array to allocate
2871  * @return the count of SIDs pulled
2872  **/
2873  int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads, 
2874                                    TALLOC_CTX *mem_ctx, 
2875                                    LDAPMessage *msg, 
2876                                    const char *field,
2877                                    enum ads_extended_dn_flags flags,
2878                                    DOM_SID **sids)
2879 {
2880         int i;
2881         size_t dn_count;
2882         char **dn_strings;
2883
2884         if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field, 
2885                                            &dn_count)) == NULL) {
2886                 return 0;
2887         }
2888
2889         (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2890         if (!(*sids)) {
2891                 TALLOC_FREE(dn_strings);
2892                 return 0;
2893         }
2894
2895         for (i=0; i<dn_count; i++) {
2896
2897                 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i], 
2898                                                   flags, &(*sids)[i])) {
2899                         TALLOC_FREE(*sids);
2900                         TALLOC_FREE(dn_strings);
2901                         return 0;
2902                 }
2903         }
2904
2905         TALLOC_FREE(dn_strings);
2906
2907         return dn_count;
2908 }
2909
2910 /********************************************************************
2911 ********************************************************************/
2912
2913 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2914 {
2915         LDAPMessage *res = NULL;
2916         ADS_STATUS status;
2917         int count = 0;
2918         char *name = NULL;
2919         
2920         status = ads_find_machine_acct(ads, &res, global_myname());
2921         if (!ADS_ERR_OK(status)) {
2922                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2923                         global_myname()));
2924                 goto out;
2925         }
2926                 
2927         if ( (count = ads_count_replies(ads, res)) != 1 ) {
2928                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2929                 goto out;
2930         }
2931                 
2932         if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
2933                 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2934         }
2935
2936 out:
2937         ads_msgfree(ads, res);
2938         
2939         return name;
2940 }
2941
2942 /********************************************************************
2943 ********************************************************************/
2944
2945 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2946 {
2947         LDAPMessage *res = NULL;
2948         ADS_STATUS status;
2949         int count = 0;
2950         char *name = NULL;
2951         
2952         status = ads_find_machine_acct(ads, &res, global_myname());
2953         if (!ADS_ERR_OK(status)) {
2954                 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
2955                         global_myname()));
2956                 goto out;
2957         }
2958                 
2959         if ( (count = ads_count_replies(ads, res)) != 1 ) {
2960                 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
2961                 goto out;
2962         }
2963                 
2964         if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
2965                 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
2966         }
2967
2968 out:
2969         ads_msgfree(ads, res);
2970         
2971         return name;
2972 }
2973
2974 /********************************************************************
2975 ********************************************************************/
2976
2977 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2978 {
2979         LDAPMessage *res = NULL;
2980         ADS_STATUS status;
2981         int count = 0;
2982         char *name = NULL;
2983         
2984         status = ads_find_machine_acct(ads, &res, global_myname());
2985         if (!ADS_ERR_OK(status)) {
2986                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2987                         global_myname()));
2988                 goto out;
2989         }
2990                 
2991         if ( (count = ads_count_replies(ads, res)) != 1 ) {
2992                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2993                 goto out;
2994         }
2995                 
2996         if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
2997                 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
2998         }
2999
3000 out:
3001         ads_msgfree(ads, res);
3002         
3003         return name;
3004 }
3005
3006 #if 0
3007
3008    SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3009
3010 /**
3011  * Join a machine to a realm
3012  *  Creates the machine account and sets the machine password
3013  * @param ads connection to ads server
3014  * @param machine name of host to add
3015  * @param org_unit Organizational unit to place machine in
3016  * @return status of join
3017  **/
3018 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3019                         uint32 account_type, const char *org_unit)
3020 {
3021         ADS_STATUS status;
3022         LDAPMessage *res = NULL;
3023         char *machine;
3024
3025         /* machine name must be lowercase */
3026         machine = SMB_STRDUP(machine_name);
3027         strlower_m(machine);
3028
3029         /*
3030         status = ads_find_machine_acct(ads, (void **)&res, machine);
3031         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3032                 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3033                 status = ads_leave_realm(ads, machine);
3034                 if (!ADS_ERR_OK(status)) {
3035                         DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3036                                 machine, ads->config.realm));
3037                         return status;
3038                 }
3039         }
3040         */
3041         status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3042         if (!ADS_ERR_OK(status)) {
3043                 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3044                 SAFE_FREE(machine);
3045                 return status;
3046         }
3047
3048         status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3049         if (!ADS_ERR_OK(status)) {
3050                 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3051                 SAFE_FREE(machine);
3052                 return status;
3053         }
3054
3055         SAFE_FREE(machine);
3056         ads_msgfree(ads, res);
3057
3058         return status;
3059 }
3060 #endif
3061
3062 /**
3063  * Delete a machine from the realm
3064  * @param ads connection to ads server
3065  * @param hostname Machine to remove
3066  * @return status of delete
3067  **/
3068 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3069 {
3070         ADS_STATUS status;
3071         void *msg;
3072         LDAPMessage *res;
3073         char *hostnameDN, *host;
3074         int rc;
3075         LDAPControl ldap_control;
3076         LDAPControl  * pldap_control[2] = {NULL, NULL};
3077
3078         pldap_control[0] = &ldap_control;
3079         memset(&ldap_control, 0, sizeof(LDAPControl));
3080         ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3081
3082         /* hostname must be lowercase */
3083         host = SMB_STRDUP(hostname);
3084         strlower_m(host);
3085
3086         status = ads_find_machine_acct(ads, &res, host);
3087         if (!ADS_ERR_OK(status)) {
3088                 DEBUG(0, ("Host account for %s does not exist.\n", host));
3089                 SAFE_FREE(host);
3090                 return status;
3091         }
3092
3093         msg = ads_first_entry(ads, res);
3094         if (!msg) {
3095                 SAFE_FREE(host);
3096                 return ADS_ERROR_SYSTEM(ENOENT);
3097         }
3098
3099         hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3100
3101         rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3102         if (rc) {
3103                 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3104         }else {
3105                 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3106         }
3107
3108         if (rc != LDAP_SUCCESS) {
3109                 const char *attrs[] = { "cn", NULL };
3110                 LDAPMessage *msg_sub;
3111
3112                 /* we only search with scope ONE, we do not expect any further
3113                  * objects to be created deeper */
3114
3115                 status = ads_do_search_retry(ads, hostnameDN,
3116                                              LDAP_SCOPE_ONELEVEL,
3117                                              "(objectclass=*)", attrs, &res);
3118
3119                 if (!ADS_ERR_OK(status)) {
3120                         SAFE_FREE(host);
3121                         ads_memfree(ads, hostnameDN);
3122                         return status;
3123                 }
3124
3125                 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3126                         msg_sub = ads_next_entry(ads, msg_sub)) {
3127
3128                         char *dn = NULL;
3129
3130                         if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3131                                 SAFE_FREE(host);
3132                                 ads_memfree(ads, hostnameDN);
3133                                 return ADS_ERROR(LDAP_NO_MEMORY);
3134                         }
3135
3136                         status = ads_del_dn(ads, dn);
3137                         if (!ADS_ERR_OK(status)) {
3138                                 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3139                                 SAFE_FREE(host);
3140                                 ads_memfree(ads, dn);
3141                                 ads_memfree(ads, hostnameDN);
3142                                 return status;
3143                         }
3144
3145                         ads_memfree(ads, dn);
3146                 }
3147
3148                 /* there should be no subordinate objects anymore */
3149                 status = ads_do_search_retry(ads, hostnameDN,
3150                                              LDAP_SCOPE_ONELEVEL,
3151                                              "(objectclass=*)", attrs, &res);
3152
3153                 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3154                         SAFE_FREE(host);
3155                         ads_memfree(ads, hostnameDN);
3156                         return status;
3157                 }
3158
3159                 /* delete hostnameDN now */
3160                 status = ads_del_dn(ads, hostnameDN);
3161                 if (!ADS_ERR_OK(status)) {
3162                         SAFE_FREE(host);
3163                         DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3164                         ads_memfree(ads, hostnameDN);
3165                         return status;
3166                 }
3167         }
3168
3169         ads_memfree(ads, hostnameDN);
3170
3171         status = ads_find_machine_acct(ads, &res, host);
3172         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3173                 DEBUG(3, ("Failed to remove host account.\n"));
3174                 SAFE_FREE(host);
3175                 return status;
3176         }
3177
3178         SAFE_FREE(host);
3179         return status;
3180 }
3181
3182 /**
3183  * pull all token-sids from an LDAP dn
3184  * @param ads connection to ads server
3185  * @param mem_ctx TALLOC_CTX for allocating sid array
3186  * @param dn of LDAP object
3187  * @param user_sid pointer to DOM_SID (objectSid)
3188  * @param primary_group_sid pointer to DOM_SID (self composed)
3189  * @param sids pointer to sid array to allocate
3190  * @param num_sids counter of SIDs pulled
3191  * @return status of token query
3192  **/
3193  ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3194                               TALLOC_CTX *mem_ctx,
3195                               const char *dn,
3196                               DOM_SID *user_sid,
3197                               DOM_SID *primary_group_sid,
3198                               DOM_SID **sids,
3199                               size_t *num_sids)
3200 {
3201         ADS_STATUS status;
3202         LDAPMessage *res = NULL;
3203         int count = 0;
3204         size_t tmp_num_sids;
3205         DOM_SID *tmp_sids;
3206         DOM_SID tmp_user_sid;
3207         DOM_SID tmp_primary_group_sid;
3208         uint32 pgid;
3209         const char *attrs[] = {
3210                 "objectSid",
3211                 "tokenGroups",
3212                 "primaryGroupID",
3213                 NULL
3214         };
3215
3216         status = ads_search_retry_dn(ads, &res, dn, attrs);
3217         if (!ADS_ERR_OK(status)) {
3218                 return status;
3219         }
3220
3221         count = ads_count_replies(ads, res);
3222         if (count != 1) {
3223                 ads_msgfree(ads, res);
3224                 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3225         }
3226
3227         if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3228                 ads_msgfree(ads, res);
3229                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3230         }
3231
3232         if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3233                 ads_msgfree(ads, res);
3234                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3235         }
3236
3237         {
3238                 /* hack to compose the primary group sid without knowing the
3239                  * domsid */
3240
3241                 DOM_SID domsid;
3242                 uint32 dummy_rid;
3243
3244                 sid_copy(&domsid, &tmp_user_sid);
3245
3246                 if (!sid_split_rid(&domsid, &dummy_rid)) {
3247                         ads_msgfree(ads, res);
3248                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3249                 }
3250
3251                 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3252                         ads_msgfree(ads, res);
3253                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3254                 }
3255         }
3256
3257         tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3258
3259         if (tmp_num_sids == 0 || !tmp_sids) {
3260                 ads_msgfree(ads, res);
3261                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3262         }
3263
3264         if (num_sids) {
3265                 *num_sids = tmp_num_sids;
3266         }
3267
3268         if (sids) {
3269                 *sids = tmp_sids;
3270         }
3271
3272         if (user_sid) {
3273                 *user_sid = tmp_user_sid;
3274         }
3275
3276         if (primary_group_sid) {
3277                 *primary_group_sid = tmp_primary_group_sid;
3278         }
3279
3280         DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3281
3282         ads_msgfree(ads, res);
3283         return ADS_ERROR_LDAP(LDAP_SUCCESS);
3284 }
3285
3286 /**
3287  * Find a sAMAccoutName in LDAP
3288  * @param ads connection to ads server
3289  * @param mem_ctx TALLOC_CTX for allocating sid array
3290  * @param samaccountname to search
3291  * @param uac_ret uint32 pointer userAccountControl attribute value
3292  * @param dn_ret pointer to dn
3293  * @return status of token query
3294  **/
3295 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3296                                TALLOC_CTX *mem_ctx,
3297                                const char *samaccountname,
3298                                uint32 *uac_ret,
3299                                const char **dn_ret)
3300 {
3301         ADS_STATUS status;
3302         const char *attrs[] = { "userAccountControl", NULL };
3303         const char *filter;
3304         LDAPMessage *res = NULL;
3305         char *dn = NULL;
3306         uint32 uac = 0;
3307
3308         filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3309                 samaccountname);
3310         if (filter == NULL) {
3311                 goto out;
3312         }
3313
3314         status = ads_do_search_all(ads, ads->config.bind_path,
3315                                    LDAP_SCOPE_SUBTREE,
3316                                    filter, attrs, &res);
3317         
3318         if (!ADS_ERR_OK(status)) {
3319                 goto out;
3320         }
3321
3322         if (ads_count_replies(ads, res) != 1) {
3323                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3324                 goto out;
3325         }
3326
3327         dn = ads_get_dn(ads, res);
3328         if (dn == NULL) {
3329                 status = ADS_ERROR(LDAP_NO_MEMORY);
3330                 goto out;
3331         }
3332
3333         if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3334                 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3335                 goto out;
3336         }
3337
3338         if (uac_ret) {
3339                 *uac_ret = uac;
3340         }
3341
3342         if (dn_ret) {
3343                 *dn_ret = talloc_strdup(mem_ctx, dn);
3344                 if (!*dn_ret) {
3345                         status = ADS_ERROR(LDAP_NO_MEMORY);
3346                         goto out;
3347                 }
3348         }
3349  out:
3350         ads_memfree(ads, dn);
3351         ads_msgfree(ads, res);
3352
3353         return status;
3354 }
3355
3356 /**
3357  * find our configuration path 
3358  * @param ads connection to ads server
3359  * @param mem_ctx Pointer to talloc context
3360  * @param config_path Pointer to the config path
3361  * @return status of search
3362  **/
3363 ADS_STATUS ads_config_path(ADS_STRUCT *ads, 
3364                            TALLOC_CTX *mem_ctx, 
3365                            char **config_path)
3366 {
3367         ADS_STATUS status;
3368         LDAPMessage *res = NULL;
3369         const char *config_context = NULL;
3370         const char *attrs[] = { "configurationNamingContext", NULL };
3371
3372         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, 
3373                                "(objectclass=*)", attrs, &res);
3374         if (!ADS_ERR_OK(status)) {
3375                 return status;
3376         }
3377
3378         config_context = ads_pull_string(ads, mem_ctx, res, 
3379                                          "configurationNamingContext");
3380         ads_msgfree(ads, res);
3381         if (!config_context) {
3382                 return ADS_ERROR(LDAP_NO_MEMORY);
3383         }
3384
3385         if (config_path) {
3386                 *config_path = talloc_strdup(mem_ctx, config_context);
3387                 if (!*config_path) {
3388                         return ADS_ERROR(LDAP_NO_MEMORY);
3389                 }
3390         }
3391
3392         return ADS_ERROR(LDAP_SUCCESS);
3393 }
3394
3395 /**
3396  * find the displayName of an extended right 
3397  * @param ads connection to ads server
3398  * @param config_path The config path
3399  * @param mem_ctx Pointer to talloc context
3400  * @param GUID struct of the rightsGUID
3401  * @return status of search
3402  **/
3403 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads, 
3404                                                 const char *config_path, 
3405                                                 TALLOC_CTX *mem_ctx, 
3406                                                 const struct GUID *rights_guid)
3407 {
3408         ADS_STATUS rc;
3409         LDAPMessage *res = NULL;
3410         char *expr = NULL;
3411         const char *attrs[] = { "displayName", NULL };
3412         const char *result = NULL;
3413         const char *path;
3414
3415         if (!ads || !mem_ctx || !rights_guid) {
3416                 goto done;
3417         }
3418
3419         expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)", 
3420                                smb_uuid_string_static(*rights_guid));
3421         if (!expr) {
3422                 goto done;
3423         }
3424
3425         path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3426         if (!path) {
3427                 goto done;
3428         }
3429
3430         rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE, 
3431                                  expr, attrs, &res);
3432         if (!ADS_ERR_OK(rc)) {
3433                 goto done;
3434         }
3435
3436         if (ads_count_replies(ads, res) != 1) {
3437                 goto done;
3438         }
3439
3440         result = ads_pull_string(ads, mem_ctx, res, "displayName");
3441
3442  done:
3443         ads_msgfree(ads, res);
3444         return result;
3445         
3446 }
3447
3448 #endif