static void CollectCallIfDue(EvalContext *ctx)
{
    /* Check whether we have established peering with a hub */
    if (CollectCallHasPending())
    {
        extern int COLLECT_WINDOW;
        int waiting_queue = 0;
        int new_client = CollectCallGetPending(&waiting_queue);
        assert(new_client >= 0);
        if (waiting_queue > COLLECT_WINDOW)
        {
            Log(LOG_LEVEL_INFO,
                "Abandoning collect call attempt with queue longer than collect_window [%d > %d]",
                waiting_queue, COLLECT_WINDOW);
            cf_closesocket(new_client);
            CollectCallMarkProcessed();
        }
        else
        {
            ConnectionInfo *info = ConnectionInfoNew();
            assert(info);

            ConnectionInfoSetSocket(info, new_client);
            info->is_call_collect = true; /* Mark processed when done. */
            ServerEntryPoint(ctx, PolicyServerGetIP(), info);
        }
    }
}
Exemple #2
0
/* Try to accept a connection; handle if we get one. */
static void CFTestD_AcceptAndHandle(int sd, CFTestD_Config *config)
{
    /* TODO embed ConnectionInfo into ServerConnectionState. */
    ConnectionInfo *info = ConnectionInfoNew(); /* Uses xcalloc() */

    info->ss_len = sizeof(info->ss);
    info->sd     = accept(sd, (struct sockaddr *)&info->ss, &info->ss_len);
    if (info->sd == -1)
    {
        Log(LOG_LEVEL_INFO, "Error accepting connection (%s)", GetErrorStr());
        ConnectionInfoDestroy(&info);
        return;
    }

    Log(LOG_LEVEL_DEBUG,
        "Socket descriptor returned from accept(): %d",
        info->sd);

    /* Just convert IP address to string, no DNS lookup. */
    char ipaddr[CF_MAX_IP_LEN] = "";
    getnameinfo(
        (const struct sockaddr *)&info->ss,
        info->ss_len,
        ipaddr,
        sizeof(ipaddr),
        NULL,
        0,
        NI_NUMERICHOST);

    /* IPv4 mapped addresses (e.g. "::ffff:192.168.1.2") are
     * hereby represented with their IPv4 counterpart. */
    CFTestD_SpawnConnection(MapAddress(ipaddr), info, config);
}
Exemple #3
0
AgentConnection *NewAgentConn(const char *server_name)
{
    AgentConnection *conn = xcalloc(1, sizeof(AgentConnection));
    ConnectionInfo *info = ConnectionInfoNew();
    conn->conn_info = info;
    conn->encryption_type = 'c';
    conn->this_server = xstrdup(server_name);
    conn->authenticated = false;
    return conn;
}
Exemple #4
0
AgentConnection *NewAgentConn(const char *server, const char *port,
                              ConnectionFlags flags)
{
    AgentConnection *conn = xcalloc(1, sizeof(AgentConnection));
    conn->conn_info = ConnectionInfoNew();
    conn->this_server = xstrdup(server);
    conn->this_port = (port == NULL) ? NULL : xstrdup(port);
    conn->flags = flags;
    conn->encryption_type = 'c';
    conn->authenticated = false;
    return conn;
}
/* Wait up to a minute for an in-coming connection.
 *
 * @param sd The listening socket or -1.
 * @retval > 0 In-coming connection.
 * @retval 0 No in-coming connection.
 * @retval -1 Error (other than interrupt).
 * @retval < -1 Interrupted while waiting.
 */
