Merge pull request #100 from victorhahncastell/master

Provide SSL certificate verification
main
Valery Komarov 9 years ago
commit d0f2f829f7
  1. 1
      LICENSE
  2. 51
      README.md
  3. 97
      ngx_http_auth_ldap_module.c

@ -1,6 +1,7 @@
/** /**
* Copyright (C) 2011-2013 Valery Komarov <komarov@valerka.net> * Copyright (C) 2011-2013 Valery Komarov <komarov@valerka.net>
* Copyright (C) 2013 Jiri Hruska <jirka@fud.cz> * Copyright (C) 2013 Jiri Hruska <jirka@fud.cz>
* Copyright (C) 2015 Victor Hahn Castell <victor.hahn@flexoptix.net>
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without

@ -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 ## 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.

@ -62,6 +62,10 @@ typedef struct {
ngx_str_t group_attribute; ngx_str_t group_attribute;
ngx_flag_t group_attribute_dn; 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_group; /* array of ngx_http_complex_value_t */
ngx_array_t *require_user; /* 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; ngx_flag_t require_valid_user;
@ -378,7 +382,13 @@ ngx_http_auth_ldap_ldap_server(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
return NGX_CONF_ERROR; return NGX_CONF_ERROR;
} }
server->connections = i; 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,connect_timeout)
else CONF_MSEC_VALUE(cf,value,server,reconnect_timeout) else CONF_MSEC_VALUE(cf,value,server,reconnect_timeout)
else CONF_MSEC_VALUE(cf,value,server,bind_timeout) else CONF_MSEC_VALUE(cf,value,server,bind_timeout)
@ -1026,7 +1036,7 @@ ngx_http_auth_ldap_close_connection(ngx_http_auth_ldap_connection_t *c)
c->rctx = NULL; c->rctx = NULL;
if (c->state != STATE_DISCONNECTED) { if (c->state != STATE_DISCONNECTED) {
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); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Connection scheduled for reconnection in %d ms", c->server->reconnect_timeout);
} }
} }
@ -1129,7 +1139,7 @@ ngx_http_auth_ldap_dummy_write_handler(ngx_event_t *wev)
#if (NGX_OPENSSL) #if (NGX_OPENSSL)
/* Make sure the event hendlers are activated. */ /* Make sure the event handlers are activated. */
static ngx_int_t static ngx_int_t
ngx_http_auth_ldap_restore_handlers(ngx_connection_t *conn) ngx_http_auth_ldap_restore_handlers(ngx_connection_t *conn)
{ {
@ -1206,29 +1216,51 @@ 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); 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; 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); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: bind_timeout=%d", c->server->bind_timeout);
} }
#if (NGX_OPENSSL) #if (NGX_OPENSSL)
static void 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; ngx_http_auth_ldap_connection_t *c;
c = conn->data; c = conn->data;
if (conn->ssl->handshaked) { if (conn->ssl->handshaked) {
conn->read->handler = &ngx_http_auth_ldap_read_handler; // verify remote certificate
ngx_http_auth_ldap_restore_handlers(conn); X509 *cert = SSL_get_peer_certificate(conn->ssl->connection);
ngx_http_auth_ldap_connection_established(c); long verified = SSL_get_verify_result(conn->ssl->connection);
return;
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;
}
} }
ngx_log_error(NGX_LOG_ERR, c->log, 0, "http_auth_ldap: SSL handshake failed"); ngx_log_error(NGX_LOG_ERR, c->log, 0, "http_auth_ldap: SSL handshake failed");
ngx_http_auth_ldap_close_connection(c); 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 static void
ngx_http_auth_ldap_ssl_handshake(ngx_http_auth_ldap_connection_t *c) ngx_http_auth_ldap_ssl_handshake(ngx_http_auth_ldap_connection_t *c)
{ {
@ -1243,14 +1275,45 @@ ngx_http_auth_ldap_ssl_handshake(ngx_http_auth_ldap_connection_t *c)
} }
c->log->action = "SSL handshaking to LDAP server"; c->log->action = "SSL handshaking to LDAP server";
ngx_connection_t *transport = c->conn.connection;
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(c->conn.connection); rc = ngx_ssl_handshake(transport);
if (rc == NGX_AGAIN) { if (rc == NGX_AGAIN) {
c->conn.connection->ssl->handler = &ngx_http_auth_ldap_ssl_handshake_handler; transport->ssl->handler = callback;
return; return;
} }
ngx_http_auth_ldap_ssl_handshake_handler(c->conn.connection); (*callback)(transport);
return; return;
} }
#endif #endif
@ -1451,7 +1514,7 @@ ngx_http_auth_ldap_connect(ngx_http_auth_ldap_connection_t *c)
if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) { 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\".", ngx_log_error(NGX_LOG_ERR, c->log, 0, "http_auth_ldap: Unable to connect to LDAP server \"%V\".",
&addr->name); &addr->name);
ngx_add_timer(&c->reconnect_event, c->server->reconnect_timeout); ngx_add_timer(&c->reconnect_event, c->server->reconnect_timeout);
return; return;
} }
@ -1462,7 +1525,7 @@ ngx_http_auth_ldap_connect(ngx_http_auth_ldap_connection_t *c)
#endif #endif
conn->write->handler = ngx_http_auth_ldap_connect_handler; conn->write->handler = ngx_http_auth_ldap_connect_handler;
conn->read->handler = ngx_http_auth_ldap_read_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); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: connect_timeout=%d.", c->server->connect_timeout);
@ -1632,7 +1695,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 * 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. * 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) - * It is quite possible that we reach this if while not having sent a request yet (ctx->iteration == 0) -
@ -1652,7 +1715,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->server = ((ngx_http_auth_ldap_server_t **) conf->servers->elts)[ctx->server_index];
ctx->outcome = OUTCOME_UNCERTAIN; 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); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http_auth_ldap: request_timeout=%d",ctx->server->request_timeout);
@ -2014,7 +2077,7 @@ ngx_http_auth_ldap_check_bind(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *c
ctx->c->msgid); ctx->c->msgid);
ctx->c->state = STATE_BINDING; ctx->c->state = STATE_BINDING;
ctx->iteration++; ctx->iteration++;
return NGX_AGAIN; return NGX_AGAIN;
} }

Loading…
Cancel
Save