/** * @return 1 success, 0 auth/ID error, -1 other error */ int TLSConnect(ConnectionInfo *conn_info, bool trust_server, const char *ipaddr, const char *username) { int ret; ret = TLSTry(conn_info); if (ret == -1) { return -1; } /* TODO username is local, fix. */ ret = TLSVerifyPeer(conn_info, ipaddr, username); if (ret == -1) /* error */ { return -1; } const char *key_hash = KeyPrintableHash(conn_info->remote_key); if (ret == 1) { Log(LOG_LEVEL_VERBOSE, "Server is TRUSTED, received key '%s' MATCHES stored one.", key_hash); } else /* ret == 0 */ { if (trust_server) /* We're most probably bootstrapping. */ { Log(LOG_LEVEL_NOTICE, "Trusting new key: %s", key_hash); SavePublicKey(username, KeyPrintableHash(conn_info->remote_key), KeyRSA(conn_info->remote_key)); } else { Log(LOG_LEVEL_ERR, "TRUST FAILED, server presented untrusted key: %s", key_hash); return -1; } } /* TLS CONNECTION IS ESTABLISHED, negotiate protocol version and send * identification data. */ ret = TLSClientIdentificationDialog(conn_info, username); return ret; }
/** * @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; }
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; }
/** * @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; conn->conn_info.ssl = SSL_new(SSLSERVERCONTEXT); if (conn->conn_info.ssl == NULL) { Log(LOG_LEVEL_ERR, "SSL_new: %s", ERR_reason_error_string(ERR_get_error())); return -1; } /* Now we are letting OpenSSL take over the open socket. */ SSL_set_fd(conn->conn_info.ssl, conn->conn_info.sd); ret = SSL_accept(conn->conn_info.ssl); if (ret <= 0) { TLSLogError(conn->conn_info.ssl, LOG_LEVEL_ERR, "Connection handshake", ret); return -1; } Log(LOG_LEVEL_VERBOSE, "TLS cipher negotiated: %s, %s", SSL_get_cipher_name(conn->conn_info.ssl), SSL_get_cipher_version(conn->conn_info.ssl)); Log(LOG_LEVEL_VERBOSE, "TLS session established, checking trust..."); /* Send/Receive "CFE_v%d" version string and agree on version. */ ret = ServerNegotiateProtocol(&conn->conn_info); if (ret <= 0) { return -1; } /* Receive IDENTITY USER=asdf plain string. */ ret = ServerIdentifyClient(&conn->conn_info, conn->username, sizeof(conn->username)); if (ret != 1) { 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, conn->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.", conn->conn_info.remote_keyhash_str); } if (ret == 0) /* untrusted key */ { Log(LOG_LEVEL_WARNING, "%s: Client's public key is UNKNOWN!", conn->conn_info.remote_keyhash_str); if ((SV.trustkeylist != NULL) && (IsMatchItemIn(conn->ctx, SV.trustkeylist, MapAddress(conn->ipaddr)))) { Log(LOG_LEVEL_VERBOSE, "Host %s was found in the \"trustkeysfrom\" list", conn->ipaddr); Log(LOG_LEVEL_WARNING, "%s: Explicitly trusting this key from now on.", conn->conn_info.remote_keyhash_str); conn->trust = true; SavePublicKey("root", conn->conn_info.remote_keyhash_str, conn->conn_info.remote_key); } else { Log(LOG_LEVEL_ERR, "TRUST FAILED, WARNING: possible MAN IN THE MIDDLE attack, dropping connection!"); Log(LOG_LEVEL_ERR, "Open server's ACL if you really want to start trusting this new key."); return -1; } } /* skipping CAUTH */ conn->id_verified = 1; /* skipping SAUTH, allow access to read-only files */ conn->rsa_auth = 1; LastSaw1(conn->ipaddr, conn->conn_info.remote_keyhash_str, LAST_SEEN_ROLE_ACCEPT); ServerSendWelcome(conn); return 1; }