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