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