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