static int WaitForIncoming(int sd)
{
    Log(LOG_LEVEL_DEBUG, "Waiting at incoming select...");
    struct timeval timeout = { .tv_sec = 60 };
    int signal_pipe = GetSignalPipe();
    fd_set rset;
    FD_ZERO(&rset);
    FD_SET(signal_pipe, &rset);

    /* sd might be -1 if "listen" attribute in body server control is set
     * to off (enterprise feature for call-collected clients). */
    if (sd != -1)
    {
        FD_SET(sd, &rset);
    }

    int result = select(MAX(sd, signal_pipe) + 1,
                        &rset, NULL, NULL, &timeout);
    if (result == -1)
    {
        return (errno == EINTR) ? -2 : -1;
    }
    assert(result >= 0);

    /* Empty the signal pipe, it is there to only detect missed
     * signals in-between checking IsPendingTermination() and calling
     * select(). */
    unsigned char buf;
    while (recv(signal_pipe, &buf, 1, 0) > 0)
    {
        /* skip */
    }

    /* We have an incoming connection if select() marked sd as ready: */
    if (sd != -1 && result > 0 && FD_ISSET(sd, &rset))
    {
        return 1;
    }
    return 0;
}

/* Check for new policy just before spawning a thread.
 *
 * Server reconfiguration can only happen when no threads are active,
 * so this is a good time to do it; but we do still have to check for
 * running threads. */
static void PolicyUpdateIfSafe(EvalContext *ctx, Policy **policy,
                               GenericAgentConfig *config)
{
    if (ThreadLock(cft_server_children))
    {
        int prior = COLLECT_INTERVAL;
        if (ACTIVE_THREADS == 0)
        {
            CheckFileChanges(ctx, policy, config);
        }
        ThreadUnlock(cft_server_children);

        /* Check for change in call-collect interval: */
        if (prior != COLLECT_INTERVAL)
        {
            /* Start, stop or change schedule, as appropriate. */
            CollectCallStart(COLLECT_INTERVAL);
        }
    }
}

