diff src/mail/ngx_mail_handler.c @ 5989:ec01b1d1fff1

Mail: client SSL certificates support. The "ssl_verify_client", "ssl_verify_depth", "ssl_client_certificate", "ssl_trusted_certificate", and "ssl_crl" directives introduced to control SSL client certificate verification in mail proxy module. If there is a certificate, detail of the certificate are passed to the auth_http script configured via Auth-SSL-Verify, Auth-SSL-Subject, Auth-SSL-Issuer, Auth-SSL-Serial, Auth-SSL-Fingerprint headers. If the auth_http_pass_client_cert directive is set, client certificate in PEM format will be passed in the Auth-SSL-Cert header (urlencoded). If there is no required certificate provided during an SSL handshake or certificate verification fails then a protocol-specific error is returned after the SSL handshake and the connection is closed. Based on previous work by Sven Peter, Franck Levionnois and Filipe Da Silva.
author Maxim Dounin <mdounin@mdounin.ru>
date Wed, 25 Feb 2015 17:48:05 +0300
parents 8e7ee4c70a3c
children fc99323a3d79
line wrap: on
line diff
--- a/src/mail/ngx_mail_handler.c
+++ b/src/mail/ngx_mail_handler.c
@@ -16,6 +16,8 @@ static void ngx_mail_init_session(ngx_co
 #if (NGX_MAIL_SSL)
 static void ngx_mail_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c);
 static void ngx_mail_ssl_handshake_handler(ngx_connection_t *c);
+static ngx_int_t ngx_mail_verify_cert(ngx_mail_session_t *s,
+    ngx_connection_t *c);
 #endif
 
 
@@ -247,6 +249,10 @@ ngx_mail_ssl_handshake_handler(ngx_conne
 
         s = c->data;
 
+        if (ngx_mail_verify_cert(s, c) != NGX_OK) {
+            return;
+        }
+
         if (s->starttls) {
             cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
 
@@ -267,6 +273,71 @@ ngx_mail_ssl_handshake_handler(ngx_conne
     ngx_mail_close_connection(c);
 }
 
+
+static ngx_int_t
+ngx_mail_verify_cert(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+    long                       rc;
+    X509                      *cert;
+    ngx_mail_ssl_conf_t       *sslcf;
+    ngx_mail_core_srv_conf_t  *cscf;
+
+    sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+
+    if (!sslcf->verify) {
+        return NGX_OK;
+    }
+
+    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(sslcf->ssl.ctx,
+                                      (SSL_get0_session(c->ssl->connection)));
+
+        cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+        s->out = cscf->protocol->cert_error;
+        s->quit = 1;
+
+        c->write->handler = ngx_mail_send;
+
+        ngx_mail_send(s->connection->write);
+        return NGX_ERROR;
+    }
+
+    if (sslcf->verify == 1) {
+        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(sslcf->ssl.ctx,
+                                       (SSL_get0_session(c->ssl->connection)));
+
+            cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+            s->out = cscf->protocol->no_cert;
+            s->quit = 1;
+
+            c->write->handler = ngx_mail_send;
+
+            ngx_mail_send(s->connection->write);
+            return NGX_ERROR;
+        }
+
+        X509_free(cert);
+    }
+
+    return NGX_OK;
+}
+
 #endif