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