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