BUG/MEDIUM: ssl/cli: crash when crt inserted into a crt-list

The crash occures when the same certificate which is used on both a
server line and a bind line is inserted in a crt-list over the CLI.

This is quite uncommon as using the same file for a client and a server
certificate does not make sense in a lot of environments.

This patch fixes the issue by skipping the insertion of the SNI when no
bind_conf is available in the ckch_inst.

Change the reg-test to reproduce this corner case.

Should fix issue #1748.

Must be backported as far as 2.2. (it was previously in ssl_sock.c)

(cherry picked from commit cb6c5f468341e1902fae7527bfe76921d9d2aea6)
Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
(cherry picked from commit 90ef04ae80d8a0e7e72957d8112899b437f4fb1f)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 0883b83ef95726bed2913b311779fd4e400947e4)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/reg-tests/ssl/add_ssl_crt-list.vtc b/reg-tests/ssl/add_ssl_crt-list.vtc
index f42e3af..d35779b 100644
--- a/reg-tests/ssl/add_ssl_crt-list.vtc
+++ b/reg-tests/ssl/add_ssl_crt-list.vtc
@@ -52,6 +52,7 @@
         bind "${tmpdir}/ssl.sock" ssl strict-sni crt-list ${testdir}/localhost.crt-list
 
         server s1 ${s1_addr}:${s1_port}
+        server s2 ${s1_addr}:${s1_port} ssl crt "${testdir}/common.pem" weight 0 verify none
 } -start
 
 
@@ -70,6 +71,7 @@
     echo "new ssl cert ${testdir}/ecdsa.pem" | socat "${tmpdir}/h1/stats" -
     printf "set ssl cert ${testdir}/ecdsa.pem <<\n$(cat ${testdir}/ecdsa.pem)\n\n" | socat "${tmpdir}/h1/stats" -
     echo "commit ssl cert ${testdir}/ecdsa.pem" | socat "${tmpdir}/h1/stats" -
+    printf "add ssl crt-list ${testdir}/localhost.crt-list/ <<\n${testdir}/common.pem [ssl-min-ver SSLv3 verify none allow-0rtt] !*\n\n" | socat "${tmpdir}/h1/stats" -
     printf "add ssl crt-list ${testdir}/localhost.crt-list/ <<\n${testdir}/ecdsa.pem [ssl-min-ver SSLv3 verify none allow-0rtt] localhost !www.test1.com\n\n" | socat "${tmpdir}/h1/stats" -
     printf "add ssl crt-list ${testdir}/localhost.crt-list <<\n${testdir}/ecdsa.pem [verify none allow-0rtt]\n\n" | socat "${tmpdir}/h1/stats" -
     printf "add ssl crt-list ${testdir}/localhost.crt-list/// <<\n${testdir}/ecdsa.pem localhost !www.test1.com\n\n" | socat "${tmpdir}/h1/stats" -
diff --git a/src/ssl_crtlist.c b/src/ssl_crtlist.c
index 95758bb..d03c993 100644
--- a/src/ssl_crtlist.c
+++ b/src/ssl_crtlist.c
@@ -1114,8 +1114,15 @@
 				appctx->st2 = SETCERT_ST_INSERT;
 				/* fallthrough */
 			case SETCERT_ST_INSERT:
-				/* insert SNIs in bind_conf */
+				/* the insertion is called for every instance of the store, not
+				 * only the one we generated.
+				 * But the ssl_sock_load_cert_sni() skip the sni already
+				 * inserted. Not every instance has a bind_conf, it could be
+				 * the store of a server so we should be careful */
+
 				list_for_each_entry(new_inst, &store->ckch_inst, by_ckchs) {
+					if (!new_inst->bind_conf) /* this is a server instance */
+						continue;
 					HA_RWLOCK_WRLOCK(SNI_LOCK, &new_inst->bind_conf->sni_lock);
 					ssl_sock_load_cert_sni(new_inst, new_inst->bind_conf);
 					HA_RWLOCK_WRUNLOCK(SNI_LOCK, &new_inst->bind_conf->sni_lock);