Add support for ldaps:// SSL connections

main
Jiri Hruska 11 years ago
parent 214de5be41
commit f4d1da9bb7
  1. 204
      ngx_http_auth_ldap_module.c

@ -70,6 +70,9 @@ typedef struct {
ngx_flag_t cache_enabled; ngx_flag_t cache_enabled;
ngx_msec_t cache_expiration_time; ngx_msec_t cache_expiration_time;
size_t cache_size; size_t cache_size;
#if (NGX_OPENSSL)
ngx_ssl_t ssl;
#endif
} ngx_http_auth_ldap_main_conf_t; } ngx_http_auth_ldap_main_conf_t;
typedef struct { typedef struct {
@ -136,6 +139,11 @@ typedef struct ngx_http_auth_ldap_connection {
ngx_peer_connection_t conn; ngx_peer_connection_t conn;
ngx_event_t reconnect_event; ngx_event_t reconnect_event;
#if (NGX_OPENSSL)
ngx_pool_t *pool;
ngx_ssl_t *ssl;
#endif
ngx_queue_t queue; ngx_queue_t queue;
ngx_http_auth_ldap_ctx_t *rctx; ngx_http_auth_ldap_ctx_t *rctx;
@ -159,6 +167,7 @@ static ngx_int_t ngx_http_auth_ldap_init_worker(ngx_cycle_t *cycle);
static ngx_int_t ngx_http_auth_ldap_init(ngx_conf_t *cf); static ngx_int_t ngx_http_auth_ldap_init(ngx_conf_t *cf);
static ngx_int_t ngx_http_auth_ldap_init_cache(ngx_cycle_t *cycle); static ngx_int_t ngx_http_auth_ldap_init_cache(ngx_cycle_t *cycle);
static void ngx_http_auth_ldap_close_connection(ngx_http_auth_ldap_connection_t *c); static void ngx_http_auth_ldap_close_connection(ngx_http_auth_ldap_connection_t *c);
static void ngx_http_auth_ldap_read_handler(ngx_event_t *rev);
static ngx_int_t ngx_http_auth_ldap_init_connections(ngx_cycle_t *cycle); static ngx_int_t ngx_http_auth_ldap_init_connections(ngx_cycle_t *cycle);
static ngx_int_t ngx_http_auth_ldap_handler(ngx_http_request_t *r); static ngx_int_t ngx_http_auth_ldap_handler(ngx_http_request_t *r);
static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx, static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx,
@ -505,7 +514,24 @@ ngx_http_auth_ldap_parse_url(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server
return NGX_CONF_ERROR; return NGX_CONF_ERROR;
} }
if (ngx_strcmp(server->ludpp->lud_scheme, "ldap") == 0) {
return NGX_CONF_OK; return NGX_CONF_OK;
#if (NGX_OPENSSL)
} else if (ngx_strcmp(server->ludpp->lud_scheme, "ldaps") == 0) {
ngx_http_auth_ldap_main_conf_t *halmcf =
ngx_http_conf_get_module_main_conf(cf, ngx_http_auth_ldap_module);
ngx_uint_t protos = NGX_SSL_SSLv2 | NGX_SSL_SSLv3 |
NGX_SSL_TLSv1 | NGX_SSL_TLSv1_1 | NGX_SSL_TLSv1_2;
if (halmcf->ssl.ctx == NULL && ngx_ssl_create(&halmcf->ssl, protos, halmcf) != NGX_OK) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
#endif
} else {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "http_auth_ldap: Protocol \"%s://\" is not supported.",
server->ludpp->lud_scheme);
return NGX_CONF_ERROR;
}
} }
/** /**
@ -929,6 +955,14 @@ ngx_http_auth_ldap_close_connection(ngx_http_auth_ldap_connection_t *c)
if (c->conn.connection) { if (c->conn.connection) {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Closing connection (fd=%d)", ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Closing connection (fd=%d)",
c->conn.connection->fd); c->conn.connection->fd);
#if (NGX_OPENSSL)
if (c->conn.connection->ssl) {
c->conn.connection->ssl->no_wait_shutdown = 1;
(void) ngx_ssl_shutdown(c->conn.connection);
}
#endif
ngx_close_connection(c->conn.connection); ngx_close_connection(c->conn.connection);
c->conn.connection = NULL; c->conn.connection = NULL;
} }
@ -1043,40 +1077,23 @@ ngx_http_auth_ldap_dummy_write_handler(ngx_event_t *wev)
} }
static void static void
ngx_http_auth_ldap_connect_handler(ngx_event_t *wev) ngx_http_auth_ldap_connection_established(ngx_http_auth_ldap_connection_t *c)
{ {
ngx_peer_connection_t *conn; ngx_connection_t *conn;
ngx_http_auth_ldap_connection_t *c;
Sockbuf *sb; Sockbuf *sb;
ngx_int_t rc; ngx_int_t rc;
int keepalive;
struct berval cred; struct berval cred;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, "http_auth_ldap: Connect handler"); conn = c->conn.connection;
ngx_del_timer(conn->read);
conn = ((ngx_connection_t *) wev->data)->data; conn->write->handler = ngx_http_auth_ldap_dummy_write_handler;
c = conn->data;
if (ngx_handle_write_event(wev, 0) != NGX_OK) {
ngx_http_auth_ldap_close_connection(c);
return;
}
ngx_del_timer(conn->connection->read);
conn->connection->write->handler = ngx_http_auth_ldap_dummy_write_handler;
keepalive = 1;
if (setsockopt(conn->connection->fd, SOL_SOCKET, SO_KEEPALIVE, (const void *) &keepalive, sizeof(int)) == -1)
{
ngx_log_error(NGX_LOG_ALERT, c->log, ngx_socket_errno, "http_auth_ldap: setsockopt(SO_KEEPALIVE) failed");
}
/* Initialize OpenLDAP on the connection */ /* Initialize OpenLDAP on the connection */
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Initializing connection using URL \"%V\"", &c->server->url); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Initializing connection using URL \"%V\"", &c->server->url);
rc = ldap_init_fd(conn->connection->fd, LDAP_PROTO_EXT, (const char *) c->server->url.data, &c->ld); rc = ldap_init_fd(c->conn.connection->fd, LDAP_PROTO_EXT, (const char *) c->server->url.data, &c->ld);
if (rc != LDAP_SUCCESS) { if (rc != LDAP_SUCCESS) {
ngx_log_error(NGX_LOG_INFO, c->log, errno, "http_auth_ldap: ldap_init_fd() failed (%d: %s)", rc, ldap_err2string(rc)); ngx_log_error(NGX_LOG_INFO, c->log, errno, "http_auth_ldap: ldap_init_fd() failed (%d: %s)", rc, ldap_err2string(rc));
ngx_http_auth_ldap_close_connection(c); ngx_http_auth_ldap_close_connection(c);
@ -1109,13 +1126,90 @@ ngx_http_auth_ldap_connect_handler(ngx_event_t *wev)
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(conn->connection->read, 5000); /* TODO: Bind timeout */ ngx_add_timer(c->conn.connection->read, 5000); /* TODO: Bind timeout */
}
#if (NGX_OPENSSL)
static void
ngx_http_auth_ldap_ssl_handshake_handler(ngx_connection_t *conn)
{
ngx_http_auth_ldap_connection_t *c;
c = conn->data;
if (conn->ssl->handshaked) {
conn->read->handler = &ngx_http_auth_ldap_read_handler;
ngx_http_auth_ldap_connection_established(c);
return;
}
ngx_log_error(NGX_LOG_INFO, c->log, 0, "http_auth_ldap: SSL handshake failed");
ngx_http_auth_ldap_close_connection(c);
}
static void
ngx_http_auth_ldap_ssl_handshake(ngx_http_auth_ldap_connection_t *c)
{
ngx_int_t rc;
c->conn.connection->pool = c->pool;
rc = ngx_ssl_create_connection(c->ssl, c->conn.connection, NGX_SSL_BUFFER | NGX_SSL_CLIENT);
if (rc != NGX_OK) {
ngx_log_error(NGX_LOG_INFO, c->log, 0, "http_auth_ldap: SSL initialization failed");
ngx_http_auth_ldap_close_connection(c);
return;
}
c->log->action = "SSL handshaking to LDAP server";
rc = ngx_ssl_handshake(c->conn.connection);
if (rc == NGX_AGAIN) {
c->conn.connection->ssl->handler = &ngx_http_auth_ldap_ssl_handshake_handler;
return;
}
ngx_http_auth_ldap_ssl_handshake(c);
return;
}
#endif
static void
ngx_http_auth_ldap_connect_handler(ngx_event_t *wev)
{
ngx_connection_t *conn;
ngx_http_auth_ldap_connection_t *c;
int keepalive;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, "http_auth_ldap: Connect handler");
conn = wev->data;
c = conn->data;
if (ngx_handle_write_event(wev, 0) != NGX_OK) {
ngx_http_auth_ldap_close_connection(c);
return;
}
keepalive = 1;
if (setsockopt(conn->fd, SOL_SOCKET, SO_KEEPALIVE, (const void *) &keepalive, sizeof(int)) == -1)
{
ngx_log_error(NGX_LOG_ALERT, c->log, ngx_socket_errno, "http_auth_ldap: setsockopt(SO_KEEPALIVE) failed");
}
#if (NGX_OPENSSL)
if (ngx_strcmp(c->server->ludpp->lud_scheme, "ldaps") == 0) {
ngx_http_auth_ldap_ssl_handshake(c);
return;
}
#endif
ngx_http_auth_ldap_connection_established(c);
} }
static void static void
ngx_http_auth_ldap_read_handler(ngx_event_t *rev) ngx_http_auth_ldap_read_handler(ngx_event_t *rev)
{ {
ngx_peer_connection_t *conn; ngx_connection_t *conn;
ngx_http_auth_ldap_connection_t *c; ngx_http_auth_ldap_connection_t *c;
ngx_int_t rc; ngx_int_t rc;
struct timeval timeout = {0, 0}; struct timeval timeout = {0, 0};
@ -1126,7 +1220,7 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev)
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http_auth_ldap: Read handler"); ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http_auth_ldap: Read handler");
conn = ((ngx_connection_t *)rev->data)->data; conn = rev->data;
c = conn->data; c = conn->data;
if (c->ld == NULL) { if (c->ld == NULL) {
@ -1137,7 +1231,7 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev)
if (rev->timedout) { if (rev->timedout) {
ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "http_auth_ldap: Request timed out (state=%d)", c->state); ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "http_auth_ldap: Request timed out (state=%d)", c->state);
conn->connection->timedout = 1; conn->timedout = 1;
ngx_http_auth_ldap_close_connection(c); ngx_http_auth_ldap_close_connection(c);
return; return;
} }
@ -1182,7 +1276,7 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev)
if (ldap_msgtype(result) != LDAP_RES_BIND) { if (ldap_msgtype(result) != LDAP_RES_BIND) {
break; break;
} }
ngx_del_timer(conn->connection->read); ngx_del_timer(conn->read);
if (error_code == LDAP_SUCCESS) { if (error_code == LDAP_SUCCESS) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Initial bind successful"); ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Initial bind successful");
c->state = STATE_READY; c->state = STATE_READY;
@ -1252,7 +1346,8 @@ ngx_http_auth_ldap_read_handler(ngx_event_t *rev)
static void static void
ngx_http_auth_ldap_connect(ngx_http_auth_ldap_connection_t *c) ngx_http_auth_ldap_connect(ngx_http_auth_ldap_connection_t *c)
{ {
ngx_peer_connection_t *conn; ngx_peer_connection_t *pconn;
ngx_connection_t *conn;
ngx_addr_t *addr; ngx_addr_t *addr;
ngx_int_t rc; ngx_int_t rc;
@ -1261,16 +1356,15 @@ ngx_http_auth_ldap_connect(ngx_http_auth_ldap_connection_t *c)
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Connecting to LDAP server \"%V\".", ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: Connecting to LDAP server \"%V\".",
&addr->name); &addr->name);
conn = &c->conn; pconn = &c->conn;
conn->data = c; pconn->sockaddr = addr->sockaddr;
conn->sockaddr = addr->sockaddr; pconn->socklen = addr->socklen;
conn->socklen = addr->socklen; pconn->name = &addr->name;
conn->name = &addr->name; pconn->get = ngx_event_get_peer;
conn->get = ngx_event_get_peer; pconn->log = c->log;
conn->log = c->log; pconn->log_error = NGX_ERROR_ERR;
conn->log_error = NGX_ERROR_ERR;
rc = ngx_event_connect_peer(pconn);
rc = ngx_event_connect_peer(conn);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: ngx_event_connect_peer() -> %d.", rc); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http_auth_ldap: ngx_event_connect_peer() -> %d.", rc);
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\".",
@ -1279,11 +1373,12 @@ ngx_http_auth_ldap_connect(ngx_http_auth_ldap_connection_t *c)
return; return;
} }
ngx_add_timer(conn->connection->read, 10000); /* TODO: Connect timeout */ conn = pconn->connection;
conn->data = c;
conn->connection->data = conn; conn->pool = c->pool;
conn->connection->write->handler = ngx_http_auth_ldap_connect_handler; conn->write->handler = ngx_http_auth_ldap_connect_handler;
conn->connection->read->handler = ngx_http_auth_ldap_read_handler; conn->read->handler = ngx_http_auth_ldap_read_handler;
ngx_add_timer(conn->read, 10000); /* TODO: Connect timeout */
c->state = STATE_CONNECTING; c->state = STATE_CONNECTING;
} }
@ -1311,7 +1406,6 @@ ngx_http_auth_ldap_init_connections(ngx_cycle_t *cycle)
ngx_pool_cleanup_t *cleanup; ngx_pool_cleanup_t *cleanup;
ngx_connection_t *dummy_conn; ngx_connection_t *dummy_conn;
ngx_uint_t i, j; ngx_uint_t i, j;
ngx_int_t rc;
int option; int option;
halmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_auth_ldap_module); halmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_auth_ldap_module);
@ -1320,14 +1414,7 @@ ngx_http_auth_ldap_init_connections(ngx_cycle_t *cycle)
} }
option = LDAP_VERSION3; option = LDAP_VERSION3;
rc = ldap_set_option(NULL, LDAP_OPT_PROTOCOL_VERSION, &option); ldap_set_option(NULL, LDAP_OPT_PROTOCOL_VERSION, &option);
option = LDAP_OPT_X_TLS_ALLOW;
rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &option);
if (rc != LDAP_OPT_SUCCESS) {
ngx_log_error(NGX_LOG_WARN, cycle->log, 0, "http_auth_ldap: Unable to set TLS_REQUIRE_CERT option (%d: %s)",
rc, ldap_err2string(rc));
}
for (i = 0; i < halmcf->servers->nelts; i++) { for (i = 0; i < halmcf->servers->nelts; i++) {
server = &((ngx_http_auth_ldap_server_t *) halmcf->servers->elts)[i]; server = &((ngx_http_auth_ldap_server_t *) halmcf->servers->elts)[i];
@ -1345,6 +1432,9 @@ ngx_http_auth_ldap_init_connections(ngx_cycle_t *cycle)
return NGX_ERROR; return NGX_ERROR;
} }
cleanup->handler = &ngx_http_auth_ldap_connection_cleanup;
cleanup->data = c;
c->log = cycle->log; c->log = cycle->log;
c->server = server; c->server = server;
c->state = STATE_DISCONNECTED; c->state = STATE_DISCONNECTED;
@ -1357,10 +1447,12 @@ ngx_http_auth_ldap_init_connections(ngx_cycle_t *cycle)
c->reconnect_event.data = dummy_conn; c->reconnect_event.data = dummy_conn;
c->reconnect_event.handler = ngx_http_auth_ldap_reconnect_handler; c->reconnect_event.handler = ngx_http_auth_ldap_reconnect_handler;
ngx_http_auth_ldap_connect(c); #if (NGX_OPENSSL)
c->pool = cycle->pool;
c->ssl = &halmcf->ssl;
#endif
cleanup->handler = &ngx_http_auth_ldap_connection_cleanup; ngx_http_auth_ldap_connect(c);
cleanup->data = c;
} }
} }

Loading…
Cancel
Save