lib: Remove sid_string_dbg
[samba.git] / source3 / libads / ldap.c
1 /* 
2    Unix SMB/CIFS implementation.
3    ads (active directory) utility library
4    Copyright (C) Andrew Tridgell 2001
5    Copyright (C) Remus Koos 2001
6    Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
7    Copyright (C) Guenther Deschner 2005
8    Copyright (C) Gerald Carter 2006
9
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 3 of the License, or
13    (at your option) any later version.
14
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19
20    You should have received a copy of the GNU General Public License
21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "includes.h"
25 #include "ads.h"
26 #include "libads/sitename_cache.h"
27 #include "libads/cldap.h"
28 #include "../lib/addns/dnsquery.h"
29 #include "../libds/common/flags.h"
30 #include "smbldap.h"
31 #include "../libcli/security/security.h"
32 #include "../librpc/gen_ndr/netlogon.h"
33 #include "lib/param/loadparm.h"
34 #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                         struct dom_sid_buf buf;
2803                         DBG_DEBUG("pulling SID: %s\n",
2804                                   dom_sid_str_buf(&(*sids)[count], &buf));
2805                         count++;
2806                 }
2807         }
2808
2809         ldap_value_free_len(values);
2810         return count;
2811 }
2812
2813 /**
2814  * pull a struct security_descriptor from a ADS result
2815  * @param ads connection to ads server
2816  * @param mem_ctx TALLOC_CTX for allocating sid array
2817  * @param msg Results of search
2818  * @param field Attribute to retrieve
2819  * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2820  * @return boolean inidicating success
2821 */
2822  bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2823                   LDAPMessage *msg, const char *field,
2824                   struct security_descriptor **sd)
2825 {
2826         struct berval **values;
2827         bool ret = true;
2828
2829         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2830
2831         if (!values) return false;
2832
2833         if (values[0]) {
2834                 NTSTATUS status;
2835                 status = unmarshall_sec_desc(mem_ctx,
2836                                              (uint8_t *)values[0]->bv_val,
2837                                              values[0]->bv_len, sd);
2838                 if (!NT_STATUS_IS_OK(status)) {
2839                         DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2840                                   nt_errstr(status)));
2841                         ret = false;
2842                 }
2843         }
2844
2845         ldap_value_free_len(values);
2846         return ret;
2847 }
2848
2849 /* 
2850  * in order to support usernames longer than 21 characters we need to 
2851  * use both the sAMAccountName and the userPrincipalName attributes 
2852  * It seems that not all users have the userPrincipalName attribute set
2853  *
2854  * @param ads connection to ads server
2855  * @param mem_ctx TALLOC_CTX for allocating sid array
2856  * @param msg Results of search
2857  * @return the username
2858  */
2859  char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2860                          LDAPMessage *msg)
2861 {
2862 #if 0   /* JERRY */
2863         char *ret, *p;
2864
2865         /* lookup_name() only works on the sAMAccountName to 
2866            returning the username portion of userPrincipalName
2867            breaks winbindd_getpwnam() */
2868
2869         ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2870         if (ret && (p = strchr_m(ret, '@'))) {
2871                 *p = 0;
2872                 return ret;
2873         }
2874 #endif
2875         return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2876 }
2877
2878
2879 /**
2880  * find the update serial number - this is the core of the ldap cache
2881  * @param ads connection to ads server
2882  * @param ads connection to ADS server
2883  * @param usn Pointer to retrieved update serial number
2884  * @return status of search
2885  **/
2886 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
2887 {
2888         const char *attrs[] = {"highestCommittedUSN", NULL};
2889         ADS_STATUS status;
2890         LDAPMessage *res;
2891
2892         status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2893         if (!ADS_ERR_OK(status)) 
2894                 return status;
2895
2896         if (ads_count_replies(ads, res) != 1) {
2897                 ads_msgfree(ads, res);
2898                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2899         }
2900
2901         if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2902                 ads_msgfree(ads, res);
2903                 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2904         }
2905
2906         ads_msgfree(ads, res);
2907         return ADS_SUCCESS;
2908 }
2909
2910 /* parse a ADS timestring - typical string is
2911    '20020917091222.0Z0' which means 09:12.22 17th September
2912    2002, timezone 0 */
2913 static time_t ads_parse_time(const char *str)
2914 {
2915         struct tm tm;
2916
2917         ZERO_STRUCT(tm);
2918
2919         if (sscanf(str, "%4d%2d%2d%2d%2d%2d", 
2920                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
2921                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2922                 return 0;
2923         }
2924         tm.tm_year -= 1900;
2925         tm.tm_mon -= 1;
2926
2927         return timegm(&tm);
2928 }
2929
2930 /********************************************************************
2931 ********************************************************************/
2932
2933 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2934 {
2935         const char *attrs[] = {"currentTime", NULL};
2936         ADS_STATUS status;
2937         LDAPMessage *res;
2938         char *timestr;
2939         TALLOC_CTX *ctx;
2940         ADS_STRUCT *ads_s = ads;
2941
2942         if (!(ctx = talloc_init("ads_current_time"))) {
2943                 return ADS_ERROR(LDAP_NO_MEMORY);
2944         }
2945
2946         /* establish a new ldap tcp session if necessary */
2947
2948         if ( !ads->ldap.ld ) {
2949                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2950                         ads->server.ldap_server )) == NULL )
2951                 {
2952                         status = ADS_ERROR(LDAP_NO_MEMORY);
2953                         goto done;
2954                 }
2955                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2956                 status = ads_connect( ads_s );
2957                 if ( !ADS_ERR_OK(status))
2958                         goto done;
2959         }
2960
2961         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2962         if (!ADS_ERR_OK(status)) {
2963                 goto done;
2964         }
2965
2966         timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2967         if (!timestr) {
2968                 ads_msgfree(ads_s, res);
2969                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2970                 goto done;
2971         }
2972
2973         /* but save the time and offset in the original ADS_STRUCT */   
2974
2975         ads->config.current_time = ads_parse_time(timestr);
2976
2977         if (ads->config.current_time != 0) {
2978                 ads->auth.time_offset = ads->config.current_time - time(NULL);
2979                 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
2980         }
2981
2982         ads_msgfree(ads, res);
2983
2984         status = ADS_SUCCESS;
2985
2986 done:
2987         /* free any temporary ads connections */
2988         if ( ads_s != ads ) {
2989                 ads_destroy( &ads_s );
2990         }
2991         talloc_destroy(ctx);
2992
2993         return status;
2994 }
2995
2996 /********************************************************************
2997 ********************************************************************/
2998
2999 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3000 {
3001         const char *attrs[] = {"domainFunctionality", NULL};
3002         ADS_STATUS status;
3003         LDAPMessage *res;
3004         ADS_STRUCT *ads_s = ads;
3005
3006         *val = DS_DOMAIN_FUNCTION_2000;
3007
3008         /* establish a new ldap tcp session if necessary */
3009
3010         if ( !ads->ldap.ld ) {
3011                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
3012                         ads->server.ldap_server )) == NULL )
3013                 {
3014                         status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3015                         goto done;
3016                 }
3017                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3018                 status = ads_connect( ads_s );
3019                 if ( !ADS_ERR_OK(status))
3020                         goto done;
3021         }
3022
3023         /* If the attribute does not exist assume it is a Windows 2000 
3024            functional domain */
3025
3026         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3027         if (!ADS_ERR_OK(status)) {
3028                 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3029                         status = ADS_SUCCESS;
3030                 }
3031                 goto done;
3032         }
3033
3034         if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3035                 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3036         }
3037         DEBUG(3,("ads_domain_func_level: %d\n", *val));
3038
3039
3040         ads_msgfree(ads, res);
3041
3042 done:
3043         /* free any temporary ads connections */
3044         if ( ads_s != ads ) {
3045                 ads_destroy( &ads_s );
3046         }
3047
3048         return status;
3049 }
3050
3051 /**
3052  * find the domain sid for our domain
3053  * @param ads connection to ads server
3054  * @param sid Pointer to domain sid
3055  * @return status of search
3056  **/
3057 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3058 {
3059         const char *attrs[] = {"objectSid", NULL};
3060         LDAPMessage *res;
3061         ADS_STATUS rc;
3062
3063         rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
3064                            attrs, &res);
3065         if (!ADS_ERR_OK(rc)) return rc;
3066         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3067                 ads_msgfree(ads, res);
3068                 return ADS_ERROR_SYSTEM(ENOENT);
3069         }
3070         ads_msgfree(ads, res);
3071
3072         return ADS_SUCCESS;
3073 }
3074
3075 /**
3076  * find our site name 
3077  * @param ads connection to ads server
3078  * @param mem_ctx Pointer to talloc context
3079  * @param site_name Pointer to the sitename
3080  * @return status of search
3081  **/
3082 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3083 {
3084         ADS_STATUS status;
3085         LDAPMessage *res;
3086         const char *dn, *service_name;
3087         const char *attrs[] = { "dsServiceName", NULL };
3088
3089         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3090         if (!ADS_ERR_OK(status)) {
3091                 return status;
3092         }
3093
3094         service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3095         if (service_name == NULL) {
3096                 ads_msgfree(ads, res);
3097                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3098         }
3099
3100         ads_msgfree(ads, res);
3101
3102         /* go up three levels */
3103         dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3104         if (dn == NULL) {
3105                 return ADS_ERROR(LDAP_NO_MEMORY);
3106         }
3107
3108         *site_name = talloc_strdup(mem_ctx, dn);
3109         if (*site_name == NULL) {
3110                 return ADS_ERROR(LDAP_NO_MEMORY);
3111         }
3112
3113         return status;
3114         /*
3115         dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3116         */                                               
3117 }
3118
3119 /**
3120  * find the site dn where a machine resides
3121  * @param ads connection to ads server
3122  * @param mem_ctx Pointer to talloc context
3123  * @param computer_name name of the machine
3124  * @param site_name Pointer to the sitename
3125  * @return status of search
3126  **/
3127 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3128 {
3129         ADS_STATUS status;
3130         LDAPMessage *res;
3131         const char *parent, *filter;
3132         char *config_context = NULL;
3133         char *dn;
3134
3135         /* shortcut a query */
3136         if (strequal(computer_name, ads->config.ldap_server_name)) {
3137                 return ads_site_dn(ads, mem_ctx, site_dn);
3138         }
3139
3140         status = ads_config_path(ads, mem_ctx, &config_context);
3141         if (!ADS_ERR_OK(status)) {
3142                 return status;
3143         }
3144
3145         filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3146         if (filter == NULL) {
3147                 return ADS_ERROR(LDAP_NO_MEMORY);
3148         }
3149
3150         status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, 
3151                                filter, NULL, &res);
3152         if (!ADS_ERR_OK(status)) {
3153                 return status;
3154         }
3155
3156         if (ads_count_replies(ads, res) != 1) {
3157                 ads_msgfree(ads, res);
3158                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3159         }
3160
3161         dn = ads_get_dn(ads, mem_ctx, res);
3162         if (dn == NULL) {
3163                 ads_msgfree(ads, res);
3164                 return ADS_ERROR(LDAP_NO_MEMORY);
3165         }
3166
3167         /* go up three levels */
3168         parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3169         if (parent == NULL) {
3170                 ads_msgfree(ads, res);
3171                 TALLOC_FREE(dn);
3172                 return ADS_ERROR(LDAP_NO_MEMORY);
3173         }
3174
3175         *site_dn = talloc_strdup(mem_ctx, parent);
3176         if (*site_dn == NULL) {
3177                 ads_msgfree(ads, res);
3178                 TALLOC_FREE(dn);
3179                 return ADS_ERROR(LDAP_NO_MEMORY);
3180         }
3181
3182         TALLOC_FREE(dn);
3183         ads_msgfree(ads, res);
3184
3185         return status;
3186 }
3187
3188 /**
3189  * get the upn suffixes for a domain
3190  * @param ads connection to ads server
3191  * @param mem_ctx Pointer to talloc context
3192  * @param suffixes Pointer to an array of suffixes
3193  * @param num_suffixes Pointer to the number of suffixes
3194  * @return status of search
3195  **/
3196 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3197 {
3198         ADS_STATUS status;
3199         LDAPMessage *res;
3200         const char *base;
3201         char *config_context = NULL;
3202         const char *attrs[] = { "uPNSuffixes", NULL };
3203
3204         status = ads_config_path(ads, mem_ctx, &config_context);
3205         if (!ADS_ERR_OK(status)) {
3206                 return status;
3207         }
3208
3209         base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3210         if (base == NULL) {
3211                 return ADS_ERROR(LDAP_NO_MEMORY);
3212         }
3213
3214         status = ads_search_dn(ads, &res, base, attrs);
3215         if (!ADS_ERR_OK(status)) {
3216                 return status;
3217         }
3218
3219         if (ads_count_replies(ads, res) != 1) {
3220                 ads_msgfree(ads, res);
3221                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3222         }
3223
3224         (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3225         if ((*suffixes) == NULL) {
3226                 ads_msgfree(ads, res);
3227                 return ADS_ERROR(LDAP_NO_MEMORY);
3228         }
3229
3230         ads_msgfree(ads, res);
3231
3232         return status;
3233 }
3234
3235 /**
3236  * get the joinable ous for a domain
3237  * @param ads connection to ads server
3238  * @param mem_ctx Pointer to talloc context
3239  * @param ous Pointer to an array of ous
3240  * @param num_ous Pointer to the number of ous
3241  * @return status of search
3242  **/
3243 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3244                                 TALLOC_CTX *mem_ctx,
3245                                 char ***ous,
3246                                 size_t *num_ous)
3247 {
3248         ADS_STATUS status;
3249         LDAPMessage *res = NULL;
3250         LDAPMessage *msg = NULL;
3251         const char *attrs[] = { "dn", NULL };
3252         int count = 0;
3253
3254         status = ads_search(ads, &res,
3255                             "(|(objectClass=domain)(objectclass=organizationalUnit))",
3256                             attrs);
3257         if (!ADS_ERR_OK(status)) {
3258                 return status;
3259         }
3260
3261         count = ads_count_replies(ads, res);
3262         if (count < 1) {
3263                 ads_msgfree(ads, res);
3264                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3265         }
3266
3267         for (msg = ads_first_entry(ads, res); msg;
3268              msg = ads_next_entry(ads, msg)) {
3269                 const char **p = discard_const_p(const char *, *ous);
3270                 char *dn = NULL;
3271
3272                 dn = ads_get_dn(ads, talloc_tos(), msg);
3273                 if (!dn) {
3274                         ads_msgfree(ads, res);
3275                         return ADS_ERROR(LDAP_NO_MEMORY);
3276                 }
3277
3278                 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3279                         TALLOC_FREE(dn);
3280                         ads_msgfree(ads, res);
3281                         return ADS_ERROR(LDAP_NO_MEMORY);
3282                 }
3283
3284                 TALLOC_FREE(dn);
3285                 *ous = discard_const_p(char *, p);
3286         }
3287
3288         ads_msgfree(ads, res);
3289
3290         return status;
3291 }
3292
3293
3294 /**
3295  * pull a struct dom_sid from an extended dn string
3296  * @param mem_ctx TALLOC_CTX
3297  * @param extended_dn string
3298  * @param flags string type of extended_dn
3299  * @param sid pointer to a struct dom_sid
3300  * @return NT_STATUS_OK on success,
3301  *         NT_INVALID_PARAMETER on error,
3302  *         NT_STATUS_NOT_FOUND if no SID present
3303  **/
3304 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3305                                         const char *extended_dn,
3306                                         enum ads_extended_dn_flags flags,
3307                                         struct dom_sid *sid)
3308 {
3309         char *p, *q, *dn;
3310
3311         if (!extended_dn) {
3312                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3313         }
3314
3315         /* otherwise extended_dn gets stripped off */
3316         if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3317                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3318         }
3319         /*
3320          * ADS_EXTENDED_DN_HEX_STRING:
3321          * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3322          *
3323          * ADS_EXTENDED_DN_STRING (only with w2k3):
3324          * <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
3325          *
3326          * Object with no SID, such as an Exchange Public Folder
3327          * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3328          */
3329
3330         p = strchr(dn, ';');
3331         if (!p) {
3332                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3333         }
3334
3335         if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3336                 DEBUG(5,("No SID present in extended dn\n"));
3337                 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3338         }
3339
3340         p += strlen(";<SID=");
3341
3342         q = strchr(p, '>');
3343         if (!q) {
3344                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3345         }
3346
3347         *q = '\0';
3348
3349         DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3350
3351         switch (flags) {
3352
3353         case ADS_EXTENDED_DN_STRING:
3354                 if (!string_to_sid(sid, p)) {
3355                         return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3356                 }
3357                 break;
3358         case ADS_EXTENDED_DN_HEX_STRING: {
3359                 fstring buf;
3360                 size_t buf_len;
3361
3362                 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3363                 if (buf_len == 0) {
3364                         return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3365                 }
3366
3367                 if (!sid_parse((const uint8_t *)buf, buf_len, sid)) {
3368                         DEBUG(10,("failed to parse sid\n"));
3369                         return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3370                 }
3371                 break;
3372                 }
3373         default:
3374                 DEBUG(10,("unknown extended dn format\n"));
3375                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3376         }
3377
3378         return ADS_ERROR_NT(NT_STATUS_OK);
3379 }
3380
3381 /********************************************************************
3382 ********************************************************************/
3383
3384 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3385 {
3386         LDAPMessage *res = NULL;
3387         ADS_STATUS status;
3388         int count = 0;
3389         char *name = NULL;
3390
3391         status = ads_find_machine_acct(ads, &res, machine_name);
3392         if (!ADS_ERR_OK(status)) {
3393                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3394                         lp_netbios_name()));
3395                 goto out;
3396         }
3397
3398         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3399                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3400                 goto out;
3401         }
3402
3403         if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3404                 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3405         }
3406
3407 out:
3408         ads_msgfree(ads, res);
3409
3410         return name;
3411 }
3412
3413 /********************************************************************
3414 ********************************************************************/
3415
3416 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3417 {
3418         LDAPMessage *res = NULL;
3419         ADS_STATUS status;
3420         int count = 0;
3421         char *name = NULL;
3422
3423         status = ads_find_machine_acct(ads, &res, machine_name);
3424         if (!ADS_ERR_OK(status)) {
3425                 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3426                         lp_netbios_name()));
3427                 goto out;
3428         }
3429
3430         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3431                 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3432                 goto out;
3433         }
3434
3435         if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3436                 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3437         }
3438
3439 out:
3440         ads_msgfree(ads, res);
3441
3442         return name;
3443 }
3444
3445 /********************************************************************
3446 ********************************************************************/
3447
3448 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3449 {
3450         LDAPMessage *res = NULL;
3451         ADS_STATUS status;
3452         int count = 0;
3453         char *name = NULL;
3454         bool ok = false;
3455
3456         status = ads_find_machine_acct(ads, &res, machine_name);
3457         if (!ADS_ERR_OK(status)) {
3458                 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
3459                         lp_netbios_name()));
3460                 goto out;
3461         }
3462
3463         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3464                 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count));
3465                 goto out;
3466         }
3467
3468         if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3469                 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
3470         }
3471
3472 out:
3473         ads_msgfree(ads, res);
3474         if (name != NULL) {
3475                 ok = (strlen(name) > 0);
3476         }
3477         TALLOC_FREE(name);
3478         return ok;
3479 }
3480
3481 #if 0
3482
3483    SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3484
3485 /**
3486  * Join a machine to a realm
3487  *  Creates the machine account and sets the machine password
3488  * @param ads connection to ads server
3489  * @param machine name of host to add
3490  * @param org_unit Organizational unit to place machine in
3491  * @return status of join
3492  **/
3493 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3494                         uint32_t account_type, const char *org_unit)
3495 {
3496         ADS_STATUS status;
3497         LDAPMessage *res = NULL;
3498         char *machine;
3499
3500         /* machine name must be lowercase */
3501         machine = SMB_STRDUP(machine_name);
3502         strlower_m(machine);
3503
3504         /*
3505         status = ads_find_machine_acct(ads, (void **)&res, machine);
3506         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3507                 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3508                 status = ads_leave_realm(ads, machine);
3509                 if (!ADS_ERR_OK(status)) {
3510                         DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3511                                 machine, ads->config.realm));
3512                         return status;
3513                 }
3514         }
3515         */
3516         status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3517         if (!ADS_ERR_OK(status)) {
3518                 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3519                 SAFE_FREE(machine);
3520                 return status;
3521         }
3522
3523         status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3524         if (!ADS_ERR_OK(status)) {
3525                 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3526                 SAFE_FREE(machine);
3527                 return status;
3528         }
3529
3530         SAFE_FREE(machine);
3531         ads_msgfree(ads, res);
3532
3533         return status;
3534 }
3535 #endif
3536
3537 /**
3538  * Delete a machine from the realm
3539  * @param ads connection to ads server
3540  * @param hostname Machine to remove
3541  * @return status of delete
3542  **/
3543 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3544 {
3545         ADS_STATUS status;
3546         void *msg;
3547         LDAPMessage *res;
3548         char *hostnameDN, *host;
3549         int rc;
3550         LDAPControl ldap_control;
3551         LDAPControl  * pldap_control[2] = {NULL, NULL};
3552
3553         pldap_control[0] = &ldap_control;
3554         memset(&ldap_control, 0, sizeof(LDAPControl));
3555         ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3556
3557         /* hostname must be lowercase */
3558         host = SMB_STRDUP(hostname);
3559         if (!strlower_m(host)) {
3560                 SAFE_FREE(host);
3561                 return ADS_ERROR_SYSTEM(EINVAL);
3562         }
3563
3564         status = ads_find_machine_acct(ads, &res, host);
3565         if (!ADS_ERR_OK(status)) {
3566                 DEBUG(0, ("Host account for %s does not exist.\n", host));
3567                 SAFE_FREE(host);
3568                 return status;
3569         }
3570
3571         msg = ads_first_entry(ads, res);
3572         if (!msg) {
3573                 SAFE_FREE(host);
3574                 return ADS_ERROR_SYSTEM(ENOENT);
3575         }
3576
3577         hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3578         if (hostnameDN == NULL) {
3579                 SAFE_FREE(host);
3580                 return ADS_ERROR_SYSTEM(ENOENT);
3581         }
3582
3583         rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3584         if (rc) {
3585                 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3586         }else {
3587                 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3588         }
3589
3590         if (rc != LDAP_SUCCESS) {
3591                 const char *attrs[] = { "cn", NULL };
3592                 LDAPMessage *msg_sub;
3593
3594                 /* we only search with scope ONE, we do not expect any further
3595                  * objects to be created deeper */
3596
3597                 status = ads_do_search_retry(ads, hostnameDN,
3598                                              LDAP_SCOPE_ONELEVEL,
3599                                              "(objectclass=*)", attrs, &res);
3600
3601                 if (!ADS_ERR_OK(status)) {
3602                         SAFE_FREE(host);
3603                         TALLOC_FREE(hostnameDN);
3604                         return status;
3605                 }
3606
3607                 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3608                         msg_sub = ads_next_entry(ads, msg_sub)) {
3609
3610                         char *dn = NULL;
3611
3612                         if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3613                                 SAFE_FREE(host);
3614                                 TALLOC_FREE(hostnameDN);
3615                                 return ADS_ERROR(LDAP_NO_MEMORY);
3616                         }
3617
3618                         status = ads_del_dn(ads, dn);
3619                         if (!ADS_ERR_OK(status)) {
3620                                 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3621                                 SAFE_FREE(host);
3622                                 TALLOC_FREE(dn);
3623                                 TALLOC_FREE(hostnameDN);
3624                                 return status;
3625                         }
3626
3627                         TALLOC_FREE(dn);
3628                 }
3629
3630                 /* there should be no subordinate objects anymore */
3631                 status = ads_do_search_retry(ads, hostnameDN,
3632                                              LDAP_SCOPE_ONELEVEL,
3633                                              "(objectclass=*)", attrs, &res);
3634
3635                 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3636                         SAFE_FREE(host);
3637                         TALLOC_FREE(hostnameDN);
3638                         return status;
3639                 }
3640
3641                 /* delete hostnameDN now */
3642                 status = ads_del_dn(ads, hostnameDN);
3643                 if (!ADS_ERR_OK(status)) {
3644                         SAFE_FREE(host);
3645                         DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3646                         TALLOC_FREE(hostnameDN);
3647                         return status;
3648                 }
3649         }
3650
3651         TALLOC_FREE(hostnameDN);
3652
3653         status = ads_find_machine_acct(ads, &res, host);
3654         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3655                 DEBUG(3, ("Failed to remove host account.\n"));
3656                 SAFE_FREE(host);
3657                 return status;
3658         }
3659
3660         SAFE_FREE(host);
3661         return status;
3662 }
3663
3664 /**
3665  * pull all token-sids from an LDAP dn
3666  * @param ads connection to ads server
3667  * @param mem_ctx TALLOC_CTX for allocating sid array
3668  * @param dn of LDAP object
3669  * @param user_sid pointer to struct dom_sid (objectSid)
3670  * @param primary_group_sid pointer to struct dom_sid (self composed)
3671  * @param sids pointer to sid array to allocate
3672  * @param num_sids counter of SIDs pulled
3673  * @return status of token query
3674  **/
3675  ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3676                               TALLOC_CTX *mem_ctx,
3677                               const char *dn,
3678                               struct dom_sid *user_sid,
3679                               struct dom_sid *primary_group_sid,
3680                               struct dom_sid **sids,
3681                               size_t *num_sids)
3682 {
3683         ADS_STATUS status;
3684         LDAPMessage *res = NULL;
3685         int count = 0;
3686         size_t tmp_num_sids;
3687         struct dom_sid *tmp_sids;
3688         struct dom_sid tmp_user_sid;
3689         struct dom_sid tmp_primary_group_sid;
3690         uint32_t pgid;
3691         const char *attrs[] = {
3692                 "objectSid",
3693                 "tokenGroups",
3694                 "primaryGroupID",
3695                 NULL
3696         };
3697
3698         status = ads_search_retry_dn(ads, &res, dn, attrs);
3699         if (!ADS_ERR_OK(status)) {
3700                 return status;
3701         }
3702
3703         count = ads_count_replies(ads, res);
3704         if (count != 1) {
3705                 ads_msgfree(ads, res);
3706                 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3707         }
3708
3709         if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3710                 ads_msgfree(ads, res);
3711                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3712         }
3713
3714         if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3715                 ads_msgfree(ads, res);
3716                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3717         }
3718
3719         {
3720                 /* hack to compose the primary group sid without knowing the
3721                  * domsid */
3722
3723                 struct dom_sid domsid;
3724
3725                 sid_copy(&domsid, &tmp_user_sid);
3726
3727                 if (!sid_split_rid(&domsid, NULL)) {
3728                         ads_msgfree(ads, res);
3729                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3730                 }
3731
3732                 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3733                         ads_msgfree(ads, res);
3734                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3735                 }
3736         }
3737
3738         tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3739
3740         if (tmp_num_sids == 0 || !tmp_sids) {
3741                 ads_msgfree(ads, res);
3742                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3743         }
3744
3745         if (num_sids) {
3746                 *num_sids = tmp_num_sids;
3747         }
3748
3749         if (sids) {
3750                 *sids = tmp_sids;
3751         }
3752
3753         if (user_sid) {
3754                 *user_sid = tmp_user_sid;
3755         }
3756
3757         if (primary_group_sid) {
3758                 *primary_group_sid = tmp_primary_group_sid;
3759         }
3760
3761         DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3762
3763         ads_msgfree(ads, res);
3764         return ADS_ERROR_LDAP(LDAP_SUCCESS);
3765 }
3766
3767 /**
3768  * Find a sAMAccoutName in LDAP
3769  * @param ads connection to ads server
3770  * @param mem_ctx TALLOC_CTX for allocating sid array
3771  * @param samaccountname to search
3772  * @param uac_ret uint32_t pointer userAccountControl attribute value
3773  * @param dn_ret pointer to dn
3774  * @return status of token query
3775  **/
3776 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3777                                TALLOC_CTX *mem_ctx,
3778                                const char *samaccountname,
3779                                uint32_t *uac_ret,
3780                                const char **dn_ret)
3781 {
3782         ADS_STATUS status;
3783         const char *attrs[] = { "userAccountControl", NULL };
3784         const char *filter;
3785         LDAPMessage *res = NULL;
3786         char *dn = NULL;
3787         uint32_t uac = 0;
3788
3789         filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3790                 samaccountname);
3791         if (filter == NULL) {
3792                 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3793                 goto out;
3794         }
3795
3796         status = ads_do_search_all(ads, ads->config.bind_path,
3797                                    LDAP_SCOPE_SUBTREE,
3798                                    filter, attrs, &res);
3799
3800         if (!ADS_ERR_OK(status)) {
3801                 goto out;
3802         }
3803
3804         if (ads_count_replies(ads, res) != 1) {
3805                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3806                 goto out;
3807         }
3808
3809         dn = ads_get_dn(ads, talloc_tos(), res);
3810         if (dn == NULL) {
3811                 status = ADS_ERROR(LDAP_NO_MEMORY);
3812                 goto out;
3813         }
3814
3815         if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3816                 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3817                 goto out;
3818         }
3819
3820         if (uac_ret) {
3821                 *uac_ret = uac;
3822         }
3823
3824         if (dn_ret) {
3825                 *dn_ret = talloc_strdup(mem_ctx, dn);
3826                 if (!*dn_ret) {
3827                         status = ADS_ERROR(LDAP_NO_MEMORY);
3828                         goto out;
3829                 }
3830         }
3831  out:
3832         TALLOC_FREE(dn);
3833         ads_msgfree(ads, res);
3834
3835         return status;
3836 }
3837
3838 /**
3839  * find our configuration path 
3840  * @param ads connection to ads server
3841  * @param mem_ctx Pointer to talloc context
3842  * @param config_path Pointer to the config path
3843  * @return status of search
3844  **/
3845 ADS_STATUS ads_config_path(ADS_STRUCT *ads, 
3846                            TALLOC_CTX *mem_ctx, 
3847                            char **config_path)
3848 {
3849         ADS_STATUS status;
3850         LDAPMessage *res = NULL;
3851         const char *config_context = NULL;
3852         const char *attrs[] = { "configurationNamingContext", NULL };
3853
3854         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, 
3855                                "(objectclass=*)", attrs, &res);
3856         if (!ADS_ERR_OK(status)) {
3857                 return status;
3858         }
3859
3860         config_context = ads_pull_string(ads, mem_ctx, res, 
3861                                          "configurationNamingContext");
3862         ads_msgfree(ads, res);
3863         if (!config_context) {
3864                 return ADS_ERROR(LDAP_NO_MEMORY);
3865         }
3866
3867         if (config_path) {
3868                 *config_path = talloc_strdup(mem_ctx, config_context);
3869                 if (!*config_path) {
3870                         return ADS_ERROR(LDAP_NO_MEMORY);
3871                 }
3872         }
3873
3874         return ADS_ERROR(LDAP_SUCCESS);
3875 }
3876
3877 /**
3878  * find the displayName of an extended right 
3879  * @param ads connection to ads server
3880  * @param config_path The config path
3881  * @param mem_ctx Pointer to talloc context
3882  * @param GUID struct of the rightsGUID
3883  * @return status of search
3884  **/
3885 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads, 
3886                                                 const char *config_path, 
3887                                                 TALLOC_CTX *mem_ctx, 
3888                                                 const struct GUID *rights_guid)
3889 {
3890         ADS_STATUS rc;
3891         LDAPMessage *res = NULL;
3892         char *expr = NULL;
3893         const char *attrs[] = { "displayName", NULL };
3894         const char *result = NULL;
3895         const char *path;
3896
3897         if (!ads || !mem_ctx || !rights_guid) {
3898                 goto done;
3899         }
3900
3901         expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)", 
3902                                GUID_string(mem_ctx, rights_guid));
3903         if (!expr) {
3904                 goto done;
3905         }
3906
3907         path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3908         if (!path) {
3909                 goto done;
3910         }
3911
3912         rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE, 
3913                                  expr, attrs, &res);
3914         if (!ADS_ERR_OK(rc)) {
3915                 goto done;
3916         }
3917
3918         if (ads_count_replies(ads, res) != 1) {
3919                 goto done;
3920         }
3921
3922         result = ads_pull_string(ads, mem_ctx, res, "displayName");
3923
3924  done:
3925         ads_msgfree(ads, res);
3926         return result;
3927 }
3928
3929 /**
3930  * verify or build and verify an account ou
3931  * @param mem_ctx Pointer to talloc context
3932  * @param ads connection to ads server
3933  * @param account_ou
3934  * @return status of search
3935  **/
3936
3937 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3938                            ADS_STRUCT *ads,
3939                            const char **account_ou)
3940 {
3941         char **exploded_dn;
3942         const char *name;
3943         char *ou_string;
3944
3945         if (account_ou == NULL) {
3946                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3947         }
3948
3949         if (*account_ou != NULL) {
3950                 exploded_dn = ldap_explode_dn(*account_ou, 0);
3951                 if (exploded_dn) {
3952                         ldap_value_free(exploded_dn);
3953                         return ADS_SUCCESS;
3954                 }
3955         }
3956
3957         ou_string = ads_ou_string(ads, *account_ou);
3958         if (!ou_string) {
3959                 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3960         }
3961
3962         name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3963                                ads->config.bind_path);
3964         SAFE_FREE(ou_string);
3965
3966         if (!name) {
3967                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3968         }
3969
3970         exploded_dn = ldap_explode_dn(name, 0);
3971         if (!exploded_dn) {
3972                 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3973         }
3974         ldap_value_free(exploded_dn);
3975
3976         *account_ou = name;
3977         return ADS_SUCCESS;
3978 }
3979
3980 #endif