MEDIUM: ssl: introduce the ckch instance structure

struct ckch_inst represents an instance of a certificate (ckch_node)
used in a bind_conf. Every sni_ctx created for 1 ckch_node in a
bind_conf are linked in this structure.

This patch allocate the ckch_inst for each bind_conf and inserts the
sni_ctx in its linked list.
diff --git a/include/types/ssl_sock.h b/include/types/ssl_sock.h
index e0b1a80..0d6ed63 100644
--- a/include/types/ssl_sock.h
+++ b/include/types/ssl_sock.h
@@ -39,6 +39,7 @@
 	uint8_t neg;              /* reject if match */
 	struct pkey_info kinfo;   /* pkey info */
 	struct ssl_bind_conf *conf; /* ssl "bind" conf for the certificate */
+	struct list by_ckch_inst; /* chained in ckch_inst's list of sni_ctx */
 	struct ebmb_node name;    /* node holding the servername value */
 };
 
@@ -105,10 +106,23 @@
 struct ckch_store {
 	struct cert_key_and_chain *ckch;
 	int multi; /* is it a multi-cert bundle ? */
+	struct list ckch_inst; /* list of ckch_inst which uses this ckch_node */
 	struct ebmb_node node;
 	char path[0];
 };
 
+/*
+ * This structure describe a ckch instance. An instance is generated for each
+ * bind_conf.  The instance contains a linked list of the sni ctx which uses
+ * the ckch in this bind_conf.
+ *
+ * XXX: the instance may evolve to handle ssl_bind_conf instead of bind_conf.
+ */
+struct ckch_inst {
+	struct bind_conf *bind_conf; /* pointer to the bind_conf that uses this ckch_inst */
+	struct list sni_ctx; /* list of sni_ctx using this ckch_inst */
+	struct list by_ckchs; /* chained in ckch_store's list of ckch_inst */
+};
 
 #if HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL
 
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index c69cb6c..0ef56e0 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -2740,8 +2740,23 @@
 }
 #endif
 
-static int ssl_sock_add_cert_sni(SSL_CTX *ctx, struct bind_conf *s, struct ssl_bind_conf *conf,
-				 struct pkey_info kinfo, char *name, int order)
+/* Alloc and init a ckch_inst */
+static struct ckch_inst *ckch_inst_new()
+{
+	struct ckch_inst *ckch_inst;
+
+	ckch_inst = calloc(1, sizeof *ckch_inst);
+	if (ckch_inst)
+		LIST_INIT(&ckch_inst->sni_ctx);
+
+	return ckch_inst;
+}
+
+
+/* This function allocates a sni_ctx and adds it to the ckch_inst */
+static int ssl_sock_add_cert_sni(SSL_CTX *ctx, struct ckch_inst *ckch_inst,
+                                 struct bind_conf *s, struct ssl_bind_conf *conf,
+                                 struct pkey_info kinfo, char *name, int order)
 {
 	struct sni_ctx *sc;
 	int wild = 0, neg = 0;
@@ -2793,6 +2808,9 @@
 			ebst_insert(&s->sni_w_ctx, &sc->name);
 		else
 			ebst_insert(&s->sni_ctx, &sc->name);
+
+		if (ckch_inst != NULL) /* TODO: remove this test later once the code is converted */
+			LIST_ADDQ(&ckch_inst->sni_ctx, &sc->by_ckch_inst);
 	}
 	return order;
 }
@@ -3113,6 +3131,8 @@
 		goto end;
 	}
 
