]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: ssl: add pattern and ACLs fetches 'ssl_c_s_dn', 'ssl_c_i_dn', 'ssl_f_s_dn...
authorEmeric Brun <ebrun@exceliance.fr>
Wed, 17 Oct 2012 15:39:35 +0000 (17:39 +0200)
committerWilly Tarreau <w@1wt.eu>
Fri, 26 Oct 2012 13:08:00 +0000 (15:08 +0200)
ssl_c_s_dn : client cert subject DN (string)
ssl_c_i_dn : client cert issuer DN (string)
ssl_f_s_dn : frontend cert subject DN (string)
ssl_f_i_dn : frontend cert issuer DN (string)

Return either the full DN without params, or just the DN entry (first param) or
its specific occurrence (second param).

doc/configuration.txt
src/ssl_sock.c

index 0d92993186e8762f9e9ecb1ef58d20b88f11e208..3cecf094352c752d9843d8111ad7c4eaa20a4941 100644 (file)
@@ -8374,6 +8374,28 @@ ssl_c_err <integer>
   matches the specified value (check man verify for possible values). Note that
   error zero means no error was encountered during this verification process.
 
+ssl_c_i_dn <string>
+ssl_c_i_dn(entry[,occ]) <string>
+  If no entry specified, returns true when the incoming connection was made
+  over an SSL/TLS transport layer, and the full distinguished name of the
+  issuer of the certificate presented by the client matches the specified
+  string. Otherwise returns true if the value of the first given entry from
+  the beginning of the DN matches the specified string. If a positive/negative
+  occurrence number is specified as the optional second argument, it returns
+  true if the value of the nth given entry value from the beginning/end of the
+  DN matches the specified string.
+
+ssl_c_s_dn <string>
+ssl_c_s_dn(entry[,occ]) <string>
+  If no entry specified, returns true when the incoming connection was made
+  over an SSL/TLS transport layer, and the full distinguished name of the
+  subject of the certificate presented by the client matches the specified
+  string. Otherwise returns true if the value of the first given entry from
+  the beginning of the DN matches the specified string. If a positive/negative
+  occurrence number is specified as the optional second argument, it returns
+  true if the value of the nth given entry value from the beginning/end of the
+  DN matches the specified string.
+
 ssl_c_serial <hexa>
   Returns true when the incoming connection was made over an SSL/TLS transport
   layer, and the serial of the certificate presented by the client matches
@@ -8389,6 +8411,28 @@ ssl_c_version <integer>
   layer, and the version of the certificate presented by the client matches
   the value.
 
+ssl_f_i_dn <string>
+ssl_f_i_dn(entry[,occ]) <string>
+  If no entry specified, returns true when the incoming connection was made
+  over an SSL/TLS transport layer, and the full distinguished name of the
+  issuer of the certificate presented by the frontend matches the specified
+  string. Otherwise returns true if the value of the first given entry from
+  the beginning of the DN matches the specified string. If a positive/negative
+  occurrence number is specified as the optional second argument, it returns
+  true if the value of the nth given entry value from the beginning/end of the
+  DN matches the specified string.
+
+ssl_f_s_dn <string>
+ssl_f_s_dn(entry[,occ]) <string>
+  If no entry specified, returns true when the incoming connection was made
+  over an SSL/TLS transport layer, and the full distinguished name of the
+  subject of the certificate presented by the frontend matches the specified
+  string. Otherwise returns true if the value of the first given entry from
+  the beginning of the DN matches the specified string. If a positive/negative
+  occurrence number is specified as the optional second argument, it returns
+  true if the value of the nth given entry value from the beginning/end of the
+  DN matches the specified string.
+
 ssl_f_serial <hexa>
   Returns true when the incoming connection was made over an SSL/TLS transport
   layer, and the serial of the certificate presented by the frontend matches
@@ -9076,6 +9120,30 @@ The list of currently supported pattern fetch functions is the following :
   ssl_c_err    Returns the ID of the first error detected during verify of the
                client certificate at depth == 0, or 0 if no errors.
 
