s3:libads: change ads_add_service_principal_name implementation
[metze/samba/wip.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 spns An array or strings for the service principals to add,
2000  *        i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2001  * @return 0 upon sucess, or non-zero if a failure occurs
2002  **/
2003
2004 ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
2005                                            const char *machine_name,
2006                                            const char **spns)
2007 {
2008         ADS_STATUS ret;
2009         TALLOC_CTX *ctx;
2010         LDAPMessage *res = NULL;
2011         ADS_MODLIST mods;
2012         char *dn_string = NULL;
2013         const char **servicePrincipalName = spns;
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 Principals have NOT been added.\n"));
2020                 ads_msgfree(ads, res);
2021                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2022         }
2023
2024         DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2025         if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2026                 ads_msgfree(ads, res);
2027                 return ADS_ERROR(LDAP_NO_MEMORY);
2028         }
2029
2030         DEBUG(5,("ads_add_service_principal_name: INFO: "
2031                 "Adding %s to host %s\n",
2032                 spns[0] ? "N/A" : spns[0], machine_name));
2033
2034
2035         DEBUG(5,("ads_add_service_principal_name: INFO: "
2036                 "Adding %s to host %s\n",
2037                 spns[1] ? "N/A" : spns[1], machine_name));
2038
2039         if ( (mods = ads_init_mods(ctx)) == NULL ) {
2040                 ret = ADS_ERROR(LDAP_NO_MEMORY);
2041                 goto out;
2042         }
2043
2044         ret = ads_add_strlist(ctx,
2045                               &mods,
2046                               "servicePrincipalName",
2047                               servicePrincipalName);
2048         if (!ADS_ERR_OK(ret)) {
2049                 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2050                 goto out;
2051         }
2052
2053         if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2054                 ret = ADS_ERROR(LDAP_NO_MEMORY);
2055                 goto out;
2056         }
2057
2058         ret = ads_gen_mod(ads, dn_string, mods);
2059         if (!ADS_ERR_OK(ret)) {
2060                 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2061                 goto out;
2062         }
2063
2064  out:
2065         TALLOC_FREE( ctx );
2066         ads_msgfree(ads, res);
2067         return ret;
2068 }
2069
2070 /**
2071  * adds a machine account to the ADS server
2072  * @param ads An intialized ADS_STRUCT
2073  * @param machine_name - the NetBIOS machine name of this account.
2074  * @param account_type A number indicating the type of account to create
2075  * @param org_unit The LDAP path in which to place this account
2076  * @return 0 upon success, or non-zero otherwise
2077 **/
2078
2079 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2080                                    const char *machine_name,
2081                                    const char *org_unit,
2082                                    uint32_t etype_list)
2083 {
2084         ADS_STATUS ret;
2085         char *samAccountName, *controlstr;
2086         TALLOC_CTX *ctx;
2087         ADS_MODLIST mods;
2088         char *machine_escaped = NULL;
2089         char *new_dn;
2090         const char *objectClass[] = {"top", "person", "organizationalPerson",
2091                                      "user", "computer", NULL};
2092         LDAPMessage *res = NULL;
2093         uint32_t acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
2094                                 UF_DONT_EXPIRE_PASSWD |\
2095                                 UF_ACCOUNTDISABLE );
2096         uint32_t func_level = 0;
2097
2098         ret = ads_domain_func_level(ads, &func_level);
2099         if (!ADS_ERR_OK(ret)) {
2100                 return ret;
2101         }
2102
2103         if (!(ctx = talloc_init("ads_add_machine_acct")))
2104                 return ADS_ERROR(LDAP_NO_MEMORY);
2105
2106         ret = ADS_ERROR(LDAP_NO_MEMORY);
2107
2108         machine_escaped = escape_rdn_val_string_alloc(machine_name);
2109         if (!machine_escaped) {
2110                 goto done;
2111         }
2112
2113         new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2114         samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2115
2116         if ( !new_dn || !samAccountName ) {
2117                 goto done;
2118         }
2119
2120         if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2121                 goto done;
2122         }
2123
2124         if (!(mods = ads_init_mods(ctx))) {
2125                 goto done;
2126         }
2127
2128         ads_mod_str(ctx, &mods, "cn", machine_name);
2129         ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2130         ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2131         ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2132
2133         if (func_level >= DS_DOMAIN_FUNCTION_2008) {
2134                 const char *etype_list_str;
2135
2136                 etype_list_str = talloc_asprintf(ctx, "%d", (int)etype_list);
2137                 if (etype_list_str == NULL) {
2138                         goto done;
2139                 }
2140                 ads_mod_str(ctx, &mods, "msDS-SupportedEncryptionTypes",
2141                             etype_list_str);
2142         }
2143
2144         ret = ads_gen_add(ads, new_dn, mods);
2145
2146 done:
2147         SAFE_FREE(machine_escaped);
2148         ads_msgfree(ads, res);
2149         talloc_destroy(ctx);
2150
2151         return ret;
2152 }
2153
2154 /**
2155  * move a machine account to another OU on the ADS server
2156  * @param ads - An intialized ADS_STRUCT
2157  * @param machine_name - the NetBIOS machine name of this account.
2158  * @param org_unit - The LDAP path in which to place this account
2159  * @param moved - whether we moved the machine account (optional)
2160  * @return 0 upon success, or non-zero otherwise
2161 **/
2162
2163 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name, 
2164                                  const char *org_unit, bool *moved)
2165 {
2166         ADS_STATUS rc;
2167         int ldap_status;
2168         LDAPMessage *res = NULL;
2169         char *filter = NULL;
2170         char *computer_dn = NULL;
2171         char *parent_dn;
2172         char *computer_rdn = NULL;
2173         bool need_move = False;
2174
2175         if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2176                 rc = ADS_ERROR(LDAP_NO_MEMORY);
2177                 goto done;
2178         }
2179
2180         /* Find pre-existing machine */
2181         rc = ads_search(ads, &res, filter, NULL);
2182         if (!ADS_ERR_OK(rc)) {
2183                 goto done;
2184         }
2185
2186         computer_dn = ads_get_dn(ads, talloc_tos(), res);
2187         if (!computer_dn) {
2188                 rc = ADS_ERROR(LDAP_NO_MEMORY);
2189                 goto done;
2190         }
2191
2192         parent_dn = ads_parent_dn(computer_dn);
2193         if (strequal(parent_dn, org_unit)) {
2194                 goto done;
2195         }
2196
2197         need_move = True;
2198
2199         if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2200                 rc = ADS_ERROR(LDAP_NO_MEMORY);
2201                 goto done;
2202         }
2203
2204         ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn, 
2205                                     org_unit, 1, NULL, NULL);
2206         rc = ADS_ERROR(ldap_status);
2207
2208 done:
2209         ads_msgfree(ads, res);
2210         SAFE_FREE(filter);
2211         TALLOC_FREE(computer_dn);
2212         SAFE_FREE(computer_rdn);
2213
2214         if (!ADS_ERR_OK(rc)) {
2215                 need_move = False;
2216         }
2217
2218         if (moved) {
2219                 *moved = need_move;
2220         }
2221
2222         return rc;
2223 }
2224
2225 /*
2226   dump a binary result from ldap
2227 */
2228 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2229 {
2230         int i, j;
2231         for (i=0; values[i]; i++) {
2232                 printf("%s: ", field);
2233                 for (j=0; j<values[i]->bv_len; j++) {
2234                         printf("%02X", (unsigned char)values[i]->bv_val[j]);
2235                 }
2236                 printf("\n");
2237         }
2238 }
2239
2240 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2241 {
2242         int i;
2243         for (i=0; values[i]; i++) {
2244                 NTSTATUS status;
2245                 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2246                 struct GUID guid;
2247
2248                 status = GUID_from_ndr_blob(&in, &guid);
2249                 if (NT_STATUS_IS_OK(status)) {
2250                         printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2251                 } else {
2252                         printf("%s: INVALID GUID\n", field);
2253                 }
2254         }
2255 }
2256
2257 /*
2258   dump a sid result from ldap
2259 */
2260 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2261 {
2262         int i;
2263         for (i=0; values[i]; i++) {
2264                 struct dom_sid sid;
2265                 fstring tmp;
2266                 if (!sid_parse((const uint8_t *)values[i]->bv_val,
2267                                values[i]->bv_len, &sid)) {
2268                         return;
2269                 }
2270                 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2271         }
2272 }
2273
2274 /*
2275   dump ntSecurityDescriptor
2276 */
2277 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2278 {
2279         TALLOC_CTX *frame = talloc_stackframe();
2280         struct security_descriptor *psd;
2281         NTSTATUS status;
2282
2283         status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2284                                      values[0]->bv_len, &psd);
2285         if (!NT_STATUS_IS_OK(status)) {
2286                 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2287                           nt_errstr(status)));
2288                 TALLOC_FREE(frame);
2289                 return;
2290         }
2291
2292         if (psd) {
2293                 ads_disp_sd(ads, talloc_tos(), psd);
2294         }
2295
2296         TALLOC_FREE(frame);
2297 }
2298
2299 /*
2300   dump a string result from ldap
2301 */
2302 static void dump_string(const char *field, char **values)
2303 {
2304         int i;
2305         for (i=0; values[i]; i++) {
2306                 printf("%s: %s\n", field, values[i]);
2307         }
2308 }
2309
2310 /*
2311   dump a field from LDAP on stdout
2312   used for debugging
2313 */
2314
2315 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2316 {
2317         const struct {
2318                 const char *name;
2319                 bool string;
2320                 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2321         } handlers[] = {
2322                 {"objectGUID", False, dump_guid},
2323                 {"netbootGUID", False, dump_guid},
2324                 {"nTSecurityDescriptor", False, dump_sd},
2325                 {"dnsRecord", False, dump_binary},
2326                 {"objectSid", False, dump_sid},
2327                 {"tokenGroups", False, dump_sid},
2328                 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2329                 {"tokengroupsGlobalandUniversal", False, dump_sid},
2330                 {"mS-DS-CreatorSID", False, dump_sid},
2331                 {"msExchMailboxGuid", False, dump_guid},
2332                 {NULL, True, NULL}
2333         };
2334         int i;
2335
2336         if (!field) { /* must be end of an entry */
2337                 printf("\n");
2338                 return False;
2339         }
2340
2341         for (i=0; handlers[i].name; i++) {
2342                 if (strcasecmp_m(handlers[i].name, field) == 0) {
2343                         if (!values) /* first time, indicate string or not */
2344                                 return handlers[i].string;
2345                         handlers[i].handler(ads, field, (struct berval **) values);
2346                         break;
2347                 }
2348         }
2349         if (!handlers[i].name) {
2350                 if (!values) /* first time, indicate string conversion */
2351                         return True;
2352                 dump_string(field, (char **)values);
2353         }
2354         return False;
2355 }
2356
2357 /**
2358  * Dump a result from LDAP on stdout
2359  *  used for debugging
2360  * @param ads connection to ads server
2361  * @param res Results to dump
2362  **/
2363
2364  void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2365 {
2366         ads_process_results(ads, res, ads_dump_field, NULL);
2367 }
2368
2369 /**
2370  * Walk through results, calling a function for each entry found.
2371  *  The function receives a field name, a berval * array of values,
2372  *  and a data area passed through from the start.  The function is
2373  *  called once with null for field and values at the end of each
2374  *  entry.
2375  * @param ads connection to ads server
2376  * @param res Results to process
2377  * @param fn Function for processing each result
2378  * @param data_area user-defined area to pass to function
2379  **/
2380  void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2381                           bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2382                           void *data_area)
2383 {
2384         LDAPMessage *msg;
2385         TALLOC_CTX *ctx;
2386         size_t converted_size;
2387
2388         if (!(ctx = talloc_init("ads_process_results")))
2389                 return;
2390
2391         for (msg = ads_first_entry(ads, res); msg; 
2392              msg = ads_next_entry(ads, msg)) {
2393                 char *utf8_field;
2394                 BerElement *b;
2395
2396                 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2397                                                      (LDAPMessage *)msg,&b); 
2398                      utf8_field;
2399                      utf8_field=ldap_next_attribute(ads->ldap.ld,
2400                                                     (LDAPMessage *)msg,b)) {
2401                         struct berval **ber_vals;
2402                         char **str_vals;
2403                         char **utf8_vals;
2404                         char *field;
2405                         bool string; 
2406
2407                         if (!pull_utf8_talloc(ctx, &field, utf8_field,
2408                                               &converted_size))
2409                         {
2410                                 DEBUG(0,("ads_process_results: "
2411                                          "pull_utf8_talloc failed: %s",
2412                                          strerror(errno)));
2413                         }
2414
2415                         string = fn(ads, field, NULL, data_area);
2416
2417                         if (string) {
2418                                 const char **p;
2419
2420                                 utf8_vals = ldap_get_values(ads->ldap.ld,
2421                                                  (LDAPMessage *)msg, field);
2422                                 p = discard_const_p(const char *, utf8_vals);
2423                                 str_vals = ads_pull_strvals(ctx, p);
2424                                 fn(ads, field, (void **) str_vals, data_area);
2425                                 ldap_value_free(utf8_vals);
2426                         } else {
2427                                 ber_vals = ldap_get_values_len(ads->ldap.ld, 
2428                                                  (LDAPMessage *)msg, field);
2429                                 fn(ads, field, (void **) ber_vals, data_area);
2430
2431                                 ldap_value_free_len(ber_vals);
2432                         }
2433                         ldap_memfree(utf8_field);
2434                 }
2435                 ber_free(b, 0);
2436                 talloc_free_children(ctx);
2437                 fn(ads, NULL, NULL, data_area); /* completed an entry */
2438
2439         }
2440         talloc_destroy(ctx);
2441 }
2442
2443 /**
2444  * count how many replies are in a LDAPMessage
2445  * @param ads connection to ads server
2446  * @param res Results to count
2447  * @return number of replies
2448  **/
2449 int ads_count_replies(ADS_STRUCT *ads, void *res)
2450 {
2451         return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2452 }
2453
2454 /**
2455  * pull the first entry from a ADS result
2456  * @param ads connection to ads server
2457  * @param res Results of search
2458  * @return first entry from result
2459  **/
2460  LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2461 {
2462         return ldap_first_entry(ads->ldap.ld, res);
2463 }
2464
2465 /**
2466  * pull the next entry from a ADS result
2467  * @param ads connection to ads server
2468  * @param res Results of search
2469  * @return next entry from result
2470  **/
2471  LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2472 {
2473         return ldap_next_entry(ads->ldap.ld, res);
2474 }
2475
2476 /**
2477  * pull the first message from a ADS result
2478  * @param ads connection to ads server
2479  * @param res Results of search
2480  * @return first message from result
2481  **/
2482  LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2483 {
2484         return ldap_first_message(ads->ldap.ld, res);
2485 }
2486
2487 /**
2488  * pull the next message from a ADS result
2489  * @param ads connection to ads server
2490  * @param res Results of search
2491  * @return next message from result
2492  **/
2493  LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2494 {
2495         return ldap_next_message(ads->ldap.ld, res);
2496 }
2497
2498 /**
2499  * pull a single string from a ADS result
2500  * @param ads connection to ads server
2501  * @param mem_ctx TALLOC_CTX to use for allocating result string
2502  * @param msg Results of search
2503  * @param field Attribute to retrieve
2504  * @return Result string in talloc context
2505  **/
2506  char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2507                        const char *field)
2508 {
2509         char **values;
2510         char *ret = NULL;
2511         char *ux_string;
2512         size_t converted_size;
2513
2514         values = ldap_get_values(ads->ldap.ld, msg, field);
2515         if (!values)
2516                 return NULL;
2517
2518         if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2519                                           &converted_size))
2520         {
2521                 ret = ux_string;
2522         }
2523         ldap_value_free(values);
2524         return ret;
2525 }
2526
2527 /**
2528  * pull an array of strings from a ADS result
2529  * @param ads connection to ads server
2530  * @param mem_ctx TALLOC_CTX to use for allocating result string
2531  * @param msg Results of search
2532  * @param field Attribute to retrieve
2533  * @return Result strings in talloc context
2534  **/
2535  char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2536                          LDAPMessage *msg, const char *field,
2537                          size_t *num_values)
2538 {
2539         char **values;
2540         char **ret = NULL;
2541         int i;
2542         size_t converted_size;
2543
2544         values = ldap_get_values(ads->ldap.ld, msg, field);
2545         if (!values)
2546                 return NULL;
2547
2548         *num_values = ldap_count_values(values);
2549
2550         ret = talloc_array(mem_ctx, char *, *num_values + 1);
2551         if (!ret) {
2552                 ldap_value_free(values);
2553                 return NULL;
2554         }
2555
2556         for (i=0;i<*num_values;i++) {
2557                 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2558                                       &converted_size))
2559                 {
2560                         ldap_value_free(values);
2561                         return NULL;
2562                 }
2563         }
2564         ret[i] = NULL;
2565
2566         ldap_value_free(values);
2567         return ret;
2568 }
2569
2570 /**
2571  * pull an array of strings from a ADS result 
2572  *  (handle large multivalue attributes with range retrieval)
2573  * @param ads connection to ads server
2574  * @param mem_ctx TALLOC_CTX to use for allocating result string
2575  * @param msg Results of search
2576  * @param field Attribute to retrieve
2577  * @param current_strings strings returned by a previous call to this function
2578  * @param next_attribute The next query should ask for this attribute
2579  * @param num_values How many values did we get this time?
2580  * @param more_values Are there more values to get?
2581  * @return Result strings in talloc context
2582  **/
2583  char **ads_pull_strings_range(ADS_STRUCT *ads, 
2584                                TALLOC_CTX *mem_ctx,
2585                                LDAPMessage *msg, const char *field,
2586                                char **current_strings,
2587                                const char **next_attribute,
2588                                size_t *num_strings,
2589                                bool *more_strings)
2590 {
2591         char *attr;
2592         char *expected_range_attrib, *range_attr;
2593         BerElement *ptr = NULL;
2594         char **strings;
2595         char **new_strings;
2596         size_t num_new_strings;
2597         unsigned long int range_start;
2598         unsigned long int range_end;
2599
2600         /* we might have been given the whole lot anyway */
2601         if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2602                 *more_strings = False;
2603                 return strings;
2604         }
2605
2606         expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2607
2608         /* look for Range result */
2609         for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr); 
2610              attr; 
2611              attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2612                 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2613                 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2614                         range_attr = attr;
2615                         break;
2616                 }
2617                 ldap_memfree(attr);
2618         }
2619         if (!attr) {
2620                 ber_free(ptr, 0);
2621                 /* nothing here - this field is just empty */
2622                 *more_strings = False;
2623                 return NULL;
2624         }
2625
2626         if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu", 
2627                    &range_start, &range_end) == 2) {
2628                 *more_strings = True;
2629         } else {
2630                 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*", 
2631                            &range_start) == 1) {
2632                         *more_strings = False;
2633                 } else {
2634                         DEBUG(1, ("ads_pull_strings_range:  Cannot parse Range attriubte (%s)\n", 
2635                                   range_attr));
2636                         ldap_memfree(range_attr);
2637                         *more_strings = False;
2638                         return NULL;
2639                 }
2640         }
2641
2642         if ((*num_strings) != range_start) {
2643                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2644                           " - aborting range retreival\n",
2645                           range_attr, (unsigned int)(*num_strings) + 1, range_start));
2646                 ldap_memfree(range_attr);
2647                 *more_strings = False;
2648                 return NULL;
2649         }
2650
2651         new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2652
2653         if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2654                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2655                           "strings in this bunch, but we only got %lu - aborting range retreival\n",
2656                           range_attr, (unsigned long int)range_end - range_start + 1, 
2657                           (unsigned long int)num_new_strings));
2658                 ldap_memfree(range_attr);
2659                 *more_strings = False;
2660                 return NULL;
2661         }
2662
2663         strings = talloc_realloc(mem_ctx, current_strings, char *,
2664                                  *num_strings + num_new_strings);
2665
2666         if (strings == NULL) {
2667                 ldap_memfree(range_attr);
2668                 *more_strings = False;
2669                 return NULL;
2670         }
2671
2672         if (new_strings && num_new_strings) {
2673                 memcpy(&strings[*num_strings], new_strings,
2674                        sizeof(*new_strings) * num_new_strings);
2675         }
2676
2677         (*num_strings) += num_new_strings;
2678
2679         if (*more_strings) {
2680                 *next_attribute = talloc_asprintf(mem_ctx,
2681                                                   "%s;range=%d-*", 
2682                                                   field,
2683                                                   (int)*num_strings);
2684
2685                 if (!*next_attribute) {
2686                         DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2687                         ldap_memfree(range_attr);
2688                         *more_strings = False;
2689                         return NULL;
2690                 }
2691         }
2692
2693         ldap_memfree(range_attr);
2694
2695         return strings;
2696 }
2697
2698 /**
2699  * pull a single uint32_t from a ADS result
2700  * @param ads connection to ads server
2701  * @param msg Results of search
2702  * @param field Attribute to retrieve
2703  * @param v Pointer to int to store result
2704  * @return boolean inidicating success
2705 */
2706  bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2707                       uint32_t *v)
2708 {
2709         char **values;
2710
2711         values = ldap_get_values(ads->ldap.ld, msg, field);
2712         if (!values)
2713                 return False;
2714         if (!values[0]) {
2715                 ldap_value_free(values);
2716                 return False;
2717         }
2718
2719         *v = atoi(values[0]);
2720         ldap_value_free(values);
2721         return True;
2722 }
2723
2724 /**
2725  * pull a single objectGUID from an ADS result
2726  * @param ads connection to ADS server
2727  * @param msg results of search
2728  * @param guid 37-byte area to receive text guid
2729  * @return boolean indicating success
2730  **/
2731  bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2732 {
2733         DATA_BLOB blob;
2734         NTSTATUS status;
2735
2736         if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2737                                         &blob)) {
2738                 return false;
2739         }
2740
2741         status = GUID_from_ndr_blob(&blob, guid);
2742         talloc_free(blob.data);
2743         return NT_STATUS_IS_OK(status);
2744 }
2745
2746
2747 /**
2748  * pull a single struct dom_sid from a ADS result
2749  * @param ads connection to ads server
2750  * @param msg Results of search
2751  * @param field Attribute to retrieve
2752  * @param sid Pointer to sid to store result
2753  * @return boolean inidicating success
2754 */
2755  bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2756                    struct dom_sid *sid)
2757 {
2758         return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2759 }
2760
2761 /**
2762  * pull an array of struct dom_sids from a ADS result
2763  * @param ads connection to ads server
2764  * @param mem_ctx TALLOC_CTX for allocating sid array
2765  * @param msg Results of search
2766  * @param field Attribute to retrieve
2767  * @param sids pointer to sid array to allocate
2768  * @return the count of SIDs pulled
2769  **/
2770  int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2771                    LDAPMessage *msg, const char *field, struct dom_sid **sids)
2772 {
2773         struct berval **values;
2774         bool ret;
2775         int count, i;
2776
2777         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2778
2779         if (!values)
2780                 return 0;
2781
2782         for (i=0; values[i]; i++)
2783                 /* nop */ ;
2784
2785         if (i) {
2786                 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2787                 if (!(*sids)) {
2788                         ldap_value_free_len(values);
2789                         return 0;
2790                 }
2791         } else {
2792                 (*sids) = NULL;
2793         }
2794
2795         count = 0;
2796         for (i=0; values[i]; i++) {
2797                 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2798                                 values[i]->bv_len, &(*sids)[count]);
2799                 if (ret) {
2800                         DEBUG(10, ("pulling SID: %s\n",
2801                                    sid_string_dbg(&(*sids)[count])));
2802                         count++;
2803                 }
2804         }
2805
2806         ldap_value_free_len(values);
2807         return count;
2808 }
2809
2810 /**
2811  * pull a struct security_descriptor from a ADS result
2812  * @param ads connection to ads server
2813  * @param mem_ctx TALLOC_CTX for allocating sid array
2814  * @param msg Results of search
2815  * @param field Attribute to retrieve
2816  * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2817  * @return boolean inidicating success
2818 */
2819  bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2820                   LDAPMessage *msg, const char *field,
2821                   struct security_descriptor **sd)
2822 {
2823         struct berval **values;
2824         bool ret = true;
2825
2826         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2827
2828         if (!values) return false;
2829
2830         if (values[0]) {
2831                 NTSTATUS status;
2832                 status = unmarshall_sec_desc(mem_ctx,
2833                                              (uint8_t *)values[0]->bv_val,
2834                                              values[0]->bv_len, sd);
2835                 if (!NT_STATUS_IS_OK(status)) {
2836                         DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2837                                   nt_errstr(status)));
2838                         ret = false;
2839                 }
2840         }
2841
2842         ldap_value_free_len(values);
2843         return ret;
2844 }
2845
2846 /* 
2847  * in order to support usernames longer than 21 characters we need to 
2848  * use both the sAMAccountName and the userPrincipalName attributes 
2849  * It seems that not all users have the userPrincipalName attribute set
2850  *
2851  * @param ads connection to ads server
2852  * @param mem_ctx TALLOC_CTX for allocating sid array
2853  * @param msg Results of search
2854  * @return the username
2855  */
2856  char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2857                          LDAPMessage *msg)
2858 {
2859 #if 0   /* JERRY */
2860         char *ret, *p;
2861
2862         /* lookup_name() only works on the sAMAccountName to 
2863            returning the username portion of userPrincipalName
2864            breaks winbindd_getpwnam() */
2865
2866         ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2867         if (ret && (p = strchr_m(ret, '@'))) {
2868                 *p = 0;
2869                 return ret;
2870         }
2871 #endif
2872         return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2873 }
2874
2875
2876 /**
2877  * find the update serial number - this is the core of the ldap cache
2878  * @param ads connection to ads server
2879  * @param ads connection to ADS server
2880  * @param usn Pointer to retrieved update serial number
2881  * @return status of search
2882  **/
2883 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
2884 {
2885         const char *attrs[] = {"highestCommittedUSN", NULL};
2886         ADS_STATUS status;
2887         LDAPMessage *res;
2888
2889         status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2890         if (!ADS_ERR_OK(status)) 
2891                 return status;
2892
2893         if (ads_count_replies(ads, res) != 1) {
2894                 ads_msgfree(ads, res);
2895                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2896         }
2897
2898         if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2899                 ads_msgfree(ads, res);
2900                 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2901         }
2902
2903         ads_msgfree(ads, res);
2904         return ADS_SUCCESS;
2905 }
2906
2907 /* parse a ADS timestring - typical string is
2908    '20020917091222.0Z0' which means 09:12.22 17th September
2909    2002, timezone 0 */
2910 static time_t ads_parse_time(const char *str)
2911 {
2912         struct tm tm;
2913
2914         ZERO_STRUCT(tm);
2915
2916         if (sscanf(str, "%4d%2d%2d%2d%2d%2d", 
2917                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
2918                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2919                 return 0;
2920         }
2921         tm.tm_year -= 1900;
2922         tm.tm_mon -= 1;
2923
2924         return timegm(&tm);
2925 }
2926
2927 /********************************************************************
2928 ********************************************************************/
2929
2930 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2931 {
2932         const char *attrs[] = {"currentTime", NULL};
2933         ADS_STATUS status;
2934         LDAPMessage *res;
2935         char *timestr;
2936         TALLOC_CTX *ctx;
2937         ADS_STRUCT *ads_s = ads;
2938
2939         if (!(ctx = talloc_init("ads_current_time"))) {
2940                 return ADS_ERROR(LDAP_NO_MEMORY);
2941         }
2942
2943         /* establish a new ldap tcp session if necessary */
2944
2945         if ( !ads->ldap.ld ) {
2946                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2947                         ads->server.ldap_server )) == NULL )
2948                 {
2949                         status = ADS_ERROR(LDAP_NO_MEMORY);
2950                         goto done;
2951                 }
2952                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2953                 status = ads_connect( ads_s );
2954                 if ( !ADS_ERR_OK(status))
2955                         goto done;
2956         }
2957
2958         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2959         if (!ADS_ERR_OK(status)) {
2960                 goto done;
2961         }
2962
2963         timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2964         if (!timestr) {
2965                 ads_msgfree(ads_s, res);
2966                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2967                 goto done;
2968         }
2969
2970         /* but save the time and offset in the original ADS_STRUCT */   
2971
2972         ads->config.current_time = ads_parse_time(timestr);
2973
2974         if (ads->config.current_time != 0) {
2975                 ads->auth.time_offset = ads->config.current_time - time(NULL);
2976                 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
2977         }
2978
2979         ads_msgfree(ads, res);
2980
2981         status = ADS_SUCCESS;
2982
2983 done:
2984         /* free any temporary ads connections */
2985         if ( ads_s != ads ) {
2986                 ads_destroy( &ads_s );
2987         }
2988         talloc_destroy(ctx);
2989
2990         return status;
2991 }
2992
2993 /********************************************************************
2994 ********************************************************************/
2995
2996 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
2997 {
2998         const char *attrs[] = {"domainFunctionality", NULL};
2999         ADS_STATUS status;
3000         LDAPMessage *res;
3001         ADS_STRUCT *ads_s = ads;
3002
3003         *val = DS_DOMAIN_FUNCTION_2000;
3004
3005         /* establish a new ldap tcp session if necessary */
3006
3007         if ( !ads->ldap.ld ) {
3008                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
3009                         ads->server.ldap_server )) == NULL )
3010                 {
3011                         status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3012                         goto done;
3013                 }
3014                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3015                 status = ads_connect( ads_s );
3016                 if ( !ADS_ERR_OK(status))
3017                         goto done;
3018         }
3019
3020         /* If the attribute does not exist assume it is a Windows 2000 
3021            functional domain */
3022
3023         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3024         if (!ADS_ERR_OK(status)) {
3025                 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3026                         status = ADS_SUCCESS;
3027                 }
3028                 goto done;
3029         }
3030
3031         if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3032                 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3033         }
3034         DEBUG(3,("ads_domain_func_level: %d\n", *val));
3035
3036
3037         ads_msgfree(ads, res);
3038
3039 done:
3040         /* free any temporary ads connections */
3041         if ( ads_s != ads ) {
3042                 ads_destroy( &ads_s );
3043         }
3044
3045         return status;
3046 }
3047
3048 /**
3049  * find the domain sid for our domain
3050  * @param ads connection to ads server
3051  * @param sid Pointer to domain sid
3052  * @return status of search
3053  **/
3054 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3055 {
3056         const char *attrs[] = {"objectSid", NULL};
3057         LDAPMessage *res;
3058         ADS_STATUS rc;
3059
3060         rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
3061                            attrs, &res);
3062         if (!ADS_ERR_OK(rc)) return rc;
3063         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3064                 ads_msgfree(ads, res);
3065                 return ADS_ERROR_SYSTEM(ENOENT);
3066         }
3067         ads_msgfree(ads, res);
3068
3069         return ADS_SUCCESS;
3070 }
3071
3072 /**
3073  * find our site name 
3074  * @param ads connection to ads server
3075  * @param mem_ctx Pointer to talloc context
3076  * @param site_name Pointer to the sitename
3077  * @return status of search
3078  **/
3079 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3080 {
3081         ADS_STATUS status;
3082         LDAPMessage *res;
3083         const char *dn, *service_name;
3084         const char *attrs[] = { "dsServiceName", NULL };
3085
3086         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3087         if (!ADS_ERR_OK(status)) {
3088                 return status;
3089         }
3090
3091         service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3092         if (service_name == NULL) {
3093                 ads_msgfree(ads, res);
3094                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3095         }
3096
3097         ads_msgfree(ads, res);
3098
3099         /* go up three levels */
3100         dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3101         if (dn == NULL) {
3102                 return ADS_ERROR(LDAP_NO_MEMORY);
3103         }
3104
3105         *site_name = talloc_strdup(mem_ctx, dn);
3106         if (*site_name == NULL) {
3107                 return ADS_ERROR(LDAP_NO_MEMORY);
3108         }
3109
3110         return status;
3111         /*
3112         dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3113         */                                               
3114 }
3115
3116 /**
3117  * find the site dn where a machine resides
3118  * @param ads connection to ads server
3119  * @param mem_ctx Pointer to talloc context
3120  * @param computer_name name of the machine
3121  * @param site_name Pointer to the sitename
3122  * @return status of search
3123  **/
3124 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3125 {
3126         ADS_STATUS status;
3127         LDAPMessage *res;
3128         const char *parent, *filter;
3129         char *config_context = NULL;
3130         char *dn;
3131
3132         /* shortcut a query */
3133         if (strequal(computer_name, ads->config.ldap_server_name)) {
3134                 return ads_site_dn(ads, mem_ctx, site_dn);
3135         }
3136
3137         status = ads_config_path(ads, mem_ctx, &config_context);
3138         if (!ADS_ERR_OK(status)) {
3139                 return status;
3140         }
3141
3142         filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3143         if (filter == NULL) {
3144                 return ADS_ERROR(LDAP_NO_MEMORY);
3145         }
3146
3147         status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, 
3148                                filter, NULL, &res);
3149         if (!ADS_ERR_OK(status)) {
3150                 return status;
3151         }
3152
3153         if (ads_count_replies(ads, res) != 1) {
3154                 ads_msgfree(ads, res);
3155                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3156         }
3157
3158         dn = ads_get_dn(ads, mem_ctx, res);
3159         if (dn == NULL) {
3160                 ads_msgfree(ads, res);
3161                 return ADS_ERROR(LDAP_NO_MEMORY);
3162         }
3163
3164         /* go up three levels */
3165         parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3166         if (parent == NULL) {
3167                 ads_msgfree(ads, res);
3168                 TALLOC_FREE(dn);
3169                 return ADS_ERROR(LDAP_NO_MEMORY);
3170         }
3171
3172         *site_dn = talloc_strdup(mem_ctx, parent);
3173         if (*site_dn == NULL) {
3174                 ads_msgfree(ads, res);
3175                 TALLOC_FREE(dn);
3176                 return ADS_ERROR(LDAP_NO_MEMORY);
3177         }
3178
3179         TALLOC_FREE(dn);
3180         ads_msgfree(ads, res);
3181
3182         return status;
3183 }
3184
3185 /**
3186  * get the upn suffixes for a domain
3187  * @param ads connection to ads server
3188  * @param mem_ctx Pointer to talloc context
3189  * @param suffixes Pointer to an array of suffixes
3190  * @param num_suffixes Pointer to the number of suffixes
3191  * @return status of search
3192  **/
3193 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3194 {
3195         ADS_STATUS status;
3196         LDAPMessage *res;
3197         const char *base;
3198         char *config_context = NULL;
3199         const char *attrs[] = { "uPNSuffixes", NULL };
3200
3201         status = ads_config_path(ads, mem_ctx, &config_context);
3202         if (!ADS_ERR_OK(status)) {
3203                 return status;
3204         }
3205
3206         base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3207         if (base == NULL) {
3208                 return ADS_ERROR(LDAP_NO_MEMORY);
3209         }
3210
3211         status = ads_search_dn(ads, &res, base, attrs);
3212         if (!ADS_ERR_OK(status)) {
3213                 return status;
3214         }
3215
3216         if (ads_count_replies(ads, res) != 1) {
3217                 ads_msgfree(ads, res);
3218                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3219         }
3220
3221         (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3222         if ((*suffixes) == NULL) {
3223                 ads_msgfree(ads, res);
3224                 return ADS_ERROR(LDAP_NO_MEMORY);
3225         }
3226
3227         ads_msgfree(ads, res);
3228
3229         return status;
3230 }
3231
3232 /**
3233  * get the joinable ous for a domain
3234  * @param ads connection to ads server
3235  * @param mem_ctx Pointer to talloc context
3236  * @param ous Pointer to an array of ous
3237  * @param num_ous Pointer to the number of ous
3238  * @return status of search
3239  **/
3240 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3241                                 TALLOC_CTX *mem_ctx,
3242                                 char ***ous,
3243                                 size_t *num_ous)
3244 {
3245         ADS_STATUS status;
3246         LDAPMessage *res = NULL;
3247         LDAPMessage *msg = NULL;
3248         const char *attrs[] = { "dn", NULL };
3249         int count = 0;
3250
3251         status = ads_search(ads, &res,
3252                             "(|(objectClass=domain)(objectclass=organizationalUnit))",
3253                             attrs);
3254         if (!ADS_ERR_OK(status)) {
3255                 return status;
3256         }
3257
3258         count = ads_count_replies(ads, res);
3259         if (count < 1) {
3260                 ads_msgfree(ads, res);
3261                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3262         }
3263
3264         for (msg = ads_first_entry(ads, res); msg;
3265              msg = ads_next_entry(ads, msg)) {
3266                 const char **p = discard_const_p(const char *, *ous);
3267                 char *dn = NULL;
3268
3269                 dn = ads_get_dn(ads, talloc_tos(), msg);
3270                 if (!dn) {
3271                         ads_msgfree(ads, res);
3272                         return ADS_ERROR(LDAP_NO_MEMORY);
3273                 }
3274
3275                 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3276                         TALLOC_FREE(dn);
3277                         ads_msgfree(ads, res);
3278                         return ADS_ERROR(LDAP_NO_MEMORY);
3279                 }
3280
3281                 TALLOC_FREE(dn);
3282                 *ous = discard_const_p(char *, p);
3283         }
3284
3285         ads_msgfree(ads, res);
3286
3287         return status;
3288 }
3289
3290
3291 /**
3292  * pull a struct dom_sid from an extended dn string
3293  * @param mem_ctx TALLOC_CTX
3294  * @param extended_dn string
3295  * @param flags string type of extended_dn
3296  * @param sid pointer to a struct dom_sid
3297  * @return NT_STATUS_OK on success,
3298  *         NT_INVALID_PARAMETER on error,
3299  *         NT_STATUS_NOT_FOUND if no SID present
3300  **/
3301 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3302                                         const char *extended_dn,
3303                                         enum ads_extended_dn_flags flags,
3304                                         struct dom_sid *sid)
3305 {
3306         char *p, *q, *dn;
3307
3308         if (!extended_dn) {
3309                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3310         }
3311
3312         /* otherwise extended_dn gets stripped off */
3313         if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3314                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3315         }
3316         /*
3317          * ADS_EXTENDED_DN_HEX_STRING:
3318          * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3319          *
3320          * ADS_EXTENDED_DN_STRING (only with w2k3):
3321          * <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
3322          *
3323          * Object with no SID, such as an Exchange Public Folder
3324          * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3325          */
3326
3327         p = strchr(dn, ';');
3328         if (!p) {
3329                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3330         }
3331
3332         if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3333                 DEBUG(5,("No SID present in extended dn\n"));
3334                 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3335         }
3336
3337         p += strlen(";<SID=");
3338
3339         q = strchr(p, '>');
3340         if (!q) {
3341                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3342         }
3343
3344         *q = '\0';
3345
3346         DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3347
3348         switch (flags) {
3349
3350         case ADS_EXTENDED_DN_STRING:
3351                 if (!string_to_sid(sid, p)) {
3352                         return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3353                 }
3354                 break;
3355         case ADS_EXTENDED_DN_HEX_STRING: {
3356                 fstring buf;
3357                 size_t buf_len;
3358
3359                 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3360                 if (buf_len == 0) {
3361                         return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3362                 }
3363
3364                 if (!sid_parse((const uint8_t *)buf, buf_len, sid)) {
3365                         DEBUG(10,("failed to parse sid\n"));
3366                         return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3367                 }
3368                 break;
3369                 }
3370         default:
3371                 DEBUG(10,("unknown extended dn format\n"));
3372                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3373         }
3374
3375         return ADS_ERROR_NT(NT_STATUS_OK);
3376 }
3377
3378 /********************************************************************
3379 ********************************************************************/
3380
3381 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3382 {
3383         LDAPMessage *res = NULL;
3384         ADS_STATUS status;
3385         int count = 0;
3386         char *name = NULL;
3387
3388         status = ads_find_machine_acct(ads, &res, machine_name);
3389         if (!ADS_ERR_OK(status)) {
3390                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3391                         lp_netbios_name()));
3392                 goto out;
3393         }
3394
3395         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3396                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3397                 goto out;
3398         }
3399
3400         if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3401                 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3402         }
3403
3404 out:
3405         ads_msgfree(ads, res);
3406
3407         return name;
3408 }
3409
3410 /********************************************************************
3411 ********************************************************************/
3412
3413 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3414 {
3415         LDAPMessage *res = NULL;
3416         ADS_STATUS status;
3417         int count = 0;
3418         char *name = NULL;
3419
3420         status = ads_find_machine_acct(ads, &res, machine_name);
3421         if (!ADS_ERR_OK(status)) {
3422                 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3423                         lp_netbios_name()));
3424                 goto out;
3425         }
3426
3427         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3428                 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3429                 goto out;
3430         }
3431
3432         if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3433                 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3434         }
3435
3436 out:
3437         ads_msgfree(ads, res);
3438
3439         return name;
3440 }
3441
3442 /********************************************************************
3443 ********************************************************************/
3444
3445 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3446 {
3447         LDAPMessage *res = NULL;
3448         ADS_STATUS status;
3449         int count = 0;
3450         char *name = NULL;
3451         bool ok = false;
3452
3453         status = ads_find_machine_acct(ads, &res, machine_name);
3454         if (!ADS_ERR_OK(status)) {
3455                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3456                         lp_netbios_name()));
3457                 goto out;
3458         }
3459
3460         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3461                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3462                 goto out;
3463         }
3464
3465         if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3466                 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3467         }
3468
3469 out:
3470         ads_msgfree(ads, res);
3471         if (name != NULL) {
3472                 ok = (strlen(name) > 0);
3473         }
3474         return ok;
3475 }
3476
3477 #if 0
3478
3479    SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3480
3481 /**
3482  * Join a machine to a realm
3483  *  Creates the machine account and sets the machine password
3484  * @param ads connection to ads server
3485  * @param machine name of host to add
3486  * @param org_unit Organizational unit to place machine in
3487  * @return status of join
3488  **/
3489 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3490                         uint32_t account_type, const char *org_unit)
3491 {
3492         ADS_STATUS status;
3493         LDAPMessage *res = NULL;
3494         char *machine;
3495
3496         /* machine name must be lowercase */
3497         machine = SMB_STRDUP(machine_name);
3498         strlower_m(machine);
3499
3500         /*
3501         status = ads_find_machine_acct(ads, (void **)&res, machine);
3502         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3503                 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3504                 status = ads_leave_realm(ads, machine);
3505                 if (!ADS_ERR_OK(status)) {
3506                         DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3507                                 machine, ads->config.realm));
3508                         return status;
3509                 }
3510         }
3511         */
3512         status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3513         if (!ADS_ERR_OK(status)) {
3514                 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3515                 SAFE_FREE(machine);
3516                 return status;
3517         }
3518
3519         status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3520         if (!ADS_ERR_OK(status)) {
3521                 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3522                 SAFE_FREE(machine);
3523                 return status;
3524         }
3525
3526         SAFE_FREE(machine);
3527         ads_msgfree(ads, res);
3528
3529         return status;
3530 }
3531 #endif
3532
3533 /**
3534  * Delete a machine from the realm
3535  * @param ads connection to ads server
3536  * @param hostname Machine to remove
3537  * @return status of delete
3538  **/
3539 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3540 {
3541         ADS_STATUS status;
3542         void *msg;
3543         LDAPMessage *res;
3544         char *hostnameDN, *host;
3545         int rc;
3546         LDAPControl ldap_control;
3547         LDAPControl  * pldap_control[2] = {NULL, NULL};
3548
3549         pldap_control[0] = &ldap_control;
3550         memset(&ldap_control, 0, sizeof(LDAPControl));
3551         ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3552
3553         /* hostname must be lowercase */
3554         host = SMB_STRDUP(hostname);
3555         if (!strlower_m(host)) {
3556                 SAFE_FREE(host);
3557                 return ADS_ERROR_SYSTEM(EINVAL);
3558         }
3559
3560         status = ads_find_machine_acct(ads, &res, host);
3561         if (!ADS_ERR_OK(status)) {
3562                 DEBUG(0, ("Host account for %s does not exist.\n", host));
3563                 SAFE_FREE(host);
3564                 return status;
3565         }
3566
3567         msg = ads_first_entry(ads, res);
3568         if (!msg) {
3569                 SAFE_FREE(host);
3570                 return ADS_ERROR_SYSTEM(ENOENT);
3571         }
3572
3573         hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3574         if (hostnameDN == NULL) {
3575                 SAFE_FREE(host);
3576                 return ADS_ERROR_SYSTEM(ENOENT);
3577         }
3578
3579         rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3580         if (rc) {
3581                 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3582         }else {
3583                 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3584         }
3585
3586         if (rc != LDAP_SUCCESS) {
3587                 const char *attrs[] = { "cn", NULL };
3588                 LDAPMessage *msg_sub;
3589
3590                 /* we only search with scope ONE, we do not expect any further
3591                  * objects to be created deeper */
3592
3593                 status = ads_do_search_retry(ads, hostnameDN,
3594                                              LDAP_SCOPE_ONELEVEL,
3595                                              "(objectclass=*)", attrs, &res);
3596
3597                 if (!ADS_ERR_OK(status)) {
3598                         SAFE_FREE(host);
3599                         TALLOC_FREE(hostnameDN);
3600                         return status;
3601                 }
3602
3603                 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3604                         msg_sub = ads_next_entry(ads, msg_sub)) {
3605
3606                         char *dn = NULL;
3607
3608                         if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3609                                 SAFE_FREE(host);
3610                                 TALLOC_FREE(hostnameDN);
3611                                 return ADS_ERROR(LDAP_NO_MEMORY);
3612                         }
3613
3614                         status = ads_del_dn(ads, dn);
3615                         if (!ADS_ERR_OK(status)) {
3616                                 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3617                                 SAFE_FREE(host);
3618                                 TALLOC_FREE(dn);
3619                                 TALLOC_FREE(hostnameDN);
3620                                 return status;
3621                         }
3622
3623                         TALLOC_FREE(dn);
3624                 }
3625
3626                 /* there should be no subordinate objects anymore */
3627                 status = ads_do_search_retry(ads, hostnameDN,
3628                                              LDAP_SCOPE_ONELEVEL,
3629                                              "(objectclass=*)", attrs, &res);
3630
3631                 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3632                         SAFE_FREE(host);
3633                         TALLOC_FREE(hostnameDN);
3634                         return status;
3635                 }
3636
3637                 /* delete hostnameDN now */
3638                 status = ads_del_dn(ads, hostnameDN);
3639                 if (!ADS_ERR_OK(status)) {
3640                         SAFE_FREE(host);
3641                         DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3642                         TALLOC_FREE(hostnameDN);
3643                         return status;
3644                 }
3645         }
3646
3647         TALLOC_FREE(hostnameDN);
3648
3649         status = ads_find_machine_acct(ads, &res, host);
3650         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3651                 DEBUG(3, ("Failed to remove host account.\n"));
3652                 SAFE_FREE(host);
3653                 return status;
3654         }
3655
3656         SAFE_FREE(host);
3657         return status;
3658 }
3659
3660 /**
3661  * pull all token-sids from an LDAP dn
3662  * @param ads connection to ads server
3663  * @param mem_ctx TALLOC_CTX for allocating sid array
3664  * @param dn of LDAP object
3665  * @param user_sid pointer to struct dom_sid (objectSid)
3666  * @param primary_group_sid pointer to struct dom_sid (self composed)
3667  * @param sids pointer to sid array to allocate
3668  * @param num_sids counter of SIDs pulled
3669  * @return status of token query
3670  **/
3671  ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3672                               TALLOC_CTX *mem_ctx,
3673                               const char *dn,
3674                               struct dom_sid *user_sid,
3675                               struct dom_sid *primary_group_sid,
3676                               struct dom_sid **sids,
3677                               size_t *num_sids)
3678 {
3679         ADS_STATUS status;
3680         LDAPMessage *res = NULL;
3681         int count = 0;
3682         size_t tmp_num_sids;
3683         struct dom_sid *tmp_sids;
3684         struct dom_sid tmp_user_sid;
3685         struct dom_sid tmp_primary_group_sid;
3686         uint32_t pgid;
3687         const char *attrs[] = {
3688                 "objectSid",
3689                 "tokenGroups",
3690                 "primaryGroupID",
3691                 NULL
3692         };
3693
3694         status = ads_search_retry_dn(ads, &res, dn, attrs);
3695         if (!ADS_ERR_OK(status)) {
3696                 return status;
3697         }
3698
3699         count = ads_count_replies(ads, res);
3700         if (count != 1) {
3701                 ads_msgfree(ads, res);
3702                 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3703         }
3704
3705         if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3706                 ads_msgfree(ads, res);
3707                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3708         }
3709
3710         if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3711                 ads_msgfree(ads, res);
3712                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3713         }
3714
3715         {
3716                 /* hack to compose the primary group sid without knowing the
3717                  * domsid */
3718
3719                 struct dom_sid domsid;
3720
3721                 sid_copy(&domsid, &tmp_user_sid);
3722
3723                 if (!sid_split_rid(&domsid, NULL)) {
3724                         ads_msgfree(ads, res);
3725                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3726                 }
3727
3728                 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3729                         ads_msgfree(ads, res);
3730                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3731                 }
3732         }
3733
3734         tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3735
3736         if (tmp_num_sids == 0 || !tmp_sids) {
3737                 ads_msgfree(ads, res);
3738                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3739         }
3740
3741         if (num_sids) {
3742                 *num_sids = tmp_num_sids;
3743         }
3744
3745         if (sids) {
3746                 *sids = tmp_sids;
3747         }
3748
3749         if (user_sid) {
3750                 *user_sid = tmp_user_sid;
3751         }
3752
3753         if (primary_group_sid) {
3754                 *primary_group_sid = tmp_primary_group_sid;
3755         }
3756
3757         DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3758
3759         ads_msgfree(ads, res);
3760         return ADS_ERROR_LDAP(LDAP_SUCCESS);
3761 }
3762
3763 /**
3764  * Find a sAMAccoutName in LDAP
3765  * @param ads connection to ads server
3766  * @param mem_ctx TALLOC_CTX for allocating sid array
3767  * @param samaccountname to search
3768  * @param uac_ret uint32_t pointer userAccountControl attribute value
3769  * @param dn_ret pointer to dn
3770  * @return status of token query
3771  **/
3772 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3773                                TALLOC_CTX *mem_ctx,
3774                                const char *samaccountname,
3775                                uint32_t *uac_ret,
3776                                const char **dn_ret)
3777 {
3778         ADS_STATUS status;
3779         const char *attrs[] = { "userAccountControl", NULL };
3780         const char *filter;
3781         LDAPMessage *res = NULL;
3782         char *dn = NULL;
3783         uint32_t uac = 0;
3784
3785         filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3786                 samaccountname);
3787         if (filter == NULL) {
3788                 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3789                 goto out;
3790         }
3791
3792         status = ads_do_search_all(ads, ads->config.bind_path,
3793                                    LDAP_SCOPE_SUBTREE,
3794                                    filter, attrs, &res);
3795
3796         if (!ADS_ERR_OK(status)) {
3797                 goto out;
3798         }
3799
3800         if (ads_count_replies(ads, res) != 1) {
3801                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3802                 goto out;
3803         }
3804
3805         dn = ads_get_dn(ads, talloc_tos(), res);
3806         if (dn == NULL) {
3807                 status = ADS_ERROR(LDAP_NO_MEMORY);
3808                 goto out;
3809         }
3810
3811         if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3812                 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3813                 goto out;
3814         }
3815
3816         if (uac_ret) {
3817                 *uac_ret = uac;
3818         }
3819
3820         if (dn_ret) {
3821                 *dn_ret = talloc_strdup(mem_ctx, dn);
3822                 if (!*dn_ret) {
3823                         status = ADS_ERROR(LDAP_NO_MEMORY);
3824                         goto out;
3825                 }
3826         }
3827  out:
3828         TALLOC_FREE(dn);
3829         ads_msgfree(ads, res);
3830
3831         return status;
3832 }
3833
3834 /**
3835  * find our configuration path 
3836  * @param ads connection to ads server
3837  * @param mem_ctx Pointer to talloc context
3838  * @param config_path Pointer to the config path
3839  * @return status of search
3840  **/
3841 ADS_STATUS ads_config_path(ADS_STRUCT *ads, 
3842                            TALLOC_CTX *mem_ctx, 
3843                            char **config_path)
3844 {
3845         ADS_STATUS status;
3846         LDAPMessage *res = NULL;
3847         const char *config_context = NULL;
3848         const char *attrs[] = { "configurationNamingContext", NULL };
3849
3850         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, 
3851                                "(objectclass=*)", attrs, &res);
3852         if (!ADS_ERR_OK(status)) {
3853                 return status;
3854         }
3855
3856         config_context = ads_pull_string(ads, mem_ctx, res, 
3857                                          "configurationNamingContext");
3858         ads_msgfree(ads, res);
3859         if (!config_context) {
3860                 return ADS_ERROR(LDAP_NO_MEMORY);
3861         }
3862
3863         if (config_path) {
3864                 *config_path = talloc_strdup(mem_ctx, config_context);
3865                 if (!*config_path) {
3866                         return ADS_ERROR(LDAP_NO_MEMORY);
3867                 }
3868         }
3869
3870         return ADS_ERROR(LDAP_SUCCESS);
3871 }
3872
3873 /**
3874  * find the displayName of an extended right 
3875  * @param ads connection to ads server
3876  * @param config_path The config path
3877  * @param mem_ctx Pointer to talloc context
3878  * @param GUID struct of the rightsGUID
3879  * @return status of search
3880  **/
3881 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads, 
3882                                                 const char *config_path, 
3883                                                 TALLOC_CTX *mem_ctx, 
3884                                                 const struct GUID *rights_guid)
3885 {
3886         ADS_STATUS rc;
3887         LDAPMessage *res = NULL;
3888         char *expr = NULL;
3889         const char *attrs[] = { "displayName", NULL };
3890         const char *result = NULL;
3891         const char *path;
3892
3893         if (!ads || !mem_ctx || !rights_guid) {
3894                 goto done;
3895         }
3896
3897         expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)", 
3898                                GUID_string(mem_ctx, rights_guid));
3899         if (!expr) {
3900                 goto done;
3901         }
3902
3903         path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3904         if (!path) {
3905                 goto done;
3906         }
3907
3908         rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE, 
3909                                  expr, attrs, &res);
3910         if (!ADS_ERR_OK(rc)) {
3911                 goto done;
3912         }
3913
3914         if (ads_count_replies(ads, res) != 1) {
3915                 goto done;
3916         }
3917
3918         result = ads_pull_string(ads, mem_ctx, res, "displayName");
3919
3920  done:
3921         ads_msgfree(ads, res);
3922         return result;
3923 }
3924
3925 /**
3926  * verify or build and verify an account ou
3927  * @param mem_ctx Pointer to talloc context
3928  * @param ads connection to ads server
3929  * @param account_ou
3930  * @return status of search
3931  **/
3932
3933 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3934                            ADS_STRUCT *ads,
3935                            const char **account_ou)
3936 {
3937         char **exploded_dn;
3938         const char *name;
3939         char *ou_string;
3940
3941         if (account_ou == NULL) {
3942                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3943         }
3944
3945         if (*account_ou != NULL) {
3946                 exploded_dn = ldap_explode_dn(*account_ou, 0);
3947                 if (exploded_dn) {
3948                         ldap_value_free(exploded_dn);
3949                         return ADS_SUCCESS;
3950                 }
3951         }
3952
3953         ou_string = ads_ou_string(ads, *account_ou);
3954         if (!ou_string) {
3955                 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3956         }
3957
3958         name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3959                                ads->config.bind_path);
3960         SAFE_FREE(ou_string);
3961
3962         if (!name) {
3963                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3964         }
3965
3966         exploded_dn = ldap_explode_dn(name, 0);
3967         if (!exploded_dn) {
3968                 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3969         }
3970         ldap_value_free(exploded_dn);
3971
3972         *account_ou = name;
3973         return ADS_SUCCESS;
3974 }
3975
3976 #endif