Index: ca.c
===================================================================
RCS file: /cvs/src/sbin/iked/ca.c,v
retrieving revision 1.78
diff -d -u -r1.78 ca.c
--- ca.c	24 Feb 2021 22:17:48 -0000	1.78
+++ ca.c	13 May 2021 02:51:06 -0000
@@ -303,6 +303,32 @@
 	return (0);
 }
 
+static int
+ca_setscert(struct iked *env, struct iked_sahdr *sh, uint8_t type, X509 *cert)
+{
+	struct iovec		iov[3];
+	int			iovcnt = 0;
+	struct ibuf		*buf;
+	int			ret;
+
+	if ((buf = ca_x509_serialize(cert)) == NULL)
+		return (-1);
+
+	iov[iovcnt].iov_base = sh;
+	iov[iovcnt].iov_len = sizeof(*sh);
+	iovcnt++;
+	iov[iovcnt].iov_base = &type;
+	iov[iovcnt].iov_len = sizeof(type);
+	iovcnt++;
+	iov[iovcnt].iov_base = ibuf_data(buf);
+	iov[iovcnt].iov_len = ibuf_size(buf);
+	iovcnt++;
+
+	ret = proc_composev(&env->sc_ps, PROC_IKEV2, IMSG_SCERT, iov, iovcnt);
+	ibuf_release(buf);
+	return (ret);
+}
+
 int
 ca_setreq(struct iked *env, struct iked_sa *sa,
     struct iked_static_id *localid, uint8_t type, uint8_t more, uint8_t *data,
@@ -500,6 +526,51 @@
 	return (0);
 }
 
