Commit 87ac4362 authored by Martin Kraemer's avatar Martin Kraemer
Browse files

Collaborative work: (Thanks, dreid!)

Implement OID checking for mod_ssl. This code allows for checking of arbitrary client
certificate extensions by OID, in a syntax like:
   SSLRequire "BaDCA Generated Certificate" in Oid("2.16.840.1.113730.1.13") \
          || "committers"                   in Oid("1.3.6.1.4.1.18060.1")
Note the following:
* A given OID can occur multiple times in one cert, with different values. Therefore
  the OID function compares the left-hand string against each of the OID values,
  until a complete match is found. If none patches, the result is FALSE
* The left hand side can be another expression, so can be a reference to a variable
  or an file() invocation etc.
* The OID is also just a reference to a string, or function, or whatever.
* My manual description is very short. Someone else please help improve the description



git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@219940 13f79535-47bb-0310-9956-ffa450edef68
parent 76c6bbdf
Loading
Loading
Loading
Loading
+7 −1
Changes for docs/manual/mod/mod_ssl.xml: 7 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -1188,7 +1188,7 @@ boolean expression is true</description>
<usage>
<p>
This directive specifies a general access requirement which has to be
fulfilled in order to allow access. It's a very powerful directive because the
fulfilled in order to allow access. It is a very powerful directive because the
requirement specification is an arbitrarily complex boolean expression
containing any number of access checks.</p>
<note type="warning">
@@ -1216,6 +1216,7 @@ comp ::= word "<strong>==</strong>" word | word "<strong>eq</strong>" word
           | word "<strong>&gt;</strong>"  word | word "<strong>gt</strong>" word
           | word "<strong>&gt;=</strong>" word | word "<strong>ge</strong>" word
           | word "<strong>in</strong>" "<strong>{</strong>" wordlist "<strong>}</strong>"
           | word "<strong>in</strong>" "<strong>OID(</strong>" word "<strong>)</strong>"
           | word "<strong>=~</strong>" regex
           | word "<strong>!~</strong>" regex

@@ -1258,6 +1259,11 @@ SSLRequire ( %{SSL_CIPHER} !~ m/^(EXP|NULL)-/ \<br />
            and %{TIME_HOUR} &gt;= 8 and %{TIME_HOUR} &lt;= 20       ) \<br />
           or %{REMOTE_ADDR} =~ m/^192\.76\.162\.[0-9]+$/
</example>
<p>The <code>OID()</code> function expects to find zero or more instances
of the given OID in the client certificate, and compares the left-hand side
string against the value of matching OID attributes. Every matching OID is
checked, until a match is found.
</p>

<p><em>Standard CGI/1.0 and Apache variables:</em></p>
<pre>
+1 −1
Changes for modules/ssl/ssl_expr.h: 1 added line, 1 removed line.
Original line number Diff line number Diff line
@@ -61,7 +61,7 @@
#endif

typedef enum {
    op_NOP, op_ListElement,
    op_NOP, op_ListElement, op_OidListElement,
    op_True, op_False, op_Not, op_Or, op_And, op_Comp,
    op_EQ, op_NE, op_LT, op_LE, op_GT, op_GE, op_IN, op_REG, op_NRE,
    op_Digit, op_String, op_Regex, op_Var, op_Func
+66 −0
Changes for modules/ssl/ssl_expr_eval.c: 66 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -36,6 +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 char *ssl_expr_eval_func_file(request_rec *, char *);
static int   ssl_expr_eval_strcmplex(char *, char *);

@@ -113,8 +114,19 @@ static BOOL ssl_expr_eval_comp(request_rec *r, ssl_expr *node)
            char *w1 = ssl_expr_eval_word(r, e1);
            BOOL found = FALSE;
            do {
                ssl_expr_node_op op = e2->node_op;
                e3 = (ssl_expr *)e2->node_arg1;
                e2 = (ssl_expr *)e2->node_arg2;

                if (op == op_OidListElement) {
                    char *w3 = ssl_expr_eval_word(r, e3);

                    found = ssl_expr_eval_oid(w1, r, w3);

                    /* There will be no more nodes on the list, so the result is authoritative */
                    break;
                }

                if (strcmp(w1, ssl_expr_eval_word(r, e3)) == 0) {
                    found = TRUE;
                    break;
@@ -186,6 +198,60 @@ 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)
{
    SSLConnRec *sslconn = myConnConfig(r->connection);
    SSL *ssl;
    X509 *xs = NULL;
    ASN1_OBJECT *oid;
    int count = 0, j;
    BOOL result = FALSE;

    if (!oidstr || !sslconn || !sslconn->ssl)
        return FALSE;

    ssl = sslconn->ssl;

    oid = OBJ_txt2obj(oidstr, 1);
    if (!oid) {
        ERR_clear_error();
        return FALSE;
    }

    xs = SSL_get_peer_certificate(ssl);
    if (xs == NULL) {
        return FALSE;
    }

    count = X509_get_ext_count(xs);

    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) {
                BUF_MEM *buf;

                BIO_get_mem_ptr(bio, &buf);

                if (strcmp(word, buf->data) == 0)
                    result = TRUE;
            }

            BIO_vfree(bio);
            if (result != FALSE)
                break;
        }
    }

    X509_free(xs);
    ERR_clear_error();
    return result;
}


static char *ssl_expr_eval_func_file(request_rec *r, char *filename)
{
    apr_file_t *fp;
+7 −1
Changes for modules/ssl/ssl_expr_parse.y: 7 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -61,6 +61,7 @@
%token  T_OP_REG
%token  T_OP_NRE
%token  T_OP_IN
%token  T_OP_OID

%token  T_OP_OR
%token  T_OP_AND
@@ -75,6 +76,7 @@
%type   <exVal>   funccall
%type   <exVal>   regex
%type   <exVal>   words
%type   <exVal>   wordlist
%type   <exVal>   word

%%
@@ -97,11 +99,15 @@ comparison: word T_OP_EQ word { $$ = ssl_expr_make(op_EQ, $1, $3); }
          | word T_OP_LE word            { $$ = ssl_expr_make(op_LE,  $1, $3); }
          | word T_OP_GT word            { $$ = ssl_expr_make(op_GT,  $1, $3); }
          | word T_OP_GE word            { $$ = ssl_expr_make(op_GE,  $1, $3); }
          | word T_OP_IN '{' words '}'   { $$ = ssl_expr_make(op_IN,  $1, $4); }
          | word T_OP_IN wordlist        { $$ = ssl_expr_make(op_IN,  $1, $3); }
          | word T_OP_REG regex          { $$ = ssl_expr_make(op_REG, $1, $3); }
          | word T_OP_NRE regex          { $$ = ssl_expr_make(op_NRE, $1, $3); }
          ;

wordlist  : T_OP_OID '(' word ')'	 { $$ = ssl_expr_make(op_OidListElement, $3, NULL); }
          | '{' words '}'                { $$ = $2 ; }
	  ;

words     : word                         { $$ = ssl_expr_make(op_ListElement, $1, NULL); }
          | words ',' word               { $$ = ssl_expr_make(op_ListElement, $3, $1);   }
          ;
+1 −0
Changes for modules/ssl/ssl_expr_scan.l: 1 added line, 0 removed lines.
Original line number Diff line number Diff line
@@ -173,6 +173,7 @@ int yyinput(char *buf, int max_size);
"not" { return T_OP_NOT; }
"!"   { return T_OP_NOT; }
"in"  { return T_OP_IN; }
[Oo][Ii][Dd] { return T_OP_OID; }

 /*
  * Functions