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