+static unsigned int
+ca_chain_by_issuer(struct ca_store *store, X509_NAME *subject,
+    struct iked_static_id *id, X509 **dst, size_t dstlen)
+{
+	STACK_OF(X509_OBJECT)	*h;
+	X509_OBJECT		*xo;
+	X509			*cert;
+	int			i;
+	unsigned int		n;
+	X509_NAME		*issuer, *subj;
+
+        if (subject == NULL || dstlen == 0)
+		return (0);
+
+	if ((cert = ca_by_issuer(store->ca_certs, subject, id)) != NULL) {
+		*dst = cert;
+		return (1);
+	}
+
+	h = X509_STORE_get0_objects(store->ca_cas);
+	for (i = 0; i < sk_X509_OBJECT_num(h); i++) {
+		xo = sk_X509_OBJECT_value(h, i);
+		if (X509_OBJECT_get_type(xo) != X509_LU_X509)
+			continue;
+		cert = X509_OBJECT_get0_X509(xo);
+		if ((issuer = X509_get_issuer_name(cert)) == NULL)
+			continue;
+		if (X509_NAME_cmp(subject, issuer) == 0) {
+			if ((subj = X509_get_subject_name(cert)) == NULL)
+				continue;
+			/* Skip root CAs */
+			if (X509_NAME_cmp(subj, issuer) == 0)
+				continue;
+			n = ca_chain_by_issuer(store, subj, id,
+			    dst + 1, dstlen - 1);
+			if (n > 0) {
+				*dst = cert;
+				return (n + 1);
+			}
+		}
+	}
+
+	return (0);
+}
+
 int
 ca_getreq(struct iked *env, struct imsg *imsg)
 {
@@ -510,6 +581,8 @@
 	size_t			 len;
 	unsigned int		 i;
 	X509			*ca = NULL, *cert = NULL;
+	X509			*chain[IKED_SCERT_MAX + 1];
+	size_t			 chain_len = 0;
 	struct ibuf		*buf;
 	struct iked_static_id	 id;
 	char			 idstr[IKED_ID_SIZE];
@@ -570,14 +643,20 @@
 				return (-1);
 			log_debug("%s: found CA %s", __func__, subj_name);
 
-			if ((cert = ca_by_issuer(store->ca_certs,
-			    subj, &id)) != NULL) {
+			chain_len = ca_chain_by_issuer(store, subj, &id,
+			    chain, nitems(chain));
+			if (chain_len > 0) {
 				/* XXX
 				 * should we re-validate our own cert here?
 				 */
+				cert = chain[chain_len - 1];
 				break;
 			}
 		}
+
+		for (i = chain_len; i >= 2; i--)
+			ca_setscert(env, &sh, type, chain[i - 2]);
+
 		/* Fallthrough */
 	case IKEV2_CERT_NONE:
  fallback:
Index: config.c
===================================================================
RCS file: /cvs/src/sbin/iked/config.c,v
retrieving revision 1.78
diff -d -u -r1.78 config.c
--- config.c	22 Feb 2021 21:58:12 -0000	1.78
+++ config.c	13 May 2021 02:51:06 -0000
@@ -110,6 +110,8 @@
 void
 config_free_sa(struct iked *env, struct iked_sa *sa)
 {
+	int i;
+
 	timer_del(env, &sa->sa_timer);
 	timer_del(env, &sa->sa_keepalive);
 	timer_del(env, &sa->sa_rekey);
@@ -165,6 +167,8 @@
 	ibuf_release(sa->sa_rid.id_buf);
 	ibuf_release(sa->sa_icert.id_buf);
 	ibuf_release(sa->sa_rcert.id_buf);
+	for (i = 0; i < IKED_SCERT_MAX; i++)
+		ibuf_release(sa->sa_scert[i].id_buf);
 	ibuf_release(sa->sa_localauth.id_buf);
 	ibuf_release(sa->sa_peerauth.id_buf);
 
Index: eap.c
===================================================================
RCS file: /cvs/src/sbin/iked/eap.c,v
retrieving revision 1.19
diff -d -u -r1.19 eap.c
--- eap.c	18 Nov 2020 22:24:03 -0000	1.19
+++ eap.c	13 May 2021 02:51:06 -0000
@@ -90,6 +90,7 @@
 	uint8_t				 firstpayload;
 	int				 ret = -1;
 	ssize_t				 len = 0;
+	int				 i;
 
 	/* Responder only */
 	if (sa->sa_hdr.sh_initiator)
@@ -128,6 +129,22 @@
 		if (ibuf_cat(e, certid->id_buf) != 0)
 			goto done;
 		len = ibuf_size(certid->id_buf) + sizeof(*cert);
+
+		for (i = 0; i < IKED_SCERT_MAX; i++) {
+			if (sa->sa_scert[i].id_type == IKEV2_CERT_NONE)
+				break;
+			if (ikev2_next_payload(pld, len,
+			    IKEV2_PAYLOAD_CERT) == -1)
+				goto done;
+			if ((pld = ikev2_add_payload(e)) == NULL)
+				goto done;
+			if ((cert = ibuf_advance(e, sizeof(*cert))) == NULL)
+				goto done;
+			cert->cert_type = sa->sa_scert[i].id_type;
+			if (ibuf_cat(e, sa->sa_scert[i].id_buf) != 0)
+				goto done;
+			len = ibuf_size(sa->sa_scert[i].id_buf) + sizeof(*cert);
+		}
 	}
 
 	if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_AUTH) == -1)
Index: iked.h
===================================================================
RCS file: /cvs/src/sbin/iked/iked.h,v
retrieving revision 1.190
diff -d -u -r1.190 iked.h
--- iked.h	20 Apr 2021 21:11:56 -0000	1.190
+++ iked.h	13 May 2021 02:51:07 -0000
@@ -463,11 +463,13 @@
 	struct iked_id			 sa_localauth;	/* local AUTH message */
 	struct iked_id			 sa_peerauth;	/* peer AUTH message */
 	int				 sa_sigsha2;	/* use SHA2 for signatures */
