s3:libnet:libnet_join: fill in output enctypes and only modify when necessary.
[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                         goto done;
2951                 }
2952                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2953                 status = ads_connect( ads_s );
2954                 if ( !ADS_ERR_OK(status))
2955                         goto done;
2956         }
2957
2958         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2959         if (!ADS_ERR_OK(status)) {
2960                 goto done;
2961         }
2962
2963         timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2964         if (!timestr) {
2965                 ads_msgfree(ads_s, res);
2966                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2967                 goto done;
2968         }
2969
2970         /* but save the time and offset in the original ADS_STRUCT */   
2971
2972         ads->config.current_time = ads_parse_time(timestr);
2973
2974         if (ads->config.current_time != 0) {
2975                 ads->auth.time_offset = ads->config.current_time - time(NULL);
2976                 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
2977         }
2978
2979         ads_msgfree(ads, res);
2980
2981         status = ADS_SUCCESS;
2982
2983 done:
2984         /* free any temporary ads connections */
2985         if ( ads_s != ads ) {
2986                 ads_destroy( &ads_s );
2987         }
2988         talloc_destroy(ctx);
2989
2990         return status;
2991 }
2992
2993 /********************************************************************
2994 ********************************************************************/
2995
2996 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
2997 {
2998         const char *attrs[] = {"domainFunctionality", NULL};
2999         ADS_STATUS status;
3000         LDAPMessage *res;
3001         ADS_STRUCT *ads_s = ads;
3002
3003         *val = DS_DOMAIN_FUNCTION_2000;
3004
3005         /* establish a new ldap tcp session if necessary */
3006
3007         if ( !ads->ldap.ld ) {
3008                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
3009                         ads->server.ldap_server )) == NULL )
3010                 {
3011                         status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3012                         goto done;
3013                 }
3014                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3015                 status = ads_connect( ads_s );
3016                 if ( !ADS_ERR_OK(status))
3017                         goto done;
3018         }
3019
3020         /* If the attribute does not exist assume it is a Windows 2000 
3021            functional domain */
3022
3023         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3024         if (!ADS_ERR_OK(status)) {
3025                 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3026                         status = ADS_SUCCESS;
3027                 }
3028                 goto done;
3029         }
3030
3031         if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3032                 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3033         }
3034         DEBUG(3,("ads_domain_func_level: %d\n", *val));
3035
3036
3037         ads_msgfree(ads, res);
3038
3039 done:
3040         /* free any temporary ads connections */
3041         if ( ads_s != ads ) {
3042                 ads_destroy( &ads_s );
3043         }
3044
3045         return status;
3046 }
3047
3048 /**
3049  * find the domain sid for our domain
3050  * @param ads connection to ads server
3051  * @param sid Pointer to domain sid
3052  * @return status of search
3053  **/
3054 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3055 {
3056         const char *attrs[] = {"objectSid", NULL};
3057         LDAPMessage *res;
3058         ADS_STATUS rc;
3059
3060         rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
3061                            attrs, &res);
3062         if (!ADS_ERR_OK(rc)) return rc;
3063         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3064                 ads_msgfree(ads, res);
3065                 return ADS_ERROR_SYSTEM(ENOENT);
3066         }
3067         ads_msgfree(ads, res);
3068
3069         return ADS_SUCCESS;
3070 }
3071
3072 /**
3073  * find our site name 
3074  * @param ads connection to ads server
3075  * @param mem_ctx Pointer to talloc context
3076  * @param site_name Pointer to the sitename
3077  * @return status of search
3078  **/
3079 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3080 {
3081         ADS_STATUS status;
3082         LDAPMessage *res;
3083         const char *dn, *service_name;
3084         const char *attrs[] = { "dsServiceName", NULL };
3085
3086         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3087         if (!ADS_ERR_OK(status)) {
3088                 return status;
3089         }
3090
3091         service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3092         if (service_name == NULL) {
3093                 ads_msgfree(ads, res);
3094                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3095         }
3096
3097         ads_msgfree(ads, res);
3098
3099         /* go up three levels */
3100         dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3101         if (dn == NULL) {
3102                 return ADS_ERROR(LDAP_NO_MEMORY);
3103         }
3104
3105         *site_name = talloc_strdup(mem_ctx, dn);
3106         if (*site_name == NULL) {
3107                 return ADS_ERROR(LDAP_NO_MEMORY);
3108         }
3109
3110         return status;
3111         /*
3112         dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3113         */                                               
3114 }
3115
3116 /**
3117  * find the site dn where a machine resides
3118  * @param ads connection to ads server
3119  * @param mem_ctx Pointer to talloc context
3120  * @param computer_name name of the machine
3121  * @param site_name Pointer to the sitename
3122  * @return status of search
3123  **/
3124 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3125 {
3126         ADS_STATUS status;
3127         LDAPMessage *res;
3128         const char *parent, *filter;
3129         char *config_context = NULL;
3130         char *dn;
3131
3132         /* shortcut a query */
3133         if (strequal(computer_name, ads->config.ldap_server_name)) {
3134                 return ads_site_dn(ads, mem_ctx, site_dn);
3135         }
3136
3137         status = ads_config_path(ads, mem_ctx, &config_context);
3138         if (!ADS_ERR_OK(status)) {
3139                 return status;
3140         }
3141
3142         filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3143         if (filter == NULL) {
3144                 return ADS_ERROR(LDAP_NO_MEMORY);
3145         }
3146
3147         status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, 
3148                                filter, NULL, &res);
3149         if (!ADS_ERR_OK(status)) {
3150                 return status;
3151         }
3152
3153         if (ads_count_replies(ads, res) != 1) {
3154                 ads_msgfree(ads, res);
3155                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3156         }
3157
3158         dn = ads_get_dn(ads, mem_ctx, res);
3159         if (dn == NULL) {
3160                 ads_msgfree(ads, res);
3161                 return ADS_ERROR(LDAP_NO_MEMORY);
3162         }
3163
3164         /* go up three levels */
3165         parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3166         if (parent == NULL) {
3167                 ads_msgfree(ads, res);
3168                 TALLOC_FREE(dn);
3169                 return ADS_ERROR(LDAP_NO_MEMORY);
3170         }
3171
3172         *site_dn = talloc_strdup(mem_ctx, parent);
3173         if (*site_dn == NULL) {
3174                 ads_msgfree(ads, res);
3175                 TALLOC_FREE(dn);
3176                 return ADS_ERROR(LDAP_NO_MEMORY);
3177         }
3178
3179         TALLOC_FREE(dn);
3180         ads_msgfree(ads, res);
3181
3182         return status;
3183 }
3184
3185 /**
3186  * get the upn suffixes for a domain
3187  * @param ads connection to ads server
3188  * @param mem_ctx Pointer to talloc context
3189  * @param suffixes Pointer to an array of suffixes
3190  * @param num_suffixes Pointer to the number of suffixes
3191  * @return status of search
3192  **/
3193 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3194 {
3195         ADS_STATUS status;
3196         LDAPMessage *res;
3197         const char *base;
3198         char *config_context = NULL;
3199         const char *attrs[] = { "uPNSuffixes", NULL };
3200
3201         status = ads_config_path(ads, mem_ctx, &config_context);
3202         if (!ADS_ERR_OK(status)) {
3203                 return status;
3204         }
3205
3206         base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3207         if (base == NULL) {
3208                 return ADS_ERROR(LDAP_NO_MEMORY);
3209         }
3210
3211         status = ads_search_dn(ads, &res, base, attrs);
3212         if (!ADS_ERR_OK(status)) {
3213                 return status;
3214         }
3215
3216         if (ads_count_replies(ads, res) != 1) {
3217                 ads_msgfree(ads, res);
3218                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3219         }
3220
3221         (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3222         if ((*suffixes) == NULL) {
3223                 ads_msgfree(ads, res);
3224                 return ADS_ERROR(LDAP_NO_MEMORY);
3225         }
3226
3227         ads_msgfree(ads, res);
3228
3229         return status;
3230 }
3231
3232 /**
3233  * get the joinable ous for a domain
3234  * @param ads connection to ads server
3235  * @param mem_ctx Pointer to talloc context
3236  * @param ous Pointer to an array of ous
3237  * @param num_ous Pointer to the number of ous
3238  * @return status of search
3239  **/
3240 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3241                                 TALLOC_CTX *mem_ctx,
3242                                 char ***ous,
3243                                 size_t *num_ous)
3244 {
3245         ADS_STATUS status;
3246         LDAPMessage *res = NULL;
3247         LDAPMessage *msg = NULL;
3248         const char *attrs[] = { "dn", NULL };
3249         int count = 0;
3250
3251         status = ads_search(ads, &res,
3252                             "(|(objectClass=domain)(objectclass=organizationalUnit))",
3253                             attrs);
3254         if (!ADS_ERR_OK(status)) {
3255                 return status;
3256         }
3257
3258         count = ads_count_replies(ads, res);
3259         if (count < 1) {
3260                 ads_msgfree(ads, res);
3261                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3262         }
3263
3264         for (msg = ads_first_entry(ads, res); msg;
3265              msg = ads_next_entry(ads, msg)) {
3266                 const char **p = discard_const_p(const char *, *ous);
3267                 char *dn = NULL;
3268
3269                 dn = ads_get_dn(ads, talloc_tos(), msg);
3270                 if (!dn) {
3271                         ads_msgfree(ads, res);
3272                         return ADS_ERROR(LDAP_NO_MEMORY);
3273                 }
3274
3275                 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3276                         TALLOC_FREE(dn);
3277                         ads_msgfree(ads, res);
3278                         return ADS_ERROR(LDAP_NO_MEMORY);
3279                 }
3280
3281                 TALLOC_FREE(dn);
3282                 *ous = discard_const_p(char *, p);
3283         }
3284
3285         ads_msgfree(ads, res);
3286
3287         return status;
3288 }
3289
3290
3291 /**
3292  * pull a struct dom_sid from an extended dn string
3293  * @param mem_ctx TALLOC_CTX
3294  * @param extended_dn string
3295  * @param flags string type of extended_dn
3296  * @param sid pointer to a struct dom_sid
3297  * @return NT_STATUS_OK on success,
3298  *         NT_INVALID_PARAMETER on error,
3299  *         NT_STATUS_NOT_FOUND if no SID present
3300  **/
3301 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3302                                         const char *extended_dn,
3303                                         enum ads_extended_dn_flags flags,
3304                                         struct dom_sid *sid)
3305 {
3306         char *p, *q, *dn;
3307
3308         if (!extended_dn) {
3309                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3310         }
3311
3312         /* otherwise extended_dn gets stripped off */
3313         if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3314                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3315         }
3316         /*
3317          * ADS_EXTENDED_DN_HEX_STRING:
3318          * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3319          *
3320          * ADS_EXTENDED_DN_STRING (only with w2k3):
3321          * <GUID=63198e23-39cb-4b0f-b032-ba0105525a29>;<SID=S-1-5-21-4257769659-666132843-1169174103-1110>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3322          *
3323          * Object with no SID, such as an Exchange Public Folder
3324          * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3325          */
3326
3327         p = strchr(dn, ';');
3328         if (!p) {
3329                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3330         }
3331
3332         if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3333                 DEBUG(5,("No SID present in extended dn\n"));
3334                 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3335         }
3336
3337         p += strlen(";<SID=");
3338
3339         q = strchr(p, '>');
3340         if (!q) {
3341                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3342         }
3343
3344         *q = '\0';
3345
3346         DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3347
3348         switch (flags) {
3349
3350         case ADS_EXTENDED_DN_STRING:
3351                 if (!string_to_sid(sid, p)) {
3352                         return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3353                 }
3354                 break;
3355         case ADS_EXTENDED_DN_HEX_STRING: {
3356                 fstring buf;
3357                 size_t buf_len;
3358
3359                 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3360                 if (buf_len == 0) {
3361                         return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3362                 }
3363
3364                 if (!sid_parse((const uint8_t *)buf, buf_len, sid)) {
3365                         DEBUG(10,("failed to parse sid\n"));
3366                         return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3367                 }
3368                 break;
3369                 }
3370         default:
3371                 DEBUG(10,("unknown extended dn format\n"));
3372                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3373         }
3374
3375         return ADS_ERROR_NT(NT_STATUS_OK);
3376 }
3377
3378 /********************************************************************
3379 ********************************************************************/
3380
3381 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3382 {
3383         LDAPMessage *res = NULL;
3384         ADS_STATUS status;
3385         int count = 0;
3386         char *name = NULL;
3387
3388         status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3389         if (!ADS_ERR_OK(status)) {
3390                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3391                         lp_netbios_name()));
3392                 goto out;
3393         }
3394
3395         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3396                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3397                 goto out;
3398         }
3399
3400         if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3401                 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3402         }
3403
3404 out:
3405         ads_msgfree(ads, res);
3406
3407         return name;
3408 }
3409
3410 /********************************************************************
3411 ********************************************************************/
3412
3413 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3414 {
3415         LDAPMessage *res = NULL;
3416         ADS_STATUS status;
3417         int count = 0;
3418         char *name = NULL;
3419
3420         status = ads_find_machine_acct(ads, &res, machine_name);
3421         if (!ADS_ERR_OK(status)) {
3422                 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3423                         lp_netbios_name()));
3424                 goto out;
3425         }
3426
3427         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3428                 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3429                 goto out;
3430         }
3431
3432         if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3433                 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3434         }
3435
3436 out:
3437         ads_msgfree(ads, res);
3438
3439         return name;
3440 }
3441
3442 /********************************************************************
3443 ********************************************************************/
3444
3445 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3446 {
3447         LDAPMessage *res = NULL;
3448         ADS_STATUS status;
3449         int count = 0;
3450         char *name = NULL;
3451
3452         status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3453         if (!ADS_ERR_OK(status)) {
3454                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3455                         lp_netbios_name()));
3456                 goto out;
3457         }
3458
3459         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3460                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3461                 goto out;
3462         }
3463
3464         if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3465                 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3466         }
3467
3468 out:
3469         ads_msgfree(ads, res);
3470
3471         return name;
3472 }
3473
3474 #if 0
3475
3476    SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3477
3478 /**
3479  * Join a machine to a realm
3480  *  Creates the machine account and sets the machine password
3481  * @param ads connection to ads server
3482  * @param machine name of host to add
3483  * @param org_unit Organizational unit to place machine in
3484  * @return status of join
3485  **/
3486 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3487                         uint32_t account_type, const char *org_unit)
3488 {
3489         ADS_STATUS status;
3490         LDAPMessage *res = NULL;
3491         char *machine;
3492
3493         /* machine name must be lowercase */
3494         machine = SMB_STRDUP(machine_name);
3495         strlower_m(machine);
3496
3497         /*
3498         status = ads_find_machine_acct(ads, (void **)&res, machine);
3499         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3500                 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3501                 status = ads_leave_realm(ads, machine);
3502                 if (!ADS_ERR_OK(status)) {
3503                         DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3504                                 machine, ads->config.realm));
3505                         return status;
3506                 }
3507         }
3508         */
3509         status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3510         if (!ADS_ERR_OK(status)) {
3511                 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3512                 SAFE_FREE(machine);
3513                 return status;
3514         }
3515
3516         status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3517         if (!ADS_ERR_OK(status)) {
3518                 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3519                 SAFE_FREE(machine);
3520                 return status;
3521         }
3522
3523         SAFE_FREE(machine);
3524         ads_msgfree(ads, res);
3525
3526         return status;
3527 }
3528 #endif
3529
3530 /**
3531  * Delete a machine from the realm
3532  * @param ads connection to ads server
3533  * @param hostname Machine to remove
3534  * @return status of delete
3535  **/
3536 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3537 {
3538         ADS_STATUS status;
3539         void *msg;
3540         LDAPMessage *res;
3541         char *hostnameDN, *host;
3542         int rc;
3543         LDAPControl ldap_control;
3544         LDAPControl  * pldap_control[2] = {NULL, NULL};
3545
3546         pldap_control[0] = &ldap_control;
3547         memset(&ldap_control, 0, sizeof(LDAPControl));
3548         ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3549
3550         /* hostname must be lowercase */
3551         host = SMB_STRDUP(hostname);
3552         if (!strlower_m(host)) {
3553                 SAFE_FREE(host);
3554                 return ADS_ERROR_SYSTEM(EINVAL);
3555         }
3556
3557         status = ads_find_machine_acct(ads, &res, host);
3558         if (!ADS_ERR_OK(status)) {
3559                 DEBUG(0, ("Host account for %s does not exist.\n", host));
3560                 SAFE_FREE(host);
3561                 return status;
3562         }
3563
3564         msg = ads_first_entry(ads, res);
3565         if (!msg) {
3566                 SAFE_FREE(host);
3567                 return ADS_ERROR_SYSTEM(ENOENT);
3568         }
3569
3570         hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3571         if (hostnameDN == NULL) {
3572                 SAFE_FREE(host);
3573                 return ADS_ERROR_SYSTEM(ENOENT);
3574         }
3575
3576         rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3577         if (rc) {
3578                 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3579         }else {
3580                 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3581         }
3582
3583         if (rc != LDAP_SUCCESS) {
3584                 const char *attrs[] = { "cn", NULL };
3585                 LDAPMessage *msg_sub;
3586
3587                 /* we only search with scope ONE, we do not expect any further
3588                  * objects to be created deeper */
3589
3590                 status = ads_do_search_retry(ads, hostnameDN,
3591                                              LDAP_SCOPE_ONELEVEL,
3592                                              "(objectclass=*)", attrs, &res);
3593
3594                 if (!ADS_ERR_OK(status)) {
3595                         SAFE_FREE(host);
3596                         TALLOC_FREE(hostnameDN);
3597                         return status;
3598                 }
3599
3600                 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3601                         msg_sub = ads_next_entry(ads, msg_sub)) {
3602
3603                         char *dn = NULL;
3604
3605                         if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3606                                 SAFE_FREE(host);
3607                                 TALLOC_FREE(hostnameDN);
3608                                 return ADS_ERROR(LDAP_NO_MEMORY);
3609                         }
3610
3611                         status = ads_del_dn(ads, dn);
3612                         if (!ADS_ERR_OK(status)) {
3613                                 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3614                                 SAFE_FREE(host);
3615                                 TALLOC_FREE(dn);
3616                                 TALLOC_FREE(hostnameDN);
3617                                 return status;
3618                         }
3619
3620                         TALLOC_FREE(dn);
3621                 }
3622
3623                 /* there should be no subordinate objects anymore */
3624                 status = ads_do_search_retry(ads, hostnameDN,
3625                                              LDAP_SCOPE_ONELEVEL,
3626                                              "(objectclass=*)", attrs, &res);
3627
3628                 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3629                         SAFE_FREE(host);
3630                         TALLOC_FREE(hostnameDN);
3631                         return status;
3632                 }
3633
3634                 /* delete hostnameDN now */
3635                 status = ads_del_dn(ads, hostnameDN);
3636                 if (!ADS_ERR_OK(status)) {
3637                         SAFE_FREE(host);
3638                         DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3639                         TALLOC_FREE(hostnameDN);
3640                         return status;
3641                 }
3642         }
3643
3644         TALLOC_FREE(hostnameDN);
3645
3646         status = ads_find_machine_acct(ads, &res, host);
3647         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3648                 DEBUG(3, ("Failed to remove host account.\n"));
3649                 SAFE_FREE(host);
3650                 return status;
3651         }
3652
3653         SAFE_FREE(host);
3654         return status;
3655 }
3656
3657 /**
3658  * pull all token-sids from an LDAP dn
3659  * @param ads connection to ads server
3660  * @param mem_ctx TALLOC_CTX for allocating sid array
3661  * @param dn of LDAP object
3662  * @param user_sid pointer to struct dom_sid (objectSid)
3663  * @param primary_group_sid pointer to struct dom_sid (self composed)
3664  * @param sids pointer to sid array to allocate
3665  * @param num_sids counter of SIDs pulled
3666  * @return status of token query
3667  **/
3668  ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3669                               TALLOC_CTX *mem_ctx,
3670                               const char *dn,
3671                               struct dom_sid *user_sid,
3672                               struct dom_sid *primary_group_sid,
3673                               struct dom_sid **sids,
3674                               size_t *num_sids)
3675 {
3676         ADS_STATUS status;
3677         LDAPMessage *res = NULL;
3678         int count = 0;
3679         size_t tmp_num_sids;
3680         struct dom_sid *tmp_sids;
3681         struct dom_sid tmp_user_sid;
3682         struct dom_sid tmp_primary_group_sid;
3683         uint32_t pgid;
3684         const char *attrs[] = {
3685                 "objectSid",
3686                 "tokenGroups",
3687                 "primaryGroupID",
3688                 NULL
3689         };
3690
3691         status = ads_search_retry_dn(ads, &res, dn, attrs);
3692         if (!ADS_ERR_OK(status)) {
3693                 return status;
3694         }
3695
3696         count = ads_count_replies(ads, res);
3697         if (count != 1) {
3698                 ads_msgfree(ads, res);
3699                 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3700         }
3701
3702         if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3703                 ads_msgfree(ads, res);
3704                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3705         }
3706
3707         if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3708                 ads_msgfree(ads, res);
3709                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3710         }
3711
3712         {
3713                 /* hack to compose the primary group sid without knowing the
3714                  * domsid */
3715
3716                 struct dom_sid domsid;
3717
3718                 sid_copy(&domsid, &tmp_user_sid);
3719
3720                 if (!sid_split_rid(&domsid, NULL)) {
3721                         ads_msgfree(ads, res);
3722                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3723                 }
3724
3725                 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3726                         ads_msgfree(ads, res);
3727                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3728                 }
3729         }
3730
3731         tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3732
3733         if (tmp_num_sids == 0 || !tmp_sids) {
3734                 ads_msgfree(ads, res);
3735                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3736         }
3737
3738         if (num_sids) {
3739                 *num_sids = tmp_num_sids;
3740         }
3741
3742         if (sids) {
3743                 *sids = tmp_sids;
3744         }
3745
3746         if (user_sid) {
3747                 *user_sid = tmp_user_sid;
3748         }
3749
3750         if (primary_group_sid) {
3751                 *primary_group_sid = tmp_primary_group_sid;
3752         }
3753
3754         DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3755
3756         ads_msgfree(ads, res);
3757         return ADS_ERROR_LDAP(LDAP_SUCCESS);
3758 }
3759
3760 /**
3761  * Find a sAMAccoutName in LDAP
3762  * @param ads connection to ads server
3763  * @param mem_ctx TALLOC_CTX for allocating sid array
3764  * @param samaccountname to search
3765  * @param uac_ret uint32_t pointer userAccountControl attribute value
3766  * @param dn_ret pointer to dn
3767  * @return status of token query
3768  **/
3769 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3770                                TALLOC_CTX *mem_ctx,
3771                                const char *samaccountname,
3772                                uint32_t *uac_ret,
3773                                const char **dn_ret)
3774 {
3775         ADS_STATUS status;
3776         const char *attrs[] = { "userAccountControl", NULL };
3777         const char *filter;
3778         LDAPMessage *res = NULL;
3779         char *dn = NULL;
3780         uint32_t uac = 0;
3781
3782         filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3783                 samaccountname);
3784         if (filter == NULL) {
3785                 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3786                 goto out;
3787         }
3788
3789         status = ads_do_search_all(ads, ads->config.bind_path,
3790                                    LDAP_SCOPE_SUBTREE,
3791                                    filter, attrs, &res);
3792
3793         if (!ADS_ERR_OK(status)) {
3794                 goto out;
3795         }
3796
3797         if (ads_count_replies(ads, res) != 1) {
3798                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3799                 goto out;
3800         }
3801
3802         dn = ads_get_dn(ads, talloc_tos(), res);
3803         if (dn == NULL) {
3804                 status = ADS_ERROR(LDAP_NO_MEMORY);
3805                 goto out;
3806         }
3807
3808         if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3809                 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3810                 goto out;
3811         }
3812
3813         if (uac_ret) {
3814                 *uac_ret = uac;
3815         }
3816
3817         if (dn_ret) {
3818                 *dn_ret = talloc_strdup(mem_ctx, dn);
3819                 if (!*dn_ret) {
3820                         status = ADS_ERROR(LDAP_NO_MEMORY);
3821                         goto out;
3822                 }
3823         }
3824  out:
3825         TALLOC_FREE(dn);
3826         ads_msgfree(ads, res);
3827
3828         return status;
3829 }
3830
3831 /**
3832  * find our configuration path 
3833  * @param ads connection to ads server
3834  * @param mem_ctx Pointer to talloc context
3835  * @param config_path Pointer to the config path
3836  * @return status of search
3837  **/
3838 ADS_STATUS ads_config_path(ADS_STRUCT *ads, 
3839                            TALLOC_CTX *mem_ctx, 
3840                            char **config_path)
3841 {
3842         ADS_STATUS status;
3843         LDAPMessage *res = NULL;
3844         const char *config_context = NULL;
3845         const char *attrs[] = { "configurationNamingContext", NULL };
3846
3847         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, 
3848                                "(objectclass=*)", attrs, &res);
3849         if (!ADS_ERR_OK(status)) {
3850                 return status;
3851         }
3852
3853         config_context = ads_pull_string(ads, mem_ctx, res, 
3854                                          "configurationNamingContext");
3855         ads_msgfree(ads, res);
3856         if (!config_context) {
3857                 return ADS_ERROR(LDAP_NO_MEMORY);
3858         }
3859
3860         if (config_path) {
3861                 *config_path = talloc_strdup(mem_ctx, config_context);
3862                 if (!*config_path) {
3863                         return ADS_ERROR(LDAP_NO_MEMORY);
3864                 }
3865         }
3866
3867         return ADS_ERROR(LDAP_SUCCESS);
3868 }
3869
3870 /**
3871  * find the displayName of an extended right 
3872  * @param ads connection to ads server
3873  * @param config_path The config path
3874  * @param mem_ctx Pointer to talloc context
3875  * @param GUID struct of the rightsGUID
3876  * @return status of search
3877  **/
3878 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads, 
3879                                                 const char *config_path, 
3880                                                 TALLOC_CTX *mem_ctx, 
3881                                                 const struct GUID *rights_guid)
3882 {
3883         ADS_STATUS rc;
3884         LDAPMessage *res = NULL;
3885         char *expr = NULL;
3886         const char *attrs[] = { "displayName", NULL };
3887         const char *result = NULL;
3888         const char *path;
3889
3890         if (!ads || !mem_ctx || !rights_guid) {
3891                 goto done;
3892         }
3893
3894         expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)", 
3895                                GUID_string(mem_ctx, rights_guid));
3896         if (!expr) {
3897                 goto done;
3898         }
3899
3900         path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3901         if (!path) {
3902                 goto done;
3903         }
3904
3905         rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE, 
3906                                  expr, attrs, &res);
3907         if (!ADS_ERR_OK(rc)) {
3908                 goto done;
3909         }
3910
3911         if (ads_count_replies(ads, res) != 1) {
3912                 goto done;
3913         }
3914
3915         result = ads_pull_string(ads, mem_ctx, res, "displayName");
3916
3917  done:
3918         ads_msgfree(ads, res);
3919         return result;
3920 }
3921
3922 /**
3923  * verify or build and verify an account ou
3924  * @param mem_ctx Pointer to talloc context
3925  * @param ads connection to ads server
3926  * @param account_ou
3927  * @return status of search
3928  **/
3929
3930 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3931                            ADS_STRUCT *ads,
3932                            const char **account_ou)
3933 {
3934         char **exploded_dn;
3935         const char *name;
3936         char *ou_string;
3937
3938         if (account_ou == NULL) {
3939                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3940         }
3941
3942         if (*account_ou != NULL) {
3943                 exploded_dn = ldap_explode_dn(*account_ou, 0);
3944                 if (exploded_dn) {
3945                         ldap_value_free(exploded_dn);
3946                         return ADS_SUCCESS;
3947                 }
3948         }
3949
3950         ou_string = ads_ou_string(ads, *account_ou);
3951         if (!ou_string) {
3952                 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3953         }
3954
3955         name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3956                                ads->config.bind_path);
3957         SAFE_FREE(ou_string);
3958
3959         if (!name) {
3960                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3961         }
3962
3963         exploded_dn = ldap_explode_dn(name, 0);
3964         if (!exploded_dn) {
3965                 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3966         }
3967         ldap_value_free(exploded_dn);
3968
3969         *account_ou = name;
3970         return ADS_SUCCESS;
3971 }
3972
3973 #endif