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