]> granicus.if.org Git - apache/commitdiff
Collaborative work: (Thanks, dreid!)
authorMartin Kraemer <martin@apache.org>
Wed, 20 Jul 2005 16:42:58 +0000 (16:42 +0000)
committerMartin Kraemer <martin@apache.org>
Wed, 20 Jul 2005 16:42:58 +0000 (16:42 +0000)
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

docs/manual/mod/mod_ssl.xml
modules/ssl/ssl_expr.h
modules/ssl/ssl_expr_eval.c
modules/ssl/ssl_expr_parse.y
modules/ssl/ssl_expr_scan.l

index a184e85f6693fb5c9dd3f1c46b5e3a2af6bc8a1b..e35b9a53833c34feb2b96d2901524321d20c9205 100644 (file)
@@ -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>
index 55f2e182306ed9af03efebfbfb2f772aa4157f1c..b7bd3e72a957020547719f7ac7b37911ba3eda1f 100644 (file)
@@ -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
index 2b3adc8402f4f9920bb4938976faa4ff15c55716..4492eaf944b4689450a002835f760195ce49c63e 100644 (file)
@@ -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;
index 0c2bee4d2984f3067cebbd5100ec4f043abf537a..9c641e1313946966c58399e0c782d20c957793be 100644 (file)
@@ -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);   }
           ;
index 6e1163e40913bfef02347abf39f1bef43d418f43..a54e09a130e1ba23c2e2933bd9fe1593367df1d9 100644 (file)
@@ -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