Пример #1
0
static void SetupOpenSSLThreadLocks(void)
{
    const int num_locks = CRYPTO_num_locks();
    assert(cf_openssl_locks == NULL);
    cf_openssl_locks = xmalloc(num_locks * sizeof(*cf_openssl_locks));

    for (int i = 0; i < num_locks; i++)
    {
        pthread_mutexattr_t attr;
        pthread_mutexattr_init(&attr);
        int ret = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
        if (ret != 0)
        {
            Log(LOG_LEVEL_ERR,
                "Failed to use error-checking mutexes for openssl,"
                " falling back to normal ones (pthread_mutexattr_settype: %s)",
                GetErrorStrFromCode(ret));
            pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
        }
        ret = pthread_mutex_init(&cf_openssl_locks[i], &attr);
        if (ret != 0)
        {
            Log(LOG_LEVEL_CRIT,
                "Failed to use initialise mutexes for openssl"
                " (pthread_mutex_init: %s)!",
                GetErrorStrFromCode(ret));
        }
        pthread_mutexattr_destroy(&attr);
    }

#ifndef __MINGW32__
    CRYPTO_set_id_callback((unsigned long (*)())ThreadId_callback);
#endif
    CRYPTO_set_locking_callback((void (*)())OpenSSLLock_callback);
}
Пример #2
0
static void OpenSSLLock_callback(int mode, int index, char *file, int line)
{
    if (mode & CRYPTO_LOCK)
    {
        int ret = pthread_mutex_lock(&(cf_openssl_locks[index]));
        if (ret != 0)
        {
            Log(LOG_LEVEL_ERR,
                "OpenSSL locking failure at %s:%d! (pthread_mutex_lock: %s)",
                file, line, GetErrorStrFromCode(ret));
        }
    }
    else
    {
        int ret = pthread_mutex_unlock(&(cf_openssl_locks[index]));
        if (ret != 0)
        {
            Log(LOG_LEVEL_ERR,
                "OpenSSL locking failure at %s:%d! (pthread_mutex_unlock: %s)",
                file, line, GetErrorStrFromCode(ret));
        }
    }
}
Пример #3
0
int cf_closesocket(int sd)
{
    int res;

#ifdef __MINGW32__
    res = closesocket(sd);
    if (res == SOCKET_ERROR)
    {
        Log(LOG_LEVEL_VERBOSE,
            "Failed to close socket (closesocket: %s)",
            GetErrorStrFromCode(WSAGetLastError()));
    }
#else
    res = close(sd);
    if (res == -1)
    {
        Log(LOG_LEVEL_VERBOSE,
            "Failed to close socket (close: %s)",
            GetErrorStr());
    }
#endif

    return res;
}
Пример #4
0
/**
 * Generic *at function.
 * @param dirfd File descriptor pointing to directory to do lookup in.
 *              AT_FDCWD constant means to look in current directory.
 * @param func Function to call while in the directory.
 * @param cleanup Function to call if we need to clean up because of a failed call.
 * @param data Private data for the supplied functions.
 */
