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

Generated by: LCOV version 1.14