MEDIUM: ssl: Add a way to load a ca-file content from memory

The updated CA content coming from the CLI during a ca-file update will
directly be in memory and not on disk so the way CAs are loaded in a
cafile_entry for now (via X509_STORE_load_locations calls) cannot be
used.
This patch adds a way to fill a cafile_entry directly from memory and to
load the contained certificate and CRL sections into an SSL store.
CRL sections are managed as well as certificates in order to mimic the
way CA files are processed when specified in an option. Indeed, when
parsing a CA file given through a ca-file or ca-verify-file option, we
iterate over the different sections in ssl_set_cert_crl_file and load
them regardless of their type. This ensures that a file that was
properly parsed when given as an option will also be accepted by the
CLI.
diff --git a/include/haproxy/ssl_ckch.h b/include/haproxy/ssl_ckch.h
index 40f133b..dd68662 100644
--- a/include/haproxy/ssl_ckch.h
+++ b/include/haproxy/ssl_ckch.h
@@ -61,6 +61,7 @@
 X509_STORE* ssl_store_get0_locations_file(char *path);
 struct cafile_entry *ssl_store_create_cafile_entry(char *path, X509_STORE *store);
 void ssl_store_delete_cafile_entry(struct cafile_entry *ca_e);
+int ssl_store_load_ca_from_buf(struct cafile_entry *ca_e, char *cert_buf);
 int ssl_store_load_locations_file(char *path, int create_if_none);
 
 #endif /* USE_OPENSSL */
diff --git a/src/ssl_ckch.c b/src/ssl_ckch.c
index 8433359..10123b4 100644
--- a/src/ssl_ckch.c
+++ b/src/ssl_ckch.c
@@ -1015,6 +1015,55 @@
 	free(ca_e);
 }
 
+/*
+ * Build a cafile_entry out of a buffer instead of out of a file.
+ * This function is used when the "commit ssl ca-file" cli command is used.
+ * It can parse CERTIFICATE sections as well as CRL ones.
+ * Returns 0 in case of success, 1 otherwise.
+ */
+int ssl_store_load_ca_from_buf(struct cafile_entry *ca_e, char *cert_buf)
+{
+	int retval = 0;
+
+	if (!ca_e)
+		return 1;
+
+	if (!ca_e->ca_store) {
+		ca_e->ca_store = X509_STORE_new();
+		if (ca_e->ca_store) {
+			BIO *bio = BIO_new_mem_buf(cert_buf, strlen(cert_buf));
+			if (bio) {
+				X509_INFO *info;
+				int i;
+				STACK_OF(X509_INFO) *infos = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL);
+				if (!infos)
+				{
+					BIO_free(bio);
+					return 1;
+				}
+
+				for (i = 0; i < sk_X509_INFO_num(infos) && !retval; i++) {
+					info = sk_X509_INFO_value(infos, i);
+					/* X509_STORE_add_cert and X509_STORE_add_crl return 1 on success */
+					if (info->x509) {
+						retval = !X509_STORE_add_cert(ca_e->ca_store, info->x509);
+					}
+					if (!retval && info->crl) {
+						retval = !X509_STORE_add_crl(ca_e->ca_store, info->crl);
+					}
+				}
+				retval = retval || (i != sk_X509_INFO_num(infos));
+
+				/* Cleanup */
+				sk_X509_INFO_pop_free(infos, X509_INFO_free);
+				BIO_free(bio);
+			}
+		}
+	}
+
+	return retval;
+}
+
 int ssl_store_load_locations_file(char *path, int create_if_none)
 {
 	X509_STORE *store = ssl_store_get0_locations_file(path);