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