MEDIUM: ssl: Insert ocsp responses in update tree when needed

When 'ocsp-update' is enabled for a given certificate, we need to insert
the certificate_ocsp member of this certificate in the OCSP update tree
as well as the already existing OCSP response tree. For such an entry to
be created, the certificate needs to contain an "OCSP URI" field, and we
also need to know the certificate's issuer, that is used to build the
OCSP_CERTID. When no OCSP response is known for a given certificate, an
empty certificate_ocsp object gets created so that it can be inserted in
the ocsp update tree.
The entry is inserted on the first spot of the update tree since its
expire time is 0. Then whenever the update task is started, it will try
to get responses for those certificates first.

In order for the update process to work, we also need to store some
information relative to the main certificate into the certificate_ocsp
structure. This avoids having to keep a reference to a ckch in an ocsp
tree entry.
This patch adds a reference to the certificate chain as well as the ocsp
issuer that might have been filled during init into the certificate_ocsp
object. It also gets the ocsp uri at this time since it is contained in
the server's certificate. We only take the first uri that might be
contained in the certificate though.
Those fields are only filled when ocsp auto update is enabled for the
concerned certificate.
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index cf918ae..2be0531 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -957,7 +957,10 @@
 	struct buffer response;
 	int refcount;
 	long expire;
+	X509 *issuer;
+	STACK_OF(X509) *chain;
 	struct eb64_node next_update;	/* Key of items inserted in ocsp_update_tree (sorted by absolute date) */
+	struct buffer *uri;	/* First OCSP URI contained in the corresponding certificate */
 };
 
 struct ocsp_cbk_arg {
@@ -1668,7 +1671,13 @@
 	if (ocsp->refcount <= 0) {
 		ebmb_delete(&ocsp->key);
 		eb64_delete(&ocsp->next_update);
+		X509_free(ocsp->issuer);
+		ocsp->issuer = NULL;
+		sk_X509_pop_free(ocsp->chain, X509_free);
+		ocsp->chain = NULL;
 		chunk_destroy(&ocsp->response);
+		free_trash_chunk(ocsp->uri);
+		ocsp->uri = NULL;
 
 		free(ocsp);
 	}
@@ -1701,12 +1710,30 @@
 #else
 	tlsextStatusCb callback;
 #endif
+	struct buffer *ocsp_uri = get_trash_chunk();
 
 
 	x = data->cert;
 	if (!x)
 		goto out;
 
+	if (data->ocsp_update_mode == SSL_SOCK_OCSP_UPDATE_ON) {
+		ssl_ocsp_get_uri_from_cert(x, ocsp_uri, NULL);
+		/* We should have an "OCSP URI" field in order for auto update to work. */
+		if (b_data(ocsp_uri) == 0)
+			goto out;
+	}
+
+	/* In case of ocsp update mode set to 'on', this function might be
+	 * called with no known ocsp response. If no ocsp uri can be found in
+	 * the certificate, nothing needs to be done here. */
+	if (!data->ocsp_response) {
+		if (data->ocsp_update_mode != SSL_SOCK_OCSP_UPDATE_ON || b_data(ocsp_uri) == 0) {
+			ret = 0;
+			goto out;
+		}
+	}
+
 	issuer = data->ocsp_issuer;
 	/* take issuer from chain over ocsp_issuer, is what is done historicaly */
 	if (chain) {
@@ -1806,11 +1833,31 @@
 	ret = 0;
 
 	warn = NULL;
-	if (ssl_sock_load_ocsp_response(data->ocsp_response, iocsp, data->ocsp_cid, &warn)) {
+	if (data->ocsp_response && ssl_sock_load_ocsp_response(data->ocsp_response, iocsp, data->ocsp_cid, &warn)) {
 		memprintf(&warn, "Loading: %s. Content will be ignored", warn ? warn : "failure");
 		ha_warning("%s.\n", warn);
 	}
 
+	if (data->ocsp_update_mode == SSL_SOCK_OCSP_UPDATE_ON) {
+
+		/* Do not insert the same certificate_ocsp structure in the
+		 * update tree more than once. */
+		if (!ocsp) {
+			iocsp->issuer = issuer;
+			X509_up_ref(issuer);
+			if (data->chain)
+				iocsp->chain = X509_chain_up_ref(data->chain);
+
+			iocsp->uri = alloc_trash_chunk();
+			if (!iocsp->uri)
+				goto out;
+			if (!chunk_cpy(iocsp->uri, ocsp_uri))
+				goto out;
+
+			ssl_ocsp_update_insert(iocsp);
+		}
+	}
+
 out:
 	if (ret && data->ocsp_cid)
 		OCSP_CERTID_free(data->ocsp_cid);
@@ -4001,14 +4048,20 @@
 #endif
 
 #if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) || defined OPENSSL_IS_BORINGSSL)
-	/* Load OCSP Info into context */
-	if (data->ocsp_response) {
-		if (ssl_sock_load_ocsp(ctx, data, find_chain) < 0) {
+	/* Load OCSP Info into context
+	 * If OCSP update mode is set to 'on', an entry will be created in the
+	 * ocsp tree even if no ocsp_response was known during init, unless the
+	 * frontend's conf disables ocsp update explicitely.
+	 */
+	if (ssl_sock_load_ocsp(ctx, data, find_chain) < 0) {
+		if (data->ocsp_response)
 			memprintf(err, "%s '%s.ocsp' is present and activates OCSP but it is impossible to compute the OCSP certificate ID (maybe the issuer could not be found)'.\n",
-			          err && *err ? *err : "", path);
-			errcode |= ERR_ALERT | ERR_FATAL;
-			goto end;
-		}
+				  err && *err ? *err : "", path);
+		else
+			memprintf(err, "%s '%s' has an OCSP URI and OCSP auto-update is set to 'on' but an error occurred (maybe the issuer could not be found)'.\n",
+				  err && *err ? *err : "", path);
+		errcode |= ERR_ALERT | ERR_FATAL;
+		goto end;
 	}
 #endif