+  ssl_c_i_dn[(entry[,occ])]
+               If no entry specified, returns the full distinguished name of
+               the issuer of the certificate presented by the client when
+               the incoming connection was made over an SSL/TLS transport
+               layer. Otherwise returns the the value of the first given entry
+               found from the the beginning of the DN. If a positive/negative
+               occurrence number is specified as the optional second argument,
+               it returns the value of the nth given entry found from the
+               beginning/end of the DN. For instance to retrieve the common
+               name ssl_c_i_dn(CN) and the second organization unit
+               ssl_c_i_dn(OU,2).
+
+  ssl_c_s_dn[(entry[,occ])]
+               If no entry specified, returns the full distinguished name of
+               the subject of the certificate presented by the client when
+               the incoming connection was made over an SSL/TLS transport
+               layer. Otherwise returns the the value of the first given entry
+               found from the the beginning of the DN. If a positive/negative
+               occurrence number is specified as the optional second argument,
+               it returns the value of the nth given entry found from the
+               beginning/end of the DN. For instance to retrieve the common
+               name ssl_c_s_dn(CN) and the second organization unit
+               ssl_c_s_dn(OU,2).
+
   ssl_c_serial Returns the serial of the certificate presented by the client
                when the incoming connection was made over an SSL/TLS transport
                layer.
@@ -9089,6 +9157,30 @@ The list of currently supported pattern fetch functions is the following :
                when the incoming connection was made over an SSL/TLS transport
                layer.
 
+  ssl_f_i_dn[(entry[,occ])]
+               If no entry specified, returns the full distinguished name of
+               the issuer of the certificate presented by the frontend when
+               the incoming connection was made over an SSL/TLS transport
+               layer. Otherwise returns the the value of the first given entry
+               found from the the beginning of the DN. If a positive/negative
+               occurrence number is specified as the optional second argument,
+               it returns the value of the nth given entry found from the
+               beginning/end of the DN. For instance to retrieve the common
+               name ssl_f_i_dn(CN) and the second organization unit
+               ssl_f_i_dn(OU,2).
+
+  ssl_f_s_dn[(entry[,occ])]
+               If no entry specified, returns the full distinguished name of
+               the subject of the certificate presented by the frontend when
+               the incoming connection was made over an SSL/TLS transport
+               layer. Otherwise returns the the value of the first given entry
+               found from the the beginning of the DN. If a positive/negative
+               occurrence number is specified as the optional second argument,
+               it returns the value of the nth given entry found from the
+               beginning/end of the DN. For instance to retrieve the common
+               name ssl_f_s_dn(CN) and the second organization unit
+               ssl_f_s_dn(OU,2).
+
   ssl_f_serial Returns the serial of the certificate presented by the frontend
                when the incoming connection was made over an SSL/TLS transport
                layer.
index 73d97c2540e357a768c12d083ddc0c74234e0dc2..295b6c9a2278f497069704ce30c13f9ee53bebb8 100644 (file)
@@ -1116,6 +1116,98 @@ ssl_sock_get_serial(X509 *crt, struct chunk *out)
        return 1;
 }
 
+/* Extract an entry from a X509_NAME and copy its value to an output chunk.
+ * Returns 1 if entry found, 0 if entry not found, or -1 if output not large enough.
+ */
+static int
+ssl_sock_get_dn_entry(X509_NAME *a, const struct chunk *entry, int pos, struct chunk *out)
+{
+       X509_NAME_ENTRY *ne;
+       int i, j, n;
+       int cur = 0;
+       const char *s;
+       char tmp[128];
+
+       out->len = 0;
+       for (i = 0; i < sk_X509_NAME_ENTRY_num(a->entries); i++) {
+               if (pos < 0)
+                       j = (sk_X509_NAME_ENTRY_num(a->entries)-1) - i;
+               else
+                       j = i;
+
+               ne = sk_X509_NAME_ENTRY_value(a->entries, j);
+               n = OBJ_obj2nid(ne->object);
+               if ((n == NID_undef) || ((s = OBJ_nid2sn(n)) == NULL)) {
+                       i2t_ASN1_OBJECT(tmp, sizeof(tmp), ne->object);
+                       s = tmp;
+               }
+
+               if (chunk_strcasecmp(entry, s) != 0)
+                       continue;
+
+               if (pos < 0)
+                       cur--;
+               else
+                       cur++;
+
+               if (cur != pos)
+                       continue;
+
+               if (ne->value->length > out->size)
+                       return -1;
+
+               memcpy(out->str, ne->value->data, ne->value->length);
+               out->len = ne->value->length;
+               return 1;
+       }
+
+       return 0;
+
+}
+
+/* Extract and format full DN from a X509_NAME and copy result into a chunk
+ * Returns 1 if dn entries exits, 0 if no dn entry found or -1 if output is not large enough.
+ */
+static int
+ssl_sock_get_dn_oneline(X509_NAME *a, struct chunk *out)
+{
+       X509_NAME_ENTRY *ne;
+       int i, n, ln;
+       int l = 0;
+       const char *s;
+       char *p;
+       char tmp[128];
+
+       out->len = 0;
+       p = out->str;
+       for (i = 0; i < sk_X509_NAME_ENTRY_num(a->entries); i++) {
+               ne = sk_X509_NAME_ENTRY_value(a->entries, i);
+               n = OBJ_obj2nid(ne->object);
+               if ((n == NID_undef) || ((s = OBJ_nid2sn(n)) == NULL)) {
+                       i2t_ASN1_OBJECT(tmp, sizeof(tmp), ne->object);
+                       s = tmp;
+               }
+               ln = strlen(s);
+
+               l += 1 + ln + 1 + ne->value->length;
+               if (l > out->size)
+                       return -1;
+               out->len = l;
+
+               *(p++)='/';
+               memcpy(p, s, ln);
+               p += ln;
+               *(p++)='=';
+               memcpy(p, ne->value->data, ne->value->length);
+               p += ne->value->length;
+       }
+
+       if (!out->len)
+               return 0;
+
+       return 1;
+}
+
 /***** Below are some sample fetching functions for ACL/patterns *****/
 
 /* boolean, returns true if client cert was present */
