/* Attempt to figure out (in a very dumb/brute force way) which ciphersuites a SSL/TLS server supports. (C) 2005 Jack Lloyd This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Portions stolen from Eric Rescorla's OpenSSL examples, http://www.rtfm.com/openssl-examples/ (C) 2000-2001 RTFM, Inc. (BSD license) */ #include #include #include #include #include #include #include #include #include const char* CIPHERSUITES[] = { /* Static RSA ciphersuites */ "DES-CBC-MD5", "DES-CBC-SHA", "DES-CBC3-MD5", "DES-CBC3-SHA", "AES128-SHA", "AES256-SHA", "RC2-CBC-MD5", "RC4-64-MD5", "RC4-MD5", "RC4-SHA", "EXP-DES-CBC-SHA", "EXP-RC2-CBC-MD5", "EXP-RC2-CBC-MD5", "EXP-RC4-MD5", "EXP-RC4-MD5", "EXP1024-DES-CBC-SHA", "EXP1024-RC2-CBC-MD5", "EXP1024-RC4-MD5", "EXP1024-RC4-SHA", /* DH + RSA ciphersuites */ "DHE-RSA-AES128-SHA", "DHE-RSA-AES256-SHA", "EDH-RSA-DES-CBC-SHA", "EDH-RSA-DES-CBC3-SHA", "EXP-EDH-RSA-DES-CBC-SHA", /* DH + DSA ciphersuites */ "DHE-DSS-AES128-SHA", "DHE-DSS-AES256-SHA", "DHE-DSS-RC4-SHA", "EDH-DSS-DES-CBC-SHA", "EDH-DSS-DES-CBC3-SHA", "EXP1024-DHE-DSS-DES-CBC-SHA", "EXP1024-DHE-DSS-RC4-SHA", "EXP-EDH-DSS-DES-CBC-SHA", 0 }; class Exception : public std::exception { public: Exception(const std::string& e) { err = e; } ~Exception() throw() {} const char* what() const throw() { return err.c_str(); } private: std::string err; }; int do_connect(const std::string& hostname, uint16_t port) { struct hostent *host = gethostbyname(hostname.c_str()); if(!host) throw Exception("Couldn't resolve " + hostname); struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_addr = *(struct in_addr*)host->h_addr_list[0]; addr.sin_family = AF_INET; addr.sin_port = htons(port); int sock = socket(AF_INET, SOCK_STREAM, 0); if(sock < 0) throw Exception("Couldn't create socket"); if(connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) throw Exception("Couldn't connect to " + hostname); return sock; } static void sigpipe_handle(int) {} enum SSL_VERSION { SSLv2, SSLv3, TLSv1 }; SSL_CTX* create_ctx(SSL_VERSION version) { SSL_METHOD *meth; SSL_CTX *ctx; /* Set up a SIGPIPE handler */ signal(SIGPIPE, sigpipe_handle); if(version == SSLv2) meth = SSLv2_method(); else if(version == SSLv3) meth = SSLv3_method(); else if(version == TLSv1) meth = TLSv1_method(); ctx = SSL_CTX_new(meth); return ctx; } void do_tests(const char* host, uint16_t port, const char* ver_string, SSL_VERSION version) { int index = 0; printf("%s Ciphersuites\n-----------------------\n", ver_string); while(CIPHERSUITES[index]) { SSL_CTX* ctx = create_ctx(version); SSL_CTX_set_cipher_list(ctx, CIPHERSUITES[index]); int socket = do_connect(host, port); SSL* ssl = SSL_new(ctx); BIO* sbio = BIO_new_socket(socket, BIO_CLOSE); SSL_set_bio(ssl, sbio, sbio); int result = SSL_connect(ssl); if(result <= 0) { int err = SSL_get_error(ssl, result); unsigned long err_code = ERR_get_error(); /* Weird special case: OpenSSL seems to return no-error in the case of SSLv2. In this particular case, the other end (another OpenSSL) had SSLv2 disabled. I'm not sure what's happening, but avoid it. This will cause false negatives in the case where an SSLv2 server negotiates connections and then immediately closes after handshake. If have no idea why you would want a server that did that, but whatever. */ if(err == SSL_ERROR_SSL || (err == SSL_ERROR_ZERO_RETURN && version == SSLv2)) { /* Make sure it's that no ciphersuite was found */ int lib = ERR_GET_LIB(err_code); int reason = ERR_GET_REASON(err_code); if( (lib == ERR_LIB_SSL && (reason == SSL_R_NO_CIPHERS_AVAILABLE || reason == SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE)) || (err = SSL_ERROR_ZERO_RETURN && version == SSLv2)) { printf("- %s\n", CIPHERSUITES[index]); } else { printf("Some odd error when testing %s\n", CIPHERSUITES[index]); printf("%s\n", ERR_error_string(err_code, NULL)); } } else { printf("Some odd error when testing %s\n", CIPHERSUITES[index]); printf("%s\n", ERR_error_string(err_code, NULL)); } } else if(result == 1) { printf("+ %s\n", CIPHERSUITES[index]); } index++; } printf("\n"); } int main(int argc, char* argv[]) { if(argc != 3) { printf("Usage: %s host port\n", argv[0]); return 1; } const char* host = argv[1]; uint16_t port = atoi(argv[2]); try { SSL_library_init(); SSL_load_error_strings(); OpenSSL_add_all_algorithms(); do_tests(host, port, "SSLv2", SSLv2); do_tests(host, port, "SSLv3", SSLv3); do_tests(host, port, "TLSv1", TLSv1); } catch(std::exception& e) { printf("%s\n", e.what()); return 1; } return 0; }