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