diff --git a/changes-entries/pr65169.txt b/changes-entries/pr65169.txt new file mode 100644 index 0000000000..9f742e6665 --- /dev/null +++ b/changes-entries/pr65169.txt @@ -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] + diff --git a/modules/ssl/ssl_engine_config.c b/modules/ssl/ssl_engine_config.c index 4d57e522ec..19621d2b15 100644 --- a/modules/ssl/ssl_engine_config.c +++ b/modules/ssl/ssl_engine_config.c @@ -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; } diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c index 886ffda4e3..b279273deb 100644 --- a/modules/ssl/ssl_engine_kernel.c +++ b/modules/ssl/ssl_engine_kernel.c @@ -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,28 +1596,24 @@ 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"); - - apr_table_setn(env, "SSL_CLIENT_CERT", val); - - 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_CERT_CHAIN_%d", i++); + } while (extract_to_env(r, env, var)); } + if (dc->nOptions & SSL_OPT_EXPORTCB64DATA) { + extract_to_env(r, env, "SSL_SERVER_B64CERT"); + extract_to_env(r, env, "SSL_CLIENT_B64CERT"); + + 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", diff --git a/modules/ssl/ssl_engine_vars.c b/modules/ssl/ssl_engine_vars.c index 2e5e06967b..197b777a3f 100644 --- a/modules/ssl/ssl_engine_vars.c +++ b/modules/ssl/ssl_engine_vars.c @@ -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) { diff --git a/modules/ssl/ssl_private.h b/modules/ssl/ssl_private.h index 417105d387..f662f8b971 100644 --- a/modules/ssl/ssl_private.h +++ b/modules/ssl/ssl_private.h @@ -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; /**