void DeleteAgentConn(AgentConnection *conn) { Stat *sp = conn->cache; while (sp != NULL) { Stat *sps = sp; sp = sp->next; free(sps); } ConnectionInfoDestroy(&conn->conn_info); if (conn->session_key) { free(conn->session_key); } if (conn->this_server) { free(conn->this_server); } *conn = (AgentConnection) { 0 }; free(conn); }
/* 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); }
static void CFTestD_DeleteConn(ServerConnectionState *conn) { int sd = ConnectionInfoSocket(conn->conn_info); if (sd != SOCKET_INVALID) { cf_closesocket(sd); } ConnectionInfoDestroy(&conn->conn_info); free(conn->session_key); free(conn); }
/** * @note This function is thread-safe. Do NOT wrap it with mutex! */ static void DeleteConn(ServerConnectionState *conn) { cf_closesocket(ConnectionInfoSocket(conn->conn_info)); ConnectionInfoDestroy(&conn->conn_info); free(conn->session_key); if (conn->ipaddr != NULL) { if (ThreadLock(cft_count)) { DeleteItemMatching(&SV.connectionlist, conn->ipaddr); ThreadUnlock(cft_count); } } *conn = (ServerConnectionState) {0}; free(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); }
static void *HandleConnection(ServerConnectionState *conn) { int ret; char output[CF_BUFSIZE]; /* Set logging prefix to be the IP address for all of thread's lifetime. */ /* Should be valid for all lifetime of the thread. Just make sure that * after calling DeleteConn(), you exit right away. */ LoggingPrivContext log_ctx = { .log_hook = LogHook, .param = conn->ipaddr }; LoggingPrivSetContext(&log_ctx); if (!ThreadLock(cft_server_children)) { DeleteConn(conn); return NULL; } ACTIVE_THREADS++; if (ACTIVE_THREADS >= CFD_MAXPROCESSES) { ACTIVE_THREADS--; if (TRIES++ > MAXTRIES) /* When to say we're hung / apoptosis threshold */ { Log(LOG_LEVEL_ERR, "Server seems to be paralyzed. DOS attack? Committing apoptosis..."); FatalError(conn->ctx, "Terminating"); } if (!ThreadUnlock(cft_server_children)) { } Log(LOG_LEVEL_ERR, "Too many threads (>=%d) -- increase server maxconnections?", CFD_MAXPROCESSES); snprintf(output, CF_BUFSIZE, "BAD: Server is currently too busy -- increase maxconnections or splaytime?"); SendTransaction(conn->conn_info, output, 0, CF_DONE); DeleteConn(conn); return NULL; } else { ThreadUnlock(cft_server_children); } TRIES = 0; /* As long as there is activity, we're not stuck */ DisableSendDelays(ConnectionInfoSocket(conn->conn_info)); struct timeval tv = { .tv_sec = CONNTIMEOUT * 20, }; SetReceiveTimeout(ConnectionInfoSocket(conn->conn_info), &tv); if (ConnectionInfoConnectionStatus(conn->conn_info) != CF_CONNECTION_ESTABLISHED) { /* Decide the protocol used. */ ret = ServerTLSPeek(conn->conn_info); if (ret == -1) { DeleteConn(conn); return NULL; } } switch (ConnectionInfoProtocolVersion(conn->conn_info)) { case CF_PROTOCOL_CLASSIC: { while (BusyWithClassicConnection(conn->ctx, conn)) { } break; } case CF_PROTOCOL_TLS: { ret = ServerTLSSessionEstablish(conn); if (ret == -1) { DeleteConn(conn); return NULL; } while (BusyWithNewProtocol(conn->ctx, conn)) { } break; } default: UnexpectedError("HandleConnection: ProtocolVersion %d!", ConnectionInfoProtocolVersion(conn->conn_info)); } Log(LOG_LEVEL_INFO, "Connection closed, terminating thread", conn->ipaddr); if (!ThreadLock(cft_server_children)) { DeleteConn(conn); return NULL; } ACTIVE_THREADS--; if (!ThreadUnlock(cft_server_children)) { } DeleteConn(conn); return NULL; } /***************************************************************/ /* Toolkit/Class: conn */ /***************************************************************/ static ServerConnectionState *NewConn(EvalContext *ctx, ConnectionInfo *info) { ServerConnectionState *conn = NULL; struct sockaddr_storage addr; socklen_t size = sizeof(addr); if (getsockname(ConnectionInfoSocket(info), (struct sockaddr *)&addr, &size) == -1) { Log(LOG_LEVEL_ERR, "Could not obtain socket address. (getsockname: '%s')", GetErrorStr()); return NULL; } conn = xcalloc(1, sizeof(*conn)); conn->ctx = ctx; conn->conn_info = info; conn->id_verified = false; conn->rsa_auth = false; conn->hostname[0] = '\0'; conn->ipaddr[0] = '\0'; conn->username[0] = '\0'; conn->session_key = NULL; conn->encryption_type = 'c'; conn->maproot = false; /* Only public files (chmod o+r) accessible */ Log(LOG_LEVEL_DEBUG, "New socket %d", ConnectionInfoSocket(info)); return conn; } /***************************************************************/ static void DeleteConn(ServerConnectionState *conn) { cf_closesocket(ConnectionInfoSocket(conn->conn_info)); ConnectionInfoDestroy(&conn->conn_info); free(conn->session_key); if (conn->ipaddr != NULL) { if (!ThreadLock(cft_count)) { return; } DeleteItemMatching(&SV.connectionlist, MapAddress(conn->ipaddr)); if (!ThreadUnlock(cft_count)) { return; } } *conn = (ServerConnectionState) {0}; free(conn); }
void ServerEntryPoint(EvalContext *ctx, char *ipaddr, ConnectionInfo *info) { char intime[64]; time_t now; Log(LOG_LEVEL_VERBOSE, "Obtained IP address of '%s' on socket %d from accept", ipaddr, ConnectionInfoSocket(info)); if ((SV.nonattackerlist) && (!IsMatchItemIn(SV.nonattackerlist, MapAddress(ipaddr)))) { Log(LOG_LEVEL_ERR, "Not allowing connection from non-authorized IP '%s'", ipaddr); cf_closesocket(ConnectionInfoSocket(info)); ConnectionInfoDestroy(&info); return; } if (IsMatchItemIn(SV.attackerlist, MapAddress(ipaddr))) { Log(LOG_LEVEL_ERR, "Denying connection from non-authorized IP '%s'", ipaddr); cf_closesocket(ConnectionInfoSocket(info)); ConnectionInfoDestroy(&info); return; } if ((now = time((time_t *) NULL)) == -1) { now = 0; } PurgeOldConnections(&SV.connectionlist, now); if (!IsMatchItemIn(SV.multiconnlist, MapAddress(ipaddr))) { if (!ThreadLock(cft_count)) { return; } if (IsItemIn(SV.connectionlist, MapAddress(ipaddr))) { ThreadUnlock(cft_count); Log(LOG_LEVEL_ERR, "Denying repeated connection from '%s'", ipaddr); cf_closesocket(ConnectionInfoSocket(info)); ConnectionInfoDestroy(&info); return; } ThreadUnlock(cft_count); } if (SV.logconns) { Log(LOG_LEVEL_INFO, "Accepting connection from %s", ipaddr); } else { Log(LOG_LEVEL_INFO, "Accepting connection from %s", ipaddr); } snprintf(intime, 63, "%d", (int) now); if (!ThreadLock(cft_count)) { cf_closesocket(ConnectionInfoSocket(info)); ConnectionInfoDestroy(&info); return; } PrependItem(&SV.connectionlist, MapAddress(ipaddr), intime); if (!ThreadUnlock(cft_count)) { cf_closesocket(ConnectionInfoSocket(info)); ConnectionInfoDestroy(&info); return; } SpawnConnection(ctx, ipaddr, info); }
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; }
void ServerEntryPoint(EvalContext *ctx, const char *ipaddr, ConnectionInfo *info) { time_t now; Log(LOG_LEVEL_VERBOSE, "Obtained IP address of '%s' on socket %d from accept", ipaddr, ConnectionInfoSocket(info)); /* TODO change nonattackerlist, attackerlist and especially connectionlist * to binary searched lists, or remove them from the main thread! */ if ((SV.nonattackerlist) && (!IsMatchItemIn(SV.nonattackerlist, ipaddr))) { Log(LOG_LEVEL_ERR, "Remote host '%s' not in allowconnects, denying connection", ipaddr); cf_closesocket(ConnectionInfoSocket(info)); ConnectionInfoDestroy(&info); return; } if (IsMatchItemIn(SV.attackerlist, ipaddr)) { Log(LOG_LEVEL_ERR, "Remote host '%s' is in denyconnects, denying connection", ipaddr); cf_closesocket(ConnectionInfoSocket(info)); ConnectionInfoDestroy(&info); return; } if ((now = time(NULL)) == -1) { now = 0; } PurgeOldConnections(&SV.connectionlist, now); if (!IsMatchItemIn(SV.multiconnlist, ipaddr)) { if (!ThreadLock(cft_count)) { cf_closesocket(ConnectionInfoSocket(info)); ConnectionInfoDestroy(&info); return; } if (IsItemIn(SV.connectionlist, ipaddr)) { ThreadUnlock(cft_count); Log(LOG_LEVEL_ERR, "Remote host '%s' is not in allowallconnects, denying second simultaneous connection", ipaddr); cf_closesocket(ConnectionInfoSocket(info)); ConnectionInfoDestroy(&info); return; } ThreadUnlock(cft_count); } char intime[PRINTSIZE(now)]; snprintf(intime, sizeof(intime), "%jd", (intmax_t) now); if (!ThreadLock(cft_count)) { cf_closesocket(ConnectionInfoSocket(info)); ConnectionInfoDestroy(&info); return; } PrependItem(&SV.connectionlist, ipaddr, intime); ThreadUnlock(cft_count); SpawnConnection(ctx, ipaddr, info); }