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