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