@@ -1173,6 +1265,107 @@ out:
        return ret;
 }
 
+/* str, returns a string of a formatted full dn \C=..\O=..\OU=.. \CN=.. */
+static int
+smp_fetch_ssl_c_i_dn(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+                       const struct arg *args, struct sample *smp)
+{
+       X509 *crt = NULL;
+       X509_NAME *name;
+       int ret = 0;
+       struct chunk *smp_trash;
+
+       if (!l4 || l4->si[0].conn.xprt != &ssl_sock)
+               return 0;
+
+       if (!(l4->si[0].conn.flags & CO_FL_CONNECTED)) {
+               smp->flags |= SMP_F_MAY_CHANGE;
+               return 0;
+       }
+
+       /* SSL_get_peer_certificate, it increase X509 * ref count */
+       crt = SSL_get_peer_certificate(l4->si[0].conn.xprt_ctx);
+       if (!crt)
+               goto out;
+
+       name = X509_get_issuer_name(crt);
+       if (!name)
+               goto out;
+
+       smp_trash = sample_get_trash_chunk();
+       if (args && args[0].type == ARGT_STR) {
+               int pos = 1;
+
+               if (args[1].type == ARGT_SINT)
+                       pos = args[1].data.sint;
+               else if (args[1].type == ARGT_UINT)
+                       pos =(int)args[1].data.uint;
+
+               if (ssl_sock_get_dn_entry(name, &args[0].data.str, pos, smp_trash) <= 0)
+                       goto out;
+       }
+       else if (ssl_sock_get_dn_oneline(name, smp_trash) <= 0)
+               goto out;
+
+       smp->type = SMP_T_STR;
+       smp->data.str = *smp_trash;
+       ret = 1;
+out:
+       if (crt)
+               X509_free(crt);
+       return ret;
+}
+
+/* str, returns a string of a formatted full dn \C=..\O=..\OU=.. \CN=.. */
+static int
+smp_fetch_ssl_c_s_dn(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+                       const struct arg *args, struct sample *smp)
+{
+       X509 *crt = NULL;
+       X509_NAME *name;
+       int ret = 0;
+       struct chunk *smp_trash;
+
+       if (!l4 || l4->si[0].conn.xprt != &ssl_sock)
+               return 0;
+
+       if (!(l4->si[0].conn.flags & CO_FL_CONNECTED)) {
+               smp->flags |= SMP_F_MAY_CHANGE;
+               return 0;
+       }
+
+       /* SSL_get_peer_certificate, it increase X509 * ref count */
+       crt = SSL_get_peer_certificate(l4->si[0].conn.xprt_ctx);
+       if (!crt)
+               goto out;
+
+       name = X509_get_subject_name(crt);
+       if (!name)
+               goto out;
+
+       smp_trash = sample_get_trash_chunk();
+       if (args && args[0].type == ARGT_STR) {
+               int pos = 1;
+
+               if (args[1].type == ARGT_SINT)
+                       pos = args[1].data.sint;
+               else if (args[1].type == ARGT_UINT)
+                       pos =(int)args[1].data.uint;
+
+               if (ssl_sock_get_dn_entry(name, &args[0].data.str, pos, smp_trash) <= 0)
+                       goto out;
+       }
+       else if (ssl_sock_get_dn_oneline(name, smp_trash) <= 0)
+               goto out;
+
+       smp->type = SMP_T_STR;
+       smp->data.str = *smp_trash;
+       ret = 1;
+out:
+       if (crt)
+               X509_free(crt);
+       return ret;
+}
 /* integer, returns the client certificate version */
 static int
 smp_fetch_ssl_c_version(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
@@ -1284,6 +1477,102 @@ smp_fetch_ssl_f_version(struct proxy *px, struct session *l4, void *l7, unsigned
        return 1;
 }
 
+/* str, returns a string of a formatted full dn \C=..\O=..\OU=.. \CN=.. */
+static int
+smp_fetch_ssl_f_i_dn(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+                       const struct arg *args, struct sample *smp)
+{
+       X509 *crt = NULL;
+       X509_NAME *name;
+       int ret = 0;
+       struct chunk *smp_trash;
+
+       if (!l4 || l4->si[0].conn.xprt != &ssl_sock)
+               return 0;
+
+       if (!(l4->si[0].conn.flags & CO_FL_CONNECTED)) {
+               smp->flags |= SMP_F_MAY_CHANGE;
+               return 0;
+       }
+
+       crt = SSL_get_certificate(l4->si[0].conn.xprt_ctx);
+       if (!crt)
+               goto out;
+
+       name = X509_get_issuer_name(crt);
+       if (!name)
+               goto out;
+
+       smp_trash = sample_get_trash_chunk();
+       if (args && args[0].type == ARGT_STR) {
+               int pos = 1;
+
+               if (args[1].type == ARGT_SINT)
+                       pos = args[1].data.sint;
+               else if (args[1].type == ARGT_UINT)
+                       pos =(int)args[1].data.uint;
+
+               if (ssl_sock_get_dn_entry(name, &args[0].data.str, pos, smp_trash) <= 0)
+                       goto out;
+       }
+       else if (ssl_sock_get_dn_oneline(name, smp_trash) <= 0)
+               goto out;
+
+       smp->type = SMP_T_STR;
+       smp->data.str = *smp_trash;
+       ret = 1;
+out:
+       return ret;
+}
+
+/* str, returns a string of a formatted full dn \C=..\O=..\OU=.. \CN=.. */
+static int
+smp_fetch_ssl_f_s_dn(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+                       const struct arg *args, struct sample *smp)
+{
+       X509 *crt = NULL;
+       X509_NAME *name;
+       int ret = 0;
+       struct chunk *smp_trash;
+
+       if (!l4 || l4->si[0].conn.xprt != &ssl_sock)
+               return 0;
+
+       if (!(l4->si[0].conn.flags & CO_FL_CONNECTED)) {
+               smp->flags |= SMP_F_MAY_CHANGE;
+               return 0;
+       }
+
+       crt = SSL_get_certificate(l4->si[0].conn.xprt_ctx);
+       if (!crt)
+               goto out;
+
+       name = X509_get_subject_name(crt);
+       if (!name)
+               goto out;
+
+       smp_trash = sample_get_trash_chunk();
+       if (args && args[0].type == ARGT_STR) {
+               int pos = 1;
+
+               if (args[1].type == ARGT_SINT)
+                       pos = args[1].data.sint;
+               else if (args[1].type == ARGT_UINT)
+                       pos =(int)args[1].data.uint;
+
+               if (ssl_sock_get_dn_entry(name, &args[0].data.str, pos, smp_trash) <= 0)
+                       goto out;
+       }
+       else if (ssl_sock_get_dn_oneline(name, smp_trash) <= 0)
+               goto out;
+
+       smp->type = SMP_T_STR;
+       smp->data.str = *smp_trash;
+       ret = 1;
+out:
+       return ret;
+}
+
 static int
 smp_fetch_ssl_fc_cipher(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
                         const struct arg *args, struct sample *smp)
@@ -1998,9 +2287,13 @@ static struct sample_fetch_kw_list sample_fetch_keywords = {{ },{
        { "ssl_c_ca_err",           smp_fetch_ssl_c_ca_err,       0,    NULL,    SMP_T_UINT, SMP_CAP_REQ|SMP_CAP_RES },
        { "ssl_c_ca_err_depth",     smp_fetch_ssl_c_ca_err_depth, 0,    NULL,    SMP_T_UINT, SMP_CAP_REQ|SMP_CAP_RES },
        { "ssl_c_err",              smp_fetch_ssl_c_err,          0,    NULL,    SMP_T_UINT, SMP_CAP_REQ|SMP_CAP_RES },
+       { "ssl_c_i_dn",             smp_fetch_ssl_c_i_dn,         ARG2(0,STR,SINT),    NULL,    SMP_T_STR,  SMP_CAP_REQ|SMP_CAP_RES },
+       { "ssl_c_s_dn",             smp_fetch_ssl_c_s_dn,         ARG2(0,STR,SINT),    NULL,    SMP_T_STR,  SMP_CAP_REQ|SMP_CAP_RES },
        { "ssl_c_serial",           smp_fetch_ssl_c_serial,       0,    NULL,    SMP_T_BIN,  SMP_CAP_REQ|SMP_CAP_RES },
        { "ssl_c_verify",           smp_fetch_ssl_c_verify,       0,    NULL,    SMP_T_UINT, SMP_CAP_REQ|SMP_CAP_RES },
        { "ssl_c_version",          smp_fetch_ssl_c_version,      0,    NULL,    SMP_T_UINT, SMP_CAP_REQ|SMP_CAP_RES },
+       { "ssl_f_i_dn",             smp_fetch_ssl_f_i_dn,         ARG2(0,STR,SINT),    NULL,    SMP_T_STR,  SMP_CAP_REQ|SMP_CAP_RES },
+       { "ssl_f_s_dn",             smp_fetch_ssl_f_s_dn,         ARG2(0,STR,SINT),    NULL,    SMP_T_STR,  SMP_CAP_REQ|SMP_CAP_RES },
        { "ssl_f_serial",           smp_fetch_ssl_f_serial,       0,    NULL,    SMP_T_BIN, SMP_CAP_REQ|SMP_CAP_RES },
        { "ssl_f_version",          smp_fetch_ssl_f_version,      0,    NULL,    SMP_T_UINT, SMP_CAP_REQ|SMP_CAP_RES },
        { "ssl_fc",                 smp_fetch_ssl_fc,             0,    NULL,    SMP_T_BOOL, SMP_CAP_REQ|SMP_CAP_RES },
@@ -2025,9 +2318,13 @@ static struct acl_kw_list acl_kws = {{ },{
        { "ssl_c_ca_err",           acl_parse_int, smp_fetch_ssl_c_ca_err,       acl_match_int,     ACL_USE_L6REQ_PERMANENT|ACL_MAY_LOOKUP, 0 },
        { "ssl_c_ca_err_depth",     acl_parse_int, smp_fetch_ssl_c_ca_err_depth, acl_match_int,     ACL_USE_L6REQ_PERMANENT|ACL_MAY_LOOKUP, 0 },
        { "ssl_c_err",              acl_parse_int, smp_fetch_ssl_c_err,          acl_match_int,     ACL_USE_L6REQ_PERMANENT|ACL_MAY_LOOKUP, 0 },
+       { "ssl_c_i_dn",             acl_parse_str, smp_fetch_ssl_c_i_dn,         acl_match_str,     ACL_USE_L6REQ_PERMANENT|ACL_MAY_LOOKUP, ARG2(0,STR,SINT) },
+       { "ssl_c_s_dn",             acl_parse_str, smp_fetch_ssl_c_s_dn,         acl_match_str,     ACL_USE_L6REQ_PERMANENT|ACL_MAY_LOOKUP, ARG2(0,STR,SINT) },
        { "ssl_c_serial",           acl_parse_bin, smp_fetch_ssl_c_serial,       acl_match_bin,     ACL_USE_L6REQ_PERMANENT|ACL_MAY_LOOKUP, 0 },
        { "ssl_c_verify",           acl_parse_int, smp_fetch_ssl_c_verify,       acl_match_int,     ACL_USE_L6REQ_PERMANENT|ACL_MAY_LOOKUP, 0 },
        { "ssl_c_version",          acl_parse_int, smp_fetch_ssl_c_version,      acl_match_int,     ACL_USE_L6REQ_PERMANENT|ACL_MAY_LOOKUP, 0 },
+       { "ssl_f_i_dn",             acl_parse_str, smp_fetch_ssl_f_i_dn,         acl_match_str,     ACL_USE_L6REQ_PERMANENT|ACL_MAY_LOOKUP, ARG2(0,STR,SINT) },
+       { "ssl_f_s_dn",             acl_parse_str, smp_fetch_ssl_f_s_dn,         acl_match_str,     ACL_USE_L6REQ_PERMANENT|ACL_MAY_LOOKUP, ARG2(0,STR,SINT) },
        { "ssl_f_serial",           acl_parse_bin, smp_fetch_ssl_f_serial,       acl_match_bin,     ACL_USE_L6REQ_PERMANENT|ACL_MAY_LOOKUP, 0 },
        { "ssl_f_version",          acl_parse_int, smp_fetch_ssl_f_version,      acl_match_int,     ACL_USE_L6REQ_PERMANENT|ACL_MAY_LOOKUP, 0 },
        { "ssl_fc",                 acl_parse_int, smp_fetch_ssl_fc,             acl_match_nothing, ACL_USE_L6REQ_PERMANENT|ACL_MAY_LOOKUP, 0 },