From ce5ad80c34748e1c5abf41e82cf6c2779e3a7059 Mon Sep 17 00:00:00 2001 From: Emeric Brun Date: Mon, 22 Oct 2012 14:11:22 +0200 Subject: [PATCH] MINOR: ssl: add pattern and ACLs fetches 'ssl_c_notbefore', 'ssl_c_notafter', 'ssl_f_notbefore' and 'ssl_f_notafter' ssl_c_notbefore: start date of client cert (string, eg: "121022182230Z" for YYMMDDhhmmss[Z]) ssl_c_notafter: end date of client cert (string, eg: "121022182230Z" for YYMMDDhhmmss[Z]) ssl_f_notbefore: start date of frontend cert (string, eg: "121022182230Z" for YYMMDDhhmmss[Z]) ssl_f_notafter: end date of frontend cert (string, eg: "121022182230Z" for YYMMDDhhmmss[Z]) --- doc/configuration.txt | 40 ++++++++++ src/ssl_sock.c | 181 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 221 insertions(+) diff --git a/doc/configuration.txt b/doc/configuration.txt index e41b1f1c0..7cb0b68b9 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -8390,6 +8390,16 @@ ssl_c_key_alg layer, and the name of the algorithm used to generate the key of the certificate presented by the client matches the string. +ssl_c_notafter + Returns true when the incoming connection was made over an SSL/TLS transport + layer, and the end date of the certificate presented by the client matches + the string formatted as YYMMDDhhmmss[Z]. + +ssl_c_notbefore + Returns true when the incoming connection was made over an SSL/TLS transport + layer, and the start date of the certificate presented by the client matches + the string formatted as YYMMDDhhmmss[Z]. + ssl_c_s_dn ssl_c_s_dn(entry[,occ]) If no entry specified, returns true when the incoming connection was made @@ -8437,6 +8447,16 @@ ssl_c_key_alg layer, and the name of the algorithm used to generate the key of the certificate presented by the frontend matches the string. +ssl_f_notafter + Returns true when the incoming connection was made over an SSL/TLS transport + layer, and the end date of the certificate presented by the frontend matches + the string formatted as YYMMDDhhmmss[Z]. + +ssl_f_notbefore + Returns true when the incoming connection was made over an SSL/TLS transport + layer, and the start date of the certificate presented by the frontend matches + the string formatted as YYMMDDhhmmss[Z]. + ssl_f_s_dn ssl_f_s_dn(entry[,occ]) If no entry specified, returns true when the incoming connection was made @@ -9157,6 +9177,16 @@ The list of currently supported pattern fetch functions is the following : the certificate presented by the client when the incoming connection was made over an SSL/TLS transport layer. + ssl_c_notafter + Returns the end date presented by the client as a formatted + string YYMMDDhhmmss[Z] when the incoming connection was made + over an SSL/TLS transport layer. + + ssl_c_notbefore + Returns the start date presented by the client as a formatted + string YYMMDDhhmmss[Z] when the incoming connection was made + over an SSL/TLS transport layer. + 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 @@ -9204,6 +9234,16 @@ The list of currently supported pattern fetch functions is the following : the certificate presented by the frontend when the incoming connection was made over an SSL/TLS transport layer. + ssl_f_notafter + Returns the end date presented by the frontend as a formatted + string YYMMDDhhmmss[Z] when the incoming connection was made + over an SSL/TLS transport layer. + + ssl_f_notbefore + Returns the start date presented by the frontend as a formatted + string YYMMDDhhmmss[Z] when the incoming connection was made + over an SSL/TLS transport layer. + 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 diff --git a/src/ssl_sock.c b/src/ssl_sock.c index ada6af229..1269932d9 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -1116,6 +1116,46 @@ ssl_sock_get_serial(X509 *crt, struct chunk *out) return 1; } + +/* Copy Date in ASN1_UTCTIME format in struct chunk out. + * Returns 1 if serial is found and copied, 0 if no valid time found + * and -1 if output is not large enough. + */ +static int +ssl_sock_get_time(ASN1_TIME *tm, struct chunk *out) +{ + if (tm->type == V_ASN1_GENERALIZEDTIME) { + ASN1_GENERALIZEDTIME *gentm = (ASN1_GENERALIZEDTIME *)tm; + + if (gentm->length < 12) + return 0; + if (gentm->data[0] != 0x32 || gentm->data[1] != 0x30) + return 0; + if (out->size < gentm->length-2) + return -1; + + memcpy(out->str, gentm->data+2, gentm->length-2); + out->len = gentm->length-2; + return 1; + } + else if (tm->type == V_ASN1_UTCTIME) { + ASN1_UTCTIME *utctm = (ASN1_UTCTIME *)tm; + + if (utctm->length < 10) + return 0; + if (utctm->data[0] >= 0x35) + return 0; + if (out->size < utctm->length) + return -1; + + memcpy(out->str, utctm->data, utctm->length); + out->len = utctm->length; + return 1; + } + + return 0; +} + /* 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. */ @@ -1265,6 +1305,41 @@ out: return ret; } +/*str, returns notafter date in ASN1_UTCTIME format */ +static int +smp_fetch_ssl_c_notafter(struct proxy *px, struct session *l4, void *l7, unsigned int opt, + const struct arg *args, struct sample *smp) +{ + X509 *crt = NULL; + 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; + + smp_trash = sample_get_trash_chunk(); + if (ssl_sock_get_time(X509_get_notAfter(crt), smp_trash) <= 0) + goto out; + + smp->data.str = *smp_trash; + smp->type = SMP_T_STR; + 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_i_dn(struct proxy *px, struct session *l4, void *l7, unsigned int opt, @@ -1316,6 +1391,41 @@ out: return ret; } +/*str, returns notbefore date in ASN1_UTCTIME format */ +static int +smp_fetch_ssl_c_notbefore(struct proxy *px, struct session *l4, void *l7, unsigned int opt, + const struct arg *args, struct sample *smp) +{ + X509 *crt = NULL; + 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; + + smp_trash = sample_get_trash_chunk(); + if (ssl_sock_get_time(X509_get_notBefore(crt), smp_trash) <= 0) + goto out; + + smp->data.str = *smp_trash; + smp->type = SMP_T_STR; + 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, @@ -1518,6 +1628,69 @@ smp_fetch_ssl_f_serial(struct proxy *px, struct session *l4, void *l7, unsigned out: return ret; } +/*str, returns notafter date in ASN1_UTCTIME format */ +static int +smp_fetch_ssl_f_notafter(struct proxy *px, struct session *l4, void *l7, unsigned int opt, + const struct arg *args, struct sample *smp) +{ + X509 *crt = NULL; + 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; + + smp_trash = sample_get_trash_chunk(); + if (ssl_sock_get_time(X509_get_notAfter(crt), smp_trash) <= 0) + goto out; + + smp->data.str = *smp_trash; + smp->type = SMP_T_STR; + ret = 1; +out: + return ret; +} + +/*str, returns notbefore date in ASN1_UTCTIME format */ +static int +smp_fetch_ssl_f_notbefore(struct proxy *px, struct session *l4, void *l7, unsigned int opt, + const struct arg *args, struct sample *smp) +{ + X509 *crt = NULL; + 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; + + smp_trash = sample_get_trash_chunk(); + if (ssl_sock_get_time(X509_get_notBefore(crt), smp_trash) <= 0) + goto out; + + smp->data.str = *smp_trash; + smp->type = SMP_T_STR; + ret = 1; +out: + return ret; +} /* integer, returns the frontend certificate version */ static int @@ -2421,6 +2594,8 @@ static struct sample_fetch_kw_list sample_fetch_keywords = {{ },{ { "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_key_alg", smp_fetch_ssl_c_key_alg, 0, NULL, SMP_T_STR, SMP_CAP_REQ|SMP_CAP_RES }, + { "ssl_c_notafter", smp_fetch_ssl_c_notafter, 0, NULL, SMP_T_STR, SMP_CAP_REQ|SMP_CAP_RES }, + { "ssl_c_notbefore", smp_fetch_ssl_c_notbefore, 0, NULL, SMP_T_STR, SMP_CAP_REQ|SMP_CAP_RES }, { "ssl_c_sig_alg", smp_fetch_ssl_c_sig_alg, 0, 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 }, @@ -2428,6 +2603,8 @@ static struct sample_fetch_kw_list sample_fetch_keywords = {{ },{ { "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_key_alg", smp_fetch_ssl_f_key_alg, 0, NULL, SMP_T_STR, SMP_CAP_REQ|SMP_CAP_RES }, + { "ssl_f_notafter", smp_fetch_ssl_f_notafter, 0, NULL, SMP_T_STR, SMP_CAP_REQ|SMP_CAP_RES }, + { "ssl_f_notbefore", smp_fetch_ssl_f_notbefore, 0, NULL, SMP_T_STR, SMP_CAP_REQ|SMP_CAP_RES }, { "ssl_f_sig_alg", smp_fetch_ssl_f_sig_alg, 0, 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 }, @@ -2456,6 +2633,8 @@ static struct acl_kw_list acl_kws = {{ },{ { "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_key_alg", acl_parse_str, smp_fetch_ssl_c_key_alg, acl_match_str, ACL_USE_L6REQ_PERMANENT|ACL_MAY_LOOKUP, 0 }, + { "ssl_c_notafter", acl_parse_str, smp_fetch_ssl_c_notafter, acl_match_str, ACL_USE_L6REQ_PERMANENT|ACL_MAY_LOOKUP, 0 }, + { "ssl_c_notbefore", acl_parse_str, smp_fetch_ssl_c_notbefore, acl_match_str, ACL_USE_L6REQ_PERMANENT|ACL_MAY_LOOKUP, 0 }, { "ssl_c_sig_alg", acl_parse_str, smp_fetch_ssl_c_sig_alg, acl_match_str, ACL_USE_L6REQ_PERMANENT|ACL_MAY_LOOKUP, 0 }, { "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 }, @@ -2463,6 +2642,8 @@ static struct acl_kw_list acl_kws = {{ },{ { "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_key_alg", acl_parse_str, smp_fetch_ssl_f_key_alg, acl_match_str, ACL_USE_L6REQ_PERMANENT|ACL_MAY_LOOKUP, 0 }, + { "ssl_f_notafter", acl_parse_str, smp_fetch_ssl_f_notafter, acl_match_str, ACL_USE_L6REQ_PERMANENT|ACL_MAY_LOOKUP, 0 }, + { "ssl_f_notbefore", acl_parse_str, smp_fetch_ssl_f_notbefore, acl_match_str, ACL_USE_L6REQ_PERMANENT|ACL_MAY_LOOKUP, 0 }, { "ssl_f_sig_alg", acl_parse_str, smp_fetch_ssl_f_sig_alg, acl_match_str, ACL_USE_L6REQ_PERMANENT|ACL_MAY_LOOKUP, 0 }, { "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 },