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