Commit 1c0474e5 authored by Martin Kraemer's avatar Martin Kraemer
Browse files

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
parent 1d4f5652
Loading
Loading
Loading
Loading
+72 −1
Changes for modules/metadata/mod_setenvif.c: 72 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -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 =
+2 −0
Changes for modules/ssl/mod_ssl.c: 2 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -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 = {
+3 −0
Changes for modules/ssl/mod_ssl.h: 3 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -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__ */
+48 −22
Changes for modules/ssl/ssl_expr_eval.c: 48 added lines, 22 removed lines.
Original line number Diff line number Diff line
@@ -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;
}