+	LIST_INIT(&ckchs->ckch_inst);
+
 	if (!multi) {
 
 		if (ssl_sock_load_crt_file_into_ckch(path, ckchs->ckch, err) == 1)
@@ -3177,7 +3197,7 @@
  * TODO: This function shouldn't access files anymore, sctl and ocsp file access
  * should be migrated to the ssl_sock_load_crt_file_into_ckch() function
  */
-static int ssl_sock_load_multi_ckchs(const char *path, struct ckch_store *ckchs,
+static int ssl_sock_load_multi_ckchs(const char *path, struct ckch_store *ckchs, struct ckch_inst *ckch_inst,
                                      struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_conf,
                                      char **sni_filter, int fcount, char **err)
 {
@@ -3355,7 +3375,8 @@
 		}
 
 		/* Update SNI Tree */
-		key_combos[i-1].order = ssl_sock_add_cert_sni(cur_ctx, bind_conf, ssl_conf,
+
+		key_combos[i-1].order = ssl_sock_add_cert_sni(cur_ctx, ckch_inst, bind_conf, ssl_conf,
 		                                              kinfo, str, key_combos[i-1].order);
 		if (key_combos[i-1].order < 0) {
 			memprintf(err, "%sunable to create a sni context.\n", err && *err ? *err : "");
@@ -3405,8 +3426,10 @@
 
 #endif /* #if HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL: Support for loading multiple certs into a single SSL_CTX */
 
-static int ssl_sock_load_ckchs(const char *path, struct ckch_store *ckchs, struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_conf,
-				   char **sni_filter, int fcount, char **err)
+static int ssl_sock_load_ckchs(const char *path, struct ckch_store *ckchs,
+                               struct ckch_inst *ckch_inst, struct bind_conf
+                               *bind_conf, struct ssl_bind_conf *ssl_conf, char
+                               **sni_filter, int fcount, char **err)
 {
 	SSL_CTX *ctx;
 	int i;
@@ -3456,7 +3479,7 @@
 
 	if (fcount) {
 		while (fcount--) {
-			order = ssl_sock_add_cert_sni(ctx, bind_conf, ssl_conf, kinfo, sni_filter[fcount], order);
+			order = ssl_sock_add_cert_sni(ctx, ckch_inst, bind_conf, ssl_conf, kinfo, sni_filter[fcount], order);
 			if (order < 0) {
 				memprintf(err, "%sunable to create a sni context.\n", err && *err ? *err : "");
 				return 1;
@@ -3471,7 +3494,7 @@
 				GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i);
 				if (name->type == GEN_DNS) {
 					if (ASN1_STRING_to_UTF8((unsigned char **)&str, name->d.dNSName) >= 0) {
-						order = ssl_sock_add_cert_sni(ctx, bind_conf, ssl_conf, kinfo, str, order);
+						order = ssl_sock_add_cert_sni(ctx, ckch_inst, bind_conf, ssl_conf, kinfo, str, order);
 						OPENSSL_free(str);
 						if (order < 0) {
 							memprintf(err, "%sunable to create a sni context.\n", err && *err ? *err : "");
@@ -3491,7 +3514,7 @@
 
 			value = X509_NAME_ENTRY_get_data(entry);
 			if (ASN1_STRING_to_UTF8((unsigned char **)&str, value) >= 0) {
-				order = ssl_sock_add_cert_sni(ctx, bind_conf, ssl_conf, kinfo, str, order);
+				order = ssl_sock_add_cert_sni(ctx, ckch_inst, bind_conf, ssl_conf, kinfo, str, order);
 				OPENSSL_free(str);
 				if (order < 0) {
 					memprintf(err, "%sunable to create a sni context.\n", err && *err ? *err : "");
@@ -3538,6 +3561,10 @@
 		bind_conf->default_ssl_conf = ssl_conf;
 	}
 
+	/* everything succeed, the ckch instance can be used */
+	ckch_inst->bind_conf = bind_conf;
+	LIST_ADDQ(&ckchs->ckch_inst, &ckch_inst->by_ckchs);
+
 	return 0;
 }
 
@@ -3555,14 +3582,23 @@
 	int is_bundle;
 	int j;
 #endif
+	struct ckch_inst *ckch_inst;
 
 	if ((ckchs = ckchs_lookup(path))) {
+		/* For each certificate (or bundle) used in the configuration, create
+		 * a certificate instance */
+		ckch_inst = ckch_inst_new();
+		if (!ckch_inst) {
+			memprintf(err, "%sunable to allocate SSL context for cert '%s'.\n",
+				  err && *err ? *err : "", path);
+			return 1;
+		}
 
 		/* we found the ckchs in the tree, we can use it directly */
 		if (ckchs->multi)
-			return ssl_sock_load_multi_ckchs(path, ckchs, bind_conf, NULL, NULL, 0, err);
+			return ssl_sock_load_multi_ckchs(path, ckchs, ckch_inst, bind_conf, NULL, NULL, 0, err);
 		else
-			return ssl_sock_load_ckchs(path, ckchs, bind_conf, NULL, NULL, 0, err);
+			return ssl_sock_load_ckchs(path, ckchs, ckch_inst, bind_conf, NULL, NULL, 0, err);
 
 	}
 
@@ -3572,7 +3608,13 @@
 			ckchs =  ckchs_load_cert_file(path, 0,  err);
 			if (!ckchs)
 				return 1;
+			ckch_inst = ckch_inst_new();
+			if (!ckch_inst) {
+				memprintf(err, "%sunable to allocate SSL context for cert '%s'.\n",
+					  err && *err ? *err : "", path);
+				return 1;
-			return ssl_sock_load_ckchs(path, ckchs, bind_conf, NULL, NULL, 0, err);
+			}
+			return ssl_sock_load_ckchs(path, ckchs, ckch_inst, bind_conf, NULL, NULL, 0, err);
 		}
 
 		/* strip trailing slashes, including first one */
@@ -3637,8 +3679,15 @@
 							ckchs =  ckchs_load_cert_file(fp, 1,  err);
 						if (!ckchs)
 							cfgerr++;
-						else
-							cfgerr += ssl_sock_load_multi_ckchs(fp, ckchs, bind_conf, NULL, NULL, 0, err);
+						else {
+							ckch_inst = ckch_inst_new();
+							if (!ckch_inst) {
+								memprintf(err, "%sunable to allocate SSL context for cert '%s'.\n",
+									  err && *err ? *err : "", path);
+								return 1;
+							}
+							cfgerr += ssl_sock_load_multi_ckchs(fp, ckchs, ckch_inst, bind_conf, NULL, NULL, 0, err);
+						}
 
 						/* Successfully processed the bundle */
 						goto ignore_entry;
@@ -3650,8 +3699,15 @@
 					ckchs =  ckchs_load_cert_file(fp, 0,  err);
 				if (!ckchs)
 					cfgerr++;
-				else
-					cfgerr += ssl_sock_load_ckchs(fp, ckchs, bind_conf, NULL, NULL, 0, err);
+				else {
+					ckch_inst = ckch_inst_new();
+					if (!ckch_inst) {
+						memprintf(err, "%sunable to allocate SSL context for cert '%s'.\n",
+						          err && *err ? *err : "", path);
+						return 1;
+					}
+					cfgerr += ssl_sock_load_ckchs(fp, ckchs, ckch_inst, bind_conf, NULL, NULL, 0, err);
+				}
 
 ignore_entry:
 				free(de);
@@ -3665,7 +3721,13 @@
 	ckchs =  ckchs_load_cert_file(path, 1,  err);
 	if (!ckchs)
 		return 1;
+	ckch_inst = ckch_inst_new();
+	if (!ckch_inst) {
+		memprintf(err, "%sunable to allocate SSL context for cert '%s'.\n",
+			  err && *err ? *err : "", path);
+		return 1;
-	cfgerr = ssl_sock_load_multi_ckchs(path, ckchs, bind_conf, NULL, NULL, 0, err);
+	}
+	cfgerr = ssl_sock_load_multi_ckchs(path, ckchs, ckch_inst, bind_conf, NULL, NULL, 0, err);
 
 	return cfgerr;
 }