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])
diff --git a/doc/configuration.txt b/doc/configuration.txt
index e41b1f1..7cb0b68 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -8390,6 +8390,16 @@
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 <string>
+ 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 <string>
+ 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 <string>
ssl_c_s_dn(entry[,occ]) <string>
If no entry specified, returns true when the incoming connection was made
@@ -8437,6 +8447,16 @@
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 <string>
+ 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 <string>
+ 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 <string>
ssl_f_s_dn(entry[,occ]) <string>
If no entry specified, returns true when the incoming connection was made
@@ -9157,6 +9177,16 @@
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 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 ada6af2..1269932 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -1116,6 +1116,46 @@
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 @@
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 @@
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 @@
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 @@
{ "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 @@
{ "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 @@
{ "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 @@
{ "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 },