static ServerConnectionState *CFTestD_NewConn(ConnectionInfo *info) { #if 1 /* TODO: why do we do this ? We fail if getsockname() fails, but * don't use the information it returned. Was the intent to use * it to fill in conn->ipaddr ? */ struct sockaddr_storage addr; socklen_t size = sizeof(addr); int sockfd = ConnectionInfoSocket(info); int sockname = getsockname(sockfd, (struct sockaddr *)&addr, &size); if (sockname == -1) { Log(LOG_LEVEL_ERR, "Could not obtain socket address. (getsockname: '%s')", GetErrorStr()); return NULL; } #endif ServerConnectionState *conn = xcalloc(1, sizeof(*conn)); conn->ctx = NULL; conn->conn_info = info; conn->encryption_type = 'c'; /* Only public files (chmod o+r) accessible to non-root */ conn->uid = CF_UNKNOWN_OWNER; /* Careful, 0 is root! */ /* conn->maproot is false: only public files (chmod o+r) are accessible */ Log(LOG_LEVEL_DEBUG, "New connection (socket %d).", ConnectionInfoSocket(info)); return conn; }
static void SpawnConnection(EvalContext *ctx, char *ipaddr, ConnectionInfo *info) { ServerConnectionState *conn = NULL; int ret; pthread_t tid; pthread_attr_t threadattrs; conn = NewConn(ctx, info); int sd_accepted = ConnectionInfoSocket(info); strlcpy(conn->ipaddr, ipaddr, CF_MAX_IP_LEN ); Log(LOG_LEVEL_VERBOSE, "New connection...(from %s, sd %d)", conn->ipaddr, sd_accepted); Log(LOG_LEVEL_VERBOSE, "Spawning new thread..."); ret = pthread_attr_init(&threadattrs); if (ret != 0) { Log(LOG_LEVEL_ERR, "SpawnConnection: Unable to initialize thread attributes (%s)", GetErrorStr()); goto err2; } ret = pthread_attr_setdetachstate(&threadattrs, PTHREAD_CREATE_DETACHED); if (ret != 0) { Log(LOG_LEVEL_ERR, "SpawnConnection: Unable to set thread to detached state (%s).", GetErrorStr()); goto err1; } ret = pthread_attr_setstacksize(&threadattrs, 1024 * 1024); if (ret != 0) { Log(LOG_LEVEL_WARNING, "SpawnConnection: Unable to set thread stack size (%s).", GetErrorStr()); /* Continue with default thread stack size. */ } ret = pthread_create(&tid, &threadattrs, (void *(*)(void *)) HandleConnection, conn); if (ret != 0) { errno = ret; Log(LOG_LEVEL_ERR, "Unable to spawn worker thread. (pthread_create: %s)", GetErrorStr()); goto err1; } err1: pthread_attr_destroy(&threadattrs); err2: if (ret != 0) { Log(LOG_LEVEL_WARNING, "Thread is being handled from main loop!"); HandleConnection(conn); } }
static void CFTestD_SpawnConnection( const char *ipaddr, ConnectionInfo *info, CFTestD_Config *config) { ServerConnectionState *conn = CFTestD_NewConn(info); ConnectionInfoSocket(info); strlcpy(conn->ipaddr, ipaddr, CF_MAX_IP_LEN); Log(LOG_LEVEL_WARNING, "Connection is being handled from main loop!"); CFTestD_HandleConnection(conn, 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); }
/** * @param len is the number of bytes to send, or 0 if buffer is a * '\0'-terminated string so strlen(buffer) can used. */ int SendTransaction(const ConnectionInfo *conn_info, const char *buffer, int len, char status) { assert(status == CF_MORE || status == CF_DONE); char work[CF_BUFSIZE] = { 0 }; int ret; if (len == 0) { len = strlen(buffer); } if (len > CF_BUFSIZE - CF_INBAND_OFFSET) { Log(LOG_LEVEL_ERR, "SendTransaction: len (%d) > %d - %d", len, CF_BUFSIZE, CF_INBAND_OFFSET); return -1; } snprintf(work, CF_INBAND_OFFSET, "%c %d", status, len); memcpy(work + CF_INBAND_OFFSET, buffer, len); Log(LOG_LEVEL_DEBUG, "SendTransaction header: %s", work); LogRaw(LOG_LEVEL_DEBUG, "SendTransaction data: ", work + CF_INBAND_OFFSET, len); switch(ConnectionInfoProtocolVersion(conn_info)) { case CF_PROTOCOL_CLASSIC: ret = SendSocketStream(ConnectionInfoSocket(conn_info), work, len + CF_INBAND_OFFSET); break; case CF_PROTOCOL_TLS: ret = TLSSend(ConnectionInfoSSL(conn_info), work, len + CF_INBAND_OFFSET); break; default: UnexpectedError("SendTransaction: ProtocolVersion %d!", ConnectionInfoProtocolVersion(conn_info)); ret = -1; } if (ret == -1) return -1; else return 0; }
/** * @brief Set the connection type to CLASSIC or TLS. * It is performed by peeking into the TLS connection to read the first bytes, * and if it's a CAUTH protocol command use the old protocol loop, else use * the TLS protocol loop. * This must be the first thing we run on an accepted connection. * * @return -1 in case of error, 1 otherwise. */ int ServerTLSPeek(ConnectionInfo *conn_info) { assert(SSLSERVERCONTEXT != NULL && PRIVKEY != NULL && PUBKEY != NULL); assert(ConnectionInfoProtocolVersion(conn_info) == CF_PROTOCOL_UNDEFINED); const int peek_size = CF_INBAND_OFFSET + sizeof("CAUTH"); char buf[peek_size]; ssize_t got = recv(ConnectionInfoSocket(conn_info), buf, sizeof(buf), MSG_PEEK); assert(got <= peek_size); if (got < 0) { assert(got == -1); Log(LOG_LEVEL_ERR, "TCP receive error: %s", GetErrorStr()); return -1; } else if (got == 0) { Log(LOG_LEVEL_INFO, "Peer closed TCP connection without sending data!"); return -1; } else if (got < peek_size) { Log(LOG_LEVEL_INFO, "Peer sent only %zd bytes! Considering the protocol as Classic", got); ConnectionInfoSetProtocolVersion(conn_info, CF_PROTOCOL_CLASSIC); } else if (memcmp(&buf[CF_INBAND_OFFSET], "CAUTH", strlen("CAUTH")) == 0) { Log(LOG_LEVEL_VERBOSE, "Peeked CAUTH in TCP stream, considering the protocol as Classic"); ConnectionInfoSetProtocolVersion(conn_info, CF_PROTOCOL_CLASSIC); } else /* got==peek_size && not "CAUTH" */ { Log(LOG_LEVEL_VERBOSE, "Peeked nothing important in TCP stream, considering the protocol as TLS"); ConnectionInfoSetProtocolVersion(conn_info, CF_PROTOCOL_TLS); } LogRaw(LOG_LEVEL_DEBUG, "Peeked data: ", buf, got); return 1; }
/** * @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); }
/** * We directly initiate a TLS handshake with the server. If the server is old * version (does not speak TLS) the connection will be denied. * @note the socket file descriptor in #conn_info must be connected and *not* * non-blocking * @return -1 in case of error */ int TLSTry(ConnectionInfo *conn_info) { /* SSL Context might not be initialised up to now due to lack of keys, as * they might be generated as part of the policy (e.g. failsafe.cf). */ if (!TLSClientInitialize()) { return -1; } assert(SSLCLIENTCONTEXT != NULL && PRIVKEY != NULL && PUBKEY != NULL); ConnectionInfoSetSSL(conn_info, SSL_new(SSLCLIENTCONTEXT)); SSL *ssl = ConnectionInfoSSL(conn_info); if (ssl == NULL) { Log(LOG_LEVEL_ERR, "SSL_new: %s", ERR_reason_error_string(ERR_get_error())); return -1; } /* Pass conn_info inside the ssl struct for TLSVerifyCallback(). */ SSL_set_ex_data(ssl, CONNECTIONINFO_SSL_IDX, conn_info); /* Initiate the TLS handshake over the already open TCP socket. */ SSL_set_fd(ssl, ConnectionInfoSocket(conn_info)); int ret = SSL_connect(ssl); if (ret <= 0) { TLSLogError(ssl, LOG_LEVEL_ERR, "Failed to establish TLS connection", ret); return -1; } Log(LOG_LEVEL_VERBOSE, "TLS cipher negotiated: %s, %s", SSL_get_cipher_name(ssl), SSL_get_cipher_version(ssl)); Log(LOG_LEVEL_VERBOSE, "TLS session established, checking trust..."); return 0; }
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); }
/** * @brief Accept a TLS connection and authenticate and identify. * @note Various fields in #conn are set, like username and keyhash. */ int ServerTLSSessionEstablish(ServerConnectionState *conn) { int ret; if (ConnectionInfoConnectionStatus(conn->conn_info) != CF_CONNECTION_ESTABLISHED) { assert(ConnectionInfoSSL(conn->conn_info) == NULL); SSL *ssl = SSL_new(SSLSERVERCONTEXT); if (ssl == NULL) { Log(LOG_LEVEL_ERR, "SSL_new: %s", TLSErrorString(ERR_get_error())); return -1; } ConnectionInfoSetSSL(conn->conn_info, ssl); /* Pass conn_info inside the ssl struct for TLSVerifyCallback(). */ SSL_set_ex_data(ssl, CONNECTIONINFO_SSL_IDX, conn->conn_info); /* Now we are letting OpenSSL take over the open socket. */ SSL_set_fd(ssl, ConnectionInfoSocket(conn->conn_info)); ret = SSL_accept(ssl); if (ret <= 0) { TLSLogError(ssl, LOG_LEVEL_ERR, "Failed to accept TLS connection", ret); return -1; } Log(LOG_LEVEL_VERBOSE, "TLS cipher negotiated: %s, %s", SSL_get_cipher_name(ssl), SSL_get_cipher_version(ssl)); Log(LOG_LEVEL_VERBOSE, "TLS session established, checking trust..."); /* Send/Receive "CFE_v%d" version string, agree on version, receive identity (username) of peer. */ char username[sizeof(conn->username)] = ""; bool b = ServerIdentificationDialog(conn->conn_info, username, sizeof(username)); if (b != true) { return -1; } /* We *now* (maybe a bit late) verify the key that the client sent us in * the TLS handshake, since we need the username to do so. TODO in the * future store keys irrelevant of username, so that we can match them * before IDENTIFY. */ ret = TLSVerifyPeer(conn->conn_info, conn->ipaddr, username); if (ret == -1) /* error */ { return -1; } if (ret == 1) /* trusted key */ { Log(LOG_LEVEL_VERBOSE, "%s: Client is TRUSTED, public key MATCHES stored one.", KeyPrintableHash(ConnectionInfoKey(conn->conn_info))); } if (ret == 0) /* untrusted key */ { if ((SV.trustkeylist != NULL) && (IsMatchItemIn(SV.trustkeylist, conn->ipaddr))) { Log(LOG_LEVEL_VERBOSE, "Peer was found in \"trustkeysfrom\" list"); Log(LOG_LEVEL_NOTICE, "Trusting new key: %s", KeyPrintableHash(ConnectionInfoKey(conn->conn_info))); SavePublicKey(username, KeyPrintableHash(conn->conn_info->remote_key), KeyRSA(ConnectionInfoKey(conn->conn_info))); } else { Log(LOG_LEVEL_NOTICE, "TRUST FAILED, peer presented an untrusted key, dropping connection!"); Log(LOG_LEVEL_VERBOSE, "Add peer to \"trustkeysfrom\" if you really want to start trusting this new key."); return -1; } } /* All checks succeeded, set conn->uid (conn->sid for Windows) * according to the received USERNAME identity. */ SetConnIdentity(conn, username); /* No CAUTH, SAUTH in non-classic protocol. */ conn->user_data_set = 1; conn->rsa_auth = 1; LastSaw1(conn->ipaddr, KeyPrintableHash(ConnectionInfoKey(conn->conn_info)), LAST_SEEN_ROLE_ACCEPT); ServerSendWelcome(conn); } return 1; }
/** * @return 0 in case of socket closed, -1 in case of other error, or * >0 the number of bytes read. */ int ReceiveTransaction(const ConnectionInfo *conn_info, char *buffer, int *more) { char proto[CF_INBAND_OFFSET + 1] = { 0 }; char status = 'x'; unsigned int len = 0; int ret; /* Get control channel. */ switch(ConnectionInfoProtocolVersion(conn_info)) { case CF_PROTOCOL_CLASSIC: ret = RecvSocketStream(ConnectionInfoSocket(conn_info), proto, CF_INBAND_OFFSET); break; case CF_PROTOCOL_TLS: ret = TLSRecv(ConnectionInfoSSL(conn_info), proto, CF_INBAND_OFFSET); break; default: UnexpectedError("ReceiveTransaction: ProtocolVersion %d!", ConnectionInfoProtocolVersion(conn_info)); ret = -1; } if (ret == -1 || ret == 0) return ret; LogRaw(LOG_LEVEL_DEBUG, "ReceiveTransaction header: ", proto, ret); ret = sscanf(proto, "%c %u", &status, &len); if (ret != 2) { Log(LOG_LEVEL_ERR, "ReceiveTransaction: Bad packet -- bogus header: %s", proto); return -1; } if (len > CF_BUFSIZE - CF_INBAND_OFFSET) { Log(LOG_LEVEL_ERR, "ReceiveTransaction: Bad packet -- too long (len=%d)", len); return -1; } if (status != CF_MORE && status != CF_DONE) { Log(LOG_LEVEL_ERR, "ReceiveTransaction: Bad packet -- bogus header (more='%c')", status); return -1; } if (more != NULL) { switch (status) { case CF_MORE: *more = true; break; case CF_DONE: *more = false; break; default: ProgrammingError("Unreachable, " "bogus headers have already been checked!"); } } /* Get data. */ switch(ConnectionInfoProtocolVersion(conn_info)) { case CF_PROTOCOL_CLASSIC: ret = RecvSocketStream(ConnectionInfoSocket(conn_info), buffer, len); break; case CF_PROTOCOL_TLS: ret = TLSRecv(ConnectionInfoSSL(conn_info), buffer, len); break; default: UnexpectedError("ReceiveTransaction: ProtocolVersion %d!", ConnectionInfoProtocolVersion(conn_info)); ret = -1; } LogRaw(LOG_LEVEL_DEBUG, "ReceiveTransaction data: ", buffer, ret); return ret; }
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); }
static void *HandleConnection(void *c) { ServerConnectionState *conn = c; int ret; /* Set logging prefix to be the IP address for all of thread's lifetime. */ /* This stack-allocated struct should be valid for all the lifetime of the * thread. Just make sure that after calling DeleteConn() (which frees * ipaddr), you exit the thread right away. */ LoggingPrivContext log_ctx = { .log_hook = LogHook, .param = conn->ipaddr }; LoggingPrivSetContext(&log_ctx); Log(LOG_LEVEL_INFO, "Accepting connection"); /* We test if number of active threads is greater than max, if so we deny connection, if it happened too many times within a short timeframe then we kill ourself.TODO this test should be done *before* spawning the thread. */ ret = ThreadLock(cft_server_children); if (!ret) { Log(LOG_LEVEL_ERR, "Unable to thread-lock, closing connection!"); goto ret2; } else if (ACTIVE_THREADS > CFD_MAXPROCESSES) { if (TRIES > MAXTRIES) { /* This happens when no thread was freed while we had to drop 5 * (or maxconnections/3) consecutive connections, because none of * the existing threads finished. */ Log(LOG_LEVEL_CRIT, "Server seems to be paralyzed. DOS attack? " "Committing apoptosis..."); ThreadUnlock(cft_server_children); FatalError(conn->ctx, "Terminating"); } TRIES++; Log(LOG_LEVEL_ERR, "Too many threads (%d > %d), dropping connection! " "Increase server maxconnections?", ACTIVE_THREADS, CFD_MAXPROCESSES); ThreadUnlock(cft_server_children); goto ret2; } ACTIVE_THREADS++; TRIES = 0; ThreadUnlock(cft_server_children); DisableSendDelays(ConnectionInfoSocket(conn->conn_info)); /* 20 times the connect() timeout should be enough to avoid MD5 * computation timeouts on big files on old slow Solaris 8 machines. */ SetReceiveTimeout(ConnectionInfoSocket(conn->conn_info), CONNTIMEOUT * 20 * 1000); if (ConnectionInfoConnectionStatus(conn->conn_info) != CF_CONNECTION_ESTABLISHED) { /* Decide the protocol used. */ ret = ServerTLSPeek(conn->conn_info); if (ret == -1) { goto ret1; } } ProtocolVersion protocol_version = ConnectionInfoProtocolVersion(conn->conn_info); if (protocol_version == CF_PROTOCOL_LATEST) { ret = ServerTLSSessionEstablish(conn); if (ret == -1) { goto ret1; } } else if (protocol_version < CF_PROTOCOL_LATEST && protocol_version > CF_PROTOCOL_UNDEFINED) { /* This connection is legacy protocol. Do we allow it? */ if (SV.allowlegacyconnects != NULL && /* By default we do */ !IsMatchItemIn(SV.allowlegacyconnects, conn->ipaddr)) { Log(LOG_LEVEL_INFO, "Connection is not using latest protocol, denying"); goto ret1; } } else { UnexpectedError("HandleConnection: ProtocolVersion %d!", ConnectionInfoProtocolVersion(conn->conn_info)); goto ret1; } /* ========================= MAIN LOOPS ========================= */ if (protocol_version >= CF_PROTOCOL_TLS) { /* New protocol does DNS reverse look up of the connected * IP address, to check hostname access_rules. */ if (NEED_REVERSE_LOOKUP) { ret = getnameinfo((const struct sockaddr *) &conn->conn_info->ss, conn->conn_info->ss_len, conn->revdns, sizeof(conn->revdns), NULL, 0, NI_NAMEREQD); if (ret != 0) { Log(LOG_LEVEL_INFO, "Reverse lookup failed (getnameinfo: %s)!", gai_strerror(ret)); } else { Log(LOG_LEVEL_INFO, "Hostname (reverse looked up): %s", conn->revdns); } } while (BusyWithNewProtocol(conn->ctx, conn)) { } } else if (protocol_version == CF_PROTOCOL_CLASSIC) { while (BusyWithClassicConnection(conn->ctx, conn)) { } } /* ============================================================ */ Log(LOG_LEVEL_INFO, "Connection closed, terminating thread"); ret1: ThreadLock(cft_server_children); ACTIVE_THREADS--; ThreadUnlock(cft_server_children); ret2: 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->user_data_set = 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 */ conn->revdns[0] = '\0'; Log(LOG_LEVEL_DEBUG, "New socket %d", ConnectionInfoSocket(info)); return conn; }