Apply by doing: cd /usr/src patch -p0 < 009_isakmpd.patch Then rebuild and install isakmpd: cd sbin/isakmpd make clean make depend make make install Index: sbin/isakmpd/crypto.c =================================================================== RCS file: /cvs/src/sbin/isakmpd/crypto.c,v retrieving revision 1.16 retrieving revision 1.16.2.1 diff -u -p -r1.16 -r1.16.2.1 --- sbin/isakmpd/crypto.c 28 Aug 2003 14:43:35 -0000 1.16 +++ sbin/isakmpd/crypto.c 13 Jan 2004 22:50:07 -0000 1.16.2.1 @@ -39,151 +39,254 @@ #include "crypto.h" #include "log.h" -enum cryptoerr evp_init (struct keystate *, u_int8_t *, u_int16_t, - const EVP_CIPHER *); enum cryptoerr des1_init (struct keystate *, u_int8_t *, u_int16_t); enum cryptoerr des3_init (struct keystate *, u_int8_t *, u_int16_t); enum cryptoerr blf_init (struct keystate *, u_int8_t *, u_int16_t); enum cryptoerr cast_init (struct keystate *, u_int8_t *, u_int16_t); enum cryptoerr aes_init (struct keystate *, u_int8_t *, u_int16_t); -void evp_encrypt (struct keystate *, u_int8_t *, u_int16_t); -void evp_decrypt (struct keystate *, u_int8_t *, u_int16_t); +void des1_encrypt (struct keystate *, u_int8_t *, u_int16_t); +void des1_decrypt (struct keystate *, u_int8_t *, u_int16_t); +void des3_encrypt (struct keystate *, u_int8_t *, u_int16_t); +void des3_decrypt (struct keystate *, u_int8_t *, u_int16_t); +void blf_encrypt (struct keystate *, u_int8_t *, u_int16_t); +void blf_decrypt (struct keystate *, u_int8_t *, u_int16_t); +void cast1_encrypt (struct keystate *, u_int8_t *, u_int16_t); +void cast1_decrypt (struct keystate *, u_int8_t *, u_int16_t); +void aes_encrypt (struct keystate *, u_int8_t *, u_int16_t); +void aes_decrypt (struct keystate *, u_int8_t *, u_int16_t); struct crypto_xf transforms[] = { #ifdef USE_DES { DES_CBC, "Data Encryption Standard (CBC-Mode)", 8, 8, BLOCKSIZE, 0, des1_init, - evp_encrypt, evp_decrypt + des1_encrypt, des1_decrypt }, #endif #ifdef USE_TRIPLEDES { TRIPLEDES_CBC, "Triple-DES (CBC-Mode)", 24, 24, BLOCKSIZE, 0, des3_init, - evp_encrypt, evp_decrypt + des3_encrypt, des3_decrypt }, #endif #ifdef USE_BLOWFISH { BLOWFISH_CBC, "Blowfish (CBC-Mode)", 12, 56, BLOCKSIZE, 0, blf_init, - evp_encrypt, evp_decrypt + blf_encrypt, blf_decrypt }, #endif #ifdef USE_CAST { CAST_CBC, "CAST (CBC-Mode)", 12, 16, BLOCKSIZE, 0, cast_init, - evp_encrypt, evp_decrypt + cast1_encrypt, cast1_decrypt }, #endif #ifdef USE_AES { - AES_CBC, "AES (CBC-Mode)", 16, 32, 2*BLOCKSIZE, 0, + AES_CBC, "AES (CBC-Mode)", 16, 32, AES_BLOCK_SIZE, 0, aes_init, - evp_encrypt, evp_decrypt + aes_encrypt, aes_decrypt }, #endif }; -#ifdef USE_DES +/* Hmm, the function prototypes for des are really dumb */ +#ifdef __OpenBSD__ +#define DC (des_cblock *) +#else +#define DC (void *) +#endif + enum cryptoerr des1_init (struct keystate *ks, u_int8_t *key, u_int16_t len) { - const EVP_CIPHER *evp; + /* des_set_key returns -1 for parity problems, and -2 for weak keys */ + des_set_odd_parity (DC key); + switch (des_set_key (DC key, ks->ks_des[0])) + { + case -2: + return EWEAKKEY; + default: + return EOKAY; + } +} + +void +des1_encrypt (struct keystate *ks, u_int8_t *d, u_int16_t len) +{ + des_cbc_encrypt (DC d, DC d, len, ks->ks_des[0], DC ks->riv, DES_ENCRYPT); +} - evp = EVP_des_cbc(); - return evp_init (ks, key, len, evp); +void +des1_decrypt (struct keystate *ks, u_int8_t *d, u_int16_t len) +{ + des_cbc_encrypt (DC d, DC d, len, ks->ks_des[0], DC ks->riv, DES_DECRYPT); } -#endif #ifdef USE_TRIPLEDES enum cryptoerr des3_init (struct keystate *ks, u_int8_t *key, u_int16_t len) { - const EVP_CIPHER *evp; + des_set_odd_parity (DC key); + des_set_odd_parity (DC (key + 8)); + des_set_odd_parity (DC (key + 16)); + + /* As of the draft Tripe-DES does not check for weak keys */ + des_set_key (DC key, ks->ks_des[0]); + des_set_key (DC (key + 8), ks->ks_des[1]); + des_set_key (DC (key + 16), ks->ks_des[2]); - evp = EVP_des_ede3_cbc(); - return evp_init (ks, key, len, evp); + return EOKAY; } -#endif + +void +des3_encrypt (struct keystate *ks, u_int8_t *data, u_int16_t len) +{ + u_int8_t iv[MAXBLK]; + + memcpy (iv, ks->riv, ks->xf->blocksize); + des_ede3_cbc_encrypt (DC data, DC data, len, ks->ks_des[0], ks->ks_des[1], + ks->ks_des[2], DC iv, DES_ENCRYPT); +} + +void +des3_decrypt (struct keystate *ks, u_int8_t *data, u_int16_t len) +{ + u_int8_t iv[MAXBLK]; + + memcpy (iv, ks->riv, ks->xf->blocksize); + des_ede3_cbc_encrypt (DC data, DC data, len, ks->ks_des[0], ks->ks_des[1], + ks->ks_des[2], DC iv, DES_DECRYPT); +} +#undef DC +#endif /* USE_TRIPLEDES */ #ifdef USE_BLOWFISH enum cryptoerr blf_init (struct keystate *ks, u_int8_t *key, u_int16_t len) { - const EVP_CIPHER *evp; + blf_key (&ks->ks_blf, key, len); - evp = EVP_bf_cbc(); - return evp_init (ks, key, len, evp); + return EOKAY; } -#endif + +void +blf_encrypt (struct keystate *ks, u_int8_t *data, u_int16_t len) +{ + u_int16_t i, blocksize = ks->xf->blocksize; + u_int8_t *iv = ks->liv; + u_int32_t xl, xr; + + memcpy (iv, ks->riv, blocksize); + + for (i = 0; i < len; data += blocksize, i += blocksize) + { + XOR64 (data, iv); + xl = GET_32BIT_BIG (data); + xr = GET_32BIT_BIG (data + 4); + Blowfish_encipher (&ks->ks_blf, &xl, &xr); + SET_32BIT_BIG (data, xl); + SET_32BIT_BIG (data + 4, xr); + SET64 (iv, data); + } +} + +void +blf_decrypt (struct keystate *ks, u_int8_t *data, u_int16_t len) +{ + u_int16_t i, blocksize = ks->xf->blocksize; + u_int32_t xl, xr; + + data += len - blocksize; + for (i = len - blocksize; i >= blocksize; data -= blocksize, i -= blocksize) + { + xl = GET_32BIT_BIG (data); + xr = GET_32BIT_BIG (data + 4); + Blowfish_decipher (&ks->ks_blf, &xl, &xr); + SET_32BIT_BIG (data, xl); + SET_32BIT_BIG (data + 4, xr); + XOR64 (data, data - blocksize); + + } + xl = GET_32BIT_BIG (data); + xr = GET_32BIT_BIG (data + 4); + Blowfish_decipher (&ks->ks_blf, &xl, &xr); + SET_32BIT_BIG (data, xl); + SET_32BIT_BIG (data + 4, xr); + XOR64 (data, ks->riv); +} +#endif /* USE_BLOWFISH */ #ifdef USE_CAST enum cryptoerr cast_init (struct keystate *ks, u_int8_t *key, u_int16_t len) { - const EVP_CIPHER *evp; + cast_setkey (&ks->ks_cast, key, len); + return EOKAY; +} + +void +cast1_encrypt (struct keystate *ks, u_int8_t *data, u_int16_t len) +{ + u_int16_t i, blocksize = ks->xf->blocksize; + u_int8_t *iv = ks->liv; + + memcpy (iv, ks->riv, blocksize); - evp = EVP_cast5_cbc(); - return evp_init (ks, key, len, evp); + for (i = 0; i < len; data += blocksize, i += blocksize) + { + XOR64 (data, iv); + cast_encrypt (&ks->ks_cast, data, data); + SET64 (iv, data); + } } -#endif -#ifdef USE_AES -enum cryptoerr -aes_init (struct keystate *ks, u_int8_t *key, u_int16_t len) +void +cast1_decrypt (struct keystate *ks, u_int8_t *data, u_int16_t len) { - const EVP_CIPHER *evp; + u_int16_t i, blocksize = ks->xf->blocksize; - switch (8 * len) + data += len - blocksize; + for (i = len - blocksize; i >= blocksize; data -= blocksize, i -= blocksize) { - case 128: - evp = EVP_aes_128_cbc(); - break; - case 192: - evp = EVP_aes_192_cbc(); - break; - case 256: - evp = EVP_aes_256_cbc(); - break; - default: - return EKEYLEN; + cast_decrypt (&ks->ks_cast, data, data); + XOR64 (data, data - blocksize); } - return evp_init (ks, key, len, evp); + cast_decrypt (&ks->ks_cast, data, data); + XOR64 (data, ks->riv); } -#endif +#endif /* USE_CAST */ +#ifdef USE_AES enum cryptoerr -evp_init (struct keystate *ks, u_int8_t *key, u_int16_t len, const EVP_CIPHER *evp) +aes_init (struct keystate *ks, u_int8_t *key, u_int16_t len) { - EVP_CIPHER_CTX_init(&ks->ks_evpenc); - EVP_CIPHER_CTX_init(&ks->ks_evpdec); - - if (EVP_CIPHER_key_length(evp) != len - && !(EVP_CIPHER_flags(evp) & EVP_CIPH_VARIABLE_LENGTH)) - return EKEYLEN; - if (EVP_CipherInit(&ks->ks_evpenc, evp, key, NULL, 1) <= 0) - return EKEYLEN; - if (EVP_CipherInit(&ks->ks_evpdec, evp, key, NULL, 0) <= 0) - return EKEYLEN; + AES_set_encrypt_key (key, len << 3, &ks->ks_aes[0]); + AES_set_decrypt_key (key, len << 3, &ks->ks_aes[1]); return EOKAY; } void -evp_encrypt (struct keystate *ks, u_int8_t *data, u_int16_t len) +aes_encrypt (struct keystate *ks, u_int8_t *data, u_int16_t len) { - (void) EVP_CipherInit(&ks->ks_evpenc, NULL, NULL, ks->riv, -1); - EVP_Cipher(&ks->ks_evpenc, data, data, len); + u_int8_t iv[MAXBLK]; + + memcpy (iv, ks->riv, ks->xf->blocksize); + AES_cbc_encrypt (data, data, len, &ks->ks_aes[0], iv, AES_ENCRYPT); } void -evp_decrypt (struct keystate *ks, u_int8_t *data, u_int16_t len) +aes_decrypt (struct keystate *ks, u_int8_t *data, u_int16_t len) { - (void) EVP_CipherInit(&ks->ks_evpdec, NULL, NULL, ks->riv, -1); - EVP_Cipher(&ks->ks_evpdec, data, data, len); + u_int8_t iv[MAXBLK]; + + memcpy (iv, ks->riv, ks->xf->blocksize); + AES_cbc_encrypt (data, data, len, &ks->ks_aes[1], iv, AES_DECRYPT); } +#endif /* USE_AES */ struct crypto_xf * crypto_get (enum transform id) @@ -257,7 +360,7 @@ crypto_init_iv (struct keystate *ks, u_i { memcpy (ks->riv, buf, len); - LOG_DBG_BUF ((LOG_CRYPTO, 50, "crypto_update_iv: initialized IV", ks->riv, + LOG_DBG_BUF ((LOG_CRYPTO, 50, "crypto_init_iv: initialized IV", ks->riv, len)); } Index: sbin/isakmpd/crypto.h =================================================================== RCS file: /cvs/src/sbin/isakmpd/crypto.h,v retrieving revision 1.9 retrieving revision 1.9.2.1 diff -u -p -r1.9 -r1.9.2.1 --- sbin/isakmpd/crypto.h 28 Aug 2003 14:43:35 -0000 1.9 +++ sbin/isakmpd/crypto.h 13 Jan 2004 22:50:07 -0000 1.9.2.1 @@ -32,16 +32,72 @@ #ifndef _CRYPTO_H_ #define _CRYPTO_H_ -#include +#if defined (__APPLE__) + +#include +#ifdef USE_BLOWFISH +#include +#endif +#ifdef USE_CAST +#include +#endif + +#else + +#include +#ifdef USE_BLOWFISH +#include +#endif +#ifdef USE_CAST +#include +#endif + +#endif /* __APPLE__ */ + +#ifdef USE_AES +#include +#endif + +#define USE_32BIT +#if defined (USE_64BIT) + +#define XOR64(x,y) *(u_int64_t *)(x) ^= *(u_int64_t *)(y); +#define SET64(x,y) *(u_int64_t *)(x) = *(u_int64_t *)(y); + +#elif defined (USE_32BIT) + +#define XOR64(x,y) *(u_int32_t *)(x) ^= *(u_int32_t *)(y); \ + *(u_int32_t *)((u_int8_t *)(x) + 4) ^= *(u_int32_t *)((u_int8_t *)(y) + 4); +#define SET64(x,y) *(u_int32_t *)(x) = *(u_int32_t *)(y); \ + *(u_int32_t *)((u_int8_t *)(x) + 4) = *(u_int32_t *)((u_int8_t *)(y) + 4); + +#else + +#define XOR8(x,y,i) (x)[i] ^= (y)[i]; +#define XOR64(x,y) XOR8(x,y,0); XOR8(x,y,1); XOR8(x,y,2); XOR8(x,y,3); \ + XOR8(x,y,4); XOR8(x,y,5); XOR8(x,y,6); XOR8(x,y,7); +#define SET8(x,y,i) (x)[i] = (y)[i]; +#define SET64(x,y) SET8(x,y,0); SET8(x,y,1); SET8(x,y,2); SET8(x,y,3); \ + SET8(x,y,4); SET8(x,y,5); SET8(x,y,6); SET8(x,y,7); + +#endif /* USE_64BIT */ + +#define SET_32BIT_BIG(x,y) (x)[3]= (y); (x)[2]= (y) >> 8; \ + (x)[1] = (y) >> 16; (x)[0]= (y) >> 24; +#define GET_32BIT_BIG(x) (u_int32_t)(x)[3] | ((u_int32_t)(x)[2] << 8) | \ + ((u_int32_t)(x)[1] << 16)| ((u_int32_t)(x)[0] << 24); /* * This is standard for all block ciphers we use at the moment. - * Theoretically this could increase in future, e.g. for TwoFish. - * Keep MAXBLK uptodate + * Keep MAXBLK uptodate. */ #define BLOCKSIZE 8 -#define MAXBLK (2*BLOCKSIZE) +#ifdef USE_AES +#define MAXBLK AES_BLOCK_SIZE +#else +#define MAXBLK BLOCKSIZE +#endif struct keystate { struct crypto_xf *xf; /* Back pointer */ @@ -51,13 +107,24 @@ struct keystate { u_int8_t iv[MAXBLK]; /* Next IV to use */ u_int8_t iv2[MAXBLK]; u_int8_t *riv, *liv; - struct { - EVP_CIPHER_CTX enc, dec; - } evp; + union { + des_key_schedule desks[3]; +#ifdef USE_BLOWFISH + blf_ctx blfks; +#endif +#ifdef USE_CAST + cast_key castks; +#endif +#ifdef USE_AES + AES_KEY aesks[2]; +#endif + } keydata; }; -#define ks_evpenc evp.enc -#define ks_evpdec evp.dec +#define ks_des keydata.desks +#define ks_blf keydata.blfks +#define ks_cast keydata.castks +#define ks_aes keydata.aesks /* * Information about the cryptotransform. Index: sbin/isakmpd/exchange.c =================================================================== RCS file: /cvs/src/sbin/isakmpd/exchange.c,v retrieving revision 1.84 retrieving revision 1.84.2.1 diff -u -p -r1.84 -r1.84.2.1 --- sbin/isakmpd/exchange.c 8 Aug 2003 08:46:59 -0000 1.84 +++ sbin/isakmpd/exchange.c 13 Jan 2004 22:50:07 -0000 1.84.2.1 @@ -220,8 +220,10 @@ exchange_validate (struct message *msg) && !TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_HASH]) && !TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_SIG])) || (*pc == EXCHANGE_SCRIPT_INFO - && !TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_NOTIFY]) - && !TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_DELETE]))) + && ((!TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_NOTIFY]) + && !TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_DELETE])) + || (TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_DELETE]) + && !TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_HASH]))))) { /* Missing payload. */ LOG_DBG ((LOG_MESSAGE, 70, Index: sbin/isakmpd/ipsec.c =================================================================== RCS file: /cvs/src/sbin/isakmpd/ipsec.c,v retrieving revision 1.80 retrieving revision 1.80.2.1 diff -u -p -r1.80 -r1.80.2.1 --- sbin/isakmpd/ipsec.c 2 Sep 2003 18:15:55 -0000 1.80 +++ sbin/isakmpd/ipsec.c 13 Jan 2004 22:50:07 -0000 1.80.2.1 @@ -1013,43 +1013,6 @@ ipsec_delete_spi_list (struct sockaddr * } } -/* - * deal with a NOTIFY of INVALID_SPI - */ -static void -ipsec_invalid_spi (struct message *msg, struct payload *p) -{ - struct sockaddr *dst; - int invspisz, off; - u_int32_t spi; - u_int16_t totsiz; - u_int8_t spisz; - - /* - * get the invalid spi out of the variable sized notification data - * field, which is after the variable sized SPI field [which specifies - * the receiving entity's phase-1 SPI, not the invalid spi] - */ - totsiz = GET_ISAKMP_GEN_LENGTH (p->p); - spisz = GET_ISAKMP_NOTIFY_SPI_SZ (p->p); - off = ISAKMP_NOTIFY_SPI_OFF + spisz; - invspisz = totsiz - off; - - if (invspisz != sizeof spi) - { - LOG_DBG ((LOG_SA, 40, - "ipsec_invalid_spi: SPI size %d in INVALID_SPI " - "payload unsupported", spisz)); - return; - } - memcpy (&spi, p->p + off, sizeof spi); - - msg->transport->vtbl->get_dst (msg->transport, &dst); - - /* delete matching SPI's from this peer */ - ipsec_delete_spi_list (dst, 0, (u_int8_t *)&spi, 1, "INVALID_SPI"); -} - static int ipsec_responder (struct message *msg) { @@ -1101,9 +1064,6 @@ ipsec_responder (struct message *msg) "ipsec_responder: got NOTIFY of type %s", tag ? tag : "")); - if (type == ISAKMP_NOTIFY_INVALID_SPI) - ipsec_invalid_spi (msg, p); - p->flags |= PL_MARK; } @@ -1656,6 +1616,31 @@ ipsec_handle_leftover_payload (struct me switch (GET_ISAKMP_NOTIFY_MSG_TYPE (payload->p)) { case IPSEC_NOTIFY_INITIAL_CONTACT: + /* + * Permit INITIAL-CONTACT if + * - this is not an AGGRESSIVE mode exchange + * - it is protected by an ISAKMP SA + * + * XXX Instead of the first condition above, we could permit this + * XXX only for phase 2. In the last packet of main-mode, this + * XXX payload, while encrypted, is not part of the hash digest. + * XXX As we currently send our own INITIAL-CONTACTs at this point, + * XXX this too would need to be changed. + */ + if (msg->exchange->type == ISAKMP_EXCH_AGGRESSIVE) + { + log_print ("ipsec_handle_leftover_payload: got INITIAL-CONTACT " + "in AGGRESSIVE mode"); + return -1; + } + + if ((msg->exchange->flags & EXCHANGE_FLAG_ENCRYPT) == 0) + { + log_print ("ipsec_handle_leftover_payload: got INITIAL-CONTACT " + "without ISAKMP SA"); + return -1; + } + /* * Find out who is sending this and then delete every SA that is * ready. Exchanges will timeout themselves and then the Index: sbin/isakmpd/message.c =================================================================== RCS file: /cvs/src/sbin/isakmpd/message.c,v retrieving revision 1.61 retrieving revision 1.61.2.1 diff -u -p -r1.61 -r1.61.2.1 --- sbin/isakmpd/message.c 2 Sep 2003 18:14:52 -0000 1.61 +++ sbin/isakmpd/message.c 13 Jan 2004 22:50:07 -0000 1.61.2.1 @@ -47,10 +47,13 @@ #include "doi.h" #include "exchange.h" #include "field.h" +#include "hash.h" +#include "ipsec.h" #include "ipsec_num.h" #include "isakmp.h" #include "log.h" #include "message.h" +#include "prf.h" #include "sa.h" #include "timer.h" #include "transport.h" @@ -439,6 +442,12 @@ message_validate_delete (struct message { u_int8_t proto = GET_ISAKMP_DELETE_PROTO (p->p); struct doi *doi; + struct sa *sa, *isakmp_sa; + struct sockaddr *dst, *dst_isa; + u_int32_t nspis = GET_ISAKMP_DELETE_NSPIS (p->p); + u_int8_t *spis = (u_int8_t *)p->p + ISAKMP_DELETE_SPI_OFF; + int i; + char *addr; doi = doi_lookup (GET_ISAKMP_DELETE_DOI (p->p)); if (!doi) @@ -472,16 +481,141 @@ message_validate_delete (struct message } /* Validate the SPIs. */ + for (i = 0; i < nspis; i++) + { + /* Get ISAKMP SA protecting this message. */ + isakmp_sa = msg->isakmp_sa; + if (!isakmp_sa) + { + /* XXX should not happen? */ + log_print ("message_validate_delete: invalid spi " + "(no valid ISAKMP SA found)"); + message_free (msg); + return -1; + } + isakmp_sa->transport->vtbl->get_dst (isakmp_sa->transport, &dst_isa); + + /* Get SA to be deleted. */ + msg->transport->vtbl->get_dst (msg->transport, &dst); + if (proto == ISAKMP_PROTO_ISAKMP) + sa = sa_lookup_isakmp_sa (dst, spis + i * ISAKMP_HDR_COOKIES_LEN); + else + sa = ipsec_sa_lookup (dst, ((u_int32_t *)spis)[i], proto); + if (!sa) + { + log_print ("message_validate_delete: invalid spi " + "(no valid SA found)"); + message_free (msg); + return -1; + } + sa->transport->vtbl->get_dst (sa->transport, &dst); + + /* Destination addresses must match. */ + if (dst->sa_family != dst_isa->sa_family || + memcmp (sockaddr_addrdata (dst_isa), sockaddr_addrdata (dst), + sockaddr_addrlen (dst))) + { + sockaddr2text (dst_isa, &addr, 0); + + log_print ("message_validate_delete: invalid spi " + "(illegal delete request from %s)", addr); + free (addr); + message_free (msg); + return -1; + } + } return 0; } /* - * Validate the hash payload P in message MSG. */ + * Validate the hash payload P in message MSG. + * XXX Currently hash payloads are processed by the particular exchanges, + * except INFORMATIONAL. This should be actually done here. + */ static int message_validate_hash (struct message *msg, struct payload *p) { - /* XXX Not implemented yet. */ + struct sa *isakmp_sa = msg->isakmp_sa; + struct ipsec_sa *isa; + struct hash *hash; + struct payload *hashp = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_HASH]); + struct prf *prf; + u_int8_t *comp_hash, *rest; + u_int8_t message_id[ISAKMP_HDR_MESSAGE_ID_LEN]; + size_t rest_len; + + if (msg->exchange) /* active exchange validates hash payload. */ + return 0; + + if (isakmp_sa == NULL) + { + log_print ("message_validate_hash: invalid hash information"); + return -1; + } + + isa = isakmp_sa->data; + hash = hash_get (isa->hash); + + if (hash == NULL) + { + log_print ("message_validate_hash: invalid hash information"); + return -1; + } + + /* If no SKEYID_a, we can not do anything (should not happen). */ + if (!isa->skeyid_a) + { + log_print ("message_validate_hash: invalid hash information"); + return -1; + } + + /* Allocate the prf and start calculating our HASH(1). */ + LOG_DBG_BUF ((LOG_MISC, 90, "message_validate_hash: SKEYID_a", isa->skeyid_a, + isa->skeyid_len)); + prf = prf_alloc (isa->prf_type, hash->type, isa->skeyid_a, isa->skeyid_len); + if (!prf) + return -1; + + comp_hash = (u_int8_t *)malloc (hash->hashsize); + if (!comp_hash) + { + log_error ("message_validate_hash: malloc (%lu) failed", + (unsigned long)hash->hashsize); + prf_free (prf); + return -1; + } + + /* This is not an active exchange. */ + GET_ISAKMP_HDR_MESSAGE_ID (msg->iov[0].iov_base, message_id); + + prf->Init (prf->prfctx); + LOG_DBG_BUF ((LOG_MISC, 90, "message_validate_hash: message_id", + message_id, ISAKMP_HDR_MESSAGE_ID_LEN)); + prf->Update (prf->prfctx, message_id, ISAKMP_HDR_MESSAGE_ID_LEN); + rest = hashp->p + GET_ISAKMP_GEN_LENGTH (hashp->p); + rest_len = (GET_ISAKMP_HDR_LENGTH (msg->iov[0].iov_base) + - (rest - (u_int8_t*)msg->iov[0].iov_base)); + LOG_DBG_BUF ((LOG_MISC, 90, "message_validate_hash: payloads after HASH(1)", + rest, rest_len)); + prf->Update (prf->prfctx, rest, rest_len); + prf->Final (comp_hash, prf->prfctx); + prf_free (prf); + + if (memcmp (hashp->p + ISAKMP_HASH_DATA_OFF, comp_hash, hash->hashsize)) + { + log_print ("message_validate_hash: invalid hash value for %s payload", + TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_DELETE]) + ? "DELETE" : "NOTIFY"); + message_drop (msg, ISAKMP_NOTIFY_INVALID_HASH_INFORMATION, 0, 1, 0); + free (comp_hash); + return -1; + } + free (comp_hash); + + /* Mark the HASH as handled. */ + hashp->flags |= PL_MARK; + return 0; } @@ -1217,10 +1351,12 @@ message_recv (struct message *msg) && (flags & ISAKMP_FLAGS_COMMIT)) msg->exchange->flags |= EXCHANGE_FLAG_HE_COMMITTED; - /* Require encryption for any phase 2 message. XXX Always? */ - if (msg->exchange->phase == 2 && (flags & ISAKMP_FLAGS_ENC) == 0) + /* Require encryption as soon as we have the keystate for it. */ + if ((flags & ISAKMP_FLAGS_ENC) == 0 && + (msg->exchange->phase == 2 || msg->exchange->keystate)) { - log_print ("message_recv: cleartext phase 2 message"); + log_print ("message_recv: cleartext phase %d message", + msg->exchange->phase); message_drop (msg, ISAKMP_NOTIFY_INVALID_FLAGS, 0, 1, 1); return -1; }