From 2e3e8a9e147a0446b5097976695fa6cc5cb6f172 Mon Sep 17 00:00:00 2001 From: Victor Hahn Date: Thu, 4 Feb 2016 08:43:07 +0100 Subject: [PATCH] Verify certificate CN/SAN --- LICENSE | 2 +- README.md | 2 + ngx_http_auth_ldap_module.c | 79 ++++++++++++++++++++++++++++--------- 3 files changed, 63 insertions(+), 20 deletions(-) diff --git a/LICENSE b/LICENSE index 6cbc170..9dd01ff 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ /** * Copyright (C) 2011-2013 Valery Komarov * Copyright (C) 2013 Jiri Hruska - * Copyright (C) 2015 Victor Hahn Castell + * Copyright (C) 2015-2016 Victor Hahn Castell * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/README.md b/README.md index 2bd7638..6af1153 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,8 @@ accepted which exposes you to possible man-in-the-middle attacks. Note that the certificate will need to be signed by a proper CA trusted by your system if this is enabled. See below how to trust CAs without installing them system-wide. +This options needs OpenSSL >= 1.0.2; it is unavailable if compiled with older versions. + ## ssl_ca_file expected value: file path diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index 8049fa1..75b9e3f 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -1,6 +1,7 @@ /** * Copyright (C) 2011-2013 Valery Komarov * Copyright (C) 2013 Jiri Hruska + * Copyright (C) 2015-2016 Victor Hahn Castell * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,6 +32,11 @@ #include #include +// used for manual warnings +#define XSTR(x) STR(x) +#define STR(x) #x +#pragma GCC diagnostic warning "-Wcpp" + #ifndef LDAP_PROTO_EXT /* Some OpenLDAP headers are accidentally missing ldap_init_fd() etc. */ @@ -383,7 +389,15 @@ ngx_http_auth_ldap_ldap_server(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) } server->connections = i; } else if (ngx_strcmp(value[0].data, "ssl_check_cert") == 0 && ngx_strcmp(value[1].data, "on") == 0) { + #if OPENSSL_VERSION_NUMBER >= 0x10002000 server->ssl_check_cert = 1; + #else + #warning "http_auth_ldap: Compiling with OpenSSL < 1.0.2, certificate verification will be unavailable. OPENSSL_VERSION_NUMBER == " XSTR(OPENSSL_VERSION_NUMBER) + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "http_auth_ldap: 'ssl_cert_check': cannot verify remote certificate's domain name because " + "your version of OpenSSL is too old. " + "Please install OpenSSL >= 1.02 and recompile nginx."); + #endif } else if (ngx_strcmp(value[0].data, "ssl_ca_dir") == 0) { server->ssl_ca_dir = value[1]; } else if (ngx_strcmp(value[0].data, "ssl_ca_file") == 0) { @@ -1224,30 +1238,57 @@ static void ngx_http_auth_ldap_ssl_handshake_handler(ngx_connection_t *conn, ngx_flag_t validate) { ngx_http_auth_ldap_connection_t *c; - c = conn->data; if (conn->ssl->handshaked) { - // verify remote certificate - X509 *cert = SSL_get_peer_certificate(conn->ssl->connection); - long verified = SSL_get_verify_result(conn->ssl->connection); - - if (!validate || (cert && verified == X509_V_OK) ) { // everything fine - conn->read->handler = &ngx_http_auth_ldap_read_handler; - ngx_http_auth_ldap_restore_handlers(conn); - ngx_http_auth_ldap_connection_established(c); - return; - } else { // smells fishy - ngx_log_error(NGX_LOG_ERR, c->log, 0, - "http_auth_ldap: Remote side presented invalid SSL certificate: error %l, %s", - verified, X509_verify_cert_error_string(verified)); - ngx_http_auth_ldap_close_connection(c); - return; + #if OPENSSL_VERSION_NUMBER >= 0x10002000 + if (validate) { // verify remote certificate if requested + X509 *cert = SSL_get_peer_certificate(conn->ssl->connection); + long chain_verified = SSL_get_verify_result(conn->ssl->connection); + + int addr_verified; + char *hostname = c->server->ludpp->lud_host; + addr_verified = X509_check_host(cert, hostname, 0, 0, 0); + + if (!addr_verified) { // domain not in cert? try IP + size_t len; // get IP length + if (conn->sockaddr->sa_family == 4) len = 4; + else if (conn->sockaddr->sa_family == 6) len = 16; + else { // very unlikely indeed + ngx_http_auth_ldap_close_connection(c); + return; + } + addr_verified = X509_check_ip(cert, (const unsigned char*)conn->sockaddr->sa_data, len, 0); + } + + // Find anything fishy? + if ( !(cert && addr_verified && chain_verified == X509_V_OK) ) { + if (!addr_verified) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "http_auth_ldap: Remote side presented invalid SSL certificate: " + "does not match address (neither server's domain nor IP in certificate's CN or SAN)"); + fprintf(stderr, "DEBUG: SSL cert domain mismatch\n"); fflush(stderr); + } else { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "http_auth_ldap: Remote side presented invalid SSL certificate: error %l, %s", + chain_verified, X509_verify_cert_error_string(chain_verified)); + } + ngx_http_auth_ldap_close_connection(c); + return; + } } - } + #endif - ngx_log_error(NGX_LOG_ERR, c->log, 0, "http_auth_ldap: SSL handshake failed"); - ngx_http_auth_ldap_close_connection(c); + // handshaked validation successful -- or not required in the first place + conn->read->handler = &ngx_http_auth_ldap_read_handler; + ngx_http_auth_ldap_restore_handlers(conn); + ngx_http_auth_ldap_connection_established(c); + return; + } + else { // handshake failed + ngx_log_error(NGX_LOG_ERR, c->log, 0, "http_auth_ldap: SSL handshake failed"); + ngx_http_auth_ldap_close_connection(c); + } } static void