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