+#define IKED_SCERT_MAX	3 /* max # of supplemental cert payloads */
 
 	struct iked_id			 sa_iid;	/* initiator id */
 	struct iked_id			 sa_rid;	/* responder id */
 	struct iked_id			 sa_icert;	/* initiator cert */
 	struct iked_id			 sa_rcert;	/* responder cert */
+	struct iked_id			 sa_scert[IKED_SCERT_MAX]; /* supplemental certs */
 #define IKESA_SRCID(x) ((x)->sa_hdr.sh_initiator ? &(x)->sa_iid : &(x)->sa_rid)
 #define IKESA_DSTID(x) ((x)->sa_hdr.sh_initiator ? &(x)->sa_rid : &(x)->sa_iid)
 
Index: ikev2.c
===================================================================
RCS file: /cvs/src/sbin/iked/ikev2.c,v
retrieving revision 1.319
diff -d -u -r1.319 ikev2.c
--- ikev2.c	23 Mar 2021 21:31:29 -0000	1.319
+++ ikev2.c	13 May 2021 02:51:07 -0000
@@ -314,6 +314,7 @@
 	size_t			 len;
 	struct iked_id		*id = NULL;
 	int			 ignore = 0;
+	int			 i;
 
 	switch (imsg->hdr.type) {
 	case IMSG_CERTREQ:
@@ -415,6 +416,51 @@
 		if (ikev2_ike_auth(env, sa) != 0)
 			log_debug("%s: failed to send ike auth", __func__);
 		break;
+	case IMSG_SCERT:
+		if ((sa = ikev2_getimsgdata(env, imsg,
+		    &sh, &type, &ptr, &len)) == NULL) {
+			log_debug("%s: invalid supplemental cert reply",
+			    __func__);
+			break;
+		}
+
+		if (sa->sa_stateflags & IKED_REQ_CERT ||
+		    type == IKEV2_CERT_NONE)
+			ignore = 1;
+
+		log_debug("%s: supplemental cert type %s length %zu, %s",
+		    __func__,
+		    print_map(type, ikev2_cert_map), len,
+		    ignore ? "ignored" : "ok");
+
+		if (ignore)
+			break;
+
+		for (i = 0; i < IKED_SCERT_MAX; i++) {
+			id = &sa->sa_scert[i];
+			if (id->id_type == IKEV2_CERT_NONE)
+				break;
+			id = NULL;
+		}
+
+		if (id == NULL) {
+			log_debug("%s: too many supplemental cert. ignored",
+			    __func__);
+			break;
+		}
+
+		id->id_type = type;
+		id->id_offset = 0;
+		ibuf_release(id->id_buf);
+		id->id_buf = NULL;
+
+		if (len <= 0 || (id->id_buf = ibuf_new(ptr, len)) == NULL) {
+			log_debug("%s: failed to get supplemental cert payload",
+			    __func__);
+			break;
+		}
+
+		break;
 	case IMSG_AUTH:
 		if ((sa = ikev2_getimsgdata(env, imsg,
 		    &sh, &type, &ptr, &len)) == NULL) {
@@ -1454,6 +1500,7 @@
 	uint8_t				 firstpayload;
 	int				 ret = -1;
 	ssize_t				 len;
+	int				 i;
 
 	if (!sa_stateok(sa, IKEV2_STATE_SA_INIT))
 		return (0);
@@ -1493,6 +1540,22 @@
 			goto done;
 		len = ibuf_size(certid->id_buf) + sizeof(*cert);
 
+		for (i = 0; i < IKED_SCERT_MAX; i++) {
+			if (sa->sa_scert[i].id_type == IKEV2_CERT_NONE)
+				break;
+			if (ikev2_next_payload(pld, len,
+			    IKEV2_PAYLOAD_CERT) == -1)
+				goto done;
+			if ((pld = ikev2_add_payload(e)) == NULL)
+				goto done;
+			if ((cert = ibuf_advance(e, sizeof(*cert))) == NULL)
+				goto done;
+			cert->cert_type = sa->sa_scert[i].id_type;
+			if (ibuf_cat(e, sa->sa_scert[i].id_buf) != 0)
+				goto done;
+			len = ibuf_size(sa->sa_scert[i].id_buf) + sizeof(*cert);
+		}
+
 		/* CERTREQ payload(s) */
 		if ((len = ikev2_add_certreq(e, &pld,
 		    len, env->sc_certreq, env->sc_certreqtype)) == -1)
@@ -3668,6 +3731,7 @@
 	uint8_t				 firstpayload;
 	int				 ret = -1;
 	ssize_t				 len;
+	int				 i;
 
 	if (sa == NULL)
 		return (-1);
@@ -3727,6 +3791,24 @@
 			if (ibuf_cat(e, certid->id_buf) != 0)
 				goto done;
 			len = ibuf_size(certid->id_buf) + sizeof(*cert);
+
+			for (i = 0; i < IKED_SCERT_MAX; i++) {
+				if (sa->sa_scert[i].id_type == IKEV2_CERT_NONE)
+					break;
+				if (ikev2_next_payload(pld, len,
+				    IKEV2_PAYLOAD_CERT) == -1)
+					goto done;
+				if ((pld = ikev2_add_payload(e)) == NULL)
+					goto done;
+				if ((cert = ibuf_advance(e,
+				    sizeof(*cert))) == NULL)
+					goto done;
+				cert->cert_type = sa->sa_scert[i].id_type;
+				if (ibuf_cat(e, sa->sa_scert[i].id_buf) != 0)
+					goto done;
+				len = ibuf_size(sa->sa_scert[i].id_buf)
+				    + sizeof(*cert);
+			}
 		}
 
 		if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_AUTH) == -1)