/* Try to accept a connection; handle if we get one. */
static void AcceptAndHandle(EvalContext *ctx, int sd)
{
    /* TODO embed ConnectionInfo into ServerConnectionState. */
    ConnectionInfo *info = ConnectionInfoNew(); /* Uses xcalloc() */

    info->ss_len = sizeof(info->ss);
    info->sd = accept(sd, (struct sockaddr *) &info->ss, &info->ss_len);
    if (info->sd == -1)
    {
        Log(LOG_LEVEL_INFO, "Error accepting connection (%s)", GetErrorStr());
        ConnectionInfoDestroy(&info);
        return;
    }

    Log(LOG_LEVEL_DEBUG, "Socket descriptor returned from accept(): %d",
        info->sd);

    /* Just convert IP address to string, no DNS lookup. */
    char ipaddr[CF_MAX_IP_LEN] = "";
    getnameinfo((const struct sockaddr *) &info->ss, info->ss_len,
                ipaddr, sizeof(ipaddr),
                NULL, 0, NI_NUMERICHOST);

    /* IPv4 mapped addresses (e.g. "::ffff:192.168.1.2") are
     * hereby represented with their IPv4 counterpart. */
    ServerEntryPoint(ctx, MapAddress(ipaddr), info);
}
Exemple #6
0
void StartServer(EvalContext *ctx, Policy **policy, GenericAgentConfig *config)
{
    int sd = -1;
    fd_set rset;
    int ret_val;
    CfLock thislock;
    time_t last_policy_reload = 0;
    extern int COLLECT_WINDOW;

    struct sockaddr_storage cin;
    socklen_t addrlen = sizeof(cin);

    MakeSignalPipe();

    signal(SIGINT, HandleSignalsForDaemon);
    signal(SIGTERM, HandleSignalsForDaemon);
    signal(SIGHUP, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);
    signal(SIGUSR1, HandleSignalsForDaemon);
    signal(SIGUSR2, HandleSignalsForDaemon);

    ServerTLSInitialize();

    sd = SetServerListenState(ctx, QUEUESIZE, SERVER_LISTEN, &InitServer);

    TransactionContext tc = {
        .ifelapsed = 0,
        .expireafter = 1,
    };

    Policy *server_cfengine_policy = PolicyNew();
    Promise *pp = NULL;
    {
        Bundle *bp = PolicyAppendBundle(server_cfengine_policy, NamespaceDefault(), "server_cfengine_bundle", "agent", NULL, NULL);
        PromiseType *tp = BundleAppendPromiseType(bp, "server_cfengine");

        pp = PromiseTypeAppendPromise(tp, config->input_file, (Rval) { NULL, RVAL_TYPE_NOPROMISEE }, NULL);
    }
    assert(pp);

    thislock = AcquireLock(ctx, pp->promiser, VUQNAME, CFSTARTTIME, tc, pp, false);

    if (thislock.lock == NULL)
    {
        PolicyDestroy(server_cfengine_policy);
        return;
    }

    if (sd != -1)
    {
        Log(LOG_LEVEL_VERBOSE, "Listening for connections ...");
    }

#ifdef __MINGW32__

    if (!NO_FORK)
    {
        Log(LOG_LEVEL_VERBOSE, "Windows does not support starting processes in the background - starting in foreground");
    }

#else /* !__MINGW32__ */

    if ((!NO_FORK) && (fork() != 0))
    {
        _exit(EXIT_SUCCESS);
    }

    if (!NO_FORK)
    {
        ActAsDaemon();
    }

#endif /* !__MINGW32__ */

    WritePID("cf-serverd.pid");

/* Andrew Stribblehill <*****@*****.**> -- close sd on exec */
#ifndef __MINGW32__
    fcntl(sd, F_SETFD, FD_CLOEXEC);
#endif
    CollectCallStart(COLLECT_INTERVAL);
    while (!IsPendingTermination())
    {
        /* Note that this loop logic is single threaded, but ACTIVE_THREADS
           might still change in threads pertaining to service handling */

        if (ThreadLock(cft_server_children))
        {
            if (ACTIVE_THREADS == 0)
            {
                CheckFileChanges(ctx, policy, config, &last_policy_reload);
            }
            ThreadUnlock(cft_server_children);
        }

        // Check whether we have established peering with a hub
        if (CollectCallHasPending())
        {
            int waiting_queue = 0;
            int new_client = CollectCallGetPending(&waiting_queue);
            if (waiting_queue > COLLECT_WINDOW)
            {
                Log(LOG_LEVEL_INFO, "Closing collect call because it would take"
                                    "longer than the allocated window [%d]", COLLECT_WINDOW);
            }
            ConnectionInfo *info = ConnectionInfoNew();
            if (info)
            {
                ConnectionInfoSetSocket(info, new_client);
                ServerEntryPoint(ctx, POLICY_SERVER, info);
                CollectCallMarkProcessed();
            }
        }
        else
        {
            /* check if listening is working */
            if (sd != -1)
            {
                // Look for normal incoming service requests
                int signal_pipe = GetSignalPipe();
                FD_ZERO(&rset);
                FD_SET(sd, &rset);
                FD_SET(signal_pipe, &rset);

                Log(LOG_LEVEL_DEBUG, "Waiting at incoming select...");
                struct timeval timeout = {
                    .tv_sec = 60,
                    .tv_usec = 0
                };
                int max_fd = (sd > signal_pipe) ? (sd + 1) : (signal_pipe + 1);
                ret_val = select(max_fd, &rset, NULL, NULL, &timeout);

                // Empty the signal pipe. We don't need the values.
                unsigned char buf;
                while (recv(signal_pipe, &buf, 1, 0) > 0) {}

                if (ret_val == -1)      /* Error received from call to select */
                {
                    if (errno == EINTR)
                    {
                        continue;
                    }
                    else
                    {
                        Log(LOG_LEVEL_ERR, "select failed. (select: %s)", GetErrorStr());
                        exit(1);
                    }
                }
                else if (!ret_val) /* No data waiting, we must have timed out! */
                {
                    continue;
                }

                if (FD_ISSET(sd, &rset))
                {
                    int new_client = accept(sd, (struct sockaddr *)&cin, &addrlen);
                    if (new_client == -1)
                    {
                        continue;
                    }
                    /* Just convert IP address to string, no DNS lookup. */
                    char ipaddr[CF_MAX_IP_LEN] = "";
                    getnameinfo((struct sockaddr *) &cin, addrlen,
                                ipaddr, sizeof(ipaddr),
                                NULL, 0, NI_NUMERICHOST);

                    ConnectionInfo *info = ConnectionInfoNew();
                    if (info)
                    {
                        ConnectionInfoSetSocket(info, new_client);
                        ServerEntryPoint(ctx, ipaddr, info);
                    }
                }
            }
        }
    }
    CollectCallStop();
    PolicyDestroy(server_cfengine_policy);
}

