[PATCH] mail_{ssl, auth_http}_module: add support for SSL client certificates
Sven Peter
sven at ha.cki.ng
Mon Jan 13 10:29:26 UTC 2014
# HG changeset patch
# User Sven Peter <sven at ha.cki.ng>
# Date 1389607375 -3600
# Mon Jan 13 11:02:55 2014 +0100
# Node ID 8744640301ae0f7d4c16108e68c9ae6eb60f2213
# Parent 4aa64f6950313311e0d322a2af1788edeb7f036c
mail_{ssl,auth_http}_module: add support for SSL client certificates
This patch adds support for SSL client certificates to the mail proxy
capabilities of nginx both for STARTTLS and SSL mode.
Just like the HTTP SSL module a root CA is defined in the mail section
of the configuration file. Verification can be optional or mandatory.
Additionally, the result of the verification is exposed to the
auth http backend via the SSL-Verify, SSL-Subject-DN and SSL-Issuer-DN
HTTP headers.
diff -r 4aa64f695031 -r 8744640301ae src/mail/ngx_mail_auth_http_module.c
--- a/src/mail/ngx_mail_auth_http_module.c Sat Jan 04 03:32:22 2014 +0400
+++ b/src/mail/ngx_mail_auth_http_module.c Mon Jan 13 11:02:55 2014 +0100
@@ -1144,6 +1144,11 @@
ngx_buf_t *b;
ngx_str_t login, passwd;
ngx_mail_core_srv_conf_t *cscf;
+ ngx_str_t ssl_client_verify = {0, NULL};
+ ngx_str_t ssl_client_raw_s_dn = {0, NULL};
+ ngx_str_t ssl_client_raw_i_dn = {0, NULL};
+ ngx_str_t ssl_client_s_dn = {0, NULL};
+ ngx_str_t ssl_client_i_dn = {0, NULL};
if (ngx_mail_auth_http_escape(pool, &s->login, &login) != NGX_OK) {
return NULL;
@@ -1153,6 +1158,29 @@
return NULL;
}
+ // ssl_client_verify doesn't need to be escaped since it comes from nginx itself
+#if (NGX_MAIL_SSL)
+ ngx_ssl_get_client_verify(s->connection, pool, &ssl_client_verify);
+ ngx_ssl_get_subject_dn(s->connection, pool, &ssl_client_s_dn);
+ ngx_ssl_get_subject_dn(s->connection, pool, &ssl_client_i_dn);
+
+ if (ssl_client_raw_s_dn.len != 0) {
+ if (ngx_mail_auth_http_escape(pool, &ssl_client_raw_s_dn, &ssl_client_s_dn) != NGX_OK) {
+ return NULL;
+ }
+ }
+
+ if (ssl_client_raw_i_dn.len != 0) {
+ if (ngx_mail_auth_http_escape(pool, &ssl_client_raw_i_dn, &ssl_client_i_dn) != NGX_OK) {
+ return NULL;
+ }
+ }
+#else
+ // avoid -Wunused-variable
+ (void)ssl_client_raw_i_dn;
+ (void)ssl_client_raw_s_dn;
+#endif
+
cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
len = sizeof("GET ") - 1 + ahcf->uri.len + sizeof(" HTTP/1.0" CRLF) - 1
@@ -1173,6 +1201,9 @@
+ sizeof("Auth-SMTP-Helo: ") - 1 + s->smtp_helo.len
+ sizeof("Auth-SMTP-From: ") - 1 + s->smtp_from.len
+ sizeof("Auth-SMTP-To: ") - 1 + s->smtp_to.len
+ + sizeof("SSL-Verify: ") - 1 + ssl_client_verify.len + sizeof(CRLF) - 1
+ + sizeof("SSL-Subject-DN: ") - 1 + ssl_client_s_dn.len + sizeof(CRLF) - 1
+ + sizeof("SSL-Issuer-DN: ") - 1 + ssl_client_i_dn.len + sizeof(CRLF) - 1
+ ahcf->header.len
+ sizeof(CRLF) - 1;
@@ -1255,6 +1286,20 @@
}
+ if (ssl_client_verify.len && ssl_client_s_dn.len && ssl_client_i_dn.len) {
+ b->last = ngx_cpymem(b->last, "SSL-Verify: ", sizeof("SSL-Verify: ") - 1);
+ b->last = ngx_copy(b->last, ssl_client_verify.data, ssl_client_verify.len);
+ *b->last++ = CR; *b->last++ = LF;
+
+ b->last = ngx_cpymem(b->last, "SSL-Subject-DN: ", sizeof("SSL-Subject-DN: ") - 1);
+ b->last = ngx_copy(b->last, ssl_client_s_dn.data, ssl_client_s_dn.len);
+ *b->last++ = CR; *b->last++ = LF;
+
+ b->last = ngx_cpymem(b->last, "SSL-Issuer-DN: ", sizeof("SSL-Issuer-DN: ") - 1);
+ b->last = ngx_copy(b->last, ssl_client_i_dn.data, ssl_client_i_dn.len);
+ *b->last++ = CR; *b->last++ = LF;
+ }
+
if (ahcf->header.len) {
b->last = ngx_copy(b->last, ahcf->header.data, ahcf->header.len);
}
diff -r 4aa64f695031 -r 8744640301ae src/mail/ngx_mail_handler.c
--- a/src/mail/ngx_mail_handler.c Sat Jan 04 03:32:22 2014 +0400
+++ b/src/mail/ngx_mail_handler.c Mon Jan 13 11:02:55 2014 +0100
@@ -236,11 +236,40 @@
{
ngx_mail_session_t *s;
ngx_mail_core_srv_conf_t *cscf;
+ ngx_mail_ssl_conf_t *sslcf;
if (c->ssl->handshaked) {
s = c->data;
+ sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+ if (sslcf->verify != NGX_MAIL_SSL_VERIFY_OFF) {
+ long rc;
+ rc = SSL_get_verify_result(c->ssl->connection);
+
+ if (rc != X509_V_OK &&
+ (sslcf->verify != NGX_MAIL_SSL_VERIFY_OPTIONAL_NO_CA && ngx_ssl_verify_error_optional(rc))) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client SSL certificate verify error: (%l:%s)",
+ rc, X509_verify_cert_error_string(rc));
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ if (sslcf->verify == NGX_MAIL_SSL_VERIFY_ON) {
+ X509 *cert;
+ cert = SSL_get_peer_certificate(c->ssl->connection);
+
+ if (cert == NULL) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent no required SSL certificate");
+ ngx_mail_close_connection(c);
+ return;
+ }
+ X509_free(cert);
+ }
+ }
+
if (s->starttls) {
cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
diff -r 4aa64f695031 -r 8744640301ae src/mail/ngx_mail_ssl_module.c
--- a/src/mail/ngx_mail_ssl_module.c Sat Jan 04 03:32:22 2014 +0400
+++ b/src/mail/ngx_mail_ssl_module.c Mon Jan 13 11:02:55 2014 +0100
@@ -43,6 +43,13 @@
{ ngx_null_string, 0 }
};
+static ngx_conf_enum_t ngx_mail_ssl_verify[] = {
+ { ngx_string("off"), NGX_MAIL_SSL_VERIFY_OFF },
+ { ngx_string("on"), NGX_MAIL_SSL_VERIFY_ON },
+ { ngx_string("optional"), NGX_MAIL_SSL_VERIFY_OPTIONAL },
+ { ngx_string("optional_no_ca"), NGX_MAIL_SSL_VERIFY_OPTIONAL_NO_CA },
+ { ngx_null_string, 0 }
+};
static ngx_command_t ngx_mail_ssl_commands[] = {
@@ -130,7 +137,40 @@
offsetof(ngx_mail_ssl_conf_t, session_timeout),
NULL },
- ngx_null_command
+ {
+ ngx_string("ssl_verify_client"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_enum_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, verify),
+ &ngx_mail_ssl_verify
+ },
+ {
+ ngx_string("ssl_verify_depth"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_num_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, verify_depth),
+ NULL
+ },
+ {
+ ngx_string("ssl_client_certificate"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, client_certificate),
+ NULL
+ },
+ {
+ ngx_string("ssl_trusted_certificate"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, trusted_certificate),
+ NULL
+ },
+
+ ngx_null_command
};
@@ -184,6 +224,8 @@
* scf->ecdh_curve = { 0, NULL };
* scf->ciphers = { 0, NULL };
* scf->shm_zone = NULL;
+ * scf->client_certificate = { 0, NULL };
+ * scf->trusted_certificate = { 0, NULL };
*/
scf->enable = NGX_CONF_UNSET;
@@ -192,6 +234,8 @@
scf->builtin_session_cache = NGX_CONF_UNSET;
scf->session_timeout = NGX_CONF_UNSET;
scf->session_ticket_keys = NGX_CONF_UNSET_PTR;
+ scf->verify = NGX_CONF_UNSET_UINT;
+ scf->verify_depth = NGX_CONF_UNSET_UINT;
return scf;
}
@@ -230,6 +274,11 @@
ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS);
+ ngx_conf_merge_uint_value(conf->verify, prev->verify, NGX_MAIL_SSL_VERIFY_OFF);
+ ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1);
+
+ ngx_conf_merge_str_value(conf->client_certificate, prev->client_certificate, "");
+ ngx_conf_merge_str_value(conf->trusted_certificate, prev->trusted_certificate, "");
conf->ssl.log = cf->log;
@@ -310,6 +359,21 @@
return NGX_CONF_ERROR;
}
+ if (conf->verify) {
+ if (conf->client_certificate.len == 0 && conf->verify != NGX_MAIL_SSL_VERIFY_OPTIONAL_NO_CA) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no ssl_client_certificate for ssl_client_verify");
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_ssl_client_certificate(cf, &conf->ssl,
+ &conf->client_certificate,
+ conf->verify_depth)
+ != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
if (conf->prefer_server_ciphers) {
SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
}
diff -r 4aa64f695031 -r 8744640301ae src/mail/ngx_mail_ssl_module.h
--- a/src/mail/ngx_mail_ssl_module.h Sat Jan 04 03:32:22 2014 +0400
+++ b/src/mail/ngx_mail_ssl_module.h Mon Jan 13 11:02:55 2014 +0100
@@ -37,8 +37,14 @@
ngx_str_t dhparam;
ngx_str_t ecdh_curve;
+ ngx_str_t client_certificate;
+ ngx_str_t trusted_certificate;
+
ngx_str_t ciphers;
+ ngx_uint_t verify;
+ ngx_uint_t verify_depth;
+
ngx_shm_zone_t *shm_zone;
ngx_array_t *session_ticket_keys;
@@ -47,6 +53,13 @@
ngx_uint_t line;
} ngx_mail_ssl_conf_t;
+enum ngx_mail_ssl_verify_enum {
+ NGX_MAIL_SSL_VERIFY_OFF = 0,
+ NGX_MAIL_SSL_VERIFY_ON,
+ NGX_MAIL_SSL_VERIFY_OPTIONAL,
+ NGX_MAIL_SSL_VERIFY_OPTIONAL_NO_CA,
+};
+
extern ngx_module_t ngx_mail_ssl_module;
More information about the nginx-devel
mailing list