/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License.
*/
/* _ _ * _ __ ___ ___ __| | ___ ___| | mod_ssl * | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL * | | | | | | (_) | (_| | \__ \__ \ | * |_| |_| |_|\___/ \__,_|___|___/___/_| * |_____| * ssl_engine_vars.c * Variable Lookup Facility
*/ /* ``Those of you who think they know everything are very annoying to those of us who do.''
-- Unknown */ #include"ssl_private.h" #include"mod_ssl.h" #include"ap_expr.h"
staticint ssl_expr_lookup(ap_expr_lookup_parms *parms)
{ switch (parms->type) { case AP_EXPR_FUNC_VAR: /* for now, we just handle everything that starts with SSL_, but * register our hook as APR_HOOK_LAST * XXX: This can be optimized
*/ if (strcEQn(parms->name, "SSL_", 4)) {
*parms->func = expr_var_fn;
*parms->data = parms->name + 4; return OK;
} break; case AP_EXPR_FUNC_STRING: /* Function SSL() is implemented by us.
*/ if (strcEQ(parms->name, "SSL")) {
*parms->func = expr_func_fn;
*parms->data = NULL; 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;
}
/* This function must remain safe to use for a non-SSL connection. */ char *ssl_var_lookup(apr_pool_t *p, server_rec *s, conn_rec *c, request_rec *r, char *var)
{
SSLModConfigRec *mc = myModConfig(s); constchar *result; BOOL resdup;
apr_time_exp_t tm;
result = NULL;
resdup = TRUE;
/* * When no pool is given try to find one
*/ if (p == NULL) { if (r != NULL)
p = r->pool; elseif (c != NULL)
p = c->pool; else
p = mc->pPool;
}
/* * Request dependent stuff
*/ 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"); elseif (strcEQ(var, "HTTP_REFERER"))
result = apr_table_get(r->headers_in, "Referer"); elseif (strcEQ(var, "HTTP_COOKIE"))
result = apr_table_get(r->headers_in, "Cookie"); elseif (strcEQ(var, "HTTP_FORWARDED"))
result = apr_table_get(r->headers_in, "Forwarded"); elseif (strcEQ(var, "HTTP_HOST"))
result = apr_table_get(r->headers_in, "Host"); elseif (strcEQ(var, "HTTP_PROXY_CONNECTION"))
result = apr_table_get(r->headers_in, "Proxy-Connection"); elseif (strcEQ(var, "HTTP_ACCEPT"))
result = apr_table_get(r->headers_in, "Accept"); elseif (strlen(var) > 5 && strcEQn(var, "HTTP:", 5)) /* all other headers from which we are still not know about */
result = apr_table_get(r->headers_in, var+5); break;
case'R': case'r': if (strcEQ(var, "REQUEST_METHOD"))
result = r->method; elseif (strcEQ(var, "REQUEST_SCHEME"))
result = ap_http_scheme(r); elseif (strcEQ(var, "REQUEST_URI"))
result = r->uri; elseif (strcEQ(var, "REQUEST_FILENAME"))
result = r->filename; elseif (strcEQ(var, "REMOTE_ADDR"))
result = r->useragent_ip; elseif (strcEQ(var, "REMOTE_HOST"))
result = ap_get_useragent_host(r, REMOTE_NAME, NULL); elseif (strcEQ(var, "REMOTE_IDENT"))
result = ap_get_remote_logname(r); elseif (strcEQ(var, "REMOTE_USER"))
result = r->user; break;
case'S': case's': if (strcEQn(var, "SSL", 3)) break; /* shortcut common case */
if (strcEQ(var, "SERVER_ADMIN"))
result = r->server->server_admin; elseif (strcEQ(var, "SERVER_NAME"))
result = ap_get_server_name_for_url(r); elseif (strcEQ(var, "SERVER_PORT"))
result = apr_psprintf(p, "%u", ap_get_server_port(r)); elseif (strcEQ(var, "SERVER_PROTOCOL"))
result = r->protocol; elseif (strcEQ(var, "SCRIPT_FILENAME"))
result = r->filename; break;
default: if (strcEQ(var, "PATH_INFO"))
result = r->path_info; elseif (strcEQ(var, "QUERY_STRING"))
result = r->args; elseif (strcEQ(var, "IS_SUBREQ"))
result = (r->main != NULL ? "true" : "false"); elseif (strcEQ(var, "DOCUMENT_ROOT"))
result = ap_document_root(r); elseif (strcEQ(var, "AUTH_TYPE"))
result = r->ap_auth_type; elseif (strcEQ(var, "THE_REQUEST"))
result = r->the_request; elseif (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;
}
}
/* * Connection stuff
*/ if (result == NULL && c != NULL) {
SSLConnRec *sslconn = ssl_get_effective_config(c); if (strlen(var) > 4 && strcEQn(var, "SSL_", 4)
&& sslconn && sslconn->ssl)
result = ssl_var_lookup_ssl(p, sslconn, r, var+4); elseif (strcEQ(var, "HTTPS")) { if (sslconn && sslconn->ssl)
result = "on"; else
result = "off";
}
}
if (strcEQn(var, "Email_", 6)) {
type = GEN_EMAIL;
var += 6;
} elseif (strcEQn(var, "DNS_", 4)) {
type = GEN_DNS;
var += 4;
} elseif (strcEQn(var, "OTHER_", 6)) {
type = GEN_OTHERNAME;
var += 6; if (strEQn(var, "msUPN_", 6)) {
var += 6;
onf = "msUPN";
} elseif (strEQn(var, "dnsSRV_", 7)) {
var += 7;
onf = "id-on-dnsSRV";
} else return NULL;
} else return NULL;
/* sanity check: number must be between 1 and 4 digits */
numlen = strspn(var, "0123456789"); if ((numlen < 1) || (numlen > 4) || (numlen != strlen(var))) return NULL;
if (modssl_X509_getSAN(p, xs, type, onf, atoi(var), &entries)) /* return the first entry from this 1-element array */ return APR_ARRAY_IDX(entries, 0, char *); else return NULL;
}
/* Return a string giving the number of days remaining until 'tm', or
* "0" if this can't be determined. */ staticchar *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; unsignedchar *dp;
/* Fail if the time isn't a valid ASN.1 TIME; RFC3280 mandates * that the seconds digits are present even though ASN.1
* doesn't. */ 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");
}
/* Add each RDN in 'xn' to the table 't' where the NID is present in
* 'nids', using key prefix 'pfx'. */ staticvoid extract_dn(apr_table_t *t, apr_hash_t *nids, constchar *pfx,
X509_NAME *xn, apr_pool_t *p)
{
X509_NAME_ENTRY *xsne;
apr_hash_t *count; int i, nid;
/* Hash of (int) NID -> (int *) counter to count each time an RDN
* with the given NID has been seen. */
count = apr_hash_make(p);
/* For each RDN... */ for (i = 0; i < X509_NAME_entry_count(xn); i++) { constchar *tag;
xsne = X509_NAME_get_entry(xn, i);
/* Retrieve the nid, and check whether this is one of the nids
* which are to be extracted. */
nid = OBJ_obj2nid((ASN1_OBJECT *)X509_NAME_ENTRY_get_object(xsne));
tag = apr_hash_get(nids, &nid, sizeof nid); if (tag) { constchar *key; int *dup; char *value;
/* Check whether a variable with this nid was already
* been used; if so, use the foo_N=bar syntax. */
dup = apr_hash_get(count, &nid, sizeof nid); if (dup) {
key = apr_psprintf(p, "%s%s_%d", pfx, tag, ++(*dup));
} else { /* Otherwise, use the plain foo=bar syntax. */
dup = apr_pcalloc(p, sizeof *dup);
apr_hash_set(count, &nid, sizeof nid, dup);
key = apr_pstrcat(p, pfx, tag, NULL);
}
value = modssl_X509_NAME_ENTRY_to_string(p, xsne, 0);
apr_table_setn(t, key, value);
}
}
}
/* Build up a hash table of (int *)NID->(char *)short-name for all
* the tags which are to be extracted: */
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);
}
}
/* Extract the server cert DNS -- note that the refcount does NOT
* increase: */
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);
}
/* Extract the client cert DNs -- note that the refcount DOES
* increase: */
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);
}
}
/* subjectAltName entries of the server certificate */
xs = SSL_get_certificate(ssl); if (xs) { if (modssl_X509_getSAN(p, xs, GEN_EMAIL, NULL, -1, &entries)) {
extract_san_array(t, "SSL_SERVER_SAN_Email", entries, p);
} if (modssl_X509_getSAN(p, xs, GEN_DNS, NULL, -1, &entries)) {
extract_san_array(t, "SSL_SERVER_SAN_DNS", entries, p);
} if (modssl_X509_getSAN(p, xs, GEN_OTHERNAME, "id-on-dnsSRV", -1,
&entries)) {
extract_san_array(t, "SSL_SERVER_SAN_OTHER_dnsSRV", entries, p);
} /* no need to free xs (refcount does not increase) */
}
/* subjectAltName entries of the client certificate */
xs = SSL_get_peer_certificate(ssl); if (xs) { if (modssl_X509_getSAN(p, xs, GEN_EMAIL, NULL, -1, &entries)) {
extract_san_array(t, "SSL_CLIENT_SAN_Email", entries, p);
} if (modssl_X509_getSAN(p, xs, GEN_DNS, NULL, -1, &entries)) {
extract_san_array(t, "SSL_CLIENT_SAN_DNS", entries, p);
} if (modssl_X509_getSAN(p, xs, GEN_OTHERNAME, "msUPN", -1, &entries)) {
extract_san_array(t, "SSL_CLIENT_SAN_OTHER_msUPN", entries, p);
}
X509_free(xs);
}
}
/* For an extension type which OpenSSL does not recognize, attempt to * parse the extension type as a primitive string. This will fail for * any structured extension type per the docs. Returns non-zero on
* success and writes the string to the given bio. */ staticint dump_extn_value(BIO *bio, ASN1_OCTET_STRING *str)
{ constunsignedchar *pp = str->data;
ASN1_STRING *ret = ASN1_STRING_new(); int rv = 0;
/* This allows UTF8String, IA5String, VisibleString, or BMPString;
* conversion to UTF-8 is forced. */ if (d2i_DISPLAYTEXT(&ret, &pp, str->length)) {
ASN1_STRING_print_ex(bio, ret, ASN1_STRFLGS_UTF8_CONVERT);
rv = 1;
}
/* We accept the "extension" string to be converted as * a long name (nsComment), short name (DN) or * numeric OID (1.2.3.4).
*/
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;
}
count = X509_get_ext_count(xs); /* Create an array large enough to accommodate every extension. This is * likely overkill, but safe.
*/
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(X509_EXTENSION_get_object(ext), oid) == 0) {
BIO *bio = BIO_new(BIO_s_mem());
/* We want to obtain a string representation of the extensions * value and add it to the array we're building. * X509V3_EXT_print() doesn't know about all the possible * data types, but the value is stored as an ASN1_OCTET_STRING * allowing us a fallback in case of X509V3_EXT_print * not knowing how to handle the data.
*/ 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) { /* only SSL_get_peer_certificate raises the refcount */
X509_free(xs);
}
/* * register us for the mod_log_config function registering phase * to establish %{...}c and to be able to expand %{...}x variables.
*/ void ssl_var_log_config_register(apr_pool_t *p)
{
APR_OPTIONAL_FN_TYPE(ap_register_log_handler) *log_pfn_register;
/* * implement the %{..}c log function * (we are the only function)
*/ staticconstchar *ssl_var_log_handler_c(request_rec *r, char *a)
{
SSLConnRec *sslconn = ssl_get_effective_config(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"); elseif (strEQ(a, "cipher"))
result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CIPHER"); elseif (strEQ(a, "subjectdn") || strEQ(a, "clientcert"))
result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_S_DN"); elseif (strEQ(a, "issuerdn") || strEQ(a, "cacert"))
result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_I_DN"); elseif (strEQ(a, "errcode"))
result = "-"; elseif (strEQ(a, "errstr"))
result = (char *)sslconn->verify_error; if (result) {
result = *result ? ap_escape_logitem(r->pool, result) : NULL;
} return result;
}
/* * extend the implementation of the %{..}x log function * (there can be more functions)
*/ staticconstchar *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) {
result = *result ? ap_escape_logitem(r->pool, result) : NULL;
} return result;
}
¤ Dauer der Verarbeitung: 0.25 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.