int generic_at_function(int dirfd, int (*func)(void *data), void (*cleanup)(void *data), void *data)
{
    int cwd;
    int mutex_err;
    int saved_errno;
    int fchdir_ret;

    mutex_err = pthread_mutex_lock(&CHDIR_LOCK);
    if (mutex_err)
    {
        UnexpectedError("Error when locking CHDIR_LOCK. Should never happen. (pthread_mutex_lock: '%s')",
                        GetErrorStrFromCode(mutex_err));
    }

    if (dirfd != AT_FDCWD)
    {
        cwd = open(".", O_RDONLY);
        if (cwd < 0)
        {
            mutex_err = pthread_mutex_unlock(&CHDIR_LOCK);
            if (mutex_err)
            {
                UnexpectedError("Error when unlocking CHDIR_LOCK. Should never happen. (pthread_mutex_unlock: '%s')",
                                GetErrorStrFromCode(mutex_err));
            }
            return -1;
        }

        if (fchdir(dirfd) < 0)
        {
            close(cwd);

            mutex_err = pthread_mutex_unlock(&CHDIR_LOCK);
            if (mutex_err)
            {
                UnexpectedError("Error when unlocking CHDIR_LOCK. Should never happen. (pthread_mutex_unlock: '%s')",
                                GetErrorStrFromCode(mutex_err));
            }

            return -1;
        }
    }

    int result = func(data);
    saved_errno = errno;

    if (dirfd != AT_FDCWD)
    {
        fchdir_ret = fchdir(cwd);
        close(cwd);
    }

    mutex_err = pthread_mutex_unlock(&CHDIR_LOCK);
    if (mutex_err)
    {
        UnexpectedError("Error when unlocking CHDIR_LOCK. Should never happen. (pthread_mutex_unlock: '%s')",
                        GetErrorStrFromCode(mutex_err));
    }

    if (dirfd != AT_FDCWD)
    {
        if (fchdir_ret < 0)
        {
            cleanup(data);
            Log(LOG_LEVEL_WARNING, "Could not return to original working directory in '%s'. "
                "Things may not behave as expected. (fchdir: '%s')", __FUNCTION__, GetErrorStr());
            return -1;
        }
    }

    errno = saved_errno;

    return result;
}
Пример #5
0
/**
   Tries to connect() to server #host, returns the socket descriptor and the
   IP address that succeeded in #txtaddr.

   @param #connect_timeout how long to wait for connect(), zero blocks forever
   @param #txtaddr If connected successfully return the IP connected in
                   textual representation
   @return Connected socket descriptor or -1 in case of failure.
*/
int SocketConnect(const char *host, const char *port,
                  unsigned int connect_timeout, bool force_ipv4,
                  char *txtaddr, size_t txtaddr_size)
{
    struct addrinfo *response = NULL, *ap;
    bool connected = false;
    int sd = -1;

    struct addrinfo query = {
        .ai_family = force_ipv4 ? AF_INET : AF_UNSPEC,
        .ai_socktype = SOCK_STREAM
    };

    int ret = getaddrinfo(host, port, &query, &response);
    if (ret != 0)
    {
        Log(LOG_LEVEL_INFO,
              "Unable to find host '%s' service '%s' (%s)",
              host, port, gai_strerror(ret));
        if (response != NULL)
        {
            freeaddrinfo(response);
        }
        return -1;
    }

    for (ap = response; !connected && ap != NULL; ap = ap->ai_next)
    {
        /* Convert address to string. */
        getnameinfo(ap->ai_addr, ap->ai_addrlen,
                    txtaddr, txtaddr_size,
                    NULL, 0, NI_NUMERICHOST);
        Log(LOG_LEVEL_VERBOSE,
            "Connecting to host %s, port %s as address %s",
            host, port, txtaddr);

        sd = socket(ap->ai_family, ap->ai_socktype, ap->ai_protocol);
        if (sd == -1)
        {
            Log(LOG_LEVEL_ERR, "Couldn't open a socket to '%s' (socket: %s)",
                txtaddr, GetErrorStr());
        }
        else
        {
            /* Bind socket to specific interface, if requested. */
            if (BINDINTERFACE[0] != '\0')
            {
                struct addrinfo query2 = {
                    .ai_family = force_ipv4 ? AF_INET : AF_UNSPEC,
                    .ai_socktype = SOCK_STREAM,
                    /* returned address is for bind() */
                    .ai_flags = AI_PASSIVE
                };

                struct addrinfo *response2 = NULL, *ap2;
                int ret2 = getaddrinfo(BINDINTERFACE, NULL, &query2, &response2);
                if (ret2 != 0)
                {
                    Log(LOG_LEVEL_ERR,
                        "Unable to lookup interface '%s' to bind. (getaddrinfo: %s)",
                        BINDINTERFACE, gai_strerror(ret2));

                    if (response2 != NULL)
                    {
                        freeaddrinfo(response2);
                    }
                    assert(response);   /* first getaddrinfo was successful */
                    freeaddrinfo(response);
                    cf_closesocket(sd);
                    return -1;
                }

                for (ap2 = response2; ap2 != NULL; ap2 = ap2->ai_next)
                {
                    if (bind(sd, ap2->ai_addr, ap2->ai_addrlen) == 0)
                    {
                        break;
                    }
                }
                if (ap2 == NULL)
                {
                    Log(LOG_LEVEL_ERR,
                        "Unable to bind to interface '%s'. (bind: %s)",
                        BINDINTERFACE, GetErrorStr());
                }
                assert(response2);     /* second getaddrinfo was successful */
                freeaddrinfo(response2);
            }

            connected = TryConnect(sd, connect_timeout * 1000,
                                   ap->ai_addr, ap->ai_addrlen);
            if (!connected)
            {
                Log(LOG_LEVEL_VERBOSE, "Unable to connect to address %s (%s)",
                    txtaddr, GetErrorStr());
                cf_closesocket(sd);
                sd = -1;
            }
        }
    }

    assert(response != NULL);           /* first getaddrinfo was successful */
    freeaddrinfo(response);

    if (connected)
    {
        Log(LOG_LEVEL_VERBOSE,
            "Connected to host %s address %s port %s",
            host, txtaddr, port);
    }
    else
    {
        Log(LOG_LEVEL_VERBOSE,
            "Unable to connect to host %s port %s",
            host, port);
    }

    return sd;
}


