From: Martin Kraemer Date: Wed, 20 Jul 2005 16:42:58 +0000 (+0000) Subject: Collaborative work: (Thanks, dreid!) X-Git-Tag: 2.1.7~5^2~89 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b5a3f87755a24db37f50773d0a40143180b92fca;p=apache 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 --- diff --git a/docs/manual/mod/mod_ssl.xml b/docs/manual/mod/mod_ssl.xml index a184e85f66..e35b9a5383 100644 --- a/docs/manual/mod/mod_ssl.xml +++ b/docs/manual/mod/mod_ssl.xml @@ -1188,7 +1188,7 @@ boolean expression is true

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.

@@ -1216,6 +1216,7 @@ comp ::= word "==" word | word "eq" word | word ">" word | word "gt" word | word ">=" word | word "ge" word | word "in" "{" wordlist "}" + | word "in" "OID(" word ")" | word "=~" regex | word "!~" regex @@ -1258,6 +1259,11 @@ SSLRequire ( %{SSL_CIPHER} !~ m/^(EXP|NULL)-/ \
and %{TIME_HOUR} >= 8 and %{TIME_HOUR} <= 20 ) \
or %{REMOTE_ADDR} =~ m/^192\.76\.162\.[0-9]+$/ +

The OID() 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. +

Standard CGI/1.0 and Apache variables:

diff --git a/modules/ssl/ssl_expr.h b/modules/ssl/ssl_expr.h
index 55f2e18230..b7bd3e72a9 100644
--- a/modules/ssl/ssl_expr.h
+++ b/modules/ssl/ssl_expr.h
@@ -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
diff --git a/modules/ssl/ssl_expr_eval.c b/modules/ssl/ssl_expr_eval.c
index 2b3adc8402..4492eaf944 100644
--- a/modules/ssl/ssl_expr_eval.c
+++ b/modules/ssl/ssl_expr_eval.c
@@ -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;
diff --git a/modules/ssl/ssl_expr_parse.y b/modules/ssl/ssl_expr_parse.y
index 0c2bee4d29..9c641e1313 100644
--- a/modules/ssl/ssl_expr_parse.y
+++ b/modules/ssl/ssl_expr_parse.y
@@ -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      funccall
 %type      regex
 %type      words
+%type      wordlist
 %type      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);   }
           ;
diff --git a/modules/ssl/ssl_expr_scan.l b/modules/ssl/ssl_expr_scan.l
index 6e1163e409..a54e09a130 100644
--- a/modules/ssl/ssl_expr_scan.l
+++ b/modules/ssl/ssl_expr_scan.l
@@ -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