1
0
mirror of https://github.com/apache/httpd.git synced 2025-08-07 04:02:58 +03:00

mod_ssl: Add base64-encoded DER certificate variables as alternative

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
This commit is contained in:
Joe Orton
2021-03-19 15:15:36 +00:00
parent f7e848dd28
commit 1c76cd3081
5 changed files with 80 additions and 37 deletions

View File

@@ -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]

View File

@@ -1438,6 +1438,9 @@ const char *ssl_cmd_SSLOptions(cmd_parms *cmd,
else if (strcEQ(w, "ExportCertData")) { else if (strcEQ(w, "ExportCertData")) {
opt = SSL_OPT_EXPORTCERTDATA; opt = SSL_OPT_EXPORTCERTDATA;
} }
else if (strcEQ(w, "ExportBase64CertData")) {
opt = SSL_OPT_EXPORTCB64DATA;
}
else if (strcEQ(w, "FakeBasicAuth")) { else if (strcEQ(w, "FakeBasicAuth")) {
opt = SSL_OPT_FAKEBASICAUTH; opt = SSL_OPT_FAKEBASICAUTH;
} }

View File

@@ -1536,6 +1536,18 @@ static const char *const ssl_hook_Fixup_vars[] = {
NULL 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) int ssl_hook_Fixup(request_rec *r)
{ {
SSLDirConfigRec *dc = myDirConfig(r); SSLDirConfigRec *dc = myDirConfig(r);
@@ -1544,7 +1556,6 @@ int ssl_hook_Fixup(request_rec *r)
#ifdef HAVE_TLSEXT #ifdef HAVE_TLSEXT
const char *servername; const char *servername;
#endif #endif
STACK_OF(X509) *peer_certs;
SSLConnRec *sslconn; SSLConnRec *sslconn;
SSL *ssl; SSL *ssl;
int i; int i;
@@ -1585,28 +1596,24 @@ int ssl_hook_Fixup(request_rec *r)
* On-demand bloat up the SSI/CGI environment with certificate data * On-demand bloat up the SSI/CGI environment with certificate data
*/ */
if (dc->nOptions & SSL_OPT_EXPORTCERTDATA) { if (dc->nOptions & SSL_OPT_EXPORTCERTDATA) {
val = ssl_var_lookup(r->pool, r->server, r->connection, extract_to_env(r, env, "SSL_SERVER_CERT");
r, "SSL_SERVER_CERT"); extract_to_env(r, env, "SSL_CLIENT_CERT");
apr_table_setn(env, "SSL_SERVER_CERT", val); i = 0;
do {
val = ssl_var_lookup(r->pool, r->server, r->connection, var = apr_psprintf(r->pool, "SSL_CLIENT_CERT_CHAIN_%d", i++);
r, "SSL_CLIENT_CERT"); } while (extract_to_env(r, env, var));
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);
}
}
}
} }
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 #ifdef SSL_get_secure_renegotiation_support
apr_table_setn(r->notes, "ssl-secure-reneg", apr_table_setn(r->notes, "ssl-secure-reneg",

View 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_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_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_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_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_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 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); 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; 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, /* SSLv3 uses 36 bytes for Finishd messages, TLS1.0 12 bytes,
* So tls-unique is max 36 bytes, however with tls-server-end-point, * 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 * 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)) { else if (ssl != NULL && strlen(var) > 18 && strcEQn(var, "CLIENT_CERT_CHAIN_", 18)) {
sk = SSL_get_peer_cert_chain(ssl); 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")) { else if (ssl != NULL && strcEQ(var, "CLIENT_CERT_RFC4523_CEA")) {
result = ssl_var_lookup_ssl_cert_rfc4523_cea(p, ssl); 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); result = (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid);
} }
else if (strcEQ(var, "CERT")) { 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; 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); 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; const char *result;
X509 *xs; 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); n = atoi(var);
if (n < sk_X509_num(sk)) { if (n < sk_X509_num(sk)) {
xs = sk_X509_value(sk, n); 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; 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, static const char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p,
const SSLConnRec *sslconn) const SSLConnRec *sslconn)
{ {

View File

@@ -356,6 +356,7 @@ APLOG_USE_MODULE(ssl);
#define SSL_OPT_STRICTREQUIRE (1<<5) #define SSL_OPT_STRICTREQUIRE (1<<5)
#define SSL_OPT_OPTRENEGOTIATE (1<<6) #define SSL_OPT_OPTRENEGOTIATE (1<<6)
#define SSL_OPT_LEGACYDNFORMAT (1<<7) #define SSL_OPT_LEGACYDNFORMAT (1<<7)
#define SSL_OPT_EXPORTCB64DATA (1<<8)
typedef int ssl_opt_t; typedef int ssl_opt_t;
/** /**