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