Support sending certificate chains with intermediate CAs in multiple CERT

payloads.  Local certificate chains as required with LetsEncrypt certs will
work between iked and other IKEv2 implementations,  iked to iked connections
won't work yet because of missing support to receive multiple CERT
payloads.

from Katsuhiro Ueno
tested by and ok sthen@
This commit is contained in:
tobhe 2022-07-08 19:51:11 +00:00
parent ecfba8db11
commit 055943a192
6 changed files with 198 additions and 8 deletions

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ca.c,v 1.87 2021/12/14 13:44:36 tobhe Exp $ */
/* $OpenBSD: ca.c,v 1.88 2022/07/08 19:51:11 tobhe Exp $ */
/*
* Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@ -328,6 +328,32 @@ ca_setcert(struct iked *env, struct iked_sahdr *sh, struct iked_id *id,
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,
@ -541,6 +567,51 @@ ca_getcert(struct iked *env, struct imsg *imsg)
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)
{
@ -551,6 +622,8 @@ ca_getreq(struct iked *env, struct imsg *imsg)
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];
@ -612,8 +685,10 @@ ca_getreq(struct iked *env, struct imsg *imsg)
log_debug("%s: found CA %s", __func__, subj_name);
free(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) {
cert = chain[chain_len - 1];
if (!ca_cert_local(env, cert)) {
log_info("%s: found cert with matching "
"ID but without matching key.",
@ -623,6 +698,10 @@ ca_getreq(struct iked *env, struct imsg *imsg)
break;
}
}
for (i = chain_len; i >= 2; i--)
ca_setscert(env, &sh, type, chain[i - 2]);
/* Fallthrough */
case IKEV2_CERT_NONE:
fallback:

View File

@ -1,4 +1,4 @@
/* $OpenBSD: config.c,v 1.85 2022/05/08 20:26:31 tobhe Exp $ */
/* $OpenBSD: config.c,v 1.86 2022/07/08 19:51:11 tobhe Exp $ */
/*
* Copyright (c) 2019 Tobias Heider <tobias.heider@stusta.de>
@ -110,6 +110,8 @@ config_free_fragments(struct iked_frag *frag)
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 @@ config_free_sa(struct iked *env, struct iked_sa *sa)
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);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: eap.c,v 1.20 2022/01/28 05:24:15 guenther Exp $ */
/* $OpenBSD: eap.c,v 1.21 2022/07/08 19:51:11 tobhe Exp $ */
/*
* Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@ -90,6 +90,7 @@ eap_identity_request(struct iked *env, struct iked_sa *sa)
uint8_t firstpayload;
int ret = -1;
ssize_t len = 0;
int i;
/* Responder only */
if (sa->sa_hdr.sh_initiator)
@ -128,6 +129,22 @@ eap_identity_request(struct iked *env, struct iked_sa *sa)
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)

View File

@ -1,4 +1,4 @@
/* $OpenBSD: iked.h,v 1.204 2022/03/14 12:58:55 tobhe Exp $ */
/* $OpenBSD: iked.h,v 1.205 2022/07/08 19:51:11 tobhe Exp $ */
/*
* Copyright (c) 2019 Tobias Heider <tobias.heider@stusta.de>
@ -469,11 +469,13 @@ struct iked_sa {
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)

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ikev2.c,v 1.348 2022/07/04 08:39:55 tobhe Exp $ */
/* $OpenBSD: ikev2.c,v 1.349 2022/07/08 19:51:11 tobhe Exp $ */
/*
* Copyright (c) 2019 Tobias Heider <tobias.heider@stusta.de>
@ -315,6 +315,7 @@ ikev2_dispatch_cert(int fd, struct privsep_proc *p, struct imsg *imsg)
size_t len;
struct iked_id *id = NULL;
int ignore = 0;
int i;
switch (imsg->hdr.type) {
case IMSG_CERTREQ:
@ -416,6 +417,51 @@ ikev2_dispatch_cert(int fd, struct privsep_proc *p, struct imsg *imsg)
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) {
@ -1490,6 +1536,7 @@ ikev2_init_ike_auth(struct iked *env, struct iked_sa *sa)
uint8_t firstpayload;
int ret = -1;
ssize_t len;
int i;
if (!sa_stateok(sa, IKEV2_STATE_SA_INIT))
return (0);
@ -1544,6 +1591,22 @@ ikev2_init_ike_auth(struct iked *env, struct iked_sa *sa)
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)
@ -3722,6 +3785,7 @@ ikev2_resp_ike_auth(struct iked *env, struct iked_sa *sa)
uint8_t firstpayload;
int ret = -1;
ssize_t len;
int i;
if (sa == NULL)
return (-1);
@ -3781,6 +3845,24 @@ ikev2_resp_ike_auth(struct iked *env, struct iked_sa *sa)
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)
@ -4458,6 +4540,7 @@ ikev2_ikesa_enable(struct iked *env, struct iked_sa *sa, struct iked_sa *nsa)
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 ",
@ -4535,11 +4618,15 @@ ikev2_ikesa_enable(struct iked *env, struct iked_sa *sa, struct iked_sa *nsa)
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) {

View File

@ -1,4 +1,4 @@
/* $OpenBSD: types.h,v 1.48 2022/04/13 20:54:55 deraadt Exp $ */
/* $OpenBSD: types.h,v 1.49 2022/07/08 19:51:11 tobhe Exp $ */
/*
* Copyright (c) 2019 Tobias Heider <tobias.heider@stusta.de>
@ -115,6 +115,7 @@ enum imsg_type {
IMSG_CERTVALID,
IMSG_CERTINVALID,
IMSG_CERT_PARTIAL_CHAIN,
IMSG_SCERT,
IMSG_IF_ADDADDR,
IMSG_IF_DELADDR,
IMSG_VROUTE_ADD,