@@ -4404,6 +4486,7 @@
 	struct iked_childsa		*csa, *csatmp, *ipcomp;
 	struct iked_flow		*flow, *flowtmp;
 	struct iked_proposal		*prop, *proptmp;
+	int				i;
 
 	log_debug("%s: IKE SA %p ispi %s rspi %s replaced"
 	    " by SA %p ispi %s rspi %s ",
@@ -4481,11 +4564,15 @@
 		nsa->sa_icert = sa->sa_rcert;
 		nsa->sa_rcert = sa->sa_icert;
 	}
+	for (i = 0; i < IKED_SCERT_MAX; i++)
+		nsa->sa_scert[i] = sa->sa_scert[i];
 	/* duplicate the actual buffer */
 	nsa->sa_iid.id_buf = ibuf_dup(nsa->sa_iid.id_buf);
 	nsa->sa_rid.id_buf = ibuf_dup(nsa->sa_rid.id_buf);
 	nsa->sa_icert.id_buf = ibuf_dup(nsa->sa_icert.id_buf);
 	nsa->sa_rcert.id_buf = ibuf_dup(nsa->sa_rcert.id_buf);
+	for (i = 0; i < IKED_SCERT_MAX; i++)
+		nsa->sa_scert[i].id_buf = ibuf_dup(nsa->sa_scert[i].id_buf);
 
 	/* Transfer sa_addrpool address */
 	if (sa->sa_addrpool) {
Index: types.h
===================================================================
RCS file: /cvs/src/sbin/iked/types.h,v
retrieving revision 1.42
diff -d -u -r1.42 types.h
--- types.h	13 Feb 2021 16:14:12 -0000	1.42
+++ types.h	13 May 2021 02:51:07 -0000
@@ -115,6 +115,7 @@
 	IMSG_CERTVALID,
 	IMSG_CERTINVALID,
 	IMSG_CERT_PARTIAL_CHAIN,
+	IMSG_SCERT,
 	IMSG_IF_ADDADDR,
 	IMSG_IF_DELADDR,
 	IMSG_VROUTE_ADD,