#if !defined(__MINGW32__)

#if defined(__hpux) && defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
// HP-UX GCC type-pun warning on FD_SET() macro:
// While the "fd_set" type is defined in /usr/include/sys/_fd_macros.h as a
// struct of an array of "long" values in accordance with the XPG4 standard's
// requirements, the macros for the FD operations "pretend it is an array of
// int32_t's so the binary layout is the same for both Narrow and Wide
// processes," as described in _fd_macros.h. In the FD_SET, FD_CLR, and
// FD_ISSET macros at line 101, the result is cast to an "__fd_mask *" type,
// which is defined as int32_t at _fd_macros.h:82.
//
// This conflict between the "long fds_bits[]" array in the XPG4-compliant
// fd_set structure, and the cast to an int32_t - not long - pointer in the
// macros, causes a type-pun warning if -Wstrict-aliasing is enabled.
// The warning is merely a side effect of HP-UX working as designed,
// so it can be ignored.
#endif

/**
 * Tries to connect for #timeout_ms milliseconds. On success sets the recv()
 * timeout to #timeout_ms as well.
 *
 * @param #timeout_ms How long to wait for connect(), if zero wait forever.
 * @return true on success, false otherwise.
 **/
bool TryConnect(int sd, unsigned long timeout_ms,
                const struct sockaddr *sa, socklen_t sa_len)
{
    assert(sa != NULL);

    if (sd >= FD_SETSIZE)
    {
        Log(LOG_LEVEL_ERR, "Open connections exceed FD_SETSIZE limit of %d",
            FD_SETSIZE);
        return false;
    }

    /* set non-blocking socket */
    int arg = fcntl(sd, F_GETFL, NULL);
    int ret = fcntl(sd, F_SETFL, arg | O_NONBLOCK);
    if (ret == -1)
    {
        Log(LOG_LEVEL_ERR,
            "Failed to set socket to non-blocking mode (fcntl: %s)",
            GetErrorStr());
    }

    ret = connect(sd, sa, sa_len);
    if (ret == -1)
    {
        if (errno != EINPROGRESS)
        {
            Log(LOG_LEVEL_INFO, "Failed to connect to server (connect: %s)",
                GetErrorStr());
            return false;
        }

        int errcode;
        socklen_t opt_len = sizeof(errcode);
        fd_set myset;
        FD_ZERO(&myset);
        FD_SET(sd, &myset);

        Log(LOG_LEVEL_VERBOSE, "Waiting to connect...");

        struct timeval tv, *tvp;
        if (timeout_ms > 0)
        {
            tv.tv_sec = timeout_ms / 1000;
            tv.tv_usec = (timeout_ms % 1000) * 1000;
            tvp = &tv;
        }
        else
        {
            tvp = NULL;                                /* wait indefinitely */
        }

        ret = select(sd + 1, NULL, &myset, NULL, tvp);
        if (ret == 0)
        {
            Log(LOG_LEVEL_INFO, "Timeout connecting to server");
            return false;
        }
        if (ret == -1)
        {
            if (errno == EINTR)
            {
                Log(LOG_LEVEL_ERR,
                    "Socket connect was interrupted by signal");
            }
            else
            {
                Log(LOG_LEVEL_ERR,
                    "Failure while connecting (select: %s)",
                    GetErrorStr());
            }
            return false;
        }

        ret = getsockopt(sd, SOL_SOCKET, SO_ERROR,
                              (void *) &errcode, &opt_len);
        if (ret == -1)
        {
            Log(LOG_LEVEL_ERR,
                "Could not check connection status (getsockopt: %s)",
                GetErrorStr());
            return false;
        }

        if (errcode != 0)
        {
            Log(LOG_LEVEL_INFO, "Failed to connect to server: %s",
                GetErrorStrFromCode(errcode));
            return false;
        }
    }

    /* Connection succeeded, return to blocking mode. */
    ret = fcntl(sd, F_SETFL, arg);
    if (ret == -1)
    {
        Log(LOG_LEVEL_ERR,
            "Failed to set socket back to blocking mode (fcntl: %s)",
            GetErrorStr());
    }

    if (timeout_ms > 0)
    {
        SetReceiveTimeout(sd, timeout_ms);
    }

    return true;
}

#if defined(__hpux) && defined(__GNUC__)
#pragma GCC diagnostic warning "-Wstrict-aliasing"
#endif

#endif /* !defined(__MINGW32__) */



/**
 * Set timeout for recv(), in milliseconds.
 * @param ms must be > 0.
 */
