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