s3-libads: Do not leak the msg on error
[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 (rc != LDAP_SUCCESS && *res != NULL) {
1040                 ads_msgfree(ads, *res);
1041                 *res = NULL;
1042         }
1043
1044         /* if/when we decide to utf8-encode attrs, take out this next line */
1045         TALLOC_FREE(search_attrs);
1046
1047         return ADS_ERROR(rc);
1048 }
1049
1050 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1051                                       int scope, const char *expr,
1052                                       const char **attrs, LDAPMessage **res, 
1053                                       int *count, struct berval **cookie)
1054 {
1055         return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1056 }
1057
1058
1059 /**
1060  * Get all results for a search.  This uses ads_do_paged_search() to return 
1061  * all entries in a large search.
1062  * @param ads connection to ads server 
1063  * @param bind_path Base dn for the search
1064  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1065  * @param expr Search expression
1066  * @param attrs Attributes to retrieve
1067  * @param res ** which will contain results - free res* with ads_msgfree()
1068  * @return status of search
1069  **/
1070  ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1071                                    int scope, const char *expr,
1072                                    const char **attrs, void *args,
1073                                    LDAPMessage **res)
1074 {
1075         struct berval *cookie = NULL;
1076         int count = 0;
1077         ADS_STATUS status;
1078
1079         *res = NULL;
1080         status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1081                                      &count, &cookie);
1082
1083         if (!ADS_ERR_OK(status)) 
1084                 return status;
1085
1086 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1087         while (cookie) {
1088                 LDAPMessage *res2 = NULL;
1089                 LDAPMessage *msg, *next;
1090
1091                 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1092                                               attrs, args, &res2, &count, &cookie);
1093                 if (!ADS_ERR_OK(status)) {
1094                         break;
1095                 }
1096
1097                 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1098                    that this works on all ldap libs, but I have only tested with openldap */
1099                 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1100                         next = ads_next_message(ads, msg);
1101                         ldap_add_result_entry((LDAPMessage **)res, msg);
1102                 }
1103                 /* note that we do not free res2, as the memory is now
1104                    part of the main returned list */
1105         }
1106 #else
1107         DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1108         status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1109 #endif
1110
1111         return status;
1112 }
1113
1114  ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1115                               int scope, const char *expr,
1116                               const char **attrs, LDAPMessage **res)
1117 {
1118         return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1119 }
1120
1121  ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1122                                        int scope, const char *expr,
1123                                        const char **attrs, uint32_t sd_flags, 
1124                                        LDAPMessage **res)
1125 {
1126         ads_control args;
1127
1128         args.control = ADS_SD_FLAGS_OID;
1129         args.val = sd_flags;
1130         args.critical = True;
1131
1132         return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1133 }
1134
1135
1136 /**
1137  * Run a function on all results for a search.  Uses ads_do_paged_search() and
1138  *  runs the function as each page is returned, using ads_process_results()
1139  * @param ads connection to ads server
1140  * @param bind_path Base dn for the search
1141  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1142  * @param expr Search expression - specified in local charset
1143  * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1144  * @param fn Function which takes attr name, values list, and data_area
1145  * @param data_area Pointer which is passed to function on each call
1146  * @return status of search
1147  **/
1148 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1149                                 int scope, const char *expr, const char **attrs,
1150                                 bool (*fn)(ADS_STRUCT *, char *, void **, void *), 
1151                                 void *data_area)
1152 {
1153         struct berval *cookie = NULL;
1154         int count = 0;
1155         ADS_STATUS status;
1156         LDAPMessage *res;
1157
1158         status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1159                                      &count, &cookie);
1160
1161         if (!ADS_ERR_OK(status)) return status;
1162
1163         ads_process_results(ads, res, fn, data_area);
1164         ads_msgfree(ads, res);
1165
1166         while (cookie) {
1167                 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1168                                              &res, &count, &cookie);
1169
1170                 if (!ADS_ERR_OK(status)) break;
1171
1172                 ads_process_results(ads, res, fn, data_area);
1173                 ads_msgfree(ads, res);
1174         }
1175
1176         return status;
1177 }
1178
1179 /**
1180  * Do a search with a timeout.
1181  * @param ads connection to ads server
1182  * @param bind_path Base dn for the search
1183  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1184  * @param expr Search expression
1185  * @param attrs Attributes to retrieve
1186  * @param res ** which will contain results - free res* with ads_msgfree()
1187  * @return status of search
1188  **/
1189  ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope, 
1190                           const char *expr,
1191                           const char **attrs, LDAPMessage **res)
1192 {
1193         int rc;
1194         char *utf8_expr, *utf8_path, **search_attrs = NULL;
1195         size_t converted_size;
1196         TALLOC_CTX *ctx;
1197
1198         *res = NULL;
1199         if (!(ctx = talloc_init("ads_do_search"))) {
1200                 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1201                 return ADS_ERROR(LDAP_NO_MEMORY);
1202         }
1203
1204         /* 0 means the conversion worked but the result was empty 
1205            so we only fail if it's negative.  In any case, it always 
1206            at least nulls out the dest */
1207         if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1208             !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1209         {
1210                 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1211                 rc = LDAP_NO_MEMORY;
1212                 goto done;
1213         }
1214
1215         if (!attrs || !(*attrs))
1216                 search_attrs = NULL;
1217         else {
1218                 /* This would be the utf8-encoded version...*/
1219                 /* if (!(search_attrs = ads_push_strvals(ctx, attrs)))  */
1220                 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1221                 {
1222                         DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1223                         rc = LDAP_NO_MEMORY;
1224                         goto done;
1225                 }
1226         }
1227
1228         /* see the note in ads_do_paged_search - we *must* disable referrals */
1229         ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1230
1231         rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1232                                       search_attrs, 0, NULL, NULL, 
1233                                       LDAP_NO_LIMIT,
1234                                       (LDAPMessage **)res);
1235
1236         if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1237                 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1238                 rc = 0;
1239         }
1240
1241  done:
1242         talloc_destroy(ctx);
1243         /* if/when we decide to utf8-encode attrs, take out this next line */
1244         TALLOC_FREE(search_attrs);
1245         return ADS_ERROR(rc);
1246 }
1247 /**
1248  * Do a general ADS search
1249  * @param ads connection to ads server
1250  * @param res ** which will contain results - free res* with ads_msgfree()
1251  * @param expr Search expression
1252  * @param attrs Attributes to retrieve
1253  * @return status of search
1254  **/
1255  ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res, 
1256                        const char *expr, const char **attrs)
1257 {
1258         return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE, 
1259                              expr, attrs, res);
1260 }
1261
1262 /**
1263  * Do a search on a specific DistinguishedName
1264  * @param ads connection to ads server
1265  * @param res ** which will contain results - free res* with ads_msgfree()
1266  * @param dn DistinguishName to search
1267  * @param attrs Attributes to retrieve
1268  * @return status of search
1269  **/
1270  ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res, 
1271                           const char *dn, const char **attrs)
1272 {
1273         return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1274                              attrs, res);
1275 }
1276
1277 /**
1278  * Free up memory from a ads_search
1279  * @param ads connection to ads server
1280  * @param msg Search results to free
1281  **/
1282  void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1283 {
1284         if (!msg) return;
1285         ldap_msgfree(msg);
1286 }
1287
1288 /**
1289  * Get a dn from search results
1290  * @param ads connection to ads server
1291  * @param msg Search result
1292  * @return dn string
1293  **/
1294  char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1295 {
1296         char *utf8_dn, *unix_dn;
1297         size_t converted_size;
1298
1299         utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1300
1301         if (!utf8_dn) {
1302                 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1303                 return NULL;
1304         }
1305
1306         if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1307                 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1308                         utf8_dn ));
1309                 return NULL;
1310         }
1311         ldap_memfree(utf8_dn);
1312         return unix_dn;
1313 }
1314
1315 /**
1316  * Get the parent from a dn
1317  * @param dn the dn to return the parent from
1318  * @return parent dn string
1319  **/
1320 char *ads_parent_dn(const char *dn)
1321 {
1322         char *p;
1323
1324         if (dn == NULL) {
1325                 return NULL;
1326         }
1327
1328         p = strchr(dn, ',');
1329
1330         if (p == NULL) {
1331                 return NULL;
1332         }
1333
1334         return p+1;
1335 }
1336
1337 /**
1338  * Find a machine account given a hostname
1339  * @param ads connection to ads server
1340  * @param res ** which will contain results - free res* with ads_msgfree()
1341  * @param host Hostname to search for
1342  * @return status of search
1343  **/
1344  ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1345                                   const char *machine)
1346 {
1347         ADS_STATUS status;
1348         char *expr;
1349         const char *attrs[] = {"*", "msDS-SupportedEncryptionTypes", "nTSecurityDescriptor", NULL};
1350
1351         *res = NULL;
1352
1353         /* the easiest way to find a machine account anywhere in the tree
1354            is to look for hostname$ */
1355         if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1356                 DEBUG(1, ("asprintf failed!\n"));
1357                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1358         }
1359
1360         status = ads_search(ads, res, expr, attrs);
1361         SAFE_FREE(expr);
1362         return status;
1363 }
1364
1365 /**
1366  * Initialize a list of mods to be used in a modify request
1367  * @param ctx An initialized TALLOC_CTX
1368  * @return allocated ADS_MODLIST
1369  **/
1370 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1371 {
1372 #define ADS_MODLIST_ALLOC_SIZE 10
1373         LDAPMod **mods;
1374
1375         if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1376                 /* -1 is safety to make sure we don't go over the end.
1377                    need to reset it to NULL before doing ldap modify */
1378                 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1379
1380         return (ADS_MODLIST)mods;
1381 }
1382
1383
1384 /*
1385   add an attribute to the list, with values list already constructed
1386 */
1387 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
1388                                   int mod_op, const char *name, 
1389                                   const void *_invals)
1390 {
1391         int curmod;
1392         LDAPMod **modlist = (LDAPMod **) *mods;
1393         struct berval **ber_values = NULL;
1394         char **char_values = NULL;
1395
1396         if (!_invals) {
1397                 mod_op = LDAP_MOD_DELETE;
1398         } else {
1399                 if (mod_op & LDAP_MOD_BVALUES) {
1400                         const struct berval **b;
1401                         b = discard_const_p(const struct berval *, _invals);
1402                         ber_values = ads_dup_values(ctx, b);
1403                 } else {
1404                         const char **c;
1405                         c = discard_const_p(const char *, _invals);
1406                         char_values = ads_push_strvals(ctx, c);
1407                 }
1408         }
1409
1410         /* find the first empty slot */
1411         for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1412              curmod++);
1413         if (modlist[curmod] == (LDAPMod *) -1) {
1414                 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1415                                 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1416                         return ADS_ERROR(LDAP_NO_MEMORY);
1417                 memset(&modlist[curmod], 0, 
1418                        ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1419                 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1420                 *mods = (ADS_MODLIST)modlist;
1421         }
1422
1423         if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1424                 return ADS_ERROR(LDAP_NO_MEMORY);
1425         modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1426         if (mod_op & LDAP_MOD_BVALUES) {
1427                 modlist[curmod]->mod_bvalues = ber_values;
1428         } else if (mod_op & LDAP_MOD_DELETE) {
1429                 modlist[curmod]->mod_values = NULL;
1430         } else {
1431                 modlist[curmod]->mod_values = char_values;
1432         }
1433
1434         modlist[curmod]->mod_op = mod_op;
1435         return ADS_ERROR(LDAP_SUCCESS);
1436 }
1437
1438 /**
1439  * Add a single string value to a mod list
1440  * @param ctx An initialized TALLOC_CTX
1441  * @param mods An initialized ADS_MODLIST
1442  * @param name The attribute name to add
1443  * @param val The value to add - NULL means DELETE
1444  * @return ADS STATUS indicating success of add
1445  **/
1446 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
1447                        const char *name, const char *val)
1448 {
1449         const char *values[2];
1450
1451         values[0] = val;
1452         values[1] = NULL;
1453
1454         if (!val)
1455                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1456         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1457 }
1458
1459 /**
1460  * Add an array of string values to a mod list
1461  * @param ctx An initialized TALLOC_CTX
1462  * @param mods An initialized ADS_MODLIST
1463  * @param name The attribute name to add
1464  * @param vals The array of string values to add - NULL means DELETE
1465  * @return ADS STATUS indicating success of add
1466  **/
1467 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1468                            const char *name, const char **vals)
1469 {
1470         if (!vals)
1471                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1472         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, 
1473                                name, (const void **) vals);
1474 }
1475
1476 #if 0
1477 /**
1478  * Add a single ber-encoded value to a mod list
1479  * @param ctx An initialized TALLOC_CTX
1480  * @param mods An initialized ADS_MODLIST
1481  * @param name The attribute name to add
1482  * @param val The value to add - NULL means DELETE
1483  * @return ADS STATUS indicating success of add
1484  **/
1485 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
1486                               const char *name, const struct berval *val)
1487 {
1488         const struct berval *values[2];
1489
1490         values[0] = val;
1491         values[1] = NULL;
1492         if (!val)
1493                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1494         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1495                                name, (const void **) values);
1496 }
1497 #endif
1498
1499 static void ads_print_error(int ret, LDAP *ld)
1500 {
1501         if (ret != 0) {
1502                 char *ld_error = NULL;
1503                 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
1504                 DEBUG(10,("AD LDAP failure %d (%s):\n%s\n", ret,
1505                         ldap_err2string(ret), ld_error));
1506                 SAFE_FREE(ld_error);
1507         }
1508 }
1509
1510 /**
1511  * Perform an ldap modify
1512  * @param ads connection to ads server
1513  * @param mod_dn DistinguishedName to modify
1514  * @param mods list of modifications to perform
1515  * @return status of modify
1516  **/
1517 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1518 {
1519         int ret,i;
1520         char *utf8_dn = NULL;
1521         size_t converted_size;
1522         /* 
1523            this control is needed to modify that contains a currently 
1524            non-existent attribute (but allowable for the object) to run
1525         */
1526         LDAPControl PermitModify = {
1527                 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1528                 {0, NULL},
1529                 (char) 1};
1530         LDAPControl *controls[2];
1531
1532         controls[0] = &PermitModify;
1533         controls[1] = NULL;
1534
1535         if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1536                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1537         }
1538
1539         /* find the end of the list, marked by NULL or -1 */
1540         for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1541         /* make sure the end of the list is NULL */
1542         mods[i] = NULL;
1543         ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1544                                 (LDAPMod **) mods, controls, NULL);
1545         ads_print_error(ret, ads->ldap.ld);
1546         TALLOC_FREE(utf8_dn);
1547         return ADS_ERROR(ret);
1548 }
1549
1550 /**
1551  * Perform an ldap add
1552  * @param ads connection to ads server
1553  * @param new_dn DistinguishedName to add
1554  * @param mods list of attributes and values for DN
1555  * @return status of add
1556  **/
1557 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1558 {
1559         int ret, i;
1560         char *utf8_dn = NULL;
1561         size_t converted_size;
1562
1563         if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1564                 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1565                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1566         }
1567
1568         /* find the end of the list, marked by NULL or -1 */
1569         for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1570         /* make sure the end of the list is NULL */
1571         mods[i] = NULL;
1572
1573         ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1574         ads_print_error(ret, ads->ldap.ld);
1575         TALLOC_FREE(utf8_dn);
1576         return ADS_ERROR(ret);
1577 }
1578
1579 /**
1580  * Delete a DistinguishedName
1581  * @param ads connection to ads server
1582  * @param new_dn DistinguishedName to delete
1583  * @return status of delete
1584  **/
1585 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1586 {
1587         int ret;
1588         char *utf8_dn = NULL;
1589         size_t converted_size;
1590         if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1591                 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1592                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1593         }
1594
1595         ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1596         ads_print_error(ret, ads->ldap.ld);
1597         TALLOC_FREE(utf8_dn);
1598         return ADS_ERROR(ret);
1599 }
1600
1601 /**
1602  * Build an org unit string
1603  *  if org unit is Computers or blank then assume a container, otherwise
1604  *  assume a / separated list of organisational units.
1605  * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1606  * @param ads connection to ads server
1607  * @param org_unit Organizational unit
1608  * @return org unit string - caller must free
1609  **/
1610 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1611 {
1612         char *ret = NULL;
1613
1614         if (!org_unit || !*org_unit) {
1615
1616                 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1617
1618                 /* samba4 might not yet respond to a wellknownobject-query */
1619                 return ret ? ret : SMB_STRDUP("cn=Computers");
1620         }
1621
1622         if (strequal(org_unit, "Computers")) {
1623                 return SMB_STRDUP("cn=Computers");
1624         }
1625
1626         /* jmcd: removed "\\" from the separation chars, because it is
1627            needed as an escape for chars like '#' which are valid in an
1628            OU name */
1629         return ads_build_path(org_unit, "/", "ou=", 1);
1630 }
1631
1632 /**
1633  * Get a org unit string for a well-known GUID
1634  * @param ads connection to ads server
1635  * @param wknguid Well known GUID
1636  * @return org unit string - caller must free
1637  **/
1638 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1639 {
1640         ADS_STATUS status;
1641         LDAPMessage *res = NULL;
1642         char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1643                 **bind_dn_exp = NULL;
1644         const char *attrs[] = {"distinguishedName", NULL};
1645         int new_ln, wkn_ln, bind_ln, i;
1646
1647         if (wknguid == NULL) {
1648                 return NULL;
1649         }
1650
1651         if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1652                 DEBUG(1, ("asprintf failed!\n"));
1653                 return NULL;
1654         }
1655
1656         status = ads_search_dn(ads, &res, base, attrs);
1657         if (!ADS_ERR_OK(status)) {
1658                 DEBUG(1,("Failed while searching for: %s\n", base));
1659                 goto out;
1660         }
1661
1662         if (ads_count_replies(ads, res) != 1) {
1663                 goto out;
1664         }
1665
1666         /* substitute the bind-path from the well-known-guid-search result */
1667         wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1668         if (!wkn_dn) {
1669                 goto out;
1670         }
1671
1672         wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1673         if (!wkn_dn_exp) {
1674                 goto out;
1675         }
1676
1677         bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1678         if (!bind_dn_exp) {
1679                 goto out;
1680         }
1681
1682         for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1683                 ;
1684         for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1685                 ;
1686
1687         new_ln = wkn_ln - bind_ln;
1688
1689         ret = SMB_STRDUP(wkn_dn_exp[0]);
1690         if (!ret) {
1691                 goto out;
1692         }
1693
1694         for (i=1; i < new_ln; i++) {
1695                 char *s = NULL;
1696
1697                 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1698                         SAFE_FREE(ret);
1699                         goto out;
1700                 }
1701
1702                 SAFE_FREE(ret);
1703                 ret = SMB_STRDUP(s);
1704                 free(s);
1705                 if (!ret) {
1706                         goto out;
1707                 }
1708         }
1709
1710  out:
1711         SAFE_FREE(base);
1712         ads_msgfree(ads, res);
1713         TALLOC_FREE(wkn_dn);
1714         if (wkn_dn_exp) {
1715                 ldap_value_free(wkn_dn_exp);
1716         }
1717         if (bind_dn_exp) {
1718                 ldap_value_free(bind_dn_exp);
1719         }
1720
1721         return ret;
1722 }
1723
1724 /**
1725  * Adds (appends) an item to an attribute array, rather then
1726  * replacing the whole list
1727  * @param ctx An initialized TALLOC_CTX
1728  * @param mods An initialized ADS_MODLIST
1729  * @param name name of the ldap attribute to append to
1730  * @param vals an array of values to add
1731  * @return status of addition
1732  **/
1733
1734 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1735                                 const char *name, const char **vals)
1736 {
1737         return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1738                                (const void *) vals);
1739 }
1740
1741 /**
1742  * Determines the an account's current KVNO via an LDAP lookup
1743  * @param ads An initialized ADS_STRUCT
1744  * @param account_name the NT samaccountname.
1745  * @return the kvno for the account, or -1 in case of a failure.
1746  **/
1747
1748 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1749 {
1750         LDAPMessage *res = NULL;
1751         uint32_t kvno = (uint32_t)-1;      /* -1 indicates a failure */
1752         char *filter;
1753         const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1754         char *dn_string = NULL;
1755         ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1756
1757         DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1758         if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1759                 return kvno;
1760         }
1761         ret = ads_search(ads, &res, filter, attrs);
1762         SAFE_FREE(filter);
1763         if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1764                 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1765                 ads_msgfree(ads, res);
1766                 return kvno;
1767         }
1768
1769         dn_string = ads_get_dn(ads, talloc_tos(), res);
1770         if (!dn_string) {
1771                 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1772                 ads_msgfree(ads, res);
1773                 return kvno;
1774         }
1775         DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1776         TALLOC_FREE(dn_string);
1777
1778         /* ---------------------------------------------------------
1779          * 0 is returned as a default KVNO from this point on...
1780          * This is done because Windows 2000 does not support key
1781          * version numbers.  Chances are that a failure in the next
1782          * step is simply due to Windows 2000 being used for a
1783          * domain controller. */
1784         kvno = 0;
1785
1786         if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1787                 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1788                 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1789                 ads_msgfree(ads, res);
1790                 return kvno;
1791         }
1792
1793         /* Success */
1794         DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1795         ads_msgfree(ads, res);
1796         return kvno;
1797 }
1798
1799 /**
1800  * Determines the computer account's current KVNO via an LDAP lookup
1801  * @param ads An initialized ADS_STRUCT
1802  * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1803  * @return the kvno for the computer account, or -1 in case of a failure.
1804  **/
1805
1806 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1807 {
1808         char *computer_account = NULL;
1809         uint32_t kvno = -1;
1810
1811         if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1812                 return kvno;
1813         }
1814
1815         kvno = ads_get_kvno(ads, computer_account);
1816         free(computer_account);
1817
1818         return kvno;
1819 }
1820
1821 /**
1822  * This clears out all registered spn's for a given hostname
1823  * @param ads An initilaized ADS_STRUCT
1824  * @param machine_name the NetBIOS name of the computer.
1825  * @return 0 upon success, non-zero otherwise.
1826  **/
1827
1828 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1829 {
1830         TALLOC_CTX *ctx;
1831         LDAPMessage *res = NULL;
1832         ADS_MODLIST mods;
1833         const char *servicePrincipalName[1] = {NULL};
1834         ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1835         char *dn_string = NULL;
1836
1837         ret = ads_find_machine_acct(ads, &res, machine_name);
1838         if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1839                 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1840                 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1841                 ads_msgfree(ads, res);
1842                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1843         }
1844
1845         DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1846         ctx = talloc_init("ads_clear_service_principal_names");
1847         if (!ctx) {
1848                 ads_msgfree(ads, res);
1849                 return ADS_ERROR(LDAP_NO_MEMORY);
1850         }
1851
1852         if (!(mods = ads_init_mods(ctx))) {
1853                 talloc_destroy(ctx);
1854                 ads_msgfree(ads, res);
1855                 return ADS_ERROR(LDAP_NO_MEMORY);
1856         }
1857         ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1858         if (!ADS_ERR_OK(ret)) {
1859                 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1860                 ads_msgfree(ads, res);
1861                 talloc_destroy(ctx);
1862                 return ret;
1863         }
1864         dn_string = ads_get_dn(ads, talloc_tos(), res);
1865         if (!dn_string) {
1866                 talloc_destroy(ctx);
1867                 ads_msgfree(ads, res);
1868                 return ADS_ERROR(LDAP_NO_MEMORY);
1869         }
1870         ret = ads_gen_mod(ads, dn_string, mods);
1871         TALLOC_FREE(dn_string);
1872         if (!ADS_ERR_OK(ret)) {
1873                 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1874                         machine_name));
1875                 ads_msgfree(ads, res);
1876                 talloc_destroy(ctx);
1877                 return ret;
1878         }
1879
1880         ads_msgfree(ads, res);
1881         talloc_destroy(ctx);
1882         return ret;
1883 }
1884
1885 /**
1886  * @brief Search for an element in a string array.
1887  *
1888  * @param[in]  el_array  The string array to search.
1889  *
1890  * @param[in]  num_el    The number of elements in the string array.
1891  *
1892  * @param[in]  el        The string to search.
1893  *
1894  * @return               True if found, false if not.
1895  */
1896 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
1897 {
1898         size_t i;
1899
1900         if (el_array == NULL || num_el == 0 || el == NULL) {
1901                 return false;
1902         }
1903
1904         for (i = 0; i < num_el && el_array[i] != NULL; i++) {
1905                 int cmp;
1906
1907                 cmp = strcasecmp_m(el_array[i], el);
1908                 if (cmp == 0) {
1909                         return true;
1910                 }
1911         }
1912
1913         return false;
1914 }
1915
1916 /**
1917  * @brief This gets the service principal names of an existing computer account.
1918  *
1919  * @param[in]  mem_ctx      The memory context to use to allocate the spn array.
1920  *
1921  * @param[in]  ads          The ADS context to use.
1922  *
1923  * @param[in]  machine_name The NetBIOS name of the computer, which is used to
1924  *                          identify the computer account.
1925  *
1926  * @param[in]  spn_array    A pointer to store the array for SPNs.
1927  *
1928  * @param[in]  num_spns     The number of principals stored in the array.
1929  *
1930  * @return                  0 on success, or a ADS error if a failure occurred.
1931  */
1932 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
1933                                            ADS_STRUCT *ads,
1934                                            const char *machine_name,
1935                                            char ***spn_array,
1936                                            size_t *num_spns)
1937 {
1938         ADS_STATUS status;
1939         LDAPMessage *res = NULL;
1940         int count;
1941
1942         status = ads_find_machine_acct(ads,
1943                                        &res,
1944                                        machine_name);
1945         if (!ADS_ERR_OK(status)) {
1946                 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
1947                          machine_name));
1948                 return status;
1949         }
1950
1951         count = ads_count_replies(ads, res);
1952         if (count != 1) {
1953                 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1954                 goto done;
1955         }
1956
1957         *spn_array = ads_pull_strings(ads,
1958                                       mem_ctx,
1959                                       res,
1960                                       "servicePrincipalName",
1961                                       num_spns);
1962         if (*spn_array == NULL) {
1963                 DEBUG(1, ("Host account for %s does not have service principal "
1964                           "names.\n",
1965                           machine_name));
1966                 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1967                 goto done;
1968         }
1969
1970 done:
1971         ads_msgfree(ads, res);
1972
1973         return status;
1974 }
1975
1976 /**
1977  * This adds a service principal name to an existing computer account
1978  * (found by hostname) in AD.
1979  * @param ads An initialized ADS_STRUCT
1980  * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1981  * @param my_fqdn The fully qualified DNS name of the machine
1982  * @param spn A string of the service principal to add, i.e. 'host'
1983  * @return 0 upon sucess, or non-zero if a failure occurs
1984  **/
1985
1986 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, 
1987                                           const char *my_fqdn, const char *spn)
1988 {
1989         ADS_STATUS ret;
1990         TALLOC_CTX *ctx;
1991         LDAPMessage *res = NULL;
1992         char *psp1, *psp2;
1993         ADS_MODLIST mods;
1994         char *dn_string = NULL;
1995         const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1996
1997         ret = ads_find_machine_acct(ads, &res, machine_name);
1998         if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1999                 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2000                         machine_name));
2001                 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
2002                         spn, machine_name, ads->config.realm));
2003                 ads_msgfree(ads, res);
2004                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2005         }
2006
2007         DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2008         if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2009                 ads_msgfree(ads, res);
2010                 return ADS_ERROR(LDAP_NO_MEMORY);
2011         }
2012
2013         /* add short name spn */
2014
2015         if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
2016                 talloc_destroy(ctx);
2017                 ads_msgfree(ads, res);
2018                 return ADS_ERROR(LDAP_NO_MEMORY);
2019         }
2020         if (!strlower_m(&psp1[strlen(spn) + 1])) {
2021                 ret = ADS_ERROR(LDAP_NO_MEMORY);
2022                 goto out;
2023         }
2024         servicePrincipalName[0] = psp1;
2025
2026         DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", 
2027                 psp1, machine_name));
2028
2029
2030         /* add fully qualified spn */
2031
2032         if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
2033                 ret = ADS_ERROR(LDAP_NO_MEMORY);
2034                 goto out;
2035         }
2036         if (!strlower_m(&psp2[strlen(spn) + 1])) {
2037                 ret = ADS_ERROR(LDAP_NO_MEMORY);
2038                 goto out;
2039         }
2040         servicePrincipalName[1] = psp2;
2041
2042         DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", 
2043                 psp2, machine_name));
2044
2045         if ( (mods = ads_init_mods(ctx)) == NULL ) {
2046                 ret = ADS_ERROR(LDAP_NO_MEMORY);
2047                 goto out;
2048         }
2049
2050         ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2051         if (!ADS_ERR_OK(ret)) {
2052                 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2053                 goto out;
2054         }
2055
2056         if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2057                 ret = ADS_ERROR(LDAP_NO_MEMORY);
2058                 goto out;
2059         }
2060
2061         ret = ads_gen_mod(ads, dn_string, mods);
2062         if (!ADS_ERR_OK(ret)) {
2063                 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2064                 goto out;
2065         }
2066
2067  out:
2068         TALLOC_FREE( ctx );
2069         ads_msgfree(ads, res);
2070         return ret;
2071 }
2072
2073 /**
2074  * adds a machine account to the ADS server
2075  * @param ads An intialized ADS_STRUCT
2076  * @param machine_name - the NetBIOS machine name of this account.
2077  * @param account_type A number indicating the type of account to create
2078  * @param org_unit The LDAP path in which to place this account
2079  * @return 0 upon success, or non-zero otherwise
2080 **/
2081
2082 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2083                                    const char *machine_name,
2084                                    const char *org_unit,
2085                                    uint32_t etype_list)
2086 {
2087         ADS_STATUS ret;
2088         char *samAccountName, *controlstr;
2089         TALLOC_CTX *ctx;
2090         ADS_MODLIST mods;
2091         char *machine_escaped = NULL;
2092         char *new_dn;
2093         const char *objectClass[] = {"top", "person", "organizationalPerson",
2094                                      "user", "computer", NULL};
2095         LDAPMessage *res = NULL;
2096         uint32_t acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
2097                                 UF_DONT_EXPIRE_PASSWD |\
2098                                 UF_ACCOUNTDISABLE );
2099         uint32_t func_level = 0;
2100
2101         ret = ads_domain_func_level(ads, &func_level);
2102         if (!ADS_ERR_OK(ret)) {
2103                 return ret;
2104         }
2105
2106         if (!(ctx = talloc_init("ads_add_machine_acct")))
2107                 return ADS_ERROR(LDAP_NO_MEMORY);
2108
2109         ret = ADS_ERROR(LDAP_NO_MEMORY);
2110
2111         machine_escaped = escape_rdn_val_string_alloc(machine_name);
2112         if (!machine_escaped) {
2113                 goto done;
2114         }
2115
2116         new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2117         samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2118
2119         if ( !new_dn || !samAccountName ) {
2120                 goto done;
2121         }
2122
2123         if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2124                 goto done;
2125         }
2126
2127         if (!(mods = ads_init_mods(ctx))) {
2128                 goto done;
2129         }
2130
2131         ads_mod_str(ctx, &mods, "cn", machine_name);
2132         ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2133         ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2134         ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2135
2136         if (func_level >= DS_DOMAIN_FUNCTION_2008) {
2137                 const char *etype_list_str;
2138
2139                 etype_list_str = talloc_asprintf(ctx, "%d", (int)etype_list);
2140                 if (etype_list_str == NULL) {
2141                         goto done;
2142                 }
2143                 ads_mod_str(ctx, &mods, "msDS-SupportedEncryptionTypes",
2144                             etype_list_str);
2145         }
2146
2147         ret = ads_gen_add(ads, new_dn, mods);
2148
2149 done:
2150         SAFE_FREE(machine_escaped);
2151         ads_msgfree(ads, res);
2152         talloc_destroy(ctx);
2153
2154         return ret;
2155 }
2156
2157 /**
2158  * move a machine account to another OU on the ADS server
2159  * @param ads - An intialized ADS_STRUCT
2160  * @param machine_name - the NetBIOS machine name of this account.
2161  * @param org_unit - The LDAP path in which to place this account
2162  * @param moved - whether we moved the machine account (optional)
2163  * @return 0 upon success, or non-zero otherwise
2164 **/
2165
2166 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name, 
2167                                  const char *org_unit, bool *moved)
2168 {
2169         ADS_STATUS rc;
2170         int ldap_status;
2171         LDAPMessage *res = NULL;
2172         char *filter = NULL;
2173         char *computer_dn = NULL;
2174         char *parent_dn;
2175         char *computer_rdn = NULL;
2176         bool need_move = False;
2177
2178         if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2179                 rc = ADS_ERROR(LDAP_NO_MEMORY);
2180                 goto done;
2181         }
2182
2183         /* Find pre-existing machine */
2184         rc = ads_search(ads, &res, filter, NULL);
2185         if (!ADS_ERR_OK(rc)) {
2186                 goto done;
2187         }
2188
2189         computer_dn = ads_get_dn(ads, talloc_tos(), res);
2190         if (!computer_dn) {
2191                 rc = ADS_ERROR(LDAP_NO_MEMORY);
2192                 goto done;
2193         }
2194
2195         parent_dn = ads_parent_dn(computer_dn);
2196         if (strequal(parent_dn, org_unit)) {
2197                 goto done;
2198         }
2199
2200         need_move = True;
2201
2202         if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2203                 rc = ADS_ERROR(LDAP_NO_MEMORY);
2204                 goto done;
2205         }
2206
2207         ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn, 
2208                                     org_unit, 1, NULL, NULL);
2209         rc = ADS_ERROR(ldap_status);
2210
2211 done:
2212         ads_msgfree(ads, res);
2213         SAFE_FREE(filter);
2214         TALLOC_FREE(computer_dn);
2215         SAFE_FREE(computer_rdn);
2216
2217         if (!ADS_ERR_OK(rc)) {
2218                 need_move = False;
2219         }
2220
2221         if (moved) {
2222                 *moved = need_move;
2223         }
2224
2225         return rc;
2226 }
2227
2228 /*
2229   dump a binary result from ldap
2230 */
2231 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2232 {
2233         int i, j;
2234         for (i=0; values[i]; i++) {
2235                 printf("%s: ", field);
2236                 for (j=0; j<values[i]->bv_len; j++) {
2237                         printf("%02X", (unsigned char)values[i]->bv_val[j]);
2238                 }
2239                 printf("\n");
2240         }
2241 }
2242
2243 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2244 {
2245         int i;
2246         for (i=0; values[i]; i++) {
2247                 NTSTATUS status;
2248                 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2249                 struct GUID guid;
2250
2251                 status = GUID_from_ndr_blob(&in, &guid);
2252                 if (NT_STATUS_IS_OK(status)) {
2253                         printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2254                 } else {
2255                         printf("%s: INVALID GUID\n", field);
2256                 }
2257         }
2258 }
2259
2260 /*
2261   dump a sid result from ldap
2262 */
2263 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2264 {
2265         int i;
2266         for (i=0; values[i]; i++) {
2267                 struct dom_sid sid;
2268                 fstring tmp;
2269                 if (!sid_parse((const uint8_t *)values[i]->bv_val,
2270                                values[i]->bv_len, &sid)) {
2271                         return;
2272                 }
2273                 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2274         }
2275 }
2276
2277 /*
2278   dump ntSecurityDescriptor
2279 */
2280 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2281 {
2282         TALLOC_CTX *frame = talloc_stackframe();
2283         struct security_descriptor *psd;
2284         NTSTATUS status;
2285
2286         status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2287                                      values[0]->bv_len, &psd);
2288         if (!NT_STATUS_IS_OK(status)) {
2289                 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2290                           nt_errstr(status)));
2291                 TALLOC_FREE(frame);
2292                 return;
2293         }
2294
2295         if (psd) {
2296                 ads_disp_sd(ads, talloc_tos(), psd);
2297         }
2298
2299         TALLOC_FREE(frame);
2300 }
2301
2302 /*
2303   dump a string result from ldap
2304 */
2305 static void dump_string(const char *field, char **values)
2306 {
2307         int i;
2308         for (i=0; values[i]; i++) {
2309                 printf("%s: %s\n", field, values[i]);
2310         }
2311 }
2312
2313 /*
2314   dump a field from LDAP on stdout
2315   used for debugging
2316 */
2317
2318 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2319 {
2320         const struct {
2321                 const char *name;
2322                 bool string;
2323                 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2324         } handlers[] = {
2325                 {"objectGUID", False, dump_guid},
2326                 {"netbootGUID", False, dump_guid},
2327                 {"nTSecurityDescriptor", False, dump_sd},
2328                 {"dnsRecord", False, dump_binary},
2329                 {"objectSid", False, dump_sid},
2330                 {"tokenGroups", False, dump_sid},
2331                 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2332                 {"tokengroupsGlobalandUniversal", False, dump_sid},
2333                 {"mS-DS-CreatorSID", False, dump_sid},
2334                 {"msExchMailboxGuid", False, dump_guid},
2335                 {NULL, True, NULL}
2336         };
2337         int i;
2338
2339         if (!field) { /* must be end of an entry */
2340                 printf("\n");
2341                 return False;
2342         }
2343
2344         for (i=0; handlers[i].name; i++) {
2345                 if (strcasecmp_m(handlers[i].name, field) == 0) {
2346                         if (!values) /* first time, indicate string or not */
2347                                 return handlers[i].string;
2348                         handlers[i].handler(ads, field, (struct berval **) values);
2349                         break;
2350                 }
2351         }
2352         if (!handlers[i].name) {
2353                 if (!values) /* first time, indicate string conversion */
2354                         return True;
2355                 dump_string(field, (char **)values);
2356         }
2357         return False;
2358 }
2359
2360 /**
2361  * Dump a result from LDAP on stdout
2362  *  used for debugging
2363  * @param ads connection to ads server
2364  * @param res Results to dump
2365  **/
2366
2367  void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2368 {
2369         ads_process_results(ads, res, ads_dump_field, NULL);
2370 }
2371
2372 /**
2373  * Walk through results, calling a function for each entry found.
2374  *  The function receives a field name, a berval * array of values,
2375  *  and a data area passed through from the start.  The function is
2376  *  called once with null for field and values at the end of each
2377  *  entry.
2378  * @param ads connection to ads server
2379  * @param res Results to process
2380  * @param fn Function for processing each result
2381  * @param data_area user-defined area to pass to function
2382  **/
2383  void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2384                           bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2385                           void *data_area)
2386 {
2387         LDAPMessage *msg;
2388         TALLOC_CTX *ctx;
2389         size_t converted_size;
2390
2391         if (!(ctx = talloc_init("ads_process_results")))
2392                 return;
2393
2394         for (msg = ads_first_entry(ads, res); msg; 
2395              msg = ads_next_entry(ads, msg)) {
2396                 char *utf8_field;
2397                 BerElement *b;
2398
2399                 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2400                                                      (LDAPMessage *)msg,&b); 
2401                      utf8_field;
2402                      utf8_field=ldap_next_attribute(ads->ldap.ld,
2403                                                     (LDAPMessage *)msg,b)) {
2404                         struct berval **ber_vals;
2405                         char **str_vals;
2406                         char **utf8_vals;
2407                         char *field;
2408                         bool string; 
2409
2410                         if (!pull_utf8_talloc(ctx, &field, utf8_field,
2411                                               &converted_size))
2412                         {
2413                                 DEBUG(0,("ads_process_results: "
2414                                          "pull_utf8_talloc failed: %s",
2415                                          strerror(errno)));
2416                         }
2417
2418                         string = fn(ads, field, NULL, data_area);
2419
2420                         if (string) {
2421                                 const char **p;
2422
2423                                 utf8_vals = ldap_get_values(ads->ldap.ld,
2424                                                  (LDAPMessage *)msg, field);
2425                                 p = discard_const_p(const char *, utf8_vals);
2426                                 str_vals = ads_pull_strvals(ctx, p);
2427                                 fn(ads, field, (void **) str_vals, data_area);
2428                                 ldap_value_free(utf8_vals);
2429                         } else {
2430                                 ber_vals = ldap_get_values_len(ads->ldap.ld, 
2431                                                  (LDAPMessage *)msg, field);
2432                                 fn(ads, field, (void **) ber_vals, data_area);
2433
2434                                 ldap_value_free_len(ber_vals);
2435                         }
2436                         ldap_memfree(utf8_field);
2437                 }
2438                 ber_free(b, 0);
2439                 talloc_free_children(ctx);
2440                 fn(ads, NULL, NULL, data_area); /* completed an entry */
2441
2442         }
2443         talloc_destroy(ctx);
2444 }
2445
2446 /**
2447  * count how many replies are in a LDAPMessage
2448  * @param ads connection to ads server
2449  * @param res Results to count
2450  * @return number of replies
2451  **/
2452 int ads_count_replies(ADS_STRUCT *ads, void *res)
2453 {
2454         return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2455 }
2456
2457 /**
2458  * pull the first entry from a ADS result
2459  * @param ads connection to ads server
2460  * @param res Results of search
2461  * @return first entry from result
2462  **/
2463  LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2464 {
2465         return ldap_first_entry(ads->ldap.ld, res);
2466 }
2467
2468 /**
2469  * pull the next entry from a ADS result
2470  * @param ads connection to ads server
2471  * @param res Results of search
2472  * @return next entry from result
2473  **/
2474  LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2475 {
2476         return ldap_next_entry(ads->ldap.ld, res);
2477 }
2478
2479 /**
2480  * pull the first message from a ADS result
2481  * @param ads connection to ads server
2482  * @param res Results of search
2483  * @return first message from result
2484  **/
2485  LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2486 {
2487         return ldap_first_message(ads->ldap.ld, res);
2488 }
2489
2490 /**
2491  * pull the next message from a ADS result
2492  * @param ads connection to ads server
2493  * @param res Results of search
2494  * @return next message from result
2495  **/
2496  LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2497 {
2498         return ldap_next_message(ads->ldap.ld, res);
2499 }
2500
2501 /**
2502  * pull a single string from a ADS result
2503  * @param ads connection to ads server
2504  * @param mem_ctx TALLOC_CTX to use for allocating result string
2505  * @param msg Results of search
2506  * @param field Attribute to retrieve
2507  * @return Result string in talloc context
2508  **/
2509  char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2510                        const char *field)
2511 {
2512         char **values;
2513         char *ret = NULL;
2514         char *ux_string;
2515         size_t converted_size;
2516
2517         values = ldap_get_values(ads->ldap.ld, msg, field);
2518         if (!values)
2519                 return NULL;
2520
2521         if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2522                                           &converted_size))
2523         {
2524                 ret = ux_string;
2525         }
2526         ldap_value_free(values);
2527         return ret;
2528 }
2529
2530 /**
2531  * pull an array of strings from a ADS result
2532  * @param ads connection to ads server
2533  * @param mem_ctx TALLOC_CTX to use for allocating result string
2534  * @param msg Results of search
2535  * @param field Attribute to retrieve
2536  * @return Result strings in talloc context
2537  **/
2538  char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2539                          LDAPMessage *msg, const char *field,
2540                          size_t *num_values)
2541 {
2542         char **values;
2543         char **ret = NULL;
2544         int i;
2545         size_t converted_size;
2546
2547         values = ldap_get_values(ads->ldap.ld, msg, field);
2548         if (!values)
2549                 return NULL;
2550
2551         *num_values = ldap_count_values(values);
2552
2553         ret = talloc_array(mem_ctx, char *, *num_values + 1);
2554         if (!ret) {
2555                 ldap_value_free(values);
2556                 return NULL;
2557         }
2558
2559         for (i=0;i<*num_values;i++) {
2560                 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2561                                       &converted_size))
2562                 {
2563                         ldap_value_free(values);
2564                         return NULL;
2565                 }
2566         }
2567         ret[i] = NULL;
2568
2569         ldap_value_free(values);
2570         return ret;
2571 }
2572
2573 /**
2574  * pull an array of strings from a ADS result 
2575  *  (handle large multivalue attributes with range retrieval)
2576  * @param ads connection to ads server
2577  * @param mem_ctx TALLOC_CTX to use for allocating result string
2578  * @param msg Results of search
2579  * @param field Attribute to retrieve
2580  * @param current_strings strings returned by a previous call to this function
2581  * @param next_attribute The next query should ask for this attribute
2582  * @param num_values How many values did we get this time?
2583  * @param more_values Are there more values to get?
2584  * @return Result strings in talloc context
2585  **/
2586  char **ads_pull_strings_range(ADS_STRUCT *ads, 
2587                                TALLOC_CTX *mem_ctx,
2588                                LDAPMessage *msg, const char *field,
2589                                char **current_strings,
2590                                const char **next_attribute,
2591                                size_t *num_strings,
2592                                bool *more_strings)
2593 {
2594         char *attr;
2595         char *expected_range_attrib, *range_attr;
2596         BerElement *ptr = NULL;
2597         char **strings;
2598         char **new_strings;
2599         size_t num_new_strings;
2600         unsigned long int range_start;
2601         unsigned long int range_end;
2602
2603         /* we might have been given the whole lot anyway */
2604         if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2605                 *more_strings = False;
2606                 return strings;
2607         }
2608
2609         expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2610
2611         /* look for Range result */
2612         for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr); 
2613              attr; 
2614              attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2615                 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2616                 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2617                         range_attr = attr;
2618                         break;
2619                 }
2620                 ldap_memfree(attr);
2621         }
2622         if (!attr) {
2623                 ber_free(ptr, 0);
2624                 /* nothing here - this field is just empty */
2625                 *more_strings = False;
2626                 return NULL;
2627         }
2628
2629         if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu", 
2630                    &range_start, &range_end) == 2) {
2631                 *more_strings = True;
2632         } else {
2633                 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*", 
2634                            &range_start) == 1) {
2635                         *more_strings = False;
2636                 } else {
2637                         DEBUG(1, ("ads_pull_strings_range:  Cannot parse Range attriubte (%s)\n", 
2638                                   range_attr));
2639                         ldap_memfree(range_attr);
2640                         *more_strings = False;
2641                         return NULL;
2642                 }
2643         }
2644
2645         if ((*num_strings) != range_start) {
2646                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2647                           " - aborting range retreival\n",
2648                           range_attr, (unsigned int)(*num_strings) + 1, range_start));
2649                 ldap_memfree(range_attr);
2650                 *more_strings = False;
2651                 return NULL;
2652         }
2653
2654         new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2655
2656         if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2657                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2658                           "strings in this bunch, but we only got %lu - aborting range retreival\n",
2659                           range_attr, (unsigned long int)range_end - range_start + 1, 
2660                           (unsigned long int)num_new_strings));
2661                 ldap_memfree(range_attr);
2662                 *more_strings = False;
2663                 return NULL;
2664         }
2665
2666         strings = talloc_realloc(mem_ctx, current_strings, char *,
2667                                  *num_strings + num_new_strings);
2668
2669         if (strings == NULL) {
2670                 ldap_memfree(range_attr);
2671                 *more_strings = False;
2672                 return NULL;
2673         }
2674
2675         if (new_strings && num_new_strings) {
2676                 memcpy(&strings[*num_strings], new_strings,
2677                        sizeof(*new_strings) * num_new_strings);
2678         }
2679
2680         (*num_strings) += num_new_strings;
2681
2682         if (*more_strings) {
2683                 *next_attribute = talloc_asprintf(mem_ctx,
2684                                                   "%s;range=%d-*", 
2685                                                   field,
2686                                                   (int)*num_strings);
2687
2688                 if (!*next_attribute) {
2689                         DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2690                         ldap_memfree(range_attr);
2691                         *more_strings = False;
2692                         return NULL;
2693                 }
2694         }
2695
2696         ldap_memfree(range_attr);
2697
2698         return strings;
2699 }
2700
2701 /**
2702  * pull a single uint32_t from a ADS result
2703  * @param ads connection to ads server
2704  * @param msg Results of search
2705  * @param field Attribute to retrieve
2706  * @param v Pointer to int to store result
2707  * @return boolean inidicating success
2708 */
2709  bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2710                       uint32_t *v)
2711 {
2712         char **values;
2713
2714         values = ldap_get_values(ads->ldap.ld, msg, field);
2715         if (!values)
2716                 return False;
2717         if (!values[0]) {
2718                 ldap_value_free(values);
2719                 return False;
2720         }
2721
2722         *v = atoi(values[0]);
2723         ldap_value_free(values);
2724         return True;
2725 }
2726
2727 /**
2728  * pull a single objectGUID from an ADS result
2729  * @param ads connection to ADS server
2730  * @param msg results of search
2731  * @param guid 37-byte area to receive text guid
2732  * @return boolean indicating success
2733  **/
2734  bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2735 {
2736         DATA_BLOB blob;
2737         NTSTATUS status;
2738
2739         if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2740                                         &blob)) {
2741                 return false;
2742         }
2743
2744         status = GUID_from_ndr_blob(&blob, guid);
2745         talloc_free(blob.data);
2746         return NT_STATUS_IS_OK(status);
2747 }
2748
2749
2750 /**
2751  * pull a single struct dom_sid from a ADS result
2752  * @param ads connection to ads server
2753  * @param msg Results of search
2754  * @param field Attribute to retrieve
2755  * @param sid Pointer to sid to store result
2756  * @return boolean inidicating success
2757 */
2758  bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2759                    struct dom_sid *sid)
2760 {
2761         return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2762 }
2763
2764 /**
2765  * pull an array of struct dom_sids from a ADS result
2766  * @param ads connection to ads server
2767  * @param mem_ctx TALLOC_CTX for allocating sid array
2768  * @param msg Results of search
2769  * @param field Attribute to retrieve
2770  * @param sids pointer to sid array to allocate
2771  * @return the count of SIDs pulled
2772  **/
2773  int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2774                    LDAPMessage *msg, const char *field, struct dom_sid **sids)
2775 {
2776         struct berval **values;
2777         bool ret;
2778         int count, i;
2779
2780         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2781
2782         if (!values)
2783                 return 0;
2784
2785         for (i=0; values[i]; i++)
2786                 /* nop */ ;
2787
2788         if (i) {
2789                 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2790                 if (!(*sids)) {
2791                         ldap_value_free_len(values);
2792                         return 0;
2793                 }
2794         } else {
2795                 (*sids) = NULL;
2796         }
2797
2798         count = 0;
2799         for (i=0; values[i]; i++) {
2800                 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2801                                 values[i]->bv_len, &(*sids)[count]);
2802                 if (ret) {
2803                         DEBUG(10, ("pulling SID: %s\n",
2804                                    sid_string_dbg(&(*sids)[count])));
2805                         count++;
2806                 }
2807         }
2808
2809         ldap_value_free_len(values);
2810         return count;
2811 }
2812
2813 /**
2814  * pull a struct security_descriptor from a ADS result
2815  * @param ads connection to ads server
2816  * @param mem_ctx TALLOC_CTX for allocating sid array
2817  * @param msg Results of search
2818  * @param field Attribute to retrieve
2819  * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2820  * @return boolean inidicating success
2821 */
2822  bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2823                   LDAPMessage *msg, const char *field,
2824                   struct security_descriptor **sd)
2825 {
2826         struct berval **values;
2827         bool ret = true;
2828
2829         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2830
2831         if (!values) return false;
2832
2833         if (values[0]) {
2834                 NTSTATUS status;
2835                 status = unmarshall_sec_desc(mem_ctx,
2836                                              (uint8_t *)values[0]->bv_val,
2837                                              values[0]->bv_len, sd);
2838                 if (!NT_STATUS_IS_OK(status)) {
2839                         DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2840                                   nt_errstr(status)));
2841                         ret = false;
2842                 }
2843         }
2844
2845         ldap_value_free_len(values);
2846         return ret;
2847 }
2848
2849 /* 
2850  * in order to support usernames longer than 21 characters we need to 
2851  * use both the sAMAccountName and the userPrincipalName attributes 
2852  * It seems that not all users have the userPrincipalName attribute set
2853  *
2854  * @param ads connection to ads server
2855  * @param mem_ctx TALLOC_CTX for allocating sid array
2856  * @param msg Results of search
2857  * @return the username
2858  */
2859  char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2860                          LDAPMessage *msg)
2861 {
2862 #if 0   /* JERRY */
2863         char *ret, *p;
2864
2865         /* lookup_name() only works on the sAMAccountName to 
2866            returning the username portion of userPrincipalName
2867            breaks winbindd_getpwnam() */
2868
2869         ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2870         if (ret && (p = strchr_m(ret, '@'))) {
2871                 *p = 0;
2872                 return ret;
2873         }
2874 #endif
2875         return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2876 }
2877
2878
2879 /**
2880  * find the update serial number - this is the core of the ldap cache
2881  * @param ads connection to ads server
2882  * @param ads connection to ADS server
2883  * @param usn Pointer to retrieved update serial number
2884  * @return status of search
2885  **/
2886 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
2887 {
2888         const char *attrs[] = {"highestCommittedUSN", NULL};
2889         ADS_STATUS status;
2890         LDAPMessage *res;
2891
2892         status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2893         if (!ADS_ERR_OK(status)) 
2894                 return status;
2895
2896         if (ads_count_replies(ads, res) != 1) {
2897                 ads_msgfree(ads, res);
2898                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2899         }
2900
2901         if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2902                 ads_msgfree(ads, res);
2903                 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2904         }
2905
2906         ads_msgfree(ads, res);
2907         return ADS_SUCCESS;
2908 }
2909
2910 /* parse a ADS timestring - typical string is
2911    '20020917091222.0Z0' which means 09:12.22 17th September
2912    2002, timezone 0 */
2913 static time_t ads_parse_time(const char *str)
2914 {
2915         struct tm tm;
2916
2917         ZERO_STRUCT(tm);
2918
2919         if (sscanf(str, "%4d%2d%2d%2d%2d%2d", 
2920                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
2921                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2922                 return 0;
2923         }
2924         tm.tm_year -= 1900;
2925         tm.tm_mon -= 1;
2926
2927         return timegm(&tm);
2928 }
2929
2930 /********************************************************************
2931 ********************************************************************/
2932
2933 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2934 {
2935         const char *attrs[] = {"currentTime", NULL};
2936         ADS_STATUS status;
2937         LDAPMessage *res;
2938         char *timestr;
2939         TALLOC_CTX *ctx;
2940         ADS_STRUCT *ads_s = ads;
2941
2942         if (!(ctx = talloc_init("ads_current_time"))) {
2943                 return ADS_ERROR(LDAP_NO_MEMORY);
2944         }
2945
2946         /* establish a new ldap tcp session if necessary */
2947
2948         if ( !ads->ldap.ld ) {
2949                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2950                         ads->server.ldap_server )) == NULL )
2951                 {
2952                         status = ADS_ERROR(LDAP_NO_MEMORY);
2953                         goto done;
2954                 }
2955                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2956                 status = ads_connect( ads_s );
2957                 if ( !ADS_ERR_OK(status))
2958                         goto done;
2959         }
2960
2961         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2962         if (!ADS_ERR_OK(status)) {
2963                 goto done;
2964         }
2965
2966         timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2967         if (!timestr) {
2968                 ads_msgfree(ads_s, res);
2969                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2970                 goto done;
2971         }
2972
2973         /* but save the time and offset in the original ADS_STRUCT */   
2974
2975         ads->config.current_time = ads_parse_time(timestr);
2976
2977         if (ads->config.current_time != 0) {
2978                 ads->auth.time_offset = ads->config.current_time - time(NULL);
2979                 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
2980         }
2981
2982         ads_msgfree(ads, res);
2983
2984         status = ADS_SUCCESS;
2985
2986 done:
2987         /* free any temporary ads connections */
2988         if ( ads_s != ads ) {
2989                 ads_destroy( &ads_s );
2990         }
2991         talloc_destroy(ctx);
2992
2993         return status;
2994 }
2995
2996 /********************************************************************
2997 ********************************************************************/
2998
2999 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3000 {
3001         const char *attrs[] = {"domainFunctionality", NULL};
3002         ADS_STATUS status;
3003         LDAPMessage *res;
3004         ADS_STRUCT *ads_s = ads;
3005
3006         *val = DS_DOMAIN_FUNCTION_2000;
3007
3008         /* establish a new ldap tcp session if necessary */
3009
3010         if ( !ads->ldap.ld ) {
3011                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
3012                         ads->server.ldap_server )) == NULL )
3013                 {
3014                         status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3015                         goto done;
3016                 }
3017                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3018                 status = ads_connect( ads_s );
3019                 if ( !ADS_ERR_OK(status))
3020                         goto done;
3021         }
3022
3023         /* If the attribute does not exist assume it is a Windows 2000 
3024            functional domain */
3025
3026         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3027         if (!ADS_ERR_OK(status)) {
3028                 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3029                         status = ADS_SUCCESS;
3030                 }
3031                 goto done;
3032         }
3033
3034         if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3035                 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3036         }
3037         DEBUG(3,("ads_domain_func_level: %d\n", *val));
3038
3039
3040         ads_msgfree(ads, res);
3041
3042 done:
3043         /* free any temporary ads connections */
3044         if ( ads_s != ads ) {
3045                 ads_destroy( &ads_s );
3046         }
3047
3048         return status;
3049 }
3050
3051 /**
3052  * find the domain sid for our domain
3053  * @param ads connection to ads server
3054  * @param sid Pointer to domain sid
3055  * @return status of search
3056  **/
3057 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3058 {
3059         const char *attrs[] = {"objectSid", NULL};
3060         LDAPMessage *res;
3061         ADS_STATUS rc;
3062
3063         rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
3064                            attrs, &res);
3065         if (!ADS_ERR_OK(rc)) return rc;
3066         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3067                 ads_msgfree(ads, res);
3068                 return ADS_ERROR_SYSTEM(ENOENT);
3069         }
3070         ads_msgfree(ads, res);
3071
3072         return ADS_SUCCESS;
3073 }
3074
3075 /**
3076  * find our site name 
3077  * @param ads connection to ads server
3078  * @param mem_ctx Pointer to talloc context
3079  * @param site_name Pointer to the sitename
3080  * @return status of search
3081  **/
3082 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3083 {
3084         ADS_STATUS status;
3085         LDAPMessage *res;
3086         const char *dn, *service_name;
3087         const char *attrs[] = { "dsServiceName", NULL };
3088
3089         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3090         if (!ADS_ERR_OK(status)) {
3091                 return status;
3092         }
3093
3094         service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3095         if (service_name == NULL) {
3096                 ads_msgfree(ads, res);
3097                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3098         }
3099
3100         ads_msgfree(ads, res);
3101
3102         /* go up three levels */
3103         dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3104         if (dn == NULL) {
3105                 return ADS_ERROR(LDAP_NO_MEMORY);
3106         }
3107
3108         *site_name = talloc_strdup(mem_ctx, dn);
3109         if (*site_name == NULL) {
3110                 return ADS_ERROR(LDAP_NO_MEMORY);
3111         }
3112
3113         return status;
3114         /*
3115         dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3116         */                                               
3117 }
3118
3119 /**
3120  * find the site dn where a machine resides
3121  * @param ads connection to ads server
3122  * @param mem_ctx Pointer to talloc context
3123  * @param computer_name name of the machine
3124  * @param site_name Pointer to the sitename
3125  * @return status of search
3126  **/
3127 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3128 {
3129         ADS_STATUS status;
3130         LDAPMessage *res;
3131         const char *parent, *filter;
3132         char *config_context = NULL;
3133         char *dn;
3134
3135         /* shortcut a query */
3136         if (strequal(computer_name, ads->config.ldap_server_name)) {
3137                 return ads_site_dn(ads, mem_ctx, site_dn);
3138         }
3139
3140         status = ads_config_path(ads, mem_ctx, &config_context);
3141         if (!ADS_ERR_OK(status)) {
3142                 return status;
3143         }
3144
3145         filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3146         if (filter == NULL) {
3147                 return ADS_ERROR(LDAP_NO_MEMORY);
3148         }
3149
3150         status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, 
3151                                filter, NULL, &res);
3152         if (!ADS_ERR_OK(status)) {
3153                 return status;
3154         }
3155
3156         if (ads_count_replies(ads, res) != 1) {
3157                 ads_msgfree(ads, res);
3158                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3159         }
3160
3161         dn = ads_get_dn(ads, mem_ctx, res);
3162         if (dn == NULL) {
3163                 ads_msgfree(ads, res);
3164                 return ADS_ERROR(LDAP_NO_MEMORY);
3165         }
3166
3167         /* go up three levels */
3168         parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3169         if (parent == NULL) {
3170                 ads_msgfree(ads, res);
3171                 TALLOC_FREE(dn);
3172                 return ADS_ERROR(LDAP_NO_MEMORY);
3173         }
3174
3175         *site_dn = talloc_strdup(mem_ctx, parent);
3176         if (*site_dn == NULL) {
3177                 ads_msgfree(ads, res);
3178                 TALLOC_FREE(dn);
3179                 return ADS_ERROR(LDAP_NO_MEMORY);
3180         }
3181
3182         TALLOC_FREE(dn);
3183         ads_msgfree(ads, res);
3184
3185         return status;
3186 }
3187
3188 /**
3189  * get the upn suffixes for a domain
3190  * @param ads connection to ads server
3191  * @param mem_ctx Pointer to talloc context
3192  * @param suffixes Pointer to an array of suffixes
3193  * @param num_suffixes Pointer to the number of suffixes
3194  * @return status of search
3195  **/
3196 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3197 {
3198         ADS_STATUS status;
3199         LDAPMessage *res;
3200         const char *base;
3201         char *config_context = NULL;
3202         const char *attrs[] = { "uPNSuffixes", NULL };
3203
3204         status = ads_config_path(ads, mem_ctx, &config_context);
3205         if (!ADS_ERR_OK(status)) {
3206                 return status;
3207         }
3208
3209         base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3210         if (base == NULL) {
3211                 return ADS_ERROR(LDAP_NO_MEMORY);
3212         }
3213
3214         status = ads_search_dn(ads, &res, base, attrs);
3215         if (!ADS_ERR_OK(status)) {
3216                 return status;
3217         }
3218
3219         if (ads_count_replies(ads, res) != 1) {
3220                 ads_msgfree(ads, res);
3221                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3222         }
3223
3224         (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3225         if ((*suffixes) == NULL) {
3226                 ads_msgfree(ads, res);
3227                 return ADS_ERROR(LDAP_NO_MEMORY);
3228         }
3229
3230         ads_msgfree(ads, res);
3231
3232         return status;
3233 }
3234
3235 /**
3236  * get the joinable ous for a domain
3237  * @param ads connection to ads server
3238  * @param mem_ctx Pointer to talloc context
3239  * @param ous Pointer to an array of ous
3240  * @param num_ous Pointer to the number of ous
3241  * @return status of search
3242  **/
3243 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3244                                 TALLOC_CTX *mem_ctx,
3245                                 char ***ous,
3246                                 size_t *num_ous)
3247 {
3248         ADS_STATUS status;
3249         LDAPMessage *res = NULL;
3250         LDAPMessage *msg = NULL;
3251         const char *attrs[] = { "dn", NULL };
3252         int count = 0;
3253
3254         status = ads_search(ads, &res,
3255                             "(|(objectClass=domain)(objectclass=organizationalUnit))",
3256                             attrs);
3257         if (!ADS_ERR_OK(status)) {
3258                 return status;
3259         }
3260
3261         count = ads_count_replies(ads, res);
3262         if (count < 1) {
3263                 ads_msgfree(ads, res);
3264                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3265         }
3266
3267         for (msg = ads_first_entry(ads, res); msg;
3268              msg = ads_next_entry(ads, msg)) {
3269                 const char **p = discard_const_p(const char *, *ous);
3270                 char *dn = NULL;
3271
3272                 dn = ads_get_dn(ads, talloc_tos(), msg);
3273                 if (!dn) {
3274                         ads_msgfree(ads, res);
3275                         return ADS_ERROR(LDAP_NO_MEMORY);
3276                 }
3277
3278                 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3279                         TALLOC_FREE(dn);
3280                         ads_msgfree(ads, res);
3281                         return ADS_ERROR(LDAP_NO_MEMORY);
3282                 }
3283
3284                 TALLOC_FREE(dn);
3285                 *ous = discard_const_p(char *, p);
3286         }
3287
3288         ads_msgfree(ads, res);
3289
3290         return status;
3291 }
3292
3293
3294 /**
3295  * pull a struct dom_sid from an extended dn string
3296  * @param mem_ctx TALLOC_CTX
3297  * @param extended_dn string
3298  * @param flags string type of extended_dn
3299  * @param sid pointer to a struct dom_sid
3300  * @return NT_STATUS_OK on success,
3301  *         NT_INVALID_PARAMETER on error,
3302  *         NT_STATUS_NOT_FOUND if no SID present
3303  **/
3304 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3305                                         const char *extended_dn,
3306                                         enum ads_extended_dn_flags flags,
3307                                         struct dom_sid *sid)
3308 {
3309         char *p, *q, *dn;
3310
3311         if (!extended_dn) {
3312                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3313         }
3314
3315         /* otherwise extended_dn gets stripped off */
3316         if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3317                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3318         }
3319         /*
3320          * ADS_EXTENDED_DN_HEX_STRING:
3321          * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3322          *
3323          * ADS_EXTENDED_DN_STRING (only with w2k3):
3324          * <GUID=63198e23-39cb-4b0f-b032-ba0105525a29>;<SID=S-1-5-21-4257769659-666132843-1169174103-1110>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3325          *
3326          * Object with no SID, such as an Exchange Public Folder
3327          * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3328          */
3329
3330         p = strchr(dn, ';');
3331         if (!p) {
3332                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3333         }
3334
3335         if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3336                 DEBUG(5,("No SID present in extended dn\n"));
3337                 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3338         }
3339
3340         p += strlen(";<SID=");
3341
3342         q = strchr(p, '>');
3343         if (!q) {
3344                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3345         }
3346
3347         *q = '\0';
3348
3349         DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3350
3351         switch (flags) {
3352
3353         case ADS_EXTENDED_DN_STRING:
3354                 if (!string_to_sid(sid, p)) {
3355                         return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3356                 }
3357                 break;
3358         case ADS_EXTENDED_DN_HEX_STRING: {
3359                 fstring buf;
3360                 size_t buf_len;
3361
3362                 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3363                 if (buf_len == 0) {
3364                         return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3365                 }
3366
3367                 if (!sid_parse((const uint8_t *)buf, buf_len, sid)) {
3368                         DEBUG(10,("failed to parse sid\n"));
3369                         return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3370                 }
3371                 break;
3372                 }
3373         default:
3374                 DEBUG(10,("unknown extended dn format\n"));
3375                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3376         }
3377
3378         return ADS_ERROR_NT(NT_STATUS_OK);
3379 }
3380
3381 /********************************************************************
3382 ********************************************************************/
3383
3384 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3385 {
3386         LDAPMessage *res = NULL;
3387         ADS_STATUS status;
3388         int count = 0;
3389         char *name = NULL;
3390
3391         status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3392         if (!ADS_ERR_OK(status)) {
3393                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3394                         lp_netbios_name()));
3395                 goto out;
3396         }
3397
3398         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3399                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3400                 goto out;
3401         }
3402
3403         if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3404                 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3405         }
3406
3407 out:
3408         ads_msgfree(ads, res);
3409
3410         return name;
3411 }
3412
3413 /********************************************************************
3414 ********************************************************************/
3415
3416 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3417 {
3418         LDAPMessage *res = NULL;
3419         ADS_STATUS status;
3420         int count = 0;
3421         char *name = NULL;
3422
3423         status = ads_find_machine_acct(ads, &res, machine_name);
3424         if (!ADS_ERR_OK(status)) {
3425                 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3426                         lp_netbios_name()));
3427                 goto out;
3428         }
3429
3430         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3431                 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3432                 goto out;
3433         }
3434
3435         if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3436                 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3437         }
3438
3439 out:
3440         ads_msgfree(ads, res);
3441
3442         return name;
3443 }
3444
3445 /********************************************************************
3446 ********************************************************************/
3447
3448 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3449 {
3450         LDAPMessage *res = NULL;
3451         ADS_STATUS status;
3452         int count = 0;
3453         char *name = NULL;
3454
3455         status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3456         if (!ADS_ERR_OK(status)) {
3457                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3458                         lp_netbios_name()));
3459                 goto out;
3460         }
3461
3462         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3463                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3464                 goto out;
3465         }
3466
3467         if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3468                 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3469         }
3470
3471 out:
3472         ads_msgfree(ads, res);
3473
3474         return name;
3475 }
3476
3477 #if 0
3478
3479    SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3480
3481 /**
3482  * Join a machine to a realm
3483  *  Creates the machine account and sets the machine password
3484  * @param ads connection to ads server
3485  * @param machine name of host to add
3486  * @param org_unit Organizational unit to place machine in
3487  * @return status of join
3488  **/
3489 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3490                         uint32_t account_type, const char *org_unit)
3491 {
3492         ADS_STATUS status;
3493         LDAPMessage *res = NULL;
3494         char *machine;
3495
3496         /* machine name must be lowercase */
3497         machine = SMB_STRDUP(machine_name);
3498         strlower_m(machine);
3499
3500         /*
3501         status = ads_find_machine_acct(ads, (void **)&res, machine);
3502         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3503                 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3504                 status = ads_leave_realm(ads, machine);
3505                 if (!ADS_ERR_OK(status)) {
3506                         DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3507                                 machine, ads->config.realm));
3508                         return status;
3509                 }
3510         }
3511         */
3512         status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3513         if (!ADS_ERR_OK(status)) {
3514                 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3515                 SAFE_FREE(machine);
3516                 return status;
3517         }
3518
3519         status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3520         if (!ADS_ERR_OK(status)) {
3521                 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3522                 SAFE_FREE(machine);
3523                 return status;
3524         }
3525
3526         SAFE_FREE(machine);
3527         ads_msgfree(ads, res);
3528
3529         return status;
3530 }
3531 #endif
3532
3533 /**
3534  * Delete a machine from the realm
3535  * @param ads connection to ads server
3536  * @param hostname Machine to remove
3537  * @return status of delete
3538  **/
3539 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3540 {
3541         ADS_STATUS status;
3542         void *msg;
3543         LDAPMessage *res;
3544         char *hostnameDN, *host;
3545         int rc;
3546         LDAPControl ldap_control;
3547         LDAPControl  * pldap_control[2] = {NULL, NULL};
3548
3549         pldap_control[0] = &ldap_control;
3550         memset(&ldap_control, 0, sizeof(LDAPControl));
3551         ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3552
3553         /* hostname must be lowercase */
3554         host = SMB_STRDUP(hostname);
3555         if (!strlower_m(host)) {
3556                 SAFE_FREE(host);
3557                 return ADS_ERROR_SYSTEM(EINVAL);
3558         }
3559
3560         status = ads_find_machine_acct(ads, &res, host);
3561         if (!ADS_ERR_OK(status)) {
3562                 DEBUG(0, ("Host account for %s does not exist.\n", host));
3563                 SAFE_FREE(host);
3564                 return status;
3565         }
3566
3567         msg = ads_first_entry(ads, res);
3568         if (!msg) {
3569                 SAFE_FREE(host);
3570                 return ADS_ERROR_SYSTEM(ENOENT);
3571         }
3572
3573         hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3574         if (hostnameDN == NULL) {
3575                 SAFE_FREE(host);
3576                 return ADS_ERROR_SYSTEM(ENOENT);
3577         }
3578
3579         rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3580         if (rc) {
3581                 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3582         }else {
3583                 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3584         }
3585
3586         if (rc != LDAP_SUCCESS) {
3587                 const char *attrs[] = { "cn", NULL };
3588                 LDAPMessage *msg_sub;
3589
3590                 /* we only search with scope ONE, we do not expect any further
3591                  * objects to be created deeper */
3592
3593                 status = ads_do_search_retry(ads, hostnameDN,
3594                                              LDAP_SCOPE_ONELEVEL,
3595                                              "(objectclass=*)", attrs, &res);
3596
3597                 if (!ADS_ERR_OK(status)) {
3598                         SAFE_FREE(host);
3599                         TALLOC_FREE(hostnameDN);
3600                         return status;
3601                 }
3602
3603                 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3604                         msg_sub = ads_next_entry(ads, msg_sub)) {
3605
3606                         char *dn = NULL;
3607
3608                         if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3609                                 SAFE_FREE(host);
3610                                 TALLOC_FREE(hostnameDN);
3611                                 return ADS_ERROR(LDAP_NO_MEMORY);
3612                         }
3613
3614                         status = ads_del_dn(ads, dn);
3615                         if (!ADS_ERR_OK(status)) {
3616                                 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3617                                 SAFE_FREE(host);
3618                                 TALLOC_FREE(dn);
3619                                 TALLOC_FREE(hostnameDN);
3620                                 return status;
3621                         }
3622
3623                         TALLOC_FREE(dn);
3624                 }
3625
3626                 /* there should be no subordinate objects anymore */
3627                 status = ads_do_search_retry(ads, hostnameDN,
3628                                              LDAP_SCOPE_ONELEVEL,
3629                                              "(objectclass=*)", attrs, &res);
3630
3631                 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3632                         SAFE_FREE(host);
3633                         TALLOC_FREE(hostnameDN);
3634                         return status;
3635                 }
3636
3637                 /* delete hostnameDN now */
3638                 status = ads_del_dn(ads, hostnameDN);
3639                 if (!ADS_ERR_OK(status)) {
3640                         SAFE_FREE(host);
3641                         DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3642                         TALLOC_FREE(hostnameDN);
3643                         return status;
3644                 }
3645         }
3646
3647         TALLOC_FREE(hostnameDN);
3648
3649         status = ads_find_machine_acct(ads, &res, host);
3650         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3651                 DEBUG(3, ("Failed to remove host account.\n"));
3652                 SAFE_FREE(host);
3653                 return status;
3654         }
3655
3656         SAFE_FREE(host);
3657         return status;
3658 }
3659
3660 /**
3661  * pull all token-sids from an LDAP dn
3662  * @param ads connection to ads server
3663  * @param mem_ctx TALLOC_CTX for allocating sid array
3664  * @param dn of LDAP object
3665  * @param user_sid pointer to struct dom_sid (objectSid)
3666  * @param primary_group_sid pointer to struct dom_sid (self composed)
3667  * @param sids pointer to sid array to allocate
3668  * @param num_sids counter of SIDs pulled
3669  * @return status of token query
3670  **/
3671  ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3672                               TALLOC_CTX *mem_ctx,
3673                               const char *dn,
3674                               struct dom_sid *user_sid,
3675                               struct dom_sid *primary_group_sid,
3676                               struct dom_sid **sids,
3677                               size_t *num_sids)
3678 {
3679         ADS_STATUS status;
3680         LDAPMessage *res = NULL;
3681         int count = 0;
3682         size_t tmp_num_sids;
3683         struct dom_sid *tmp_sids;
3684         struct dom_sid tmp_user_sid;
3685         struct dom_sid tmp_primary_group_sid;
3686         uint32_t pgid;
3687         const char *attrs[] = {
3688                 "objectSid",
3689                 "tokenGroups",
3690                 "primaryGroupID",
3691                 NULL
3692         };
3693
3694         status = ads_search_retry_dn(ads, &res, dn, attrs);
3695         if (!ADS_ERR_OK(status)) {
3696                 return status;
3697         }
3698
3699         count = ads_count_replies(ads, res);
3700         if (count != 1) {
3701                 ads_msgfree(ads, res);
3702                 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3703         }
3704
3705         if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3706                 ads_msgfree(ads, res);
3707                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3708         }
3709
3710         if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3711                 ads_msgfree(ads, res);
3712                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3713         }
3714
3715         {
3716                 /* hack to compose the primary group sid without knowing the
3717                  * domsid */
3718
3719                 struct dom_sid domsid;
3720
3721                 sid_copy(&domsid, &tmp_user_sid);
3722
3723                 if (!sid_split_rid(&domsid, NULL)) {
3724                         ads_msgfree(ads, res);
3725                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3726                 }
3727
3728                 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3729                         ads_msgfree(ads, res);
3730                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3731                 }
3732         }
3733
3734         tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3735
3736         if (tmp_num_sids == 0 || !tmp_sids) {
3737                 ads_msgfree(ads, res);
3738                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3739         }
3740
3741         if (num_sids) {
3742                 *num_sids = tmp_num_sids;
3743         }
3744
3745         if (sids) {
3746                 *sids = tmp_sids;
3747         }
3748
3749         if (user_sid) {
3750                 *user_sid = tmp_user_sid;
3751         }
3752
3753         if (primary_group_sid) {
3754                 *primary_group_sid = tmp_primary_group_sid;
3755         }
3756
3757         DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3758
3759         ads_msgfree(ads, res);
3760         return ADS_ERROR_LDAP(LDAP_SUCCESS);
3761 }
3762
3763 /**
3764  * Find a sAMAccoutName in LDAP
3765  * @param ads connection to ads server
3766  * @param mem_ctx TALLOC_CTX for allocating sid array
3767  * @param samaccountname to search
3768  * @param uac_ret uint32_t pointer userAccountControl attribute value
3769  * @param dn_ret pointer to dn
3770  * @return status of token query
3771  **/
3772 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3773                                TALLOC_CTX *mem_ctx,
3774                                const char *samaccountname,
3775                                uint32_t *uac_ret,
3776                                const char **dn_ret)
3777 {
3778         ADS_STATUS status;
3779         const char *attrs[] = { "userAccountControl", NULL };
3780         const char *filter;
3781         LDAPMessage *res = NULL;
3782         char *dn = NULL;
3783         uint32_t uac = 0;
3784
3785         filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3786                 samaccountname);
3787         if (filter == NULL) {
3788                 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3789                 goto out;
3790         }
3791
3792         status = ads_do_search_all(ads, ads->config.bind_path,
3793                                    LDAP_SCOPE_SUBTREE,
3794                                    filter, attrs, &res);
3795
3796         if (!ADS_ERR_OK(status)) {
3797                 goto out;
3798         }
3799
3800         if (ads_count_replies(ads, res) != 1) {
3801                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3802                 goto out;
3803         }
3804
3805         dn = ads_get_dn(ads, talloc_tos(), res);
3806         if (dn == NULL) {
3807                 status = ADS_ERROR(LDAP_NO_MEMORY);
3808                 goto out;
3809         }
3810
3811         if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3812                 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3813                 goto out;
3814         }
3815
3816         if (uac_ret) {
3817                 *uac_ret = uac;
3818         }
3819
3820         if (dn_ret) {
3821                 *dn_ret = talloc_strdup(mem_ctx, dn);
3822                 if (!*dn_ret) {
3823                         status = ADS_ERROR(LDAP_NO_MEMORY);
3824                         goto out;
3825                 }
3826         }
3827  out:
3828         TALLOC_FREE(dn);
3829         ads_msgfree(ads, res);
3830
3831         return status;
3832 }
3833
3834 /**
3835  * find our configuration path 
3836  * @param ads connection to ads server
3837  * @param mem_ctx Pointer to talloc context
3838  * @param config_path Pointer to the config path
3839  * @return status of search
3840  **/
3841 ADS_STATUS ads_config_path(ADS_STRUCT *ads, 
3842                            TALLOC_CTX *mem_ctx, 
3843                            char **config_path)
3844 {
3845         ADS_STATUS status;
3846         LDAPMessage *res = NULL;
3847         const char *config_context = NULL;
3848         const char *attrs[] = { "configurationNamingContext", NULL };
3849
3850         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, 
3851                                "(objectclass=*)", attrs, &res);
3852         if (!ADS_ERR_OK(status)) {
3853                 return status;
3854         }
3855
3856         config_context = ads_pull_string(ads, mem_ctx, res, 
3857                                          "configurationNamingContext");
3858         ads_msgfree(ads, res);
3859         if (!config_context) {
3860                 return ADS_ERROR(LDAP_NO_MEMORY);
3861         }
3862
3863         if (config_path) {
3864                 *config_path = talloc_strdup(mem_ctx, config_context);
3865                 if (!*config_path) {
3866                         return ADS_ERROR(LDAP_NO_MEMORY);
3867                 }
3868         }
3869
3870         return ADS_ERROR(LDAP_SUCCESS);
3871 }
3872
3873 /**
3874  * find the displayName of an extended right 
3875  * @param ads connection to ads server
3876  * @param config_path The config path
3877  * @param mem_ctx Pointer to talloc context
3878  * @param GUID struct of the rightsGUID
3879  * @return status of search
3880  **/
3881 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads, 
3882                                                 const char *config_path, 
3883                                                 TALLOC_CTX *mem_ctx, 
3884                                                 const struct GUID *rights_guid)
3885 {
3886         ADS_STATUS rc;
3887         LDAPMessage *res = NULL;
3888         char *expr = NULL;
3889         const char *attrs[] = { "displayName", NULL };
3890         const char *result = NULL;
3891         const char *path;
3892
3893         if (!ads || !mem_ctx || !rights_guid) {
3894                 goto done;
3895         }
3896
3897         expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)", 
3898                                GUID_string(mem_ctx, rights_guid));
3899         if (!expr) {
3900                 goto done;
3901         }
3902
3903         path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3904         if (!path) {
3905                 goto done;
3906         }
3907
3908         rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE, 
3909                                  expr, attrs, &res);
3910         if (!ADS_ERR_OK(rc)) {
3911                 goto done;
3912         }
3913
3914         if (ads_count_replies(ads, res) != 1) {
3915                 goto done;
3916         }
3917
3918         result = ads_pull_string(ads, mem_ctx, res, "displayName");
3919
3920  done:
3921         ads_msgfree(ads, res);
3922         return result;
3923 }
3924
3925 /**
3926  * verify or build and verify an account ou
3927  * @param mem_ctx Pointer to talloc context
3928  * @param ads connection to ads server
3929  * @param account_ou
3930  * @return status of search
3931  **/
3932
3933 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3934                            ADS_STRUCT *ads,
3935                            const char **account_ou)
3936 {
3937         char **exploded_dn;
3938         const char *name;
3939         char *ou_string;
3940
3941         if (account_ou == NULL) {
3942                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3943         }
3944
3945         if (*account_ou != NULL) {
3946                 exploded_dn = ldap_explode_dn(*account_ou, 0);
3947                 if (exploded_dn) {
3948                         ldap_value_free(exploded_dn);
3949                         return ADS_SUCCESS;
3950                 }
3951         }
3952
3953         ou_string = ads_ou_string(ads, *account_ou);
3954         if (!ou_string) {
3955                 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3956         }
3957
3958         name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3959                                ads->config.bind_path);
3960         SAFE_FREE(ou_string);
3961
3962         if (!name) {
3963                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3964         }
3965
3966         exploded_dn = ldap_explode_dn(name, 0);
3967         if (!exploded_dn) {
3968                 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3969         }
3970         ldap_value_free(exploded_dn);
3971
3972         *account_ou = name;
3973         return ADS_SUCCESS;
3974 }
3975
3976 #endif