int SetReceiveTimeout(int fd, unsigned long ms)
{
    assert(ms > 0);

    Log(LOG_LEVEL_VERBOSE, "Setting socket timeout to %lu seconds.", ms/1000);

/* On windows SO_RCVTIMEO is set by a DWORD indicating the timeout in
 * milliseconds, on UNIX it's a struct timeval. */

#if !defined(__MINGW32__)
    struct timeval tv = {
        .tv_sec = ms / 1000,
        .tv_usec = (ms % 1000) * 1000
    };
    int ret = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
#else
    int ret = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &ms, sizeof(ms));
#endif

    if (ret != 0)
    {
        Log(LOG_LEVEL_INFO,
            "Failed to set socket timeout to %lu milliseconds.", ms);
        return -1;
    }

    return 0;
}
Пример #6
0
DBPriv *DBPrivOpenDB(const char *dbpath, dbid id)
{
    DBPriv *db = xcalloc(1, sizeof(DBPriv));
    MDB_txn *txn = NULL;
    int rc;

    rc = pthread_key_create(&db->txn_key, &DestroyTransaction);
    if (rc)
    {
        Log(LOG_LEVEL_ERR, "Could not create transaction key. (pthread_key_create: '%s')",
            GetErrorStrFromCode(rc));
        free(db);
        return NULL;
    }

    rc = mdb_env_create(&db->env);
    if (rc)
    {
        Log(LOG_LEVEL_ERR, "Could not create handle for database %s: %s",
              dbpath, mdb_strerror(rc));
        goto err;
    }
    rc = mdb_env_set_mapsize(db->env, LMDB_MAXSIZE);
    if (rc)
    {
        Log(LOG_LEVEL_ERR, "Could not set mapsize for database %s: %s",
              dbpath, mdb_strerror(rc));
        goto err;
    }
    if (DB_MAX_READERS > 0)
    {
        rc = mdb_env_set_maxreaders(db->env, DB_MAX_READERS);
        if (rc)
        {
            Log(LOG_LEVEL_ERR, "Could not set maxreaders for database %s: %s",
                dbpath, mdb_strerror(rc));
            goto err;
        }
    }

    unsigned int open_flags = MDB_NOSUBDIR;
    if (id == dbid_locks
        || (GetAmPolicyHub() && id == dbid_lastseen))
    {
        open_flags |= MDB_NOSYNC;
    }

#ifdef __hpux
    /*
     * On HP-UX, a unified file cache was not introduced until version 11.31.
     * This means that on 11.23 there are separate file caches for mmap()'ed
     * files and open()'ed files. When these two are mixed, changes made using
     * one mode won't be immediately seen by the other mode, which is an
     * assumption LMDB is relying on. The MDB_WRITEMAP flag causes LMDB to use
     * mmap() only, so that we stay within one file cache.
     */
    open_flags |= MDB_WRITEMAP;
#endif

    rc = mdb_env_open(db->env, dbpath, open_flags, 0644);
    if (rc)
    {
        Log(LOG_LEVEL_ERR, "Could not open database %s: %s",
              dbpath, mdb_strerror(rc));
        goto err;
    }
    if (DB_MAX_READERS > 0)
    {
        int max_readers;
        rc = mdb_env_get_maxreaders(db->env, &max_readers);
        if (rc)
        {
            Log(LOG_LEVEL_ERR, "Could not get maxreaders for database %s: %s",
                dbpath, mdb_strerror(rc));
            goto err;
        }
        if (max_readers < DB_MAX_READERS)
        {
            // LMDB will only reinitialize maxreaders if no database handles are
            // open, including in other processes, which is how we might end up
            // here.
            Log(LOG_LEVEL_VERBOSE, "Failed to set LMDB max reader limit on database '%s', "
                "consider restarting CFEngine",
                dbpath);
        }
    }
    rc = mdb_txn_begin(db->env, NULL, MDB_RDONLY, &txn);
    if (rc)
    {
        Log(LOG_LEVEL_ERR, "Could not open database txn %s: %s",
              dbpath, mdb_strerror(rc));
        goto err;
    }
    rc = mdb_open(txn, NULL, 0, &db->dbi);
    if (rc)
    {
        Log(LOG_LEVEL_ERR, "Could not open database dbi %s: %s",
              dbpath, mdb_strerror(rc));
        mdb_txn_abort(txn);
        goto err;
    }
    rc = mdb_txn_commit(txn);
    if (rc)
    {
        Log(LOG_LEVEL_ERR, "Could not commit database dbi %s: %s",
              dbpath, mdb_strerror(rc));
        goto err;
    }

    return db;

err:
    if (db->env)
    {
        mdb_env_close(db->env);
    }
    pthread_key_delete(db->txn_key);
    free(db);
    if (rc == MDB_INVALID)
    {
        return DB_PRIV_DATABASE_BROKEN;
    }
    return NULL;
}