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:
6
changes-entries/pr65169.txt
Normal file
6
changes-entries/pr65169.txt
Normal 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]
|
||||||
|
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
@@ -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",
|
||||||
|
@@ -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)
|
||||||
{
|
{
|
||||||
|
@@ -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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user