diff options
Diffstat (limited to 'src/pluto/ipsec_doi.c')
-rw-r--r-- | src/pluto/ipsec_doi.c | 185 |
1 files changed, 114 insertions, 71 deletions
diff --git a/src/pluto/ipsec_doi.c b/src/pluto/ipsec_doi.c index 7ec547b0c..c8a347b45 100644 --- a/src/pluto/ipsec_doi.c +++ b/src/pluto/ipsec_doi.c @@ -104,6 +104,14 @@ #define RETURN_STF_FAILURE(f) \ { int r = (f); if (r != ISAKMP_NOTHING_WRONG) return STF_FAIL + r; } +/* The endpoint(s) for which an SA is getting installed, so keying material + * can be properly wiped. + */ +enum endpoint { + EP_LOCAL = 1, + EP_REMOTE = 1 << 1, +}; + /* create output HDR as replica of input HDR */ void echo_hdr(struct msg_digest *md, bool enc, u_int8_t np) { @@ -2196,9 +2204,9 @@ static void decode_cert(struct msg_digest *md) cert_t x509cert = cert_empty; x509cert.cert = lib->creds->create(lib->creds, - CRED_CERTIFICATE, CERT_X509, - BUILD_BLOB_ASN1_DER, blob, - BUILD_END); + CRED_CERTIFICATE, CERT_X509, + BUILD_BLOB_ASN1_DER, blob, + BUILD_END); if (x509cert.cert) { if (verify_x509cert(&x509cert, strict_crl_policy, &valid_until)) @@ -2741,13 +2749,59 @@ static bool has_preloaded_public_key(struct state *st) return FALSE; } +/* Compute keying material for an SA + */ +static void compute_keymat_internal(struct state *st, u_int8_t protoid, + ipsec_spi_t spi, size_t needed_len, + u_char **keymat_out) +{ + size_t i = 0, prf_block_size, needed_space; + chunk_t protoid_chunk = chunk_from_thing(protoid); + chunk_t spi_chunk = chunk_from_thing(spi); + pseudo_random_function_t prf_alg = oakley_to_prf(st->st_oakley.hash); + prf_t *prf = lib->crypto->create_prf(lib->crypto, prf_alg); + + prf->set_key(prf, st->st_skeyid_d); + prf_block_size = prf->get_block_size(prf); + + /* Although only needed_len bytes are desired, we must round up to a + * multiple of prf_block_size so that the buffer isn't overrun */ + needed_space = needed_len + pad_up(needed_len, prf_block_size); + replace(*keymat_out, malloc(needed_space)); + + for (;;) + { + char *keymat_i = (*keymat_out) + i; + chunk_t keymat = { keymat_i, prf_block_size }; + + if (st->st_shared.ptr != NULL) + { /* PFS: include the g^xy */ + prf->get_bytes(prf, st->st_shared, NULL); + } + prf->get_bytes(prf, protoid_chunk, NULL); + prf->get_bytes(prf, spi_chunk, NULL); + prf->get_bytes(prf, st->st_ni, NULL); + prf->get_bytes(prf, st->st_nr, keymat_i); + + i += prf_block_size; + if (i >= needed_space) + { + break; + } + + /* more keying material needed: prepare to go around again */ + prf->get_bytes(prf, keymat, NULL); + } + prf->destroy(prf); +} + /* * Produce the new key material of Quick Mode. * RFC 2409 "IKE" section 5.5 * specifies how this is to be done. */ static void compute_proto_keymat(struct state *st, u_int8_t protoid, - struct ipsec_proto_info *pi) + struct ipsec_proto_info *pi, enum endpoint ep) { size_t needed_len = 0; /* bytes of keying material needed */ @@ -2833,82 +2887,57 @@ static void compute_proto_keymat(struct state *st, u_int8_t protoid, pi->keymat_len = needed_len; - /* Allocate space for the keying material. Although only needed_len bytes - * are desired, we must round up to a multiple of hash_size - * so that our buffer isn't overrun. - */ + if (ep & EP_LOCAL) { - size_t needed_space; /* space needed for keying material (rounded up) */ - size_t i, prf_block_size; - chunk_t protoid_chunk = chunk_from_thing(protoid); - chunk_t spi_our = chunk_from_thing(pi->our_spi); - chunk_t spi_peer = chunk_from_thing(pi->attrs.spi); - pseudo_random_function_t prf_alg; - prf_t *prf_our, *prf_peer; - - prf_alg = oakley_to_prf(st->st_oakley.hash); - prf_our = lib->crypto->create_prf(lib->crypto, prf_alg); - prf_peer = lib->crypto->create_prf(lib->crypto, prf_alg); - prf_our->set_key(prf_our, st->st_skeyid_d); - prf_peer->set_key(prf_peer, st->st_skeyid_d); - prf_block_size = prf_our->get_block_size(prf_our); - - needed_space = needed_len + pad_up(needed_len, prf_block_size); - replace(pi->our_keymat, malloc(needed_space)); - replace(pi->peer_keymat, malloc(needed_space)); - - for (i = 0;; ) - { - char *keymat_i_our = pi->our_keymat + i; - char *keymat_i_peer = pi->peer_keymat + i; - chunk_t keymat_our = { keymat_i_our, prf_block_size }; - chunk_t keymat_peer = { keymat_i_peer, prf_block_size }; - - if (st->st_shared.ptr != NULL) - { - /* PFS: include the g^xy */ - prf_our->get_bytes(prf_our, st->st_shared, NULL); - prf_peer->get_bytes(prf_peer, st->st_shared, NULL); - } - prf_our->get_bytes(prf_our, protoid_chunk, NULL); - prf_peer->get_bytes(prf_peer, protoid_chunk, NULL); - - prf_our->get_bytes(prf_our, spi_our, NULL); - prf_peer->get_bytes(prf_peer, spi_peer, NULL); - - prf_our->get_bytes(prf_our, st->st_ni, NULL); - prf_peer->get_bytes(prf_peer, st->st_ni, NULL); - - prf_our->get_bytes(prf_our, st->st_nr, keymat_i_our); - prf_peer->get_bytes(prf_peer, st->st_nr, keymat_i_peer); + compute_keymat_internal(st, protoid, pi->our_spi, needed_len, + &pi->our_keymat); + DBG(DBG_CRYPT, + DBG_dump("KEYMAT computed:\n", pi->our_keymat, + pi->keymat_len)); + } + if (ep & EP_REMOTE) + { + compute_keymat_internal(st, protoid, pi->attrs.spi, needed_len, + &pi->peer_keymat); + DBG(DBG_CRYPT, + DBG_dump("Peer KEYMAT computed:\n", pi->peer_keymat, + pi->keymat_len)); + } +} - i += prf_block_size; - if (i >= needed_space) - { - break; - } +static void compute_keymats(struct state *st, enum endpoint ep) +{ + if (st->st_ah.present) + { + compute_proto_keymat(st, PROTO_IPSEC_AH, &st->st_ah, ep); + } + if (st->st_esp.present) + { + compute_proto_keymat(st, PROTO_IPSEC_ESP, &st->st_esp, ep); + } +} - /* more keying material needed: prepare to go around again */ - prf_our->get_bytes(prf_our, keymat_our, NULL); - prf_peer->get_bytes(prf_peer, keymat_peer, NULL); - } - prf_our->destroy(prf_our); - prf_peer->destroy(prf_peer); +static void wipe_proto_keymat(struct ipsec_proto_info *pi, enum endpoint ep) +{ + if (ep & EP_LOCAL) + { + memwipe(pi->our_keymat, pi->keymat_len); + } + if (ep & EP_REMOTE) + { + memwipe(pi->peer_keymat, pi->keymat_len); } - DBG(DBG_CRYPT, - DBG_dump("KEYMAT computed:\n", pi->our_keymat, pi->keymat_len); - DBG_dump("Peer KEYMAT computed:\n", pi->peer_keymat, pi->keymat_len)); } -static void compute_keymats(struct state *st) +static void wipe_keymats(struct state *st, enum endpoint ep) { if (st->st_ah.present) { - compute_proto_keymat(st, PROTO_IPSEC_AH, &st->st_ah); + wipe_proto_keymat(&st->st_ah, ep); } if (st->st_esp.present) { - compute_proto_keymat(st, PROTO_IPSEC_ESP, &st->st_esp); + wipe_proto_keymat(&st->st_esp, ep); } } @@ -3824,7 +3853,7 @@ main_id_and_auth(struct msg_digest *md case XAUTHInitRSA: case XAUTHRespRSA: r = check_signature(KEY_RSA, peer, st, hash, - &md->chain[ISAKMP_NEXT_SIG]->pbs, + &md->chain[ISAKMP_NEXT_SIG]->pbs, #ifdef USE_KEYRR kc == NULL ? NULL : kc->ac.keys_from_dns, #endif /* USE_KEYRR */ @@ -4975,6 +5004,7 @@ static stf_status quick_inI1_outR1_tail(struct verify_oppo_bundle *b, /* now that we are sure of our connection, create our new state */ { + enum endpoint ep = EP_LOCAL; struct state *const st = duplicate_state(p1st); /* first: fill in missing bits of our new state object @@ -5152,7 +5182,7 @@ static stf_status quick_inI1_outR1_tail(struct verify_oppo_bundle *b, , st, &st->st_msgid, TRUE); /* Derive new keying material */ - compute_keymats(st); + compute_keymats(st, ep); /* Tell the kernel to establish the new inbound SA * (unless the commit bit is set -- which we don't support). @@ -5161,8 +5191,10 @@ static stf_status quick_inI1_outR1_tail(struct verify_oppo_bundle *b, */ if (!install_inbound_ipsec_sa(st)) { + wipe_keymats(st, ep); return STF_INTERNAL_ERROR; /* ??? we may be partly committed */ } + wipe_keymats(st, ep); /* encrypt message, except for fixed part of header */ @@ -5206,6 +5238,7 @@ static void dpd_init(struct state *st) */ stf_status quick_inR1_outI2(struct msg_digest *md) { + enum endpoint ep = EP_LOCAL | EP_REMOTE; struct state *const st = md->st; const connection_t *c = st->st_connection; @@ -5325,7 +5358,7 @@ stf_status quick_inR1_outI2(struct msg_digest *md) } /* Derive new keying material */ - compute_keymats(st); + compute_keymats(st, ep); /* Tell the kernel to establish the inbound, outbound, and routing part * of the new SA (unless the commit bit is set -- which we don't support). @@ -5334,8 +5367,10 @@ stf_status quick_inR1_outI2(struct msg_digest *md) */ if (!install_ipsec_sa(st, TRUE)) { + wipe_keymats(st, ep); return STF_INTERNAL_ERROR; } + wipe_keymats(st, ep); /* encrypt message, except for fixed part of header */ @@ -5374,12 +5409,16 @@ stf_status quick_inR1_outI2(struct msg_digest *md) */ stf_status quick_inI2(struct msg_digest *md) { + enum endpoint ep = EP_REMOTE; struct state *const st = md->st; /* HASH(3) in */ CHECK_QUICK_HASH(md, quick_mode_hash3(hash_val, st) , "HASH(3)", "Quick I2"); + /* Derive keying material */ + compute_keymats(st, ep); + /* Tell the kernel to establish the outbound and routing part of the new SA * (the previous state established inbound) * (unless the commit bit is set -- which we don't support). @@ -5388,8 +5427,11 @@ stf_status quick_inI2(struct msg_digest *md) */ if (!install_ipsec_sa(st, FALSE)) { + wipe_keymats(st, ep); return STF_INTERNAL_ERROR; } + wipe_keymats(st, ep); + DBG(DBG_CONTROLMORE, DBG_log("inI2: instance %s[%ld], setting newest_ipsec_sa to #%ld (was #%ld) (spd.eroute=#%ld)" , st->st_connection->name @@ -5851,6 +5893,7 @@ dpd_timeout(struct state *st) /* caching the connection name before deletion */ strncpy(cname, c->name, BUF_LEN); + cname[BUF_LEN-1] = '\0'; if (c->kind == CK_INSTANCE) { |