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); } } }
/* 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); }
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; }
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); }
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; }
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; }