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