/*********************************************************************/
/* Level 2                                                           */
/*********************************************************************/

int InitServer(size_t queue_size)
{
    int sd = -1;

    if ((sd = OpenReceiverChannel()) == -1)
    {
        Log(LOG_LEVEL_ERR, "Unable to start server");
        exit(EXIT_FAILURE);
    }

    if (listen(sd, queue_size) == -1)
    {
        Log(LOG_LEVEL_ERR, "listen failed. (listen: %s)", GetErrorStr());
        exit(EXIT_FAILURE);
    }

    return sd;
}
Exemple #7
0
static void test_TLSVerifyPeer(void)
{
    ASSERT_IF_NOT_INITIALIZED;
    RESET_STATUS;

    SSL *ssl = NULL;
    ConnectionInfo *conn_info = NULL;

    /*
     * Open a socket and establish a tcp connection.
     */
    struct sockaddr_in server_addr;
    int server = 0;
    int result = 0;

    conn_info = ConnectionInfoNew();

    memset(&server_addr, 0, sizeof(struct sockaddr_in));
    server = socket(AF_INET, SOCK_STREAM, 0);
    assert_int_not_equal(-1, server);
    server_addr.sin_family = AF_INET;
    ConnectionInfoSetSocket(conn_info, server);
    /* We should not use inet_addr, but it is easier for this particular case. */
    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    server_addr.sin_port = htons(8035);
    /*
     * Connect
     */
    result = connect(server, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in));
    assert_int_not_equal(-1, result);
    /*
     * Create a SSL instance
     */
    ssl = SSL_new(SSLCLIENTCONTEXT);
    assert_true(ssl != NULL);
    SSL_set_fd(ssl, server);
    /*
     * Establish the TLS connection over the socket.
     */
    result = SSL_connect(ssl);
    assert_int_not_equal(-1, result);
    /*
     * Fill the remaining fields on ConnectionInfo
     */
    ConnectionInfoSetProtocolVersion(conn_info, CF_PROTOCOL_TLS);
    ConnectionInfoSetSSL(conn_info, ssl);
    /*
     * Fill in the structures we need for testing.
     */
    X509 *certificate = NULL;
    FILE *certificate_stream = fopen(server_certificate_template_public, "r");
    certificate = PEM_read_X509(certificate_stream, (X509 **)NULL, NULL, NULL);
    assert_true(certificate != NULL);

    /*
     * Start testing
     */
    USE_MOCK(SSL_get_peer_certificate);
    assert_int_equal(-1, TLSVerifyPeer(conn_info, "127.0.0.1", "root"));
    SSL_GET_PEER_CERTIFICATE_RETURN(certificate);

    USE_MOCK(X509_get_pubkey);
    X509_GET_PUBKEY_RETURN(NULL);
    assert_int_equal(-1, TLSVerifyPeer(conn_info, "127.0.0.1", "root"));

    /*
     * Due to the cleaning up we do after failing, we need to re read the certificate after
     * very failure. The same is true for the public key.
     */
    REREAD_CERTIFICATE(certificate_stream, certificate);
    SSL_GET_PEER_CERTIFICATE_RETURN(certificate);
    EVP_PKEY *server_pubkey = NULL;
    FILE *stream = NULL;
    stream = fopen(server_name_template_public, "r");
    RSA *pubkey = PEM_read_RSAPublicKey(stream, (RSA **)NULL, NULL, NULL);
    server_pubkey = EVP_PKEY_new();
    EVP_PKEY_assign_RSA(server_pubkey, pubkey);
    X509_GET_PUBKEY_RETURN(server_pubkey);

    USE_MOCK(EVP_PKEY_type);
    EVP_PKEY_TYPE_RETURN(EVP_PKEY_DSA);
    assert_int_equal(-1, TLSVerifyPeer(conn_info, "127.0.0.1", "root"));
    EVP_PKEY_TYPE_RETURN(EVP_PKEY_RSA);

    REREAD_CERTIFICATE(certificate_stream, certificate);
    SSL_GET_PEER_CERTIFICATE_RETURN(certificate);
    REREAD_PUBLIC_KEY(stream, pubkey, server_pubkey);
    X509_GET_PUBKEY_RETURN(server_pubkey);
    USE_MOCK(X509_verify);
    X509_VERIFY_RETURN(-1);
    assert_int_equal(-1, TLSVerifyPeer(conn_info, "127.0.0.1", "root"));
    X509_VERIFY_RETURN(0);
    REREAD_CERTIFICATE(certificate_stream, certificate);
    SSL_GET_PEER_CERTIFICATE_RETURN(certificate);
    REREAD_PUBLIC_KEY(stream, pubkey, server_pubkey);
    X509_GET_PUBKEY_RETURN(server_pubkey);
    assert_int_equal(-1, TLSVerifyPeer(conn_info, "127.0.0.1", "root"));
    X509_VERIFY_RETURN(1);

    USE_MOCK(HavePublicKey);
    HAVEPUBLICKEY_RETURN(NULL);
    REREAD_CERTIFICATE(certificate_stream, certificate);
    SSL_GET_PEER_CERTIFICATE_RETURN(certificate);
    REREAD_PUBLIC_KEY(stream, pubkey, server_pubkey);
    X509_GET_PUBKEY_RETURN(server_pubkey);
    assert_int_equal(0, TLSVerifyPeer(conn_info, "127.0.0.1", "root"));

    USE_MOCK(EVP_PKEY_cmp);
    EVP_PKEY_CMP_RETURN(-1);
    REREAD_CERTIFICATE(certificate_stream, certificate);
    SSL_GET_PEER_CERTIFICATE_RETURN(certificate);
    REREAD_PUBLIC_KEY(stream, pubkey, server_pubkey);
    X509_GET_PUBKEY_RETURN(server_pubkey);
    HAVEPUBLICKEY_RETURN(pubkey);
    assert_int_equal(0, TLSVerifyPeer(conn_info, "127.0.0.1", "root"));
    EVP_PKEY_CMP_RETURN(0);
    REREAD_CERTIFICATE(certificate_stream, certificate);
    SSL_GET_PEER_CERTIFICATE_RETURN(certificate);
    REREAD_PUBLIC_KEY(stream, pubkey, server_pubkey);
    X509_GET_PUBKEY_RETURN(server_pubkey);
    HAVEPUBLICKEY_RETURN(pubkey);
    assert_int_equal(0, TLSVerifyPeer(conn_info, "127.0.0.1", "root"));
    EVP_PKEY_CMP_RETURN(-2);
    REREAD_CERTIFICATE(certificate_stream, certificate);
    SSL_GET_PEER_CERTIFICATE_RETURN(certificate);
    REREAD_PUBLIC_KEY(stream, pubkey, server_pubkey);
    X509_GET_PUBKEY_RETURN(server_pubkey);
    HAVEPUBLICKEY_RETURN(pubkey);
    assert_int_equal(-1, TLSVerifyPeer(conn_info, "127.0.0.1", "root"));
    EVP_PKEY_CMP_RETURN(1);
    REREAD_CERTIFICATE(certificate_stream, certificate);
    SSL_GET_PEER_CERTIFICATE_RETURN(certificate);
    REREAD_PUBLIC_KEY(stream, pubkey, server_pubkey);
    X509_GET_PUBKEY_RETURN(server_pubkey);
    HAVEPUBLICKEY_RETURN(pubkey);
    assert_int_equal(1, TLSVerifyPeer(conn_info, "127.0.0.1", "root"));

    /*
     * Shutting down is not as easy as it seems.
     */
    do {
        result = SSL_shutdown(ssl);
        assert_int_not_equal(-1, result);
    } while (result != 1);
    ConnectionInfoDestroy(&conn_info);
    RESET_STATUS;
}