]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
mod_ssl: Add base64-encoded DER certificate variables as alternative
authorJoe Orton <jorton@apache.org>
Fri, 19 Mar 2021 15:15:36 +0000 (15:15 +0000)
committerJoe Orton <jorton@apache.org>
Fri, 19 Mar 2021 15:15:36 +0000 (15:15 +0000)
to PEM, to avoid newline mangling issues when using PEM in header
values.

* modules/ssl/ssl_private.h (SSL_OPT_EXPORTCB64DATA): New constant.

* modules/ssl/ssl_engine_vars.c (ssl_var_lookup_ssl_cert_data):
  New function, replacing ssl_var_lookup_ssl_cert_PEM.
  (ssl_var_lookup_ssl): Use it, and add _B64CERT variants of
  SSL_{CLIENT,SERVER}_CERT.
  (ssl_var_lookup_ssl_cert_chain): Use it.

* modules/ssl/ssl_engine_config.c (ssl_cmd_SSLOptions): Support
  "ExportBase64CertData" argument.

* modules/ssl/ssl_engine_kernel.c (extract_to_env): New function.
  (ssl_hook_Fixup): Use it, also export _B64CERT variables if
  SSL_OPT_EXPORTCB64DATA is set; simplify the client cert chain
  handling.

PR: 65169
Reviewed by: michaelo
Github: closes #177

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1887811 13f79535-47bb-0310-9956-ffa450edef68

changes-entries/pr65169.txt [new file with mode: 0644]
modules/ssl/ssl_engine_config.c
modules/ssl/ssl_engine_kernel.c
modules/ssl/ssl_engine_vars.c
modules/ssl/ssl_private.h

diff --git a/changes-entries/pr65169.txt b/changes-entries/pr65169.txt
new file mode 100644 (file)
index 0000000..9f742e6
--- /dev/null
@@ -0,0 +1,6 @@
+  *) mod_ssl: Add SSL_{CLIENT,SERVER}_B64CERT variables with
+     base64-encoded DER certificates.  Add SSL_CLIENT_B64CERT_CHAIN_n
+     equivalents for SSL_CLIENT_CERT_CHAIN_n, and new
+     "ExportBase64CertData" argument for SSLOptions.  PR 65169.
+     [Joe Orton]
+
index 4d57e522ec402289d0f4f1e478281b5fa640485a..19621d2b15873a774c46ca4464bca0f89e6c0abb 100644 (file)
@@ -1438,6 +1438,9 @@ const char *ssl_cmd_SSLOptions(cmd_parms *cmd,
         else if (strcEQ(w, "ExportCertData")) {
             opt = SSL_OPT_EXPORTCERTDATA;
         }
+        else if (strcEQ(w, "ExportBase64CertData")) {
+            opt = SSL_OPT_EXPORTCB64DATA;
+        }
         else if (strcEQ(w, "FakeBasicAuth")) {
             opt = SSL_OPT_FAKEBASICAUTH;
         }
index 886ffda4e34a4edb609816e122d4f01e06f492fa..b279273debb4901af58105d5611cdf112d3b992f 100644 (file)
@@ -1536,6 +1536,18 @@ static const char *const ssl_hook_Fixup_vars[] = {
     NULL
 };
 
+/* Lookup SSL variable @arg varname and set in the table @arg env.
+ * Returns the value if the value is non-NULL and not the empty
+ * string; otherwise returns NULL. */
+static const char *extract_to_env(request_rec *r, apr_table_t *env,
+                                  const char *varname)
+{
+    const char *val = ssl_var_lookup(r->pool, r->server, r->connection,
+                                     r, varname);
+    apr_table_setn(env, varname, val);
+    return val && *val ? val : NULL;
+}
+
 int ssl_hook_Fixup(request_rec *r)
 {
     SSLDirConfigRec *dc = myDirConfig(r);
@@ -1544,7 +1556,6 @@ int ssl_hook_Fixup(request_rec *r)
 #ifdef HAVE_TLSEXT
     const char *servername;
 #endif
-    STACK_OF(X509) *peer_certs;
     SSLConnRec *sslconn;
     SSL *ssl;
     int i;
@@ -1585,29 +1596,25 @@ int ssl_hook_Fixup(request_rec *r)
      * On-demand bloat up the SSI/CGI environment with certificate data
      */
     if (dc->nOptions & SSL_OPT_EXPORTCERTDATA) {
-        val = ssl_var_lookup(r->pool, r->server, r->connection,
-                             r, "SSL_SERVER_CERT");
+        extract_to_env(r, env, "SSL_SERVER_CERT");
+        extract_to_env(r, env, "SSL_CLIENT_CERT");
 
-        apr_table_setn(env, "SSL_SERVER_CERT", val);
-
-        val = ssl_var_lookup(r->pool, r->server, r->connection,
-                             r, "SSL_CLIENT_CERT");
+        i = 0;
+        do {
+            var = apr_psprintf(r->pool, "SSL_CLIENT_CERT_CHAIN_%d", i++);
+        } while (extract_to_env(r, env, var));
+    }
 
-        apr_table_setn(env, "SSL_CLIENT_CERT", val);
+    if (dc->nOptions & SSL_OPT_EXPORTCB64DATA) {
+        extract_to_env(r, env, "SSL_SERVER_B64CERT");
+        extract_to_env(r, env, "SSL_CLIENT_B64CERT");
 
-        if ((peer_certs = (STACK_OF(X509) *)SSL_get_peer_cert_chain(ssl))) {
-            for (i = 0; i < sk_X509_num(peer_certs); i++) {
-                var = apr_psprintf(r->pool, "SSL_CLIENT_CERT_CHAIN_%d", i);
-                val = ssl_var_lookup(r->pool, r->server, r->connection,
-                                     r, var);
-                if (val) {
-                    apr_table_setn(env, var, val);
-                }
-            }
-        }
+        i = 0;
+        do {
+            var = apr_psprintf(r->pool, "SSL_CLIENT_B64CERT_CHAIN_%d", i++);
+        } while (extract_to_env(r, env, var));
     }
 
-
 #ifdef SSL_get_secure_renegotiation_support
     apr_table_setn(r->notes, "ssl-secure-reneg",
                    SSL_get_secure_renegotiation_support(ssl) ? "1" : "0");
index 2e5e06967bf0624b86173dc66a506880d0c71aae..197b777a3f901086ad7c1a626fc2bdb95f444604 100644 (file)
@@ -46,9 +46,8 @@ static const char *ssl_var_lookup_ssl_cert_san(apr_pool_t *p, X509 *xs, const ch
 static const char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, ASN1_TIME *tm);
 static const char *ssl_var_lookup_ssl_cert_remain(apr_pool_t *p, ASN1_TIME *tm);
 static const char *ssl_var_lookup_ssl_cert_serial(apr_pool_t *p, X509 *xs);
-static const char *ssl_var_lookup_ssl_cert_chain(apr_pool_t *p, STACK_OF(X509) *sk, const char *var);
+static const char *ssl_var_lookup_ssl_cert_chain(apr_pool_t *p, STACK_OF(X509) *sk, const char *var, int pem);
 static const char *ssl_var_lookup_ssl_cert_rfc4523_cea(apr_pool_t *p, SSL *ssl);
-static const char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t *p, X509 *xs);
 static const char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, const SSLConnRec *sslconn);
 static const char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, const SSLConnRec *sslconn, const char *var);
 static void  ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize);
@@ -71,6 +70,36 @@ static int ssl_is_https(conn_rec *c)
     return sslconn && sslconn->ssl;
 }
 
+/* Returns certificate data, either PEM encoded if 'pem' is non-zero,
+ * else plain base64-encoded DER. */
+static const char *ssl_var_lookup_ssl_cert_data(apr_pool_t *p, X509 *xs,
+                                                int pem)
+{
+    BIO *bio;
+
+    if ((bio = BIO_new(BIO_s_mem())) == NULL)
+        return NULL;
+
+    if (pem) {
+        PEM_write_bio_X509(bio, xs);
+    }
+    else {
+        BIO *b64 = BIO_new(BIO_f_base64());
+        if (b64 == NULL) {
+            BIO_free(bio);
+            return NULL;
+        }
+        BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
+        b64 = BIO_push(b64, bio);
+        i2d_X509_bio(b64, xs);
+        BIO_flush(b64); /* ensures trailing bytes are padded */
+        BIO_pop(b64);
+        BIO_free(b64);
+    }
+
+    return modssl_bio_free_read(p, bio);
+}
+
 /* SSLv3 uses 36 bytes for Finishd messages, TLS1.0 12 bytes,
  * So tls-unique is max 36 bytes, however with tls-server-end-point,
  * the CB data is the certificate signature, so we use the maximum
@@ -445,7 +474,11 @@ static const char *ssl_var_lookup_ssl(apr_pool_t *p, const SSLConnRec *sslconn,
     }
     else if (ssl != NULL && strlen(var) > 18 && strcEQn(var, "CLIENT_CERT_CHAIN_", 18)) {
         sk = SSL_get_peer_cert_chain(ssl);
-        result = ssl_var_lookup_ssl_cert_chain(p, sk, var+18);
+        result = ssl_var_lookup_ssl_cert_chain(p, sk, var+18, 1);
+    }
+    else if (ssl != NULL && strlen(var) > 21 && strcEQn(var, "CLIENT_B64CERT_CHAIN_", 21)) {
+        sk = SSL_get_peer_cert_chain(ssl);
+        result = ssl_var_lookup_ssl_cert_chain(p, sk, var+21, 0);
     }
     else if (ssl != NULL && strcEQ(var, "CLIENT_CERT_RFC4523_CEA")) {
         result = ssl_var_lookup_ssl_cert_rfc4523_cea(p, ssl);
@@ -593,7 +626,10 @@ static const char *ssl_var_lookup_ssl_cert(apr_pool_t *p, request_rec *r, X509 *
         result = (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid);
     }
     else if (strcEQ(var, "CERT")) {
-        result = ssl_var_lookup_ssl_cert_PEM(p, xs);
+        result = ssl_var_lookup_ssl_cert_data(p, xs, 1);
+    }
+    else if (strcEQ(var, "B64CERT")) {
+        result = ssl_var_lookup_ssl_cert_data(p, xs, 0);
     }
 
     return result;
@@ -781,7 +817,8 @@ static const char *ssl_var_lookup_ssl_cert_serial(apr_pool_t *p, X509 *xs)
     return modssl_bio_free_read(p, bio);
 }
 
-static const char *ssl_var_lookup_ssl_cert_chain(apr_pool_t *p, STACK_OF(X509) *sk, const char *var)
+static const char *ssl_var_lookup_ssl_cert_chain(apr_pool_t *p, STACK_OF(X509) *sk,
+                                                 const char *var, int pem)
 {
     const char *result;
     X509 *xs;
@@ -793,7 +830,7 @@ static const char *ssl_var_lookup_ssl_cert_chain(apr_pool_t *p, STACK_OF(X509) *
         n = atoi(var);
         if (n < sk_X509_num(sk)) {
             xs = sk_X509_value(sk, n);
-            result = ssl_var_lookup_ssl_cert_PEM(p, xs);
+            result = ssl_var_lookup_ssl_cert_data(p, xs, pem);
         }
     }
 
@@ -831,17 +868,6 @@ static const char *ssl_var_lookup_ssl_cert_rfc4523_cea(apr_pool_t *p, SSL *ssl)
     return result;
 }
 
-static const char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t *p, X509 *xs)
-{
-    BIO *bio;
-
-    if ((bio = BIO_new(BIO_s_mem())) == NULL)
-        return NULL;
-    PEM_write_bio_X509(bio, xs);
-
-    return modssl_bio_free_read(p, bio);
-}
-
 static const char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p,
                                                   const SSLConnRec *sslconn)
 {
index 417105d3874a01fedd23075e5851514de163997e..f662f8b971e31c15c1ca14f75790d156c8c2fb44 100644 (file)
@@ -356,6 +356,7 @@ APLOG_USE_MODULE(ssl);
 #define SSL_OPT_STRICTREQUIRE  (1<<5)
 #define SSL_OPT_OPTRENEGOTIATE (1<<6)
 #define SSL_OPT_LEGACYDNFORMAT (1<<7)
+#define SSL_OPT_EXPORTCB64DATA (1<<8)
 typedef int ssl_opt_t;
 
 /**