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