LCOV - code coverage report
Current view: top level - source3/libads - ldap.c (source / functions) Hit Total Coverage
Test: coverage report for v4-17-test 1498b464 Lines: 747 1827 40.9 %
Date: 2024-06-13 04:01:37 Functions: 56 96 58.3 %

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

Generated by: LCOV version 1.13