From: Martin Kraemer Date: Fri, 22 Jul 2005 12:11:55 +0000 (+0000) Subject: Allow extraction of the values of SSL certificate extensions into X-Git-Tag: 2.1.7~5^2~79 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=4c8c57327534587c0b56f007e24616d373cb0963;p=apache Allow extraction of the values of SSL certificate extensions into environment variables, so that their value can be used by any module that is aware of environment variables, as in: SetEnvIf OID("2.16.840.1.113730.1.13") "(.*) Generated (Certificate)" ca=$1 sets ca=TinyCA if the cert was issued by TinyCA. Similarly, SetenvIf OID("2.16.840.1.113730.1.13") "(.*)" NetscapeComment=$1 will set $NetscapeComment to the whole string. It is technically allowed to have multiple instances of an extension field, all with the same oid. In this case, the environment variable will be set to the list of all fields, separated by commas. The [PATCH] uses a cross-module call from mod_setenvif to mod_ssl (the latter may also be missing: in this case the variable will never be set). It calls a common function in the ssl module that is also used for the SSLRequire directive's test. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@220307 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/modules/metadata/mod_setenvif.c b/modules/metadata/mod_setenvif.c index f07a9f5925..0010e3a943 100644 --- a/modules/metadata/mod_setenvif.c +++ b/modules/metadata/mod_setenvif.c @@ -102,7 +102,8 @@ enum special { SPECIAL_REQUEST_URI, SPECIAL_REQUEST_METHOD, SPECIAL_REQUEST_PROTOCOL, - SPECIAL_SERVER_ADDR + SPECIAL_SERVER_ADDR, + SPECIAL_OID_VALUE }; typedef struct { char *name; /* header name */ @@ -120,6 +121,12 @@ typedef struct { } sei_cfg_rec; module AP_MODULE_DECLARE_DATA setenvif_module; +#if (MODULE_MAGIC_NUMBER_MAJOR > 20020903) +#include "mod_ssl.h" +#else +APR_DECLARE_OPTIONAL_FN(apr_array_header_t *, ssl_extlist_by_oid, (request_rec *r, const char *oidstr)); +#endif +static APR_OPTIONAL_FN_TYPE(ssl_extlist_by_oid) *ssl_extlist_by_oid_func = NULL; /* * These routines, the create- and merge-config functions, are called @@ -345,6 +352,31 @@ static const char *add_setenvif_core(cmd_parms *cmd, void *mconfig, else if (!strcasecmp(fname, "server_addr")) { new->special_type = SPECIAL_SERVER_ADDR; } + else if (!strncasecmp(fname, "oid(",4)) { + ap_regmatch_t match[AP_MAX_REG_MATCH]; + + new->special_type = SPECIAL_OID_VALUE; + + /* Syntax check and extraction of the OID as a regex: */ + new->pnamereg = ap_pregcomp(cmd->pool, + "^oid\\(\"?([0-9.]+)\"?\\)$", + (AP_REG_EXTENDED // | AP_REG_NOSUB + | AP_REG_ICASE)); + /* this can never happen, as long as pcre works: + if (new->pnamereg == NULL) + return apr_pstrcat(cmd->pool, cmd->cmd->name, + "OID regex could not be compiled.", NULL); + */ + if (ap_regexec(new->pnamereg, fname, AP_MAX_REG_MATCH, match, 0) == AP_REG_NOMATCH) { + return apr_pstrcat(cmd->pool, cmd->cmd->name, + "OID syntax is: oid(\"1.2.3.4.5\"); error in: ", + fname, NULL); + } + new->pnamereg = NULL; + /* The name field is used for the stripped oid string */ + new->name = fname = apr_pstrdup(cmd->pool, fname+match[1].rm_so); + fname[match[1].rm_eo - match[1].rm_so] = '\0'; + } else { new->special_type = SPECIAL_NOT; /* Handle fname as a regular expression. @@ -475,6 +507,8 @@ static int match_headers(request_rec *r) * same header. Remember we don't need to strcmp the two header * names because we made sure the pointers were equal during * configuration. + * In the case of SPECIAL_OID_VALUE values, each oid string is + * dynamically allocated, thus there are no duplicates. */ if (b->name != last_name) { last_name = b->name; @@ -498,6 +532,34 @@ static int match_headers(request_rec *r) case SPECIAL_REQUEST_PROTOCOL: val = r->protocol; break; + case SPECIAL_OID_VALUE: + /* If mod_ssl is not loaded, the accessor function is NULL */ + if (ssl_extlist_by_oid_func != NULL) + { + apr_array_header_t *oid_array; + char **oid_value; + int j, len = 0; + char *retval = NULL; + + /* The given oid can occur multiple times. Concatenate the values */ + if ((oid_array = ssl_extlist_by_oid_func(r, b->name)) != NULL) { + oid_value = (char **) oid_array->elts; + /* pass 1: determine the size of the string */ + for (len=j=0; j < oid_array->nelts; j++) { + len += strlen(oid_value[j]) + 1; /* +1 for ',' or terminating NIL */ + } + retval = apr_palloc(r->pool, len); + /* pass 2: fill the string */ + for (j=0; j < oid_array->nelts; j++) { + if (j > 0) { + strcat(retval, ","); + } + strcat(retval, oid_value[j]); + } + } + val = retval; + } + break; case SPECIAL_NOT: if (b->pnamereg) { /* Matching headers_in against a regex. Iterate through @@ -568,10 +630,19 @@ static int match_headers(request_rec *r) return DECLINED; } +static int +setenvif_post_config() +{ + ssl_extlist_by_oid_func = APR_RETRIEVE_OPTIONAL_FN(ssl_extlist_by_oid); + return OK; +} + static void register_hooks(apr_pool_t *p) { ap_hook_header_parser(match_headers, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_post_read_request(match_headers, NULL, NULL, APR_HOOK_MIDDLE); + /* post config handling */ + ap_hook_post_config(setenvif_post_config, NULL, NULL, APR_HOOK_MIDDLE); } module AP_MODULE_DECLARE_DATA setenvif_module = diff --git a/modules/ssl/mod_ssl.c b/modules/ssl/mod_ssl.c index 0fcbd52283..4fcb172dfa 100644 --- a/modules/ssl/mod_ssl.c +++ b/modules/ssl/mod_ssl.c @@ -503,6 +503,8 @@ static void ssl_register_hooks(apr_pool_t *p) APR_REGISTER_OPTIONAL_FN(ssl_proxy_enable); APR_REGISTER_OPTIONAL_FN(ssl_engine_disable); + + APR_REGISTER_OPTIONAL_FN(ssl_extlist_by_oid); } module AP_MODULE_DECLARE_DATA ssl_module = { diff --git a/modules/ssl/mod_ssl.h b/modules/ssl/mod_ssl.h index fdde641196..5cc37c1ee6 100644 --- a/modules/ssl/mod_ssl.h +++ b/modules/ssl/mod_ssl.h @@ -49,4 +49,7 @@ APR_DECLARE_OPTIONAL_FN(int, ssl_proxy_enable, (conn_rec *)); APR_DECLARE_OPTIONAL_FN(int, ssl_engine_disable, (conn_rec *)); +extern apr_array_header_t *ssl_extlist_by_oid(request_rec *r, const char *oidstr); +APR_DECLARE_OPTIONAL_FN(apr_array_header_t *, ssl_extlist_by_oid, (request_rec *r, const char *oidstr)); + #endif /* __MOD_SSL_H__ */ diff --git a/modules/ssl/ssl_expr_eval.c b/modules/ssl/ssl_expr_eval.c index 4492eaf944..3350437a94 100644 --- a/modules/ssl/ssl_expr_eval.c +++ b/modules/ssl/ssl_expr_eval.c @@ -36,7 +36,7 @@ static BOOL ssl_expr_eval_comp(request_rec *, ssl_expr *); static char *ssl_expr_eval_word(request_rec *, ssl_expr *); -static BOOL ssl_expr_eval_oid(const char *w, request_rec *r, const char *oidstr); +static BOOL ssl_expr_eval_oid(request_rec *r, const char *word, const char *oidstr); static char *ssl_expr_eval_func_file(request_rec *, char *); static int ssl_expr_eval_strcmplex(char *, char *); @@ -121,7 +121,7 @@ static BOOL ssl_expr_eval_comp(request_rec *r, ssl_expr *node) if (op == op_OidListElement) { char *w3 = ssl_expr_eval_word(r, e3); - found = ssl_expr_eval_oid(w1, r, w3); + found = ssl_expr_eval_oid(r, w1, w3); /* There will be no more nodes on the list, so the result is authoritative */ break; @@ -198,33 +198,35 @@ static char *ssl_expr_eval_word(request_rec *r, ssl_expr *node) } } -static BOOL ssl_expr_eval_oid(const char *word, request_rec *r, const char *oidstr) +#define NUM_OID_ELTS 8 /* start with 8 oid slots, resize when needed */ + +apr_array_header_t *ssl_extlist_by_oid(request_rec *r, const char *oidstr) { - SSLConnRec *sslconn = myConnConfig(r->connection); - SSL *ssl; + int count = 0, j; X509 *xs = NULL; ASN1_OBJECT *oid; - int count = 0, j; - BOOL result = FALSE; - - if (!oidstr || !sslconn || !sslconn->ssl) - return FALSE; + apr_array_header_t *val_array; + SSLConnRec *sslconn = myConnConfig(r->connection); - ssl = sslconn->ssl; + /* trivia */ + if (oidstr == NULL || sslconn == NULL || sslconn->ssl == NULL) + return NULL; - oid = OBJ_txt2obj(oidstr, 1); - if (!oid) { + /* Determine the oid we are looking for */ + if ((oid = OBJ_txt2obj(oidstr, 1)) == NULL) { ERR_clear_error(); - return FALSE; + return NULL; } - xs = SSL_get_peer_certificate(ssl); - if (xs == NULL) { - return FALSE; + /* are there any extensions in the cert? */ + if ((xs = SSL_get_peer_certificate(sslconn->ssl)) == NULL || + (count = X509_get_ext_count(xs)) == 0) { + return NULL; } - count = X509_get_ext_count(xs); + val_array = apr_array_make(r->pool, NUM_OID_ELTS, sizeof(char *)); + /* Loop over all extensions, extract the desired oids */ for (j = 0; j < count; j++) { X509_EXTENSION *ext = X509_get_ext(xs, j); @@ -233,21 +235,45 @@ static BOOL ssl_expr_eval_oid(const char *word, request_rec *r, const char *oids if (X509V3_EXT_print(bio, ext, 0, 0) == 1) { BUF_MEM *buf; + char **new = apr_array_push(val_array); BIO_get_mem_ptr(bio, &buf); - if (strcmp(word, buf->data) == 0) - result = TRUE; + *new = apr_pstrdup(r->pool, buf->data); } BIO_vfree(bio); - if (result != FALSE) - break; } } X509_free(xs); ERR_clear_error(); + + if (val_array->nelts == 0) + return NULL; + else + return val_array; +} + +static BOOL ssl_expr_eval_oid(request_rec *r, const char *word, const char *oidstr) +{ + SSLConnRec *sslconn = myConnConfig(r->connection); + int j; + BOOL result = FALSE; + apr_array_header_t *oid_array; + char **oid_value; + + if (NULL == (oid_array = ssl_extlist_by_oid(r, oidstr))) { + return FALSE; + } + + oid_value = (char **) oid_array->elts; + for (j = 0; j < oid_array->nelts; j++) { + if (strcmp(word, oid_value[j]) == 0) { + result = TRUE; + break; + } + } return result; }