[PATCH] Mail: added support for SSL client certificate
Filipe da Silva
fdasilvayy at gmail.com
Sat Jan 25 08:47:09 UTC 2014
# HG changeset patch
# User Franck Levionnois <flevionnois at gmail.com>
# Date 1390577176 -3600
# Fri Jan 24 16:26:16 2014 +0100
# Node ID 9dc48eeb8e5cb022676dbbe56e3435d20e822ab3
# Parent a387ce36744aa36b50e8171dbf01ef716748327e
Mail: added support for SSL client certificate.
Add support for SSL Mutual Authentification like in HTTP module.
Added mail configuration directives (like http):
ssl_verify_client, ssl_verify_depth, ssl_client_certificate, ssl_trusted_certificate, ssl_crl
Added headers:
Auth-Certificate, Auth-Certificate-Verify, Auth-Issuer-DN, Auth-Subject-DN, Auth-Subject-Serial
diff -r a387ce36744a -r 9dc48eeb8e5c src/mail/ngx_mail_auth_http_module.c
--- a/src/mail/ngx_mail_auth_http_module.c Thu Jan 23 22:09:59 2014 +0900
+++ b/src/mail/ngx_mail_auth_http_module.c Fri Jan 24 16:26:16 2014 +0100
@@ -1135,6 +1135,35 @@ ngx_mail_auth_http_dummy_handler(ngx_eve
"mail auth http dummy handler");
}
+#if (NGX_MAIL_SSL)
+
+static ngx_int_t
+ngx_ssl_get_certificate_oneline(ngx_connection_t *c, ngx_pool_t *pool,
+ ngx_str_t *b64_cert)
+{
+ ngx_str_t pem_cert;
+ if (ngx_ssl_get_raw_certificate(c, pool, &pem_cert) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (pem_cert.len == 0) {
+ b64_cert->len = 0;
+ return NGX_OK;
+ }
+
+ b64_cert->len = ngx_base64_encoded_length(pem_cert.len);
+ b64_cert->data = ngx_palloc(pool, b64_cert->len);
+ if (b64_cert->data == NULL) {
+ b64_cert->len = 0;
+ return NGX_ERROR;
+ }
+ ngx_encode_base64(b64_cert, &pem_cert);
+
+ return NGX_OK;
+}
+
+#endif
+
static ngx_buf_t *
ngx_mail_auth_http_create_request(ngx_mail_session_t *s, ngx_pool_t *pool,
@@ -1143,6 +1172,11 @@ ngx_mail_auth_http_create_request(ngx_ma
size_t len;
ngx_buf_t *b;
ngx_str_t login, passwd;
+#if (NGX_MAIL_SSL)
+ ngx_str_t client_cert, client_verify,
+ client_subject, client_issuer,
+ client_serial;
+#endif
ngx_mail_core_srv_conf_t *cscf;
if (ngx_mail_auth_http_escape(pool, &s->login, &login) != NGX_OK) {
@@ -1155,6 +1189,41 @@ ngx_mail_auth_http_create_request(ngx_ma
cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+#if (NGX_MAIL_SSL)
+ if (s->connection->ssl) {
+ if (ngx_ssl_get_client_verify(s->connection, pool,
+ &client_verify) != NGX_OK) {
+ return NULL;
+ }
+
+ if (ngx_ssl_get_subject_dn(s->connection, pool,
+ &client_subject) != NGX_OK) {
+ return NULL;
+ }
+
+ if (ngx_ssl_get_issuer_dn(s->connection, pool,
+ &client_issuer) != NGX_OK) {
+ return NULL;
+ }
+
+ if (ngx_ssl_get_serial_number(s->connection, pool,
+ &client_serial) != NGX_OK) {
+ return NULL;
+ }
+
+ if (ngx_ssl_get_certificate_oneline(s->connection, pool,
+ &client_cert) != NGX_OK) {
+ return NULL;
+ }
+ } else {
+ client_verify.len = 0;
+ client_issuer.len = 0;
+ client_subject.len = 0;
+ client_serial.len = 0;
+ client_cert.len = 0;
+ }
+#endif
+
len = sizeof("GET ") - 1 + ahcf->uri.len + sizeof(" HTTP/1.0" CRLF) - 1
+ sizeof("Host: ") - 1 + ahcf->host_header.len + sizeof(CRLF) - 1
+ sizeof("Auth-Method: ") - 1
@@ -1163,6 +1232,18 @@ ngx_mail_auth_http_create_request(ngx_ma
+ sizeof("Auth-User: ") - 1 + login.len + sizeof(CRLF) - 1
+ sizeof("Auth-Pass: ") - 1 + passwd.len + sizeof(CRLF) - 1
+ sizeof("Auth-Salt: ") - 1 + s->salt.len
+#if (NGX_MAIL_SSL)
+ + sizeof("Auth-Certificate: ") - 1 + client_cert.len
+ + sizeof(CRLF) - 1
+ + sizeof("Auth-Certificate-Verify: ") - 1 + client_verify.len
+ + sizeof(CRLF) - 1
+ + sizeof("Auth-Issuer-DN: ") - 1 + client_issuer.len
+ + sizeof(CRLF) - 1
+ + sizeof("Auth-Subject-DN: ") - 1 + client_subject.len
+ + sizeof(CRLF) - 1
+ + sizeof("Auth-Subject-Serial: ") - 1 + client_serial.len
+ + sizeof(CRLF) - 1
+#endif
+ sizeof("Auth-Protocol: ") - 1 + cscf->protocol->name.len
+ sizeof(CRLF) - 1
+ sizeof("Auth-Login-Attempt: ") - 1 + NGX_INT_T_LEN
@@ -1213,6 +1294,44 @@ ngx_mail_auth_http_create_request(ngx_ma
s->passwd.data = NULL;
}
+#if (NGX_MAIL_SSL)
+ if (client_cert.len) {
+ b->last = ngx_cpymem(b->last, "Auth-Certificate: ",
+ sizeof("Auth-Certificate: ") - 1);
+ b->last = ngx_copy(b->last, client_cert.data, client_cert.len);
+ *b->last++ = CR; *b->last++ = LF;
+ }
+
+ if (client_verify.len) {
+ b->last = ngx_cpymem(b->last, "Auth-Certificate-Verify: ",
+ sizeof("Auth-Certificate-Verify: ") - 1);
+ b->last = ngx_copy(b->last, client_verify.data, client_verify.len);
+ *b->last++ = CR; *b->last++ = LF;
+ }
+
+ if (client_issuer.len) {
+ b->last = ngx_cpymem(b->last, "Auth-Issuer-DN: ",
+ sizeof("Auth-Issuer-DN: ") - 1);
+ b->last = ngx_copy(b->last, client_issuer.data, client_issuer.len);
+ *b->last++ = CR; *b->last++ = LF;
+ }
+
+ if (client_subject.len) {
+ b->last = ngx_cpymem(b->last, "Auth-Subject-DN: ",
+ sizeof("Auth-Subject-DN: ") - 1);
+ b->last = ngx_copy(b->last, client_subject.data, client_subject.len);
+ *b->last++ = CR; *b->last++ = LF;
+ }
+
+ if (client_serial.len) {
+ b->last = ngx_cpymem(b->last, "Auth-Subject-Serial: ",
+ sizeof("Auth-Subject-Serial: ") - 1);
+ b->last = ngx_copy(b->last, client_serial.data, client_serial.len);
+ *b->last++ = CR; *b->last++ = LF;
+ }
+
+#endif
+
b->last = ngx_cpymem(b->last, "Auth-Protocol: ",
sizeof("Auth-Protocol: ") - 1);
b->last = ngx_cpymem(b->last, cscf->protocol->name.data,
diff -r a387ce36744a -r 9dc48eeb8e5c src/mail/ngx_mail_handler.c
--- a/src/mail/ngx_mail_handler.c Thu Jan 23 22:09:59 2014 +0900
+++ b/src/mail/ngx_mail_handler.c Fri Jan 24 16:26:16 2014 +0100
@@ -236,11 +236,61 @@ ngx_mail_ssl_handshake_handler(ngx_conne
{
ngx_mail_session_t *s;
ngx_mail_core_srv_conf_t *cscf;
+#if (NGX_MAIL_SSL)
+ ngx_mail_ssl_conf_t *sslcf;
+#endif
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "ngx_mail_ssl_handshake_handler handshaked: %d",
+ c->ssl->handshaked);
if (c->ssl->handshaked) {
s = c->data;
+#if (NGX_MAIL_SSL)
+
+ sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+ if (sslcf->verify) {
+ long rc;
+
+ rc = SSL_get_verify_result(c->ssl->connection);
+
+ if (rc != X509_V_OK
+ && (sslcf->verify != 3 || !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_ssl_remove_cached_session(sscf->ssl.ctx,
+ (SSL_get0_session(c->ssl->connection)));
+
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ if (sslcf->verify == 1) {
+ 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_ssl_remove_cached_session(sscf->ssl.ctx,
+ (SSL_get0_session(c->ssl->connection)));
+
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ X509_free(cert);
+ }
+ }
+
+#endif
+
if (s->starttls) {
cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
diff -r a387ce36744a -r 9dc48eeb8e5c src/mail/ngx_mail_ssl_module.c
--- a/src/mail/ngx_mail_ssl_module.c Thu Jan 23 22:09:59 2014 +0900
+++ b/src/mail/ngx_mail_ssl_module.c Fri Jan 24 16:26:16 2014 +0100
@@ -43,6 +43,14 @@ static ngx_conf_bitmask_t ngx_mail_ssl_
{ ngx_null_string, 0 }
};
+static ngx_conf_enum_t ngx_mail_ssl_verify[] = {
+ { ngx_string("off"), 0 },
+ { ngx_string("on"), 1 },
+ { ngx_string("optional"), 2 },
+ { ngx_string("optional_no_ca"), 3 },
+ { ngx_null_string, 0 }
+};
+
static ngx_command_t ngx_mail_ssl_commands[] = {
@@ -102,6 +110,34 @@ static ngx_command_t ngx_mail_ssl_comma
offsetof(ngx_mail_ssl_conf_t, ciphers),
NULL },
+ { 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_TAKE1,
+ 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_string("ssl_prefer_server_ciphers"),
NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
@@ -137,6 +173,13 @@ static ngx_command_t ngx_mail_ssl_comma
offsetof(ngx_mail_ssl_conf_t, session_timeout),
NULL },
+ { ngx_string("ssl_crl"),
+ 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, crl),
+ NULL },
+
ngx_null_command
};
@@ -189,6 +232,9 @@ ngx_mail_ssl_create_conf(ngx_conf_t *cf)
* scf->certificate_key = { 0, NULL };
* scf->dhparam = { 0, NULL };
* scf->ecdh_curve = { 0, NULL };
+ * scf->client_certificate = { 0, NULL };
+ * scf->trusted_certificate = { 0, NULL };
+ * scf->crl = { 0, NULL };
* scf->ciphers = { 0, NULL };
* scf->shm_zone = NULL;
*/
@@ -196,6 +242,8 @@ ngx_mail_ssl_create_conf(ngx_conf_t *cf)
scf->enable = NGX_CONF_UNSET;
scf->starttls = NGX_CONF_UNSET_UINT;
scf->prefer_server_ciphers = NGX_CONF_UNSET;
+ scf->verify = NGX_CONF_UNSET_UINT;
+ scf->verify_depth = NGX_CONF_UNSET_UINT;
scf->builtin_session_cache = NGX_CONF_UNSET;
scf->session_timeout = NGX_CONF_UNSET;
scf->session_tickets = NGX_CONF_UNSET;
@@ -228,11 +276,20 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf,
(NGX_CONF_BITMASK_SET|NGX_SSL_SSLv3|NGX_SSL_TLSv1
|NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2));
+ ngx_conf_merge_uint_value(conf->verify, prev->verify, 0);
+ ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1);
+
ngx_conf_merge_str_value(conf->certificate, prev->certificate, "");
ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key, "");
ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");
+ ngx_conf_merge_str_value(conf->client_certificate, prev->client_certificate,
+ "");
+ ngx_conf_merge_str_value(conf->trusted_certificate,
+ prev->trusted_certificate, "");
+ ngx_conf_merge_str_value(conf->crl, prev->crl, "");
+
ngx_conf_merge_str_value(conf->ecdh_curve, prev->ecdh_curve,
NGX_DEFAULT_ECDH_CURVE);
@@ -318,6 +375,35 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf,
return NGX_CONF_ERROR;
}
+ if (conf->verify) {
+
+ if (conf->client_certificate.len == 0 && conf->verify != 3) {
+ 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 (ngx_ssl_trusted_certificate(cf, &conf->ssl,
+ &conf->trusted_certificate,
+ conf->verify_depth)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_ssl_crl(cf, &conf->ssl, &conf->crl) != 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 a387ce36744a -r 9dc48eeb8e5c src/mail/ngx_mail_ssl_module.h
--- a/src/mail/ngx_mail_ssl_module.h Thu Jan 23 22:09:59 2014 +0900
+++ b/src/mail/ngx_mail_ssl_module.h Fri Jan 24 16:26:16 2014 +0100
@@ -28,6 +28,8 @@ typedef struct {
ngx_uint_t starttls;
ngx_uint_t protocols;
+ ngx_uint_t verify;
+ ngx_uint_t verify_depth;
ssize_t builtin_session_cache;
time_t session_timeout;
@@ -36,6 +38,9 @@ typedef struct {
ngx_str_t certificate_key;
ngx_str_t dhparam;
ngx_str_t ecdh_curve;
+ ngx_str_t client_certificate;
+ ngx_str_t trusted_certificate;
+ ngx_str_t crl;
ngx_str_t ciphers;
-------------- next part --------------
A non-text attachment was scrubbed...
Name: Mail-SSL-MutualAuthentification.patch
Type: text/x-patch
Size: 14356 bytes
Desc: not available
URL: <http://mailman.nginx.org/pipermail/nginx-devel/attachments/20140125/aea4e968/attachment-0001.bin>
More information about the nginx-devel
mailing list