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