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