From 7802d53f0a0f983ee1a923d0232e20b4fe30949d Mon Sep 17 00:00:00 2001 From: Victor Hahn Castell Date: Mon, 28 Sep 2015 18:39:02 +0200 Subject: [PATCH 1/4] Fix typo --- ngx_http_auth_ldap_module.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index 4ca1d1e..0bfb513 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -1128,7 +1128,7 @@ ngx_http_auth_ldap_dummy_write_handler(ngx_event_t *wev) #if (NGX_OPENSSL) -/* Make sure the event hendlers are activated. */ +/* Make sure the event handlers are activated. */ static ngx_int_t ngx_http_auth_ldap_restore_handlers(ngx_connection_t *conn) { From 65522703ae8f2ea28c5194aa723a4449176a5ead Mon Sep 17 00:00:00 2001 From: Victor Hahn Date: Mon, 28 Sep 2015 22:13:26 +0200 Subject: [PATCH 2/4] Verify remote SSL certificate --- ngx_http_auth_ldap_module.c | 51 ++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index 0bfb513..97150e8 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -378,7 +378,7 @@ ngx_http_auth_ldap_ldap_server(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) return NGX_CONF_ERROR; } server->connections = i; - } + } else CONF_MSEC_VALUE(cf,value,server,connect_timeout) else CONF_MSEC_VALUE(cf,value,server,reconnect_timeout) else CONF_MSEC_VALUE(cf,value,server,bind_timeout) @@ -1025,7 +1025,7 @@ ngx_http_auth_ldap_close_connection(ngx_http_auth_ldap_connection_t *c) c->rctx = NULL; if (c->state != STATE_DISCONNECTED) { c->state = STATE_DISCONNECTED; - ngx_add_timer(&c->reconnect_event, c->server->reconnect_timeout); + ngx_add_timer(&c->reconnect_event, c->server->reconnect_timeout); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Connection scheduled for reconnection in %d ms", c->server->reconnect_timeout); } } @@ -1205,7 +1205,7 @@ ngx_http_auth_ldap_connection_established(ngx_http_auth_ldap_connection_t *c) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: ldap_sasl_bind() -> msgid=%d", c->msgid); c->state = STATE_INITIAL_BINDING; - ngx_add_timer(c->conn.connection->read, c->server->bind_timeout); + ngx_add_timer(c->conn.connection->read, c->server->bind_timeout); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: bind_timeout=%d", c->server->bind_timeout); } @@ -1218,10 +1218,22 @@ ngx_http_auth_ldap_ssl_handshake_handler(ngx_connection_t *conn) c = conn->data; if (conn->ssl->handshaked) { - conn->read->handler = &ngx_http_auth_ldap_read_handler; - ngx_http_auth_ldap_restore_handlers(conn); - ngx_http_auth_ldap_connection_established(c); - return; + // verify remote certificate + X509 *cert = SSL_get_peer_certificate(conn->ssl->connection); + long verified = SSL_get_verify_result(conn->ssl->connection); + + if (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; + } } ngx_log_error(NGX_LOG_ERR, c->log, 0, "http_auth_ldap: SSL handshake failed"); @@ -1242,14 +1254,23 @@ ngx_http_auth_ldap_ssl_handshake(ngx_http_auth_ldap_connection_t *c) } c->log->action = "SSL handshaking to LDAP server"; + ngx_connection_t *transport = c->conn.connection; - rc = ngx_ssl_handshake(c->conn.connection); + //int setcode = SSL_CTX_load_verify_locations(transport->ssl->connection->ctx, "file", "dir"); + int setcode = SSL_CTX_set_default_verify_paths(transport->ssl->connection->ctx); + if (setcode != 1) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "http_auth_ldap: SSL initialization failed. Could not set CA certificate location. " + "Error code: %lu", ERR_get_error()); + } + + rc = ngx_ssl_handshake(transport); if (rc == NGX_AGAIN) { - c->conn.connection->ssl->handler = &ngx_http_auth_ldap_ssl_handshake_handler; + transport->ssl->handler = &ngx_http_auth_ldap_ssl_handshake_handler; return; } - ngx_http_auth_ldap_ssl_handshake_handler(c->conn.connection); + ngx_http_auth_ldap_ssl_handshake_handler(transport); return; } #endif @@ -1450,7 +1471,7 @@ ngx_http_auth_ldap_connect(ngx_http_auth_ldap_connection_t *c) if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "http_auth_ldap: Unable to connect to LDAP server \"%V\".", &addr->name); - ngx_add_timer(&c->reconnect_event, c->server->reconnect_timeout); + ngx_add_timer(&c->reconnect_event, c->server->reconnect_timeout); return; } @@ -1461,7 +1482,7 @@ ngx_http_auth_ldap_connect(ngx_http_auth_ldap_connection_t *c) #endif conn->write->handler = ngx_http_auth_ldap_connect_handler; conn->read->handler = ngx_http_auth_ldap_read_handler; - ngx_add_timer(conn->read, c->server->connect_timeout); + ngx_add_timer(conn->read, c->server->connect_timeout); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: connect_timeout=%d.", c->server->connect_timeout); @@ -1631,7 +1652,7 @@ ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t /* * If we are not starting up a request (ctx->phase != PHASE_START) and we actually already - * sent a request (ctx->iteration > 0) and didn't receive a reply yet (!ctx->replied) we + * sent a request (ctx->iteration > 0) and didn't receive a reply yet (!ctx->replied) we * ask to be called again at a later time when we hopefully have received a reply. * * It is quite possible that we reach this if while not having sent a request yet (ctx->iteration == 0) - @@ -1651,7 +1672,7 @@ ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t ctx->server = ((ngx_http_auth_ldap_server_t **) conf->servers->elts)[ctx->server_index]; ctx->outcome = OUTCOME_UNCERTAIN; - ngx_add_timer(r->connection->write, ctx->server->request_timeout); + ngx_add_timer(r->connection->write, ctx->server->request_timeout); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: request_timeout=%d",ctx->server->request_timeout); @@ -2013,7 +2034,7 @@ ngx_http_auth_ldap_check_bind(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *c ctx->c->msgid); ctx->c->state = STATE_BINDING; ctx->iteration++; - + return NGX_AGAIN; } From 6389f806d546783c62d15bdb891c14b9437d4fce Mon Sep 17 00:00:00 2001 From: Victor Hahn Date: Tue, 29 Sep 2015 12:41:20 +0200 Subject: [PATCH 3/4] Expose SSL certificate verification as config option --- ngx_http_auth_ldap_module.c | 62 +++++++++++++++++++++++++++++++------ 1 file changed, 52 insertions(+), 10 deletions(-) diff --git a/ngx_http_auth_ldap_module.c b/ngx_http_auth_ldap_module.c index 97150e8..8049fa1 100644 --- a/ngx_http_auth_ldap_module.c +++ b/ngx_http_auth_ldap_module.c @@ -62,6 +62,10 @@ typedef struct { ngx_str_t group_attribute; ngx_flag_t group_attribute_dn; + ngx_flag_t ssl_check_cert; + ngx_str_t ssl_ca_dir; + ngx_str_t ssl_ca_file; + ngx_array_t *require_group; /* array of ngx_http_complex_value_t */ ngx_array_t *require_user; /* array of ngx_http_complex_value_t */ ngx_flag_t require_valid_user; @@ -378,6 +382,12 @@ ngx_http_auth_ldap_ldap_server(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) return NGX_CONF_ERROR; } server->connections = i; + } else if (ngx_strcmp(value[0].data, "ssl_check_cert") == 0 && ngx_strcmp(value[1].data, "on") == 0) { + server->ssl_check_cert = 1; + } 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) { + server->ssl_ca_file = value[1]; } else CONF_MSEC_VALUE(cf,value,server,connect_timeout) else CONF_MSEC_VALUE(cf,value,server,reconnect_timeout) @@ -1211,7 +1221,7 @@ ngx_http_auth_ldap_connection_established(ngx_http_auth_ldap_connection_t *c) #if (NGX_OPENSSL) static void -ngx_http_auth_ldap_ssl_handshake_handler(ngx_connection_t *conn) +ngx_http_auth_ldap_ssl_handshake_handler(ngx_connection_t *conn, ngx_flag_t validate) { ngx_http_auth_ldap_connection_t *c; @@ -1222,7 +1232,7 @@ ngx_http_auth_ldap_ssl_handshake_handler(ngx_connection_t *conn) X509 *cert = SSL_get_peer_certificate(conn->ssl->connection); long verified = SSL_get_verify_result(conn->ssl->connection); - if (cert && verified == X509_V_OK) { // everything fine + 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); @@ -1240,6 +1250,16 @@ ngx_http_auth_ldap_ssl_handshake_handler(ngx_connection_t *conn) ngx_http_auth_ldap_close_connection(c); } +static void +ngx_http_auth_ldap_ssl_handshake_validating_handler(ngx_connection_t *conn) +{ ngx_http_auth_ldap_ssl_handshake_handler(conn, 1); } + +static void +ngx_http_auth_ldap_ssl_handshake_non_validating_handler(ngx_connection_t *conn) +{ ngx_http_auth_ldap_ssl_handshake_handler(conn, 0); } + +typedef void (*ngx_http_auth_ldap_ssl_callback)(ngx_connection_t *conn); + static void ngx_http_auth_ldap_ssl_handshake(ngx_http_auth_ldap_connection_t *c) { @@ -1256,21 +1276,43 @@ ngx_http_auth_ldap_ssl_handshake(ngx_http_auth_ldap_connection_t *c) c->log->action = "SSL handshaking to LDAP server"; ngx_connection_t *transport = c->conn.connection; - //int setcode = SSL_CTX_load_verify_locations(transport->ssl->connection->ctx, "file", "dir"); - int setcode = SSL_CTX_set_default_verify_paths(transport->ssl->connection->ctx); - if (setcode != 1) { - ngx_log_error(NGX_LOG_ERR, c->log, 0, - "http_auth_ldap: SSL initialization failed. Could not set CA certificate location. " - "Error code: %lu", ERR_get_error()); + ngx_http_auth_ldap_ssl_callback callback; + if (c->server->ssl_check_cert) { + // load CA certificates: custom ones if specified, default ones instead + if (c->server->ssl_ca_file.data || c->server->ssl_ca_dir.data) { + int setcode = SSL_CTX_load_verify_locations(transport->ssl->connection->ctx, + (char*)(c->server->ssl_ca_file.data), (char*)(c->server->ssl_ca_dir.data)); + if (setcode != 1) { + unsigned long error_code = ERR_get_error(); + char *error_msg = ERR_error_string(error_code, NULL); + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "http_auth_ldap: SSL initialization failed. Could not set custom CA certificate location. " + "Error: %lu, %s", error_code, error_msg); + } + } + int setcode = SSL_CTX_set_default_verify_paths(transport->ssl->connection->ctx); + if (setcode != 1) { + unsigned long error_code = ERR_get_error(); + char *error_msg = ERR_error_string(error_code, NULL); + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "http_auth_ldap: SSL initialization failed. Could not use default CA certificate location. " + "Error: %lu, %s", error_code, error_msg); + } + + // use validating version of next function + callback = &ngx_http_auth_ldap_ssl_handshake_validating_handler; + } else { + // use non-validating version of next function + callback = &ngx_http_auth_ldap_ssl_handshake_non_validating_handler; } rc = ngx_ssl_handshake(transport); if (rc == NGX_AGAIN) { - transport->ssl->handler = &ngx_http_auth_ldap_ssl_handshake_handler; + transport->ssl->handler = callback; return; } - ngx_http_auth_ldap_ssl_handshake_handler(transport); + (*callback)(transport); return; } #endif From acb13cffafc7f70036188337e450b8655c66b23d Mon Sep 17 00:00:00 2001 From: Victor Hahn Date: Tue, 29 Sep 2015 12:52:42 +0200 Subject: [PATCH 4/4] Amend documentation --- LICENSE | 1 + README.md | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 46319ca..6cbc170 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ /** * Copyright (C) 2011-2013 Valery Komarov * Copyright (C) 2013 Jiri Hruska + * Copyright (C) 2015 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 cfc1996..2bd7638 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Check HTTP_AUTH_LDAP options ``` -[*] HTTP_AUTH_LDAP 3rd party http_auth_ldap module +[*] HTTP_AUTH_LDAP 3rd party http_auth_ldap module ``` ## Linux @@ -71,3 +71,52 @@ And add required servers in correct order into your location/server directive: } ``` + +# Available config parameters + +## url +expected value: string + +Available URL schemes: ldap://, ldaps:// + +## binddn +expected value: string + +## binddn_passwd +expected value: string + +## group_attribute +expected value: string + +## group_attribute_is_dn +expected value: on or off, default off + +## require +expected value: valid_user, user, group + +## satisfy +expected value: all, any + +## connections +expected value: a number greater than 0 + +## ssl_check_cert +expected value: on or off, default off + +Verify the remote certificate for LDAPs connections. If disabled, any remote ceritificate will be +accepted which exposes you to possible man-in-the-middle attacks. Note that the server's +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. + +## ssl_ca_file +expected value: file path + +Trust the CA certificate in this file (see ssl_check_cert above). + +## ssl_ca_dir +expected value: directory path + +Trust all CA certificates in this directory (see ssl_check_cert above). + +Note that you need to provide hash-based symlinks in the directory for this to work; +you'll basically need to run OpenSSL's c_rehash command in this directory.