Hi,
the following diffs allows to use iked/OpenIKED with iOS9 IKEv2
clients. You will be able to connect your super-large iPad Pro or
premium iPhone 6s+ via OpenBSD :) I don't have El Capitan yet, but it
might fix it for your super-light MacBook as well.
Problem: iOS doesn't send a CERTREQ but just a CERT and expects us to
respond with a CERT as well. Without the diff, iked does not send
back a CERT without a CERTREQ payload in the request (list of CA SHA1
hashes). RFC 7296 (yep, they updated the IKEv2 RFC again) says in
section 3.7 that the CERTREQ payload is optional and only a
suggestion. The implementation is allowed to select its certificate
based on other policies. Apple's implementation (btw., what is it
based on?) seems to interpret it as "select and send a CERT even if
the CERTREQ is missing".
The attached diff always responds with a CERT or public key. If the
peer didn't send a CERTREQ, iked now picks a cert based on its own
trusted CAs (which usually includes the CA that signed your local
cert).
In the iOS VPN configuration, I successfully tested it with User
Authentication "Username" (EAP-MSCHAPv2), "None" + "Use Certificate",
and "None" + "Secret" (psk). Take care to configure the "Remote ID"
and "Local ID" correctly (it should be (U)FQDN or IPv4/6, not
ASN1_DN). I also had to import .pfx cert+key and a .crt CA via Safari
on the device. User Authentication "Certificate" is not supported, as
it is EAP-TLS. The matching "Username" iked.conf follows:
user "user1" "password123"
ikev2 "ios9" passive esp \
from 0.0.0.0/0 to 0.0.0.0/0 \
local any peer any \
eap "mschap-v2" \
config address 10.2.0.1/24 \
config name-server 10.2.0.2 \
tag "$name-$id"
The iOS9 client currently only advertises enc aes-128 auth hmac-sha1
group modp1024, which is fairly vintage crypto, but there was a hint
that we could potentially get it to send better proposals by
responding with "no proposal chosen" at some point. Will try it later.
OK?
Reyk
Index: sbin/iked/ca.c
===================================================================
RCS file: /cvs/src/sbin/iked/ca.c,v
retrieving revision 1.36
diff -u -p -u -p -r1.36 ca.c
--- sbin/iked/ca.c 21 Aug 2015 11:59:27 -0000 1.36
+++ sbin/iked/ca.c 30 Sep 2015 14:37:28 -0000
@@ -248,7 +248,7 @@ ca_setcert(struct iked *env, struct iked
}
int
-ca_setreq(struct iked *env, struct iked_sahdr *sh,
+ca_setreq(struct iked *env, struct iked_sa *sa,
struct iked_static_id *localid, uint8_t type, uint8_t *data,
size_t len, enum privsep_procid procid)
{
@@ -273,8 +273,8 @@ ca_setreq(struct iked *env, struct iked_
iov[iovcnt].iov_len = sizeof(idb);
iovcnt++;
- iov[iovcnt].iov_base = sh;
- iov[iovcnt].iov_len = sizeof(*sh);
+ iov[iovcnt].iov_base = &sa->sa_hdr;
+ iov[iovcnt].iov_len = sizeof(sa->sa_hdr);
iovcnt++;
iov[iovcnt].iov_base = &type;
iov[iovcnt].iov_len = sizeof(type);
@@ -286,6 +286,8 @@ ca_setreq(struct iked *env, struct iked_
if (proc_composev_imsg(&env->sc_ps, procid, -1,
IMSG_CERTREQ, -1, iov, iovcnt) == -1)
goto done;
+
+ sa_stateflags(sa, IKED_REQ_CERTREQ);
ret = 0;
done:
Index: sbin/iked/iked.h
===================================================================
RCS file: /cvs/src/sbin/iked/iked.h,v
retrieving revision 1.88
diff -u -p -u -p -r1.88 iked.h
--- sbin/iked/iked.h 21 Aug 2015 11:59:27 -0000 1.88
+++ sbin/iked/iked.h 30 Sep 2015 14:37:29 -0000
@@ -322,17 +322,19 @@ struct iked_id {
struct ibuf *id_buf;
};
-#define IKED_REQ_CERT 0x01 /* get local certificate (if required)
*/
-#define IKED_REQ_CERTVALID 0x02 /* validated the peer cert */
-#define IKED_REQ_AUTH 0x04 /* AUTH payload */
-#define IKED_REQ_AUTHVALID 0x08 /* AUTH payload has been verified */
-#define IKED_REQ_SA 0x10 /* SA available */
-#define IKED_REQ_EAPVALID 0x20 /* EAP payload has been verified */
-#define IKED_REQ_CHILDSA 0x40 /* Child SA initiated */
-#define IKED_REQ_INF 0x80 /* Informational exchange initiated */
+#define IKED_REQ_CERT 0x0001 /* get local certificate (if required)
*/
+#define IKED_REQ_CERTVALID 0x0002 /* validated the peer cert */
+#define IKED_REQ_CERTREQ 0x0004 /* CERTREQ has been received */
+#define IKED_REQ_AUTH 0x0008 /* AUTH payload */
+#define IKED_REQ_AUTHVALID 0x0010 /* AUTH payload has been verified */
+#define IKED_REQ_SA 0x0020 /* SA available */
+#define IKED_REQ_EAPVALID 0x0040 /* EAP payload has been verified */
+#define IKED_REQ_CHILDSA 0x0080 /* Child SA initiated */
+#define IKED_REQ_INF 0x0100 /* Informational exchange initiated */
#define IKED_REQ_BITS \
- "\20\01CERT\02CERTVALID\03AUTH\04AUTHVALID\05SA\06EAP"
+ "\20\01CERT\02CERTVALID\03CERTREQ\04AUTH\05AUTHVALID\06SA\07EAPVALID" \
+ "\10CHILDSA\11INF"
TAILQ_HEAD(iked_msgqueue, iked_message);
@@ -838,7 +840,7 @@ void pfkey_init(struct iked *, int fd);
/* ca.c */
pid_t caproc(struct privsep *, struct privsep_proc *);
-int ca_setreq(struct iked *, struct iked_sahdr *, struct iked_static_id *,
+int ca_setreq(struct iked *, struct iked_sa *, struct iked_static_id *,
uint8_t, uint8_t *, size_t, enum privsep_procid);
int ca_setcert(struct iked *, struct iked_sahdr *, struct iked_id *,
uint8_t, uint8_t *, size_t, enum privsep_procid);
Index: sbin/iked/ikev2.c
===================================================================
RCS file: /cvs/src/sbin/iked/ikev2.c,v
retrieving revision 1.123
diff -u -p -u -p -r1.123 ikev2.c
--- sbin/iked/ikev2.c 21 Aug 2015 11:59:27 -0000 1.123
+++ sbin/iked/ikev2.c 30 Sep 2015 14:37:32 -0000
@@ -652,6 +652,8 @@ ikev2_ike_auth_recv(struct iked *env, st
int
ikev2_ike_auth(struct iked *env, struct iked_sa *sa)
{
+ struct iked_policy *pol = sa->sa_policy;
+
/* Attempt state transition */
if (sa->sa_state == IKEV2_STATE_EAP_SUCCESS)
sa_state(env, sa, IKEV2_STATE_EAP_VALID);
@@ -664,6 +666,24 @@ ikev2_ike_auth(struct iked *env, struct
else
return (ikev2_init_ike_auth(env, sa));
}
+
+ /*
+ * If we have to send a local certificate but did not receive an
+ * optional CERTREQ, use our own certreq to find a local certificate.
+ * We could alternatively extract the CA from the peer certificate
+ * to find a matching local one.
+ */
+ if (sa->sa_statevalid & IKED_REQ_CERT) {
+ if ((sa->sa_stateflags & IKED_REQ_CERTREQ) == 0) {
+ log_debug("%s: no CERTREQ, using default", __func__);
+ return (ca_setreq(env, sa,
+ &pol->pol_localid, pol->pol_certreqtype,
+ ibuf_data(env->sc_certreq),
+ ibuf_size(env->sc_certreq), PROC_CERT));
+ } else if ((sa->sa_stateflags & IKED_REQ_CERT) == 0)
+ return (0); /* ignored, wait for cert */
+ }
+
return (ikev2_resp_ike_auth(env, sa));
}
Index: sbin/iked/ikev2_pld.c
===================================================================
RCS file: /cvs/src/sbin/iked/ikev2_pld.c,v
retrieving revision 1.52
diff -u -p -u -p -r1.52 ikev2_pld.c
--- sbin/iked/ikev2_pld.c 21 Aug 2015 11:59:27 -0000 1.52
+++ sbin/iked/ikev2_pld.c 30 Sep 2015 14:37:34 -0000
@@ -931,7 +931,7 @@ ikev2_pld_certreq(struct iked *env, stru
else
sa->sa_statevalid |= IKED_REQ_CERT;
- ca_setreq(env, &sa->sa_hdr, &sa->sa_policy->pol_localid,
+ ca_setreq(env, sa, &sa->sa_policy->pol_localid,
cert.cert_type, buf, len, PROC_CERT);
return (0);
Index: sbin/iked/policy.c
===================================================================
RCS file: /cvs/src/sbin/iked/policy.c,v
retrieving revision 1.39
diff -u -p -u -p -r1.39 policy.c
--- sbin/iked/policy.c 21 Aug 2015 11:59:28 -0000 1.39
+++ sbin/iked/policy.c 30 Sep 2015 14:37:34 -0000
@@ -272,7 +272,7 @@ sa_stateflags(struct iked_sa *sa, unsign
else
require = sa->sa_stateinit;
- log_debug("%s: 0x%02x -> 0x%02x %s (required 0x%02x %s)", __func__,
+ log_debug("%s: 0x%04x -> 0x%04x %s (required 0x%04x %s)", __func__,
sa->sa_stateflags, sa->sa_stateflags | flags,
print_bits(sa->sa_stateflags | flags, IKED_REQ_BITS), require,
print_bits(require, IKED_REQ_BITS));
@@ -296,7 +296,7 @@ sa_stateok(struct iked_sa *sa, int state
if (state == IKEV2_STATE_SA_INIT ||
state == IKEV2_STATE_VALID ||
state == IKEV2_STATE_EAP_VALID) {
- log_debug("%s: %s flags 0x%02x, require 0x%02x %s", __func__,
+ log_debug("%s: %s flags 0x%04x, require 0x%04x %s", __func__,
print_map(state, ikev2_state_map),
(sa->sa_stateflags & require), require,
print_bits(require, IKED_REQ_BITS));