diff --git a/libhttp/httpconnection.c b/libhttp/httpconnection.c index 12ea365..67cc644 100644 --- a/libhttp/httpconnection.c +++ b/libhttp/httpconnection.c @@ -88,7 +88,7 @@ static int httpPromoteToSSL(struct HttpConnection *http, const char *buf, int len) { if (http->ssl->enabled && !http->sslHndl) { - debug("Switching to SSL (replaying %d+%d bytes)", + debug("SSL: switching to SSL (replaying %d+%d bytes)", http->partialLength, len); if (http->partial && len > 0) { check(http->partial = realloc(http->partial, @@ -102,6 +102,8 @@ static int httpPromoteToSSL(struct HttpConnection *http, const char *buf, http->partial ? http->partialLength : len); if (http->sslHndl) { check(!rc); + // Reset renegotiations count for connections promoted to SSL. + http->ssl->renegotiationCount = 0; SSL_set_app_data(http->sslHndl, http); } free(http->partial); @@ -138,6 +140,13 @@ static ssize_t httpRead(struct HttpConnection *http, char *buf, ssize_t len) { break; } dcheck(!ERR_peek_error()); + + // Shutdown SSL connection, if client initiated renegotiation. + if (http->ssl->renegotiationCount > 1) { + debug("SSL: connection shutdown due to client initiated renegotiation!"); + rc = 0; + errno = EINVAL; + } } else { rc = NOINTR(read(http->fd, buf, len)); } diff --git a/libhttp/ssl.c b/libhttp/ssl.c index 9a62726..4d6d0d5 100644 --- a/libhttp/ssl.c +++ b/libhttp/ssl.c @@ -102,15 +102,20 @@ BIO * (*BIO_new)(BIO_METHOD *); BIO * (*BIO_new_socket)(int, int); BIO * (*BIO_pop)(BIO *); BIO * (*BIO_push)(BIO *, BIO *); +#if defined(HAVE_OPENSSL_EC) +void (*EC_KEY_free)(EC_KEY *); +EC_KEY * (*EC_KEY_new_by_curve_name)(int); +#endif void (*ERR_clear_error)(void); -void (*ERR_clear_error)(void); -unsigned long (*ERR_peek_error)(void); unsigned long (*ERR_peek_error)(void); long (*SSL_CTX_callback_ctrl)(SSL_CTX *, int, void (*)(void)); int (*SSL_CTX_check_private_key)(const SSL_CTX *); long (*SSL_CTX_ctrl)(SSL_CTX *, int, long, void *); void (*SSL_CTX_free)(SSL_CTX *); SSL_CTX * (*SSL_CTX_new)(SSL_METHOD *); +int (*SSL_CTX_set_cipher_list)(SSL_CTX *, const char *); +void (*SSL_CTX_set_info_callback)(SSL_CTX *, + void (*)(const SSL *, int, int)); int (*SSL_CTX_use_PrivateKey_file)(SSL_CTX *, const char *, int); int (*SSL_CTX_use_PrivateKey_ASN1)(int, SSL_CTX *, const unsigned char *, long); @@ -136,7 +141,6 @@ int (*SSL_write)(SSL *, const void *, int); SSL_METHOD * (*SSLv23_server_method)(void); X509 * (*d2i_X509)(X509 **px, const unsigned char **in, int len); void (*X509_free)(X509 *a); -int (*x_SSL_CTX_set_cipher_list)(SSL_CTX *ctx, const char *str); void (*x_sk_zero)(void *st); void * (*x_SSL_COMP_get_compression_methods)(void); #endif @@ -166,6 +170,7 @@ void initSSL(struct SSLSupport *ssl) { ssl->sslContext = NULL; ssl->sniCertificatePattern = NULL; ssl->generateMissing = 0; + ssl->renegotiationCount = 0; initTrie(&ssl->sniContexts, sslDestroyCachedContext, ssl); } @@ -280,11 +285,17 @@ static void loadSSL(void) { { { &ERR_clear_error }, "ERR_clear_error" }, { { &ERR_peek_error }, "ERR_peek_error" }, { { &ERR_peek_error }, "ERR_peek_error" }, +#ifdef HAVE_OPENSSL_EC + { { &EC_KEY_free }, "EC_KEY_free" }, + { { &EC_KEY_new_by_curve_name }, "EC_KEY_new_by_curve_name" }, +#endif { { &SSL_CTX_callback_ctrl }, "SSL_CTX_callback_ctrl" }, { { &SSL_CTX_check_private_key }, "SSL_CTX_check_private_key" }, { { &SSL_CTX_ctrl }, "SSL_CTX_ctrl" }, { { &SSL_CTX_free }, "SSL_CTX_free" }, { { &SSL_CTX_new }, "SSL_CTX_new" }, + { { &SSL_CTX_set_cipher_list }, "SSL_CTX_set_cipher_list" }, + { { &SSL_CTX_set_info_callback }, "SSL_CTX_set_info_callback" }, { { &SSL_CTX_use_PrivateKey_file }, "SSL_CTX_use_PrivateKey_file" }, { { &SSL_CTX_use_PrivateKey_ASN1 }, "SSL_CTX_use_PrivateKey_ASN1" }, { { &SSL_CTX_use_certificate_file },"SSL_CTX_use_certificate_file"}, @@ -312,12 +323,11 @@ static void loadSSL(void) { { { &SSLv23_server_method }, "SSLv23_server_method" }, { { &d2i_X509 }, "d2i_X509" }, { { &X509_free }, "X509_free" }, - { { &x_SSL_CTX_set_cipher_list }, "SSL_CTX_set_cipher_list" }, { { &x_sk_zero }, "sk_zero" } }; for (unsigned i = 0; i < sizeof(symbols)/sizeof(symbols[0]); i++) { if (!(*symbols[i].var = loadSymbol(path_libssl, symbols[i].fn))) { - debug("Failed to load SSL support. Could not find \"%s\"", + debug("SSL: failed to load SSL support. Could not find \"%s\"", symbols[i].fn); for (unsigned j = 0; j < sizeof(symbols)/sizeof(symbols[0]); j++) { *symbols[j].var = NULL; @@ -329,9 +339,10 @@ static void loadSSL(void) { x_SSL_COMP_get_compression_methods = loadSymbol(path_libssl, "SSL_COMP_get_compression_methods"); // ends + SSL_library_init(); dcheck(!ERR_peek_error()); - debug("Loaded SSL suppport"); + debug("SSL: loaded SSL suppport"); } #endif @@ -368,12 +379,12 @@ int serverSupportsSSL(void) { #if defined(HAVE_OPENSSL) static void sslGenerateCertificate(const char *certificate, const char *serverName) { - debug("Auto-generating missing certificate \"%s\" for \"%s\"", + debug("SSL: auto-generating missing certificate \"%s\" for \"%s\"", certificate, serverName); pid_t pid = fork(); if (pid == -1) { - warn("Failed to generate self-signed certificate \"%s\"", certificate); + warn("SSL: failed to generate self-signed certificate \"%s\"", certificate); } else if (pid == 0) { int fd = NOINTR(open("/dev/null", O_RDONLY)); check(fd != -1); @@ -390,14 +401,14 @@ static void sslGenerateCertificate(const char *certificate, if (execlp("openssl", "openssl", "req", "-x509", "-nodes", "-days", "7300", "-newkey", "rsa:2048", "-keyout", certificate, "-out", certificate, "-subj", subject, (char *)NULL) < 0) { - warn("Failed to generate self-signed certificate \"%s\"", certificate); + warn("SSL: failed to generate self-signed certificate \"%s\"", certificate); free(subject); } } else { int status; check(NOINTR(waitpid(pid, &status, 0)) == pid); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - warn("Failed to generate self-signed certificate \"%s\"", certificate); + warn("SSL: failed to generate self-signed certificate \"%s\"", certificate); } } } @@ -595,29 +606,73 @@ static int sslSetCertificateFromFile(SSL_CTX *context, return rc; } +static void sslInfoCallback(const SSL *sslHndl, int type, int val) { + // Count the number of renegotiations for each SSL session. + if (type & SSL_CB_HANDSHAKE_START) { + struct HttpConnection *http = + (struct HttpConnection *) SSL_get_app_data(sslHndl); + http->ssl->renegotiationCount += 1; + } +} + static SSL_CTX *sslMakeContext(void) { + SSL_CTX *context; check(context = SSL_CTX_new(SSLv23_server_method())); - SSL_CTX_set_options(context, SSL_OP_ALL); - SSL_CTX_set_options(context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); + + long options = SSL_OP_ALL; + options |= SSL_OP_NO_SSLv2; + options |= SSL_OP_NO_SSLv3; + options |= SSL_OP_SINGLE_DH_USE; + #ifdef SSL_OP_NO_COMPRESSION - SSL_CTX_set_options(context, SSL_OP_NO_COMPRESSION); + options |= SSL_OP_NO_COMPRESSION; #endif -#if defined(HAVE_DLOPEN) + +#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION + options |= SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION; +#endif + + // Set default SSL options. + SSL_CTX_set_options(context, options); + + // Workaround for SSL_OP_NO_COMPRESSION with older OpenSSL versions. +#ifdef HAVE_DLOPEN if (SSL_COMP_get_compression_methods) { sk_SSL_COMP_zero(SSL_COMP_get_compression_methods()); } #elif OPENSSL_VERSION_NUMBER >= 0x00908000L sk_SSL_COMP_zero(SSL_COMP_get_compression_methods()); #endif - SSL_CTX_set_options(context, SSL_OP_SINGLE_DH_USE); -#ifdef SSL_OP_SINGLE_ECDH_USE + + // For Perfect Forward Secrecy (PFS) support we need to enable some additional + // SSL options, provide eliptic curve key object for handshake and add chipers + // suits with ECDHE handshake on top of the ciper list. +#ifdef HAVE_OPENSSL_EC SSL_CTX_set_options(context, SSL_OP_SINGLE_ECDH_USE); + SSL_CTX_set_options(context, SSL_OP_CIPHER_SERVER_PREFERENCE); + + EC_KEY *ecKey; + check(ecKey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); + SSL_CTX_set_tmp_ecdh(context, ecKey); + EC_KEY_free(ecKey); + + debug("SSL: support for PFS enabled..."); #endif -#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION - SSL_CTX_set_options(context, SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); -#endif - check(SSL_CTX_set_cipher_list(context, "HIGH:MEDIUM:!aNULL:!MD5")); + + check(SSL_CTX_set_cipher_list(context, + "ECDHE-RSA-AES256-GCM-SHA384:" + "ECDHE-RSA-AES128-GCM-SHA256:" + "ECDHE-RSA-AES256-SHA384:" + "ECDHE-RSA-AES128-SHA256:" + "ECDHE-RSA-AES256-SHA:" + "ECDHE-RSA-AES128-SHA:" + "ECDHE-RSA-DES-CBC3-SHA:" + "HIGH:MEDIUM:!RC4:!aNULL:!MD5")); + + SSL_CTX_set_info_callback(context, sslInfoCallback); + + debug("SSL: server context succesfully initialized..."); return context; } #endif @@ -634,7 +689,7 @@ static int sslSNICallback(SSL *sslHndl, int *al ATTR_UNUSED, } struct HttpConnection *http = (struct HttpConnection *)SSL_get_app_data(sslHndl); - debug("Received SNI callback for virtual host \"%s\" from \"%s:%d\"", + debug("SSL: received SNI callback for virtual host \"%s\" from \"%s:%d\"", name, httpGetPeerName(http), httpGetPort(http)); char *serverName; check(serverName = malloc(strlen(name)+2)); @@ -671,7 +726,7 @@ static int sslSNICallback(SSL *sslHndl, int *al ATTR_UNUSED, // the default certificate, instead. sslSetCertificateFromFile(context, certificate); } else { - warn("Could not find matching certificate \"%s\" for \"%s\"", + warn("SSL: could not find matching certificate \"%s\" for \"%s\"", certificate, serverName + 1); SSL_CTX_free(context); context = ssl->sslContext; @@ -748,7 +803,7 @@ void sslSetCertificate(struct SSLSupport *ssl, const char *filename, sslGenerateCertificate(defaultCertificate, he->h_name); } else { if (h_err) { - warn("Error getting host information: \"%s\".", hstrerror(h_err)); + warn("SSL: error getting host information: \"%s\".", hstrerror(h_err)); } sslGenerateCertificate(defaultCertificate, hostname); } @@ -757,7 +812,7 @@ void sslSetCertificate(struct SSLSupport *ssl, const char *filename, } } if (sslSetCertificateFromFile(ssl->sslContext, defaultCertificate) < 0) { - fatal("Cannot read valid certificate from \"%s\". " + fatal("SSL: cannot read valid certificate from \"%s\". " "Check file permissions and file format.", defaultCertificate); } valid_certificate: @@ -823,7 +878,7 @@ void sslSetCertificateFd(struct SSLSupport *ssl, int fd) { ssl->sslContext = sslMakeContext(); char *filename = sslFdToFilename(fd); if (!sslSetCertificateFromFd(ssl->sslContext, fd)) { - fatal("Cannot read valid certificate from %s. Check file format.", + fatal("SSL: cannot read valid certificate from %s. Check file format.", filename); } free(filename); @@ -996,7 +1051,7 @@ void sslFreeHndl(SSL **sslHndl) { // We do not know, how to fix this situation. Something must have // changed in the OpenSSL internals. Either, this is a new bug, or // somebody fixed the code in a way that we did not anticipate. - fatal("Unexpected corruption of OpenSSL data structures"); + fatal("SSL: unexpected corruption of OpenSSL data structures"); } } SSL_free(*sslHndl); diff --git a/libhttp/ssl.h b/libhttp/ssl.h index e1477ab..0be3830 100644 --- a/libhttp/ssl.h +++ b/libhttp/ssl.h @@ -61,6 +61,7 @@ #undef HAVE_OPENSSL typedef struct BIO BIO; typedef struct BIO_METHOD BIO_METHOD; +typedef struct EC_KEY EC_KEY; typedef struct SSL SSL; typedef struct SSL_CTX SSL_CTX; typedef struct SSL_METHOD SSL_METHOD; @@ -69,6 +70,12 @@ typedef struct X509 X509; #define SSL_ERROR_WANT_WRITE 3 #endif +// EC support was added to OpenSSL in 0.9.8, but it can be disabled in some +// distributions. +#if OPENSSL_VERSION_NUMBER >= 0x0090800fL && !defined(OPENSSL_NO_EC) +# define HAVE_OPENSSL_EC +#endif + #if defined(HAVE_DLOPEN) extern long (*x_BIO_ctrl)(BIO *, int, long, void *); extern BIO_METHOD *(*x_BIO_f_buffer)(void); @@ -77,15 +84,20 @@ extern BIO *(*x_BIO_new)(BIO_METHOD *); extern BIO *(*x_BIO_new_socket)(int, int); extern BIO *(*x_BIO_pop)(BIO *); extern BIO *(*x_BIO_push)(BIO *, BIO *); +#if defined(HAVE_OPENSSL_EC) +extern void (*x_EC_KEY_free)(EC_KEY *); +extern EC_KEY *(*x_EC_KEY_new_by_curve_name)(int); +#endif extern void (*x_ERR_clear_error)(void); -extern void (*x_ERR_clear_error)(void); -extern unsigned long (*x_ERR_peek_error)(void); extern unsigned long (*x_ERR_peek_error)(void); extern long (*x_SSL_CTX_callback_ctrl)(SSL_CTX *, int, void (*)(void)); extern int (*x_SSL_CTX_check_private_key)(const SSL_CTX *); extern long (*x_SSL_CTX_ctrl)(SSL_CTX *, int, long, void *); extern void (*x_SSL_CTX_free)(SSL_CTX *); extern SSL_CTX*(*x_SSL_CTX_new)(SSL_METHOD *); +extern int (*x_SSL_CTX_set_cipher_list)(SSL_CTX *, const char *); +extern void (*x_SSL_CTX_set_info_callback)(SSL_CTX *, + void (*)(const SSL *, int, int)); extern int (*x_SSL_CTX_use_PrivateKey_file)(SSL_CTX *, const char *, int); extern int (*x_SSL_CTX_use_PrivateKey_ASN1)(int, SSL_CTX *, const unsigned char *, long); @@ -111,7 +123,6 @@ extern int (*x_SSL_write)(SSL *, const void *, int); extern SSL_METHOD *(*x_SSLv23_server_method)(void); extern X509 * (*x_d2i_X509)(X509 **px, const unsigned char **in, int len); extern void (*x_X509_free)(X509 *a); -extern int (*x_SSL_CTX_set_cipher_list)(SSL_CTX *ctx, const char *str); extern void (*x_sk_zero)(void *st); extern void *(*x_SSL_COMP_get_compression_methods)(void); @@ -122,15 +133,17 @@ extern void *(*x_SSL_COMP_get_compression_methods)(void); #define BIO_new_socket x_BIO_new_socket #define BIO_pop x_BIO_pop #define BIO_push x_BIO_push +#define EC_KEY_free x_EC_KEY_free +#define EC_KEY_new_by_curve_name x_EC_KEY_new_by_curve_name #define ERR_clear_error x_ERR_clear_error -#define ERR_clear_error x_ERR_clear_error -#define ERR_peek_error x_ERR_peek_error #define ERR_peek_error x_ERR_peek_error #define SSL_CTX_callback_ctrl x_SSL_CTX_callback_ctrl #define SSL_CTX_check_private_key x_SSL_CTX_check_private_key #define SSL_CTX_ctrl x_SSL_CTX_ctrl #define SSL_CTX_free x_SSL_CTX_free #define SSL_CTX_new x_SSL_CTX_new +#define SSL_CTX_set_cipher_list x_SSL_CTX_set_cipher_list +#define SSL_CTX_set_info_callback x_SSL_CTX_set_info_callback #define SSL_CTX_use_PrivateKey_file x_SSL_CTX_use_PrivateKey_file #define SSL_CTX_use_PrivateKey_ASN1 x_SSL_CTX_use_PrivateKey_ASN1 #define SSL_CTX_use_certificate_file x_SSL_CTX_use_certificate_file @@ -154,13 +167,13 @@ extern void *(*x_SSL_COMP_get_compression_methods)(void); #define SSLv23_server_method x_SSLv23_server_method #define d2i_X509 x_d2i_X509 #define X509_free x_X509_free -#define SSL_CTX_set_cipher_list x_SSL_CTX_set_cipher_list #define sk_zero x_sk_zero #define SSL_COMP_get_compression_methods x_SSL_COMP_get_compression_methods #undef BIO_set_buffer_read_data #undef SSL_CTX_set_tlsext_servername_arg #undef SSL_CTX_set_tlsext_servername_callback +#undef SSL_CTX_set_tmp_ecdh #undef SSL_get_app_data #undef SSL_set_app_data #undef SSL_set_mode @@ -175,6 +188,9 @@ extern void *(*x_SSL_COMP_get_compression_methods)(void); (x_SSL_CTX_callback_ctrl(ctx, \ SSL_CTRL_SET_TLSEXT_SERVERNAME_CB, \ (void (*)(void))cb)) +#define SSL_CTX_set_tmp_ecdh(ctx, ecdh) \ + (x_SSL_CTX_ctrl(ctx, SSL_CTRL_SET_TMP_ECDH, \ + 0, (char *)ecdh)) #define SSL_get_app_data(s) (x_SSL_get_ex_data(s, 0)) #define SSL_set_app_data(s, arg) (x_SSL_set_ex_data(s, 0, (char *)arg)) #define SSL_set_mode(ssl, op) (x_SSL_ctrl((ssl), SSL_CTRL_MODE, (op), NULL)) @@ -185,6 +201,7 @@ struct SSLSupport { SSL_CTX *sslContext; char *sniCertificatePattern; int generateMissing; + int renegotiationCount; struct Trie sniContexts; };