#include "ssl_private.h"
#include "mod_ssl.h"
#include "ap_expr.h"
#include "apr_time.h"
static char *ssl_var_lookup_ssl(apr_pool_t *p, conn_rec *c, request_rec *r, char *var);
static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, request_rec *r, X509 *xs, char *var);
static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, X509_NAME *xsname, char *var);
static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, ASN1_TIME *tm);
static char *ssl_var_lookup_ssl_cert_remain(apr_pool_t *p, ASN1_TIME *tm);
static char *ssl_var_lookup_ssl_cert_serial(apr_pool_t *p, X509 *xs);
static char *ssl_var_lookup_ssl_cert_chain(apr_pool_t *p, STACK_OF(X509) *sk, char *var);
static char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t *p, X509 *xs);
static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, conn_rec *c);
static char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, conn_rec *c, char *var);
static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize);
static char *ssl_var_lookup_ssl_version(apr_pool_t *p, char *var);
static char *ssl_var_lookup_ssl_compress_meth(SSL *ssl);
static int ssl_is_https(conn_rec *c)
{
SSLConnRec *sslconn = myConnConfig(c);
return sslconn && sslconn->ssl;
}
static const char var_interface[] = "mod_ssl/" AP_SERVER_BASEREVISION;
static char var_library_interface[] = SSL_LIBRARY_TEXT;
static char *var_library = NULL;
static apr_array_header_t *expr_peer_ext_list_fn(ap_expr_eval_ctx_t *ctx,
const void *dummy,
const char *arg)
{
return ssl_ext_list(ctx->p, ctx->c, 1, arg);
}
static const char *expr_var_fn(ap_expr_eval_ctx_t *ctx, const void *data)
{
char *var = (char *)data;
return ssl_var_lookup_ssl(ctx->p, ctx->c, ctx->r, var);
}
static int ssl_expr_lookup(ap_expr_lookup_parms *parms)
{
switch (parms->type) {
case AP_EXPR_FUNC_VAR:
if (strcEQn(parms->name, "SSL_", 4)) {
*parms->func = expr_var_fn;
*parms->data = parms->name + 4;
return OK;
}
break;
case AP_EXPR_FUNC_LIST:
if (strcEQ(parms->name, "PeerExtList")) {
*parms->func = expr_peer_ext_list_fn;
*parms->data = "PeerExtList";
return OK;
}
break;
}
return DECLINED;
}
void ssl_var_register(apr_pool_t *p)
{
char *cp, *cp2;
APR_REGISTER_OPTIONAL_FN(ssl_is_https);
APR_REGISTER_OPTIONAL_FN(ssl_var_lookup);
APR_REGISTER_OPTIONAL_FN(ssl_ext_list);
var_library = apr_pstrdup(p, SSL_LIBRARY_DYNTEXT);
if ((cp = strchr(var_library, ' ')) != NULL) {
*cp = '/';
if ((cp2 = strchr(cp, ' ')) != NULL)
*cp2 = NUL;
}
if ((cp = strchr(var_library_interface, ' ')) != NULL) {
*cp = '/';
if ((cp2 = strchr(cp, ' ')) != NULL)
*cp2 = NUL;
}
ap_hook_expr_lookup(ssl_expr_lookup, NULL, NULL, APR_HOOK_MIDDLE);
}
char *ssl_var_lookup(apr_pool_t *p, server_rec *s, conn_rec *c, request_rec *r, char *var)
{
SSLModConfigRec *mc = myModConfig(s);
const char *result;
BOOL resdup;
apr_time_exp_t tm;
result = NULL;
resdup = TRUE;
if (p == NULL) {
if (r != NULL)
p = r->pool;
else if (c != NULL)
p = c->pool;
else
p = mc->pPool;
}
if (r != NULL) {
switch (var[0]) {
case 'H':
case 'h':
if (strcEQ(var, "HTTP_USER_AGENT"))
result = apr_table_get(r->headers_in, "User-Agent");
else if (strcEQ(var, "HTTP_REFERER"))
result = apr_table_get(r->headers_in, "Referer");
else if (strcEQ(var, "HTTP_COOKIE"))
result = apr_table_get(r->headers_in, "Cookie");
else if (strcEQ(var, "HTTP_FORWARDED"))
result = apr_table_get(r->headers_in, "Forwarded");
else if (strcEQ(var, "HTTP_HOST"))
result = apr_table_get(r->headers_in, "Host");
else if (strcEQ(var, "HTTP_PROXY_CONNECTION"))
result = apr_table_get(r->headers_in, "Proxy-Connection");
else if (strcEQ(var, "HTTP_ACCEPT"))
result = apr_table_get(r->headers_in, "Accept");
else if (strlen(var) > 5 && strcEQn(var, "HTTP:", 5))
result = apr_table_get(r->headers_in, var+5);
break;
case 'R':
case 'r':
if (strcEQ(var, "REQUEST_METHOD"))
result = r->method;
else if (strcEQ(var, "REQUEST_SCHEME"))
result = ap_http_scheme(r);
else if (strcEQ(var, "REQUEST_URI"))
result = r->uri;
else if (strcEQ(var, "REQUEST_FILENAME"))
result = r->filename;
else if (strcEQ(var, "REMOTE_ADDR"))
result = r->useragent_ip;
else if (strcEQ(var, "REMOTE_HOST"))
result = ap_get_remote_host(r->connection, r->per_dir_config,
REMOTE_NAME, NULL);
else if (strcEQ(var, "REMOTE_IDENT"))
result = ap_get_remote_logname(r);
else if (strcEQ(var, "REMOTE_USER"))
result = r->user;
break;
case 'S':
case 's':
if (strcEQn(var, "SSL", 3)) break;
if (strcEQ(var, "SERVER_ADMIN"))
result = r->server->server_admin;
else if (strcEQ(var, "SERVER_NAME"))
result = ap_get_server_name_for_url(r);
else if (strcEQ(var, "SERVER_PORT"))
result = apr_psprintf(p, "%u", ap_get_server_port(r));
else if (strcEQ(var, "SERVER_PROTOCOL"))
result = r->protocol;
else if (strcEQ(var, "SCRIPT_FILENAME"))
result = r->filename;
break;
default:
if (strcEQ(var, "PATH_INFO"))
result = r->path_info;
else if (strcEQ(var, "QUERY_STRING"))
result = r->args;
else if (strcEQ(var, "IS_SUBREQ"))
result = (r->main != NULL ? "true" : "false");
else if (strcEQ(var, "DOCUMENT_ROOT"))
result = ap_document_root(r);
else if (strcEQ(var, "AUTH_TYPE"))
result = r->ap_auth_type;
else if (strcEQ(var, "THE_REQUEST"))
result = r->the_request;
else if (strlen(var) > 4 && strcEQn(var, "ENV:", 4)) {
result = apr_table_get(r->notes, var+4);
if (result == NULL)
result = apr_table_get(r->subprocess_env, var+4);
}
break;
}
}
if (result == NULL && c != NULL) {
SSLConnRec *sslconn = myConnConfig(c);
if (strlen(var) > 4 && strcEQn(var, "SSL_", 4)
&& sslconn && sslconn->ssl)
result = ssl_var_lookup_ssl(p, c, r, var+4);
else if (strcEQ(var, "HTTPS")) {
if (sslconn && sslconn->ssl)
result = "on";
else
result = "off";
}
}
if (result == NULL) {
if (strlen(var) > 12 && strcEQn(var, "SSL_VERSION_", 12))
result = ssl_var_lookup_ssl_version(p, var+12);
else if (strcEQ(var, "SERVER_SOFTWARE"))
result = ap_get_server_banner();
else if (strcEQ(var, "API_VERSION")) {
result = apr_itoa(p, MODULE_MAGIC_NUMBER);
resdup = FALSE;
}
else if (strcEQ(var, "TIME_YEAR")) {
apr_time_exp_lt(&tm, apr_time_now());
result = apr_psprintf(p, "%02d%02d",
(tm.tm_year / 100) + 19, tm.tm_year % 100);
resdup = FALSE;
}
#define MKTIMESTR(format, tmfield) \
apr_time_exp_lt(&tm, apr_time_now()); \
result = apr_psprintf(p, format, tm.tmfield); \
resdup = FALSE;
else if (strcEQ(var, "TIME_MON")) {
MKTIMESTR("%02d", tm_mon+1)
}
else if (strcEQ(var, "TIME_DAY")) {
MKTIMESTR("%02d", tm_mday)
}
else if (strcEQ(var, "TIME_HOUR")) {
MKTIMESTR("%02d", tm_hour)
}
else if (strcEQ(var, "TIME_MIN")) {
MKTIMESTR("%02d", tm_min)
}
else if (strcEQ(var, "TIME_SEC")) {
MKTIMESTR("%02d", tm_sec)
}
else if (strcEQ(var, "TIME_WDAY")) {
MKTIMESTR("%d", tm_wday)
}
else if (strcEQ(var, "TIME")) {
apr_time_exp_lt(&tm, apr_time_now());
result = apr_psprintf(p,
"%02d%02d%02d%02d%02d%02d%02d", (tm.tm_year / 100) + 19,
(tm.tm_year % 100), tm.tm_mon+1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
resdup = FALSE;
}
else if (strlen(var) > 4 && strcEQn(var, "ENV:", 4)) {
result = getenv(var+4);
}
}
if (result != NULL && resdup)
result = apr_pstrdup(p, result);
if (result == NULL)
result = "";
return (char *)result;
}
static char *ssl_var_lookup_ssl(apr_pool_t *p, conn_rec *c, request_rec *r,
char *var)
{
SSLConnRec *sslconn = myConnConfig(c);
char *result;
X509 *xs;
STACK_OF(X509) *sk;
SSL *ssl;
result = NULL;
ssl = sslconn->ssl;
if (strlen(var) > 8 && strcEQn(var, "VERSION_", 8)) {
result = ssl_var_lookup_ssl_version(p, var+8);
}
else if (ssl != NULL && strcEQ(var, "PROTOCOL")) {
result = (char *)SSL_get_version(ssl);
}
else if (ssl != NULL && strcEQ(var, "SESSION_ID")) {
char buf[SSL_SESSION_ID_STRING_LEN];
SSL_SESSION *pSession = SSL_get_session(ssl);
if (pSession) {
unsigned char *id;
unsigned int idlen;
#ifdef OPENSSL_NO_SSL_INTERN
id = (unsigned char *)SSL_SESSION_get_id(pSession, &idlen);
#else
id = pSession->session_id;
idlen = pSession->session_id_length;
#endif
result = apr_pstrdup(p, SSL_SESSION_id2sz(id, idlen,
buf, sizeof(buf)));
}
}
else if(ssl != NULL && strcEQ(var, "SESSION_RESUMED")) {
if (SSL_session_reused(ssl) == 1)
result = "Resumed";
else
result = "Initial";
}
else if (ssl != NULL && strlen(var) >= 6 && strcEQn(var, "CIPHER", 6)) {
result = ssl_var_lookup_ssl_cipher(p, c, var+6);
}
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);
}
else if (ssl != NULL && strcEQ(var, "CLIENT_VERIFY")) {
result = ssl_var_lookup_ssl_cert_verify(p, c);
}
else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "CLIENT_", 7)) {
if ((xs = SSL_get_peer_certificate(ssl)) != NULL) {
result = ssl_var_lookup_ssl_cert(p, r, xs, var+7);
X509_free(xs);
}
}
else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "SERVER_", 7)) {
if ((xs = SSL_get_certificate(ssl)) != NULL) {
result = ssl_var_lookup_ssl_cert(p, r, xs, var+7);
}
}
else if (ssl != NULL && strcEQ(var, "COMPRESS_METHOD")) {
result = ssl_var_lookup_ssl_compress_meth(ssl);
}
#ifdef HAVE_TLSEXT
else if (ssl != NULL && strcEQ(var, "TLS_SNI")) {
result = apr_pstrdup(p, SSL_get_servername(ssl,
TLSEXT_NAMETYPE_host_name));
}
#endif
else if (ssl != NULL && strcEQ(var, "SECURE_RENEG")) {
int flag = 0;
#ifdef SSL_get_secure_renegotiation_support
flag = SSL_get_secure_renegotiation_support(ssl);
#endif
result = apr_pstrdup(p, flag ? "true" : "false");
}
#ifdef HAVE_SRP
else if (ssl != NULL && strcEQ(var, "SRP_USER")) {
if ((result = SSL_get_srp_username(ssl)) != NULL) {
result = apr_pstrdup(p, result);
}
}
else if (ssl != NULL && strcEQ(var, "SRP_USERINFO")) {
if ((result = SSL_get_srp_userinfo(ssl)) != NULL) {
result = apr_pstrdup(p, result);
}
}
#endif
return result;
}
static char *ssl_var_lookup_ssl_cert_dn_oneline(apr_pool_t *p, request_rec *r,
X509_NAME *xsname)
{
char *result = NULL;
SSLDirConfigRec *dc;
int legacy_format = 0;
if (r) {
dc = myDirConfig(r);
legacy_format = dc->nOptions & SSL_OPT_LEGACYDNFORMAT;
}
if (legacy_format) {
char *cp = X509_NAME_oneline(xsname, NULL, 0);
result = apr_pstrdup(p, cp);
OPENSSL_free(cp);
}
else {
BIO* bio;
int n;
unsigned long flags = XN_FLAG_RFC2253 & ~ASN1_STRFLGS_ESC_MSB;
if ((bio = BIO_new(BIO_s_mem())) == NULL)
return NULL;
X509_NAME_print_ex(bio, xsname, 0, flags);
n = BIO_pending(bio);
if (n > 0) {
result = apr_palloc(p, n+1);
n = BIO_read(bio, result, n);
result[n] = NUL;
}
BIO_free(bio);
}
return result;
}
static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, request_rec *r, X509 *xs,
char *var)
{
char *result;
BOOL resdup;
X509_NAME *xsname;
int nid;
result = NULL;
resdup = TRUE;
if (strcEQ(var, "M_VERSION")) {
result = apr_psprintf(p, "%lu", X509_get_version(xs)+1);
resdup = FALSE;
}
else if (strcEQ(var, "M_SERIAL")) {
result = ssl_var_lookup_ssl_cert_serial(p, xs);
}
else if (strcEQ(var, "V_START")) {
result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notBefore(xs));
}
else if (strcEQ(var, "V_END")) {
result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notAfter(xs));
}
else if (strcEQ(var, "V_REMAIN")) {
result = ssl_var_lookup_ssl_cert_remain(p, X509_get_notAfter(xs));
resdup = FALSE;
}
else if (*var && strcEQ(var+1, "_DN")) {
if (*var == 'S')
xsname = X509_get_subject_name(xs);
else if (*var == 'I')
xsname = X509_get_issuer_name(xs);
else
return NULL;
result = ssl_var_lookup_ssl_cert_dn_oneline(p, r, xsname);
resdup = FALSE;
}
else if (strlen(var) > 5 && strcEQn(var+1, "_DN_", 4)) {
if (*var == 'S')
xsname = X509_get_subject_name(xs);
else if (*var == 'I')
xsname = X509_get_issuer_name(xs);
else
return NULL;
result = ssl_var_lookup_ssl_cert_dn(p, xsname, var+5);
resdup = FALSE;
}
else if (strcEQ(var, "A_SIG")) {
nid = OBJ_obj2nid((ASN1_OBJECT *)(xs->cert_info->signature->algorithm));
result = apr_pstrdup(p,
(nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid));
resdup = FALSE;
}
else if (strcEQ(var, "A_KEY")) {
nid = OBJ_obj2nid((ASN1_OBJECT *)(xs->cert_info->key->algor->algorithm));
result = apr_pstrdup(p,
(nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid));
resdup = FALSE;
}
else if (strcEQ(var, "CERT")) {
result = ssl_var_lookup_ssl_cert_PEM(p, xs);
}
if (resdup)
result = apr_pstrdup(p, result);
return result;
}
static const struct {
char *name;
int nid;
int extract;
} ssl_var_lookup_ssl_cert_dn_rec[] = {
{ "C", NID_countryName, 1 },
{ "ST", NID_stateOrProvinceName, 1 },
{ "SP", NID_stateOrProvinceName, 0 },
{ "L", NID_localityName, 1 },
{ "O", NID_organizationName, 1 },
{ "OU", NID_organizationalUnitName, 1 },
{ "CN", NID_commonName, 1 },
{ "T", NID_title, 1 },
{ "I", NID_initials, 1 },
{ "G", NID_givenName, 1 },
{ "S", NID_surname, 1 },
{ "D", NID_description, 1 },
#ifdef NID_userId
{ "UID", NID_userId, 1 },
#endif
{ "Email", NID_pkcs9_emailAddress, 1 },
{ NULL, 0, 0 }
};
static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, X509_NAME *xsname, char *var)
{
char *result, *ptr;
X509_NAME_ENTRY *xsne;
int i, j, n, idx = 0;
apr_size_t varlen;
ptr = strchr(var, '_');
if (ptr != NULL && strspn(ptr + 1, "0123456789") == strlen(ptr + 1)) {
idx = atoi(ptr + 1);
varlen = ptr - var;
} else {
varlen = strlen(var);
}
result = NULL;
for (i = 0; ssl_var_lookup_ssl_cert_dn_rec[i].name != NULL; i++) {
if (strEQn(var, ssl_var_lookup_ssl_cert_dn_rec[i].name, varlen)
&& strlen(ssl_var_lookup_ssl_cert_dn_rec[i].name) == varlen) {
for (j = 0; j < sk_X509_NAME_ENTRY_num((STACK_OF(X509_NAME_ENTRY) *)
xsname->entries);
j++) {
xsne = sk_X509_NAME_ENTRY_value((STACK_OF(X509_NAME_ENTRY) *)
xsname->entries, j);
n =OBJ_obj2nid((ASN1_OBJECT *)X509_NAME_ENTRY_get_object(xsne));
if (n == ssl_var_lookup_ssl_cert_dn_rec[i].nid && idx-- == 0) {
result = SSL_X509_NAME_ENTRY_to_string(p, xsne);
break;
}
}
break;
}
}
return result;
}
static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, ASN1_TIME *tm)
{
char *result;
BIO* bio;
int n;
if ((bio = BIO_new(BIO_s_mem())) == NULL)
return NULL;
ASN1_TIME_print(bio, tm);
n = BIO_pending(bio);
result = apr_pcalloc(p, n+1);
n = BIO_read(bio, result, n);
result[n] = NUL;
BIO_free(bio);
return result;
}
#define DIGIT2NUM(x) (((x)[0] - '0') * 10 + (x)[1] - '0')
static char *ssl_var_lookup_ssl_cert_remain(apr_pool_t *p, ASN1_TIME *tm)
{
apr_time_t then, now = apr_time_now();
apr_time_exp_t exp = {0};
long diff;
unsigned char *dp;
if ((tm->type == V_ASN1_UTCTIME && tm->length < 11) ||
(tm->type == V_ASN1_GENERALIZEDTIME && tm->length < 13) ||
!ASN1_TIME_check(tm)) {
return apr_pstrdup(p, "0");
}
if (tm->type == V_ASN1_UTCTIME) {
exp.tm_year = DIGIT2NUM(tm->data);
if (exp.tm_year <= 50) exp.tm_year += 100;
dp = tm->data + 2;
} else {
exp.tm_year = DIGIT2NUM(tm->data) * 100 + DIGIT2NUM(tm->data + 2) - 1900;
dp = tm->data + 4;
}
exp.tm_mon = DIGIT2NUM(dp) - 1;
exp.tm_mday = DIGIT2NUM(dp + 2) + 1;
exp.tm_hour = DIGIT2NUM(dp + 4);
exp.tm_min = DIGIT2NUM(dp + 6);
exp.tm_sec = DIGIT2NUM(dp + 8);
if (apr_time_exp_gmt_get(&then, &exp) != APR_SUCCESS) {
return apr_pstrdup(p, "0");
}
diff = (long)((apr_time_sec(then) - apr_time_sec(now)) / (60*60*24));
return diff > 0 ? apr_ltoa(p, diff) : apr_pstrdup(p, "0");
}
static char *ssl_var_lookup_ssl_cert_serial(apr_pool_t *p, X509 *xs)
{
char *result;
BIO *bio;
int n;
if ((bio = BIO_new(BIO_s_mem())) == NULL)
return NULL;
i2a_ASN1_INTEGER(bio, X509_get_serialNumber(xs));
n = BIO_pending(bio);
result = apr_pcalloc(p, n+1);
n = BIO_read(bio, result, n);
result[n] = NUL;
BIO_free(bio);
return result;
}
static char *ssl_var_lookup_ssl_cert_chain(apr_pool_t *p, STACK_OF(X509) *sk, char *var)
{
char *result;
X509 *xs;
int n;
result = NULL;
if (strspn(var, "0123456789") == strlen(var)) {
n = atoi(var);
if (n < sk_X509_num(sk)) {
xs = sk_X509_value(sk, n);
result = ssl_var_lookup_ssl_cert_PEM(p, xs);
}
}
return result;
}
static char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t *p, X509 *xs)
{
char *result;
BIO *bio;
int n;
if ((bio = BIO_new(BIO_s_mem())) == NULL)
return NULL;
PEM_write_bio_X509(bio, xs);
n = BIO_pending(bio);
result = apr_pcalloc(p, n+1);
n = BIO_read(bio, result, n);
result[n] = NUL;
BIO_free(bio);
return result;
}
static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, conn_rec *c)
{
SSLConnRec *sslconn = myConnConfig(c);
char *result;
long vrc;
const char *verr;
const char *vinfo;
SSL *ssl;
X509 *xs;
result = NULL;
ssl = sslconn->ssl;
verr = sslconn->verify_error;
vinfo = sslconn->verify_info;
vrc = SSL_get_verify_result(ssl);
xs = SSL_get_peer_certificate(ssl);
if (vrc == X509_V_OK && verr == NULL && xs == NULL)
result = "NONE";
else if (vrc == X509_V_OK && verr == NULL && vinfo == NULL && xs != NULL)
result = "SUCCESS";
else if (vrc == X509_V_OK && vinfo != NULL && strEQ(vinfo, "GENEROUS"))
result = "GENEROUS";
else
result = apr_psprintf(p, "FAILED:%s",
verr ? verr : X509_verify_cert_error_string(vrc));
if (xs)
X509_free(xs);
return result;
}
static char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, conn_rec *c, char *var)
{
SSLConnRec *sslconn = myConnConfig(c);
char *result;
BOOL resdup;
int usekeysize, algkeysize;
SSL *ssl;
result = NULL;
resdup = TRUE;
ssl = sslconn->ssl;
ssl_var_lookup_ssl_cipher_bits(ssl, &usekeysize, &algkeysize);
if (ssl && strEQ(var, "")) {
MODSSL_SSL_CIPHER_CONST SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
result = (cipher != NULL ? (char *)SSL_CIPHER_get_name(cipher) : NULL);
}
else if (strcEQ(var, "_EXPORT"))
result = (usekeysize < 56 ? "true" : "false");
else if (strcEQ(var, "_USEKEYSIZE")) {
result = apr_itoa(p, usekeysize);
resdup = FALSE;
}
else if (strcEQ(var, "_ALGKEYSIZE")) {
result = apr_itoa(p, algkeysize);
resdup = FALSE;
}
if (result != NULL && resdup)
result = apr_pstrdup(p, result);
return result;
}
static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize)
{
MODSSL_SSL_CIPHER_CONST SSL_CIPHER *cipher;
*usekeysize = 0;
*algkeysize = 0;
if (ssl != NULL)
if ((cipher = SSL_get_current_cipher(ssl)) != NULL)
*usekeysize = SSL_CIPHER_get_bits(cipher, algkeysize);
return;
}
static char *ssl_var_lookup_ssl_version(apr_pool_t *p, char *var)
{
if (strEQ(var, "INTERFACE")) {
return apr_pstrdup(p, var_interface);
}
else if (strEQ(var, "LIBRARY_INTERFACE")) {
return apr_pstrdup(p, var_library_interface);
}
else if (strEQ(var, "LIBRARY")) {
return apr_pstrdup(p, var_library);
}
return NULL;
}
static void extract_dn(apr_table_t *t, apr_hash_t *nids, const char *pfx,
X509_NAME *xn, apr_pool_t *p)
{
STACK_OF(X509_NAME_ENTRY) *ents = xn->entries;
X509_NAME_ENTRY *xsne;
apr_hash_t *count;
int i, nid;
count = apr_hash_make(p);
for (i = 0; i < sk_X509_NAME_ENTRY_num(ents); i++) {
const char *tag;
xsne = sk_X509_NAME_ENTRY_value(ents, i);
nid = OBJ_obj2nid((ASN1_OBJECT *)X509_NAME_ENTRY_get_object(xsne));
tag = apr_hash_get(nids, &nid, sizeof nid);
if (tag) {
const char *key;
int *dup;
char *value;
dup = apr_hash_get(count, &nid, sizeof nid);
if (dup) {
key = apr_psprintf(p, "%s%s_%d", pfx, tag, ++(*dup));
}
else {
dup = apr_pcalloc(p, sizeof *dup);
apr_hash_set(count, &nid, sizeof nid, dup);
key = apr_pstrcat(p, pfx, tag, NULL);
}
value = SSL_X509_NAME_ENTRY_to_string(p, xsne);
apr_table_setn(t, key, value);
}
}
}
void modssl_var_extract_dns(apr_table_t *t, SSL *ssl, apr_pool_t *p)
{
apr_hash_t *nids;
unsigned n;
X509 *xs;
nids = apr_hash_make(p);
for (n = 0; ssl_var_lookup_ssl_cert_dn_rec[n].name; n++) {
if (ssl_var_lookup_ssl_cert_dn_rec[n].extract) {
apr_hash_set(nids, &ssl_var_lookup_ssl_cert_dn_rec[n].nid,
sizeof(ssl_var_lookup_ssl_cert_dn_rec[0].nid),
ssl_var_lookup_ssl_cert_dn_rec[n].name);
}
}
xs = SSL_get_certificate(ssl);
if (xs) {
extract_dn(t, nids, "SSL_SERVER_S_DN_", X509_get_subject_name(xs), p);
extract_dn(t, nids, "SSL_SERVER_I_DN_", X509_get_issuer_name(xs), p);
}
xs = SSL_get_peer_certificate(ssl);
if (xs) {
extract_dn(t, nids, "SSL_CLIENT_S_DN_", X509_get_subject_name(xs), p);
extract_dn(t, nids, "SSL_CLIENT_I_DN_", X509_get_issuer_name(xs), p);
X509_free(xs);
}
}
static int dump_extn_value(BIO *bio, ASN1_OCTET_STRING *str)
{
const unsigned char *pp = str->data;
ASN1_STRING *ret = ASN1_STRING_new();
int rv = 0;
if (d2i_DISPLAYTEXT(&ret, &pp, str->length)) {
ASN1_STRING_print_ex(bio, ret, ASN1_STRFLGS_UTF8_CONVERT);
rv = 1;
}
ASN1_STRING_free(ret);
return rv;
}
apr_array_header_t *ssl_ext_list(apr_pool_t *p, conn_rec *c, int peer,
const char *extension)
{
SSLConnRec *sslconn = myConnConfig(c);
SSL *ssl = NULL;
apr_array_header_t *array = NULL;
X509 *xs = NULL;
ASN1_OBJECT *oid = NULL;
int count = 0, j;
if (!sslconn || !sslconn->ssl || !extension) {
return NULL;
}
ssl = sslconn->ssl;
oid = OBJ_txt2obj(extension, 0);
if (!oid) {
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01970)
"could not parse OID '%s'", extension);
ERR_clear_error();
return NULL;
}
xs = peer ? SSL_get_peer_certificate(ssl) : SSL_get_certificate(ssl);
if (xs == NULL) {
return NULL;
}
count = X509_get_ext_count(xs);
array = apr_array_make(p, count, sizeof(char *));
for (j = 0; j < count; j++) {
X509_EXTENSION *ext = X509_get_ext(xs, j);
if (OBJ_cmp(ext->object, oid) == 0) {
BIO *bio = BIO_new(BIO_s_mem());
if (X509V3_EXT_print(bio, ext, 0, 0) == 1 ||
dump_extn_value(bio, X509_EXTENSION_get_data(ext)) == 1) {
BUF_MEM *buf;
char **ptr = apr_array_push(array);
BIO_get_mem_ptr(bio, &buf);
*ptr = apr_pstrmemdup(p, buf->data, buf->length);
} else {
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01971)
"Found an extension '%s', but failed to "
"create a string from it", extension);
}
BIO_vfree(bio);
}
}
if (array->nelts == 0)
array = NULL;
if (peer) {
X509_free(xs);
}
ASN1_OBJECT_free(oid);
ERR_clear_error();
return array;
}
static char *ssl_var_lookup_ssl_compress_meth(SSL *ssl)
{
char *result = "NULL";
#ifndef OPENSSL_NO_COMP
SSL_SESSION *pSession = SSL_get_session(ssl);
if (pSession) {
#ifdef OPENSSL_NO_SSL_INTERN
switch (SSL_SESSION_get_compress_id(pSession)) {
#else
switch (pSession->compress_meth) {
#endif
case 0:
break;
case 1:
result = "DEFLATE";
break;
case 0x40:
result = "LZS";
break;
default:
result = "UNKNOWN";
break;
}
}
#endif
return result;
}
#include "../../modules/loggers/mod_log_config.h"
static const char *ssl_var_log_handler_c(request_rec *r, char *a);
static const char *ssl_var_log_handler_x(request_rec *r, char *a);
void ssl_var_log_config_register(apr_pool_t *p)
{
static APR_OPTIONAL_FN_TYPE(ap_register_log_handler) *log_pfn_register;
log_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler);
if (log_pfn_register) {
log_pfn_register(p, "c", ssl_var_log_handler_c, 0);
log_pfn_register(p, "x", ssl_var_log_handler_x, 0);
}
return;
}
static const char *ssl_var_log_handler_c(request_rec *r, char *a)
{
SSLConnRec *sslconn = myConnConfig(r->connection);
char *result;
if (sslconn == NULL || sslconn->ssl == NULL)
return NULL;
result = NULL;
if (strEQ(a, "version"))
result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_PROTOCOL");
else if (strEQ(a, "cipher"))
result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CIPHER");
else if (strEQ(a, "subjectdn") || strEQ(a, "clientcert"))
result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_S_DN");
else if (strEQ(a, "issuerdn") || strEQ(a, "cacert"))
result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_I_DN");
else if (strEQ(a, "errcode"))
result = "-";
else if (strEQ(a, "errstr"))
result = (char *)sslconn->verify_error;
if (result != NULL && result[0] == NUL)
result = NULL;
return result;
}
static const char *ssl_var_log_handler_x(request_rec *r, char *a)
{
char *result;
result = ssl_var_lookup(r->pool, r->server, r->connection, r, a);
if (result != NULL && result[0] == NUL)
result = NULL;
return result;
}