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 },