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