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