From a7ad6f58fa374e341a1551456c1536c235cf7516 Mon Sep 17 00:00:00 2001
From: Andrea Ceccanti <andrea.ceccanti@gmail.com>
Date: Tue, 26 Jun 2018 16:14:43 +0200
Subject: [PATCH] Support for ssl_client_ee_i_dn

---
 src/ngx_http_voms_module.cpp | 92 ++++++++++++++++++++++++++++++++++--
 t/eec_subject.t              | 10 ++++
 2 files changed, 99 insertions(+), 3 deletions(-)

diff --git a/src/ngx_http_voms_module.cpp b/src/ngx_http_voms_module.cpp
index f08cf6e..f16e6fa 100644
--- a/src/ngx_http_voms_module.cpp
+++ b/src/ngx_http_voms_module.cpp
@@ -33,6 +33,11 @@ using X509Ptr = std::unique_ptr<X509, decltype(&X509_free)>;
 using VomsAc = voms;
 using MaybeVomsAc = boost::optional<VomsAc>;
 
+enum EECSubjectOrIssuer {
+  SUBJECT,
+  ISSUER
+};
+
 static ngx_int_t add_variables(ngx_conf_t* cf);
 
 static ngx_http_module_t ctx = {
@@ -67,7 +72,8 @@ static ngx_int_t generic_getter(  //
     ngx_http_request_t* r,
     ngx_http_variable_value_t* v,
     uintptr_t data);
-static ngx_int_t get_ssl_client_ee_s_dn(  //
+
+static ngx_int_t get_ssl_client_ee_dn(  //
     ngx_http_request_t* r,
     ngx_http_variable_value_t* v,
     uintptr_t data);
@@ -177,8 +183,16 @@ static ngx_http_variable_t variables[] = {
     {
         ngx_string("ssl_client_ee_s_dn"),
         NULL,
-        get_ssl_client_ee_s_dn,
-        0,
+        get_ssl_client_ee_dn,
+        SUBJECT,
+        NGX_HTTP_VAR_NOCACHEABLE,
+        0  //
+    },
+    {
+        ngx_string("ssl_client_ee_i_dn"),
+        NULL,
+        get_ssl_client_ee_dn,
+        ISSUER,
         NGX_HTTP_VAR_NOCACHEABLE,
         0  //
     },
@@ -218,6 +232,78 @@ static bool is_proxy(X509* cert)
   return X509_get_extension_flags(cert) & EXFLAG_PROXY;
 }
 
+static ngx_int_t get_ssl_client_ee_dn(ngx_http_request_t* r,
+                                        ngx_http_variable_value_t* v,
+                                        uintptr_t data)
+{
+
+  ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "%s", __func__);
+
+  v->not_found = 1;
+  v->valid = 0;
+
+  auto chain = SSL_get_peer_cert_chain(r->connection->ssl->connection);
+  if (!chain) {
+    ngx_log_error(
+        NGX_LOG_ERR, r->connection->log, 0, "SSL_get_peer_cert_chain() failed");
+    return NGX_OK;
+  }
+  
+  X509* ee_cert = nullptr;
+
+  if (sk_X509_num(chain) == 0) {
+    ee_cert = SSL_get_peer_certificate(r->connection->ssl->connection);
+  } else {
+    // find first non-proxy
+    for (int i = 0; i != sk_X509_num(chain); ++i) {
+      auto cert = sk_X509_value(chain, i);
+      if (cert && !is_proxy(cert)) {
+        ee_cert = cert;
+        break;
+      }
+    }
+  }
+
+  if (!ee_cert) {
+    ngx_log_error(NGX_LOG_DEBUG,
+                  r->connection->log,
+                  0,
+                  "cannot identify end-entity certificate");
+    return NGX_OK;
+  }
+
+  X509_NAME* dn = nullptr;
+
+  if (data == SUBJECT) {
+    dn = X509_get_subject_name(ee_cert);
+  } else {
+    dn = X509_get_issuer_name(ee_cert);
+  }
+
+  if (!dn) {
+    ngx_log_error(NGX_LOG_DEBUG,
+                  r->connection->log,
+                  0,
+                  "cannot get dn from certificate");
+    return NGX_OK;
+  }
+  std::string value = to_rfc2253(dn);
+
+  auto buffer = static_cast<u_char*>(ngx_pnalloc(r->pool, value.size()));
+  if (!buffer) {
+    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_pnalloc() failed");
+    return NGX_OK;
+  }
+  ngx_memcpy(buffer, value.c_str(), value.size());
+
+  v->data = buffer;
+  v->len = value.size();
+  v->valid = 1;
+  v->not_found = 0;
+  v->no_cacheable = 0;
+  return NGX_OK;
+}
+      
 static ngx_int_t get_ssl_client_ee_s_dn(ngx_http_request_t* r,
                                         ngx_http_variable_value_t* v,
                                         uintptr_t data)
diff --git a/t/eec_subject.t b/t/eec_subject.t
index 9932f4e..a1e316e 100644
--- a/t/eec_subject.t
+++ b/t/eec_subject.t
@@ -21,6 +21,7 @@ __DATA__
 	location = / {
             default_type text/plain;
             echo $ssl_client_ee_s_dn;
+            echo $ssl_client_ee_i_dn;
         }
     }
 --- config
@@ -34,6 +35,7 @@ __DATA__
 GET / 
 --- response_body
 CN=test0,O=IGI,C=IT
+CN=Test CA,O=IGI,C=IT
 --- error_code: 200
 
 === TEST 2: standard x.509 certificate 
@@ -54,6 +56,8 @@ CN=test0,O=IGI,C=IT
             default_type text/plain;
             echo $ssl_client_ee_s_dn;
             echo $ssl_client_s_dn;
+            echo $ssl_client_ee_i_dn;
+            echo $ssl_client_i_dn;
         }
     }
 --- config
@@ -68,6 +72,8 @@ GET /
 --- response_body
 CN=nginx-voms.example,O=IGI,C=IT
 CN=nginx-voms.example,O=IGI,C=IT
+CN=Test CA,O=IGI,C=IT
+CN=Test CA,O=IGI,C=IT
 --- error_code: 200
 
 === TEST 3: three delegations proxy
@@ -87,6 +93,7 @@ CN=nginx-voms.example,O=IGI,C=IT
 	location = / {
             default_type text/plain;
             echo $ssl_client_ee_s_dn;
+            echo $ssl_client_ee_i_dn;
         }
     }
 --- config
@@ -100,6 +107,7 @@ CN=nginx-voms.example,O=IGI,C=IT
 GET / 
 --- response_body
 CN=test0,O=IGI,C=IT
+CN=Test CA,O=IGI,C=IT
 --- error_code: 200
 
 
@@ -120,6 +128,7 @@ CN=test0,O=IGI,C=IT
 	location = / {
             default_type text/plain;
             echo $ssl_client_ee_s_dn;
+            echo $ssl_client_ee_i_dn;
         }
     }
 --- config
@@ -133,4 +142,5 @@ CN=test0,O=IGI,C=IT
 GET / 
 --- response_body
 CN=test0,O=IGI,C=IT
+CN=Test CA,O=IGI,C=IT
 --- error_code: 200
-- 
GitLab