diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:49:46 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:49:46 +0000 |
commit | 50b37d4a27d3295a29afca2286f1a5a086142cec (patch) | |
tree | 9212f763934ee090ef72d823f559f52ce387f268 /src/modules/rlm_eap/libeap | |
parent | Initial commit. (diff) | |
download | freeradius-upstream/3.2.1+dfsg.tar.xz freeradius-upstream/3.2.1+dfsg.zip |
Adding upstream version 3.2.1+dfsg.upstream/3.2.1+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/modules/rlm_eap/libeap')
-rw-r--r-- | src/modules/rlm_eap/libeap/all.mk | 10 | ||||
-rw-r--r-- | src/modules/rlm_eap/libeap/comp128.c | 460 | ||||
-rw-r--r-- | src/modules/rlm_eap/libeap/comp128.h | 11 | ||||
-rw-r--r-- | src/modules/rlm_eap/libeap/eap_chbind.c | 290 | ||||
-rw-r--r-- | src/modules/rlm_eap/libeap/eap_chbind.h | 64 | ||||
-rw-r--r-- | src/modules/rlm_eap/libeap/eap_sim.h | 122 | ||||
-rw-r--r-- | src/modules/rlm_eap/libeap/eap_tls.c | 1206 | ||||
-rw-r--r-- | src/modules/rlm_eap/libeap/eap_tls.h | 109 | ||||
-rw-r--r-- | src/modules/rlm_eap/libeap/eap_types.h | 162 | ||||
-rw-r--r-- | src/modules/rlm_eap/libeap/eapclient.h | 8 | ||||
-rw-r--r-- | src/modules/rlm_eap/libeap/eapcommon.c | 401 | ||||
-rw-r--r-- | src/modules/rlm_eap/libeap/eapcrypto.c | 301 | ||||
-rw-r--r-- | src/modules/rlm_eap/libeap/eapsimlib.c | 508 | ||||
-rw-r--r-- | src/modules/rlm_eap/libeap/fips186prf.c | 270 | ||||
-rw-r--r-- | src/modules/rlm_eap/libeap/mppe_keys.c | 384 |
15 files changed, 4306 insertions, 0 deletions
diff --git a/src/modules/rlm_eap/libeap/all.mk b/src/modules/rlm_eap/libeap/all.mk new file mode 100644 index 0000000..6a32129 --- /dev/null +++ b/src/modules/rlm_eap/libeap/all.mk @@ -0,0 +1,10 @@ +TARGET := libfreeradius-eap.a + +SOURCES := eapcommon.c eapcrypto.c eap_chbind.c eapsimlib.c fips186prf.c comp128.c +ifneq (${OPENSSL_LIBS},) +SOURCES += eap_tls.c mppe_keys.c +endif + +SRC_CFLAGS := -DEAPLIB + +SRC_INCDIRS := . .. diff --git a/src/modules/rlm_eap/libeap/comp128.c b/src/modules/rlm_eap/libeap/comp128.c new file mode 100644 index 0000000..e624877 --- /dev/null +++ b/src/modules/rlm_eap/libeap/comp128.c @@ -0,0 +1,460 @@ +/* + * This program is is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * $Id$ + * @file comp128.c + * @brief Implementations of comp128v1, comp128v2, comp128v3 algorithms + * + * Comp128v1 was inspired by code from: + * Marc Briceno <marc@scard.org>, Ian Goldberg <iang@cs.berkeley.edu>, + * and David Wagner <daw@cs.berkeley.edu> + * + * But it has been fully rewritten (Sylvain Munaut <tnt@246tNt.com>) from various PDFs found online + * describing the algorithm because the licence of the code referenced above was unclear. + * A comment snippet from the original code is included below, it describes where the doc came + * from and how the algorithm was reverse engineered. + * + * Comp128v2 & v3 is a port of the python code from: + * http://www.hackingprojects.net/ + * The author of the original code is Tamas Jos <tamas.jos@skelsec.com> + * + * @note The above GPL license only applies to comp128v1, the license for comp128v2 and comp128v3 is unknown. + * + * @copyright 2013 The FreeRADIUS server project + * @copyright 2013 Hacking projects [http://www.hackingprojects.net/] + * @copyright 2009 Sylvain Munaut <tnt@246tNt.com> + */ + +#include "comp128.h" +#include <stdio.h> +/* 512 bytes */ +static uint8_t const comp128v1_t0[] = { + 102, 177, 186, 162, 2, 156, 112, 75, 55, 25, 8, 12, 251, 193, 246, 188, + 109, 213, 151, 53, 42, 79, 191, 115, 233, 242, 164, 223, 209, 148, 108, 161, + 252, 37, 244, 47, 64, 211, 6, 237, 185, 160, 139, 113, 76, 138, 59, 70, + 67, 26, 13, 157, 63, 179, 221, 30, 214, 36, 166, 69, 152, 124, 207, 116, + 247, 194, 41, 84, 71, 1, 49, 14, 95, 35, 169, 21, 96, 78, 215, 225, + 182, 243, 28, 92, 201, 118, 4, 74, 248, 128, 17, 11, 146, 132, 245, 48, + 149, 90, 120, 39, 87, 230, 106, 232, 175, 19, 126, 190, 202, 141, 137, 176, + 250, 27, 101, 40, 219, 227, 58, 20, 51, 178, 98, 216, 140, 22, 32, 121, + 61, 103, 203, 72, 29, 110, 85, 212, 180, 204, 150, 183, 15, 66, 172, 196, + 56, 197, 158, 0, 100, 45, 153, 7, 144, 222, 163, 167, 60, 135, 210, 231, + 174, 165, 38, 249, 224, 34, 220, 229, 217, 208, 241, 68, 206, 189, 125, 255, + 239, 54, 168, 89, 123, 122, 73, 145, 117, 234, 143, 99, 129, 200, 192, 82, + 104, 170, 136, 235, 93, 81, 205, 173, 236, 94, 105, 52, 46, 228, 198, 5, + 57, 254, 97, 155, 142, 133, 199, 171, 187, 50, 65, 181, 127, 107, 147, 226, + 184, 218, 131, 33, 77, 86, 31, 44, 88, 62, 238, 18, 24, 43, 154, 23, + 80, 159, 134, 111, 9, 114, 3, 91, 16, 130, 83, 10, 195, 240, 253, 119, + 177, 102, 162, 186, 156, 2, 75, 112, 25, 55, 12, 8, 193, 251, 188, 246, + 213, 109, 53, 151, 79, 42, 115, 191, 242, 233, 223, 164, 148, 209, 161, 108, + 37, 252, 47, 244, 211, 64, 237, 6, 160, 185, 113, 139, 138, 76, 70, 59, + 26, 67, 157, 13, 179, 63, 30, 221, 36, 214, 69, 166, 124, 152, 116, 207, + 194, 247, 84, 41, 1, 71, 14, 49, 35, 95, 21, 169, 78, 96, 225, 215, + 243, 182, 92, 28, 118, 201, 74, 4, 128, 248, 11, 17, 132, 146, 48, 245, + 90, 149, 39, 120, 230, 87, 232, 106, 19, 175, 190, 126, 141, 202, 176, 137, + 27, 250, 40, 101, 227, 219, 20, 58, 178, 51, 216, 98, 22, 140, 121, 32, + 103, 61, 72, 203, 110, 29, 212, 85, 204, 180, 183, 150, 66, 15, 196, 172, + 197, 56, 0, 158, 45, 100, 7, 153, 222, 144, 167, 163, 135, 60, 231, 210, + 165, 174, 249, 38, 34, 224, 229, 220, 208, 217, 68, 241, 189, 206, 255, 125, + 54, 239, 89, 168, 122, 123, 145, 73, 234, 117, 99, 143, 200, 129, 82, 192, + 170, 104, 235, 136, 81, 93, 173, 205, 94, 236, 52, 105, 228, 46, 5, 198, + 254, 57, 155, 97, 133, 142, 171, 199, 50, 187, 181, 65, 107, 127, 226, 147, + 218, 184, 33, 131, 86, 77, 44, 31, 62, 88, 18, 238, 43, 24, 23, 154, + 159, 80, 111, 134, 114, 9, 91, 3, 130, 16, 10, 83, 240, 195, 119, 253}; + +/* 256 bytes */ +static uint8_t const comp128v1_t1[] = { + 19, 11, 80, 114, 43, 1, 69, 94, 39, 18, 127, 117, 97, 3, 85, 43, + 27, 124, 70, 83, 47, 71, 63, 10, 47, 89, 79, 4, 14, 59, 11, 5, + 35, 107, 103, 68, 21, 86, 36, 91, 85, 126, 32, 50, 109, 94, 120, 6, + 53, 79, 28, 45, 99, 95, 41, 34, 88, 68, 93, 55, 110, 125, 105, 20, + 90, 80, 76, 96, 23, 60, 89, 64, 121, 56, 14, 74, 101, 8, 19, 78, + 76, 66, 104, 46, 111, 50, 32, 3, 39, 0, 58, 25, 92, 22, 18, 51, + 57, 65, 119, 116, 22, 109, 7, 86, 59, 93, 62, 110, 78, 99, 77, 67, + 12, 113, 87, 98, 102, 5, 88, 33, 38, 56, 23, 8, 75, 45, 13, 75, + 95, 63, 28, 49, 123, 120, 20, 112, 44, 30, 15, 98, 106, 2, 103, 29, + 82, 107, 42, 124, 24, 30, 41, 16, 108, 100, 117, 40, 73, 40, 7, 114, + 82, 115, 36, 112, 12, 102, 100, 84, 92, 48, 72, 97, 9, 54, 55, 74, + 113, 123, 17, 26, 53, 58, 4, 9, 69, 122, 21, 118, 42, 60, 27, 73, + 118, 125, 34, 15, 65, 115, 84, 64, 62, 81, 70, 1, 24, 111, 121, 83, + 104, 81, 49, 127, 48, 105, 31, 10, 6, 91, 87, 37, 16, 54, 116, 126, + 31, 38, 13, 0, 72, 106, 77, 61, 26, 67, 46, 29, 96, 37, 61, 52, + 101, 17, 44, 108, 71, 52, 66, 57, 33, 51, 25, 90, 2, 119, 122, 35}; + +/* 128 bytes */ +static uint8_t const comp128v1_t2[] = { + 52, 50, 44, 6, 21, 49, 41, 59, 39, 51, 25, 32, 51, 47, 52, 43, + 37, 4, 40, 34, 61, 12, 28, 4, 58, 23, 8, 15, 12, 22, 9, 18, + 55, 10, 33, 35, 50, 1, 43, 3, 57, 13, 62, 14, 7, 42, 44, 59, + 62, 57, 27, 6, 8, 31, 26, 54, 41, 22, 45, 20, 39, 3, 16, 56, + 48, 2, 21, 28, 36, 42, 60, 33, 34, 18, 0, 11, 24, 10, 17, 61, + 29, 14, 45, 26, 55, 46, 11, 17, 54, 46, 9, 24, 30, 60, 32, 0, + 20, 38, 2, 30, 58, 35, 1, 16, 56, 40, 23, 48, 13, 19, 19, 27, + 31, 53, 47, 38, 63, 15, 49, 5, 37, 53, 25, 36, 63, 29, 5, 7}; + +/* 64 bytes */ +static uint8_t const comp128v1_t3[] = { + 1, 5, 29, 6, 25, 1, 18, 23, 17, 19, 0, 9, 24, 25, 6, 31, + 28, 20, 24, 30, 4, 27, 3, 13, 15, 16, 14, 18, 4, 3, 8, 9, + 20, 0, 12, 26, 21, 8, 28, 2, 29, 2, 15, 7, 11, 22, 14, 10, + 17, 21, 12, 30, 26, 27, 16, 31, 11, 7, 13, 23, 10, 5, 22, 19}; + +/* 32 bytes */ +static uint8_t const comp128v1_t4[] = { + 15, 12, 10, 4, 1, 14, 11, 7, 5, 0, 14, 7, 1, 2, 13, 8, + 10, 3, 4, 9, 6, 0, 3, 2, 5, 6, 8, 9, 11, 13, 15, 12}; + +static uint8_t const *_comp128_table[] = { comp128v1_t0, comp128v1_t1, comp128v1_t2, comp128v1_t3, comp128v1_t4 }; + +/* 256 bytes */ +static uint8_t const comp128v23_t0[] = { + 197, 235, 60, 151, 98, 96, 3, 100, 248, 118, 42, 117, 172, 211, 181, 203, + 61, 126, 156, 87, 149, 224, 55, 132, 186, 63, 238, 255, 85, 83, 152, 33, + 160, 184, 210, 219, 159, 11, 180, 194, 130, 212, 147, 5, 215, 92, 27, 46, + 113, 187, 52, 25, 185, 79, 221, 48, 70, 31, 101, 15, 195, 201, 50, 222, + 137, 233, 229, 106, 122, 183, 178, 177, 144, 207, 234, 182, 37, 254, 227, 231, + 54, 209, 133, 65, 202, 69, 237, 220, 189, 146, 120, 68, 21, 125, 38, 30, + 2, 155, 53, 196, 174, 176, 51, 246, 167, 76, 110, 20, 82, 121, 103, 112, + 56, 173, 49, 217, 252, 0, 114, 228, 123, 12, 93, 161, 253, 232, 240, 175, + 67, 128, 22, 158, 89, 18, 77, 109, 190, 17, 62, 4, 153, 163, 59, 145, + 138, 7, 74, 205, 10, 162, 80, 45, 104, 111, 150, 214, 154, 28, 191, 169, + 213, 88, 193, 198, 200, 245, 39, 164, 124, 84, 78, 1, 188, 170, 23, 86, + 226, 141, 32, 6, 131, 127, 199, 40, 135, 16, 57, 71, 91, 225, 168, 242, + 206, 97, 166, 44, 14, 90, 236, 239, 230, 244, 223, 108, 102, 119, 148, 251, + 29, 216, 8, 9, 249, 208, 24, 105, 94, 34, 64, 95, 115, 72, 134, 204, + 43, 247, 243, 218, 47, 58, 73, 107, 241, 179, 116, 66, 36, 143, 81, 250, + 139, 19, 13, 142, 140, 129, 192, 99, 171, 157, 136, 41, 75, 35, 165, 26}; + +/* 256 bytes */ +static uint8_t const comp128v23_t1[] = { + 170, 42, 95, 141, 109, 30, 71, 89, 26, 147, 231, 205, 239, 212, 124, 129, + 216, 79, 15, 185, 153, 14, 251, 162, 0, 241, 172, 197, 43, 10, 194, 235, + 6, 20, 72, 45, 143, 104, 161, 119, 41, 136, 38, 189, 135, 25, 93, 18, + 224, 171, 252, 195, 63, 19, 58, 165, 23, 55, 133, 254, 214, 144, 220, 178, + 156, 52, 110, 225, 97, 183, 140, 39, 53, 88, 219, 167, 16, 198, 62, 222, + 76, 139, 175, 94, 51, 134, 115, 22, 67, 1, 249, 217, 3, 5, 232, 138, + 31, 56, 116, 163, 70, 128, 234, 132, 229, 184, 244, 13, 34, 73, 233, 154, + 179, 131, 215, 236, 142, 223, 27, 57, 246, 108, 211, 8, 253, 85, 66, 245, + 193, 78, 190, 4, 17, 7, 150, 127, 152, 213, 37, 186, 2, 243, 46, 169, + 68, 101, 60, 174, 208, 158, 176, 69, 238, 191, 90, 83, 166, 125, 77, 59, + 21, 92, 49, 151, 168, 99, 9, 50, 146, 113, 117, 228, 65, 230, 40, 82, + 54, 237, 227, 102, 28, 36, 107, 24, 44, 126, 206, 201, 61, 114, 164, 207, + 181, 29, 91, 64, 221, 255, 48, 155, 192, 111, 180, 210, 182, 247, 203, 148, + 209, 98, 173, 11, 75, 123, 250, 118, 32, 47, 240, 202, 74, 177, 100, 80, + 196, 33, 248, 86, 157, 137, 120, 130, 84, 204, 122, 81, 242, 188, 200, 149, + 226, 218, 160, 187, 106, 35, 87, 105, 96, 145, 199, 159, 12, 121, 103, 112}; + +static inline void _comp128_compression_round(uint8_t *x, int n, const uint8_t *tbl) +{ + int i, j, m, a, b, y, z; + m = 4 - n; + for (i = 0; i < (1 << n); i++) { + for (j = 0; j < (1 << m); j++) { + a = j + i * (2 << m); + b = a + (1 << m); + y = (x[a] + (x[b] << 1)) & ((32 << m) - 1); + z = ((x[a] << 1) + x[b]) & ((32 << m) - 1); + x[a] = tbl[y]; + x[b] = tbl[z]; + } + } +} + +static inline void _comp128_compression(uint8_t *x) +{ + int n; + for (n = 0; n < 5; n++) { + _comp128_compression_round(x, n, _comp128_table[n]); + } +} + +static inline void _comp128_bitsfrombytes(uint8_t *x, uint8_t *bits) +{ + int i; + + memset(bits, 0x00, 128); + for (i = 0; i < 128; i++) { + if (x[i >> 2] & (1 << (3 - (i & 3)))) { + bits[i] = 1; + } + } +} + +static inline void _comp128_permutation(uint8_t *x, uint8_t *bits) +{ + int i; + memset(&x[16], 0x00, 16); + for (i = 0; i < 128; i++) { + x[(i >> 3) + 16] |= bits[(i * 17) & 127] << (7 - (i & 7)); + } +} + +/** Calculate comp128v1 sres and kc from ki and rand + * + * This code derived from a leaked document from the GSM standards. + * Some missing pieces were filled in by reverse-engineering a working SIM. + * We have verified that this is the correct COMP128 algorithm. + * + * The first page of the document identifies it as + * _Technical Information: GSM System Security Study_. + * 10-1617-01, 10th June 1988. + * The bottom of the title page is marked + * Racal Research Ltd. + * Worton Drive, Worton Grange Industrial Estate, + * Reading, Berks. RG2 0SB, England. + * Telephone: Reading (0734) 868601 Telex: 847152 + * The relevant bits are in Part I, Section 20 (pages 66--67). Enjoy! + * + * Note: There are three typos in the spec (discovered by reverse-engineering). + * First, "z = (2 * x[n] + x[n]) mod 2^(9-j)" should clearly read + * "z = (2 * x[m] + x[n]) mod 2^(9-j)". + * Second, the "k" loop in the "Form bits from bytes" section is severely + * botched: the k index should run only from 0 to 3, and clearly the range + * on "the (8-k)th bit of byte j" is also off (should be 0..7, not 1..8, + * to be consistent with the subsequent section). + * Third, SRES is taken from the first 8 nibbles of x[], not the last 8 as + * claimed in the document. (And the document doesn't specify how Kc is + * derived, but that was also easily discovered with reverse engineering.) + * All of these typos have been corrected in the following code. + * + * @param[out] sres 4 byte value derived from ki and rand. + * @param[out] kc 12 byte value derived from ki and rand. + * @param[in] ki known only by the SIM and AuC (us in this case). + * @param[in] rand 16 bytes of randomness. + */ +void comp128v1(uint8_t *sres, uint8_t *kc, uint8_t const *ki, uint8_t const *rand) +{ + int i; + uint8_t x[32], bits[128]; + + /* x[16-31] = RAND */ + memcpy(&x[16], rand, 16); + + /* + * Round 1-7 + */ + for (i=0; i < 7; i++) { + /* x[0-15] = Ki */ + memcpy(x, ki, 16); + + /* Compression */ + _comp128_compression(x); + + /* FormBitFromBytes */ + _comp128_bitsfrombytes(x, bits); + + /* Permutation */ + _comp128_permutation(x, bits); + } + + /* + * Round 8 (final) + * x[0-15] = Ki + */ + memcpy(x, ki, 16); + + /* Compression */ + _comp128_compression(x); + + /* Output stage */ + for (i = 0; i < 8; i += 2) { + sres[i >> 1] = x[i] << 4 | x[i + 1]; + } + + for (i = 0; i < 12; i += 2) { + kc[i>>1] = (x[i + 18] << 6) | + (x[i + 19] << 2) | + (x[i + 20] >> 2); + } + + kc[6] = (x[30] << 6) | (x[31] << 2); + kc[7] = 0; +} + +static void _comp128v23(uint8_t *rand, uint8_t const *kxor) +{ + uint8_t temp[16]; + uint8_t km_rm[32]; + + int j, i, k, z; + + memset(&temp, 0, sizeof(temp)); + memcpy(km_rm, rand, 16); + memcpy(km_rm + 16, kxor, 16); + memset(rand, 0, 16); + + for (i = 0; i < 5; i++) { + j = 0; + + for (z = 0; z < 16; z++) { + temp[z] = comp128v23_t0[comp128v23_t1[km_rm[16 + z]] ^ km_rm[z]]; + } + + while ((1 << i) > j) { + k = 0; + + while ((1 << (4 - i)) > k) { + km_rm[(((2 * k) + 1) << i) + j] = + comp128v23_t0[comp128v23_t1[temp[(k << i) + j]] ^ (km_rm[(k << i) + 16 + j])]; + km_rm[(k << (i + 1)) + j] = temp[(k << i) + j]; + k++; + } + j++; + } + } + + for (i = 0; i < 16; i++) { + for (j = 0; j < 8; j++) { + rand[i] = rand[i] ^ (((km_rm[(19 * (j + 8 * i) + 19) % 256 / 8] >> (3 * j + 3) % 8) & 1) << j); + } + } +} + +/** Calculate comp128v2 or comp128v3 sres and kc from ki and rand + * + * @param[out] sres 4 byte value derived from ki and rand. + * @param[out] kc 8 byte value derived from ki and rand. + * @param[in] ki known only by the SIM and AuC (us in this case). + * @param[in] rand 16 bytes of randomness. + * @param[in] v2 if true we use version comp128-2 else we use comp128-3. + + */ +void comp128v23(uint8_t *sres, uint8_t *kc, uint8_t const *ki, uint8_t const *rand, bool v2) +{ + uint8_t k_mix[16]; + uint8_t rand_mix[16]; + uint8_t katyvasz[16]; + uint8_t buffer[16]; + + /* Every day IM suffling... */ + int i; + + for (i = 0; i < 8; i++) { + k_mix[i] = ki[15 - i]; + k_mix[15 - i] = ki[i]; + } + + for (i = 0; i < 8; i++) { + rand_mix[i] = rand[15 - i]; + rand_mix[15 - i] = rand[i]; + } + + for (i = 0; i < 16; i++) { + katyvasz[i] = k_mix[i] ^ rand_mix[i]; + } + + for (i = 0; i < 8; i++) { + _comp128v23(rand_mix, katyvasz); + } + + for (i = 0; i < 16; i++) { + buffer[i] = rand_mix[15 - i]; + } + + if (v2) { + buffer[15] = 0x00; + buffer[14] = 4 * (buffer[14] >> 2); + } + + for (i = 0; i < 4; i++) { + buffer[8 + i - 4] = buffer[8 + i]; + buffer[8 + i] = buffer[8 + i + 4]; + } + + /* + * The algorithm uses 16 bytes until this point, but only 12 bytes are effective + * also 12 bytes coming out from the SIM card. + */ + memcpy(sres, buffer, 4); + memcpy(kc, buffer + 4, 8); +} + +#if 0 +#include <stdlib.h> +#include <ctype.h> +static int hextoint(char x) +{ + x = toupper(x); + if (x >= 'A' && x <= 'F') { + return x-'A' + 10; + } else if (x >= '0' && x <= '9') { + return x-'0'; + } + + fprintf(stderr, "Bad input.\n"); + + exit(1); +} + +int main(int argc, char **argv) +{ + uint8_t rand[16], key[16], sres[4], kc[8]; + int version; + int i; + + if ((argc != 4) || + (strlen(argv[1]) != 34) || (strlen(argv[2]) != 34) || + (strncmp(argv[1], "0x", 2) != 0) || (strncmp(argv[2], "0x", 2) != 0) || + !(version = atoi(argv[3]))) { + error: + fprintf(stderr, "Usage: %s 0x<key> 0x<rand> [1|2|3]\n", argv[0]); + exit(1); + } + + for (i = 0; i < 16; i++) { + key[i] = (hextoint(argv[1][(2 * i) + 2]) << 4) | hextoint(argv[1][(2 * i) + 3]); + } + + for (i = 0; i < 16; i++) { + rand[i] = (hextoint(argv[2][(2 * i) + 2]) << 4) | hextoint(argv[2][(2 * i) + 3]); + } + + switch (version) { + case 3: + comp128v23(sres, kc, key, rand, false); + break; + case 2: + comp128v23(sres, kc, key, rand, true); + break; + case 1: + comp128v1(sres, kc, key, rand); + break; + default: + fprintf(stderr, "Invalid version, must be 1,2 or 3"); + goto error; + } + + /* Output in vector format <Ki>,<rand>,<sres><Kc> */ + for (i = 0; i < 16; i++) { + printf("%02X", key[i]); + } + printf(","); + for (i = 0; i < 16; i++) { + printf("%02X", rand[i]); + } + printf(","); + for (i = 0; i < 4; i++) { + printf("%02X", sres[i]); + } + for (i = 0; i < 8; i++) { + printf("%02X", kc[i]); + } + printf("\n"); + + return 0; +} +#endif diff --git a/src/modules/rlm_eap/libeap/comp128.h b/src/modules/rlm_eap/libeap/comp128.h new file mode 100644 index 0000000..4cd2199 --- /dev/null +++ b/src/modules/rlm_eap/libeap/comp128.h @@ -0,0 +1,11 @@ +#ifndef _COMP128_H +#define _COMP128_H + +#include <string.h> +#include <stdint.h> +#include <stdbool.h> + +void comp128v1(uint8_t *sres, uint8_t *kc, const uint8_t *ki, const uint8_t *rand); +void comp128v23(uint8_t *sres, uint8_t *kc, uint8_t const *ki, uint8_t const *rand, bool v2); + +#endif diff --git a/src/modules/rlm_eap/libeap/eap_chbind.c b/src/modules/rlm_eap/libeap/eap_chbind.c new file mode 100644 index 0000000..21b2584 --- /dev/null +++ b/src/modules/rlm_eap/libeap/eap_chbind.c @@ -0,0 +1,290 @@ +/* + * eap_chbind.c + * + * Version: $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * Copyright 2014 Network RADIUS SARL + * Copyright 2014 The FreeRADIUS server project + */ + + +RCSID("$Id$") + +#include "eap_chbind.h" + +static bool chbind_build_response(REQUEST *request, CHBIND_REQ *chbind) +{ + int length; + size_t total; + uint8_t *ptr, *end; + VALUE_PAIR const *vp; + vp_cursor_t cursor; + + total = 0; + for (vp = fr_cursor_init(&cursor, &request->reply->vps); + vp != NULL; + vp = fr_cursor_next(&cursor)) { + /* + * Skip things which shouldn't be in channel bindings. + */ + if (vp->da->flags.encrypt != FLAG_ENCRYPT_NONE) continue; + if (!vp->da->vendor && (vp->da->attr == PW_MESSAGE_AUTHENTICATOR)) continue; + + total += 2 + vp->vp_length; + } + + /* + * No attributes: just send a 1-byte response code. + */ + if (!total) { + ptr = talloc_zero_array(chbind, uint8_t, 1); + } else { + ptr = talloc_zero_array(chbind, uint8_t, total + 4); + } + if (!ptr) return false; + chbind->response = (chbind_packet_t *) ptr; + + /* + * Set the response code. Default to "fail" if none was + * specified. + */ + vp = fr_pair_find_by_num(request->config, PW_CHBIND_RESPONSE_CODE, 0, TAG_ANY); + if (vp) { + ptr[0] = vp->vp_integer; + } else { + ptr[0] = CHBIND_CODE_FAILURE; + } + + if (!total) return true; /* nothing to encode */ + + /* Write the length field into the header */ + ptr[1] = (total >> 8) & 0xff; + ptr[2] = total & 0xff; + ptr[3] = CHBIND_NSID_RADIUS; + + RDEBUG("Sending chbind response: code %i", (int )(ptr[0])); + rdebug_pair_list(L_DBG_LVL_1, request, request->reply->vps, NULL); + + /* Encode the chbind attributes into the response */ + ptr += 4; + end = ptr + total; + for (vp = fr_cursor_init(&cursor, &request->reply->vps); + vp != NULL; + vp = fr_cursor_next(&cursor)) { + /* + * Skip things which shouldn't be in channel bindings. + */ + if (vp->da->flags.encrypt != FLAG_ENCRYPT_NONE) continue; + if (!vp->da->vendor && (vp->da->attr == PW_MESSAGE_AUTHENTICATOR)) continue; + + length = rad_vp2attr(NULL, NULL, NULL, &vp, ptr, end - ptr); + if (length < 0) continue; + ptr += length; + } + + return true; +} + + +/* + * Parse channel binding packet to obtain data for a specific + * NSID. + * + * See: + * http://tools.ietf.org/html/draft-ietf-emu-chbind-13#section-5.3.2 + */ +static size_t chbind_get_data(chbind_packet_t const *packet, + int desired_nsid, + uint8_t const **data) +{ + uint8_t const *ptr; + uint8_t const *end; + + if (packet->code != CHBIND_CODE_REQUEST) { + return 0; + } + + ptr = (uint8_t const *) packet; + end = ptr + talloc_array_length((uint8_t const *) packet); + + ptr++; /* skip the code at the start of the packet */ + while (ptr < end) { + uint8_t nsid; + size_t length; + + /* + * Need room for length(2) + NSID + data. + */ + if ((end - ptr) < 4) return 0; + + length = (ptr[0] << 8) | ptr[1]; + if (length == 0) return 0; + + if ((ptr + length + 3) > end) return 0; + + nsid = ptr[2]; + if (nsid == desired_nsid) { + ptr += 3; + *data = ptr; + return length; + } + + ptr += 3 + length; + } + + return 0; +} + + +PW_CODE chbind_process(REQUEST *request, CHBIND_REQ *chbind) +{ + PW_CODE rcode; + REQUEST *fake = NULL; + VALUE_PAIR *vp = NULL; + uint8_t const *attr_data; + size_t data_len = 0; + + /* check input parameters */ + rad_assert((request != NULL) && + (chbind != NULL) && + (chbind->request != NULL) && + (chbind->response == NULL)); + + /* Set-up the fake request */ + fake = request_alloc_fake(request); + fr_pair_make(fake->packet, &fake->packet->vps, "Freeradius-Proxied-To", "127.0.0.1", T_OP_EQ); + + /* Add the username to the fake request */ + if (chbind->username) { + vp = fr_pair_copy(fake->packet, chbind->username); + fr_pair_add(&fake->packet->vps, vp); + fake->username = vp; + } + + /* + * Maybe copy the State over, too? + */ + + /* Add the channel binding attributes to the fake packet */ + data_len = chbind_get_data(chbind->request, CHBIND_NSID_RADIUS, &attr_data); + if (data_len) { + rad_assert(data_len <= talloc_array_length((uint8_t const *) chbind->request)); + + while (data_len > 0) { + int attr_len = rad_attr2vp(fake->packet, NULL, NULL, NULL, attr_data, data_len, &vp); + if (attr_len <= 0) { + /* If radaddr2vp fails, return NULL string for + channel binding response */ + talloc_free(fake); + return PW_CODE_ACCESS_ACCEPT; + } + if (vp) { + fr_pair_add(&fake->packet->vps, vp); + } + attr_data += attr_len; + data_len -= attr_len; + } + } + + /* + * Set virtual server based on configuration for channel + * bindings, this is hard-coded for now. + */ + fake->server = "channel_bindings"; + fake->packet->code = PW_CODE_ACCESS_REQUEST; + + switch (rad_virtual_server(fake)) { + /* If rad_authenticate succeeded, build a reply */ + case RLM_MODULE_OK: + case RLM_MODULE_HANDLED: + if (chbind_build_response(fake, chbind)) { + rcode = PW_CODE_ACCESS_ACCEPT; + break; + } + /* FALL-THROUGH */ + + /* If we got any other response from rad_authenticate, it maps to a reject */ + default: + rcode = PW_CODE_ACCESS_REJECT; + break; + } + + talloc_free(fake); + + return rcode; +} + +/* + * Handles multiple EAP-channel-binding Message attrs + * ie concatenates all to get the complete EAP-channel-binding packet. + */ +chbind_packet_t *eap_chbind_vp2packet(TALLOC_CTX *ctx, VALUE_PAIR *vps) +{ + size_t length; + uint8_t *ptr; + VALUE_PAIR *first, *vp; + chbind_packet_t *packet; + vp_cursor_t cursor; + + first = fr_pair_find_by_num(vps, PW_UKERNA_CHBIND, VENDORPEC_UKERNA, TAG_ANY); + if (!first) return NULL; + + /* + * Compute the total length of the channel binding data. + */ + length = 0; + fr_cursor_init(&cursor, &first); + while ((vp = fr_cursor_next_by_num(&cursor, PW_UKERNA_CHBIND, VENDORPEC_UKERNA, TAG_ANY))) { + length += vp->vp_length; + } + + if (length < 4) { + DEBUG("Invalid length %u for channel binding data", (unsigned int) length); + return NULL; + } + + /* + * Now that we know the length, allocate memory for the packet. + */ + ptr = talloc_zero_array(ctx, uint8_t, length); + if (!ptr) return NULL; + + /* + * Copy the data over to our packet. + */ + packet = (chbind_packet_t *) ptr; + fr_cursor_init(&cursor, &first); + while ((vp = fr_cursor_next_by_num(&cursor, PW_UKERNA_CHBIND, VENDORPEC_UKERNA, TAG_ANY))) { + memcpy(ptr, vp->vp_octets, vp->vp_length); + ptr += vp->vp_length; + } + + return packet; +} + +VALUE_PAIR *eap_chbind_packet2vp(RADIUS_PACKET *packet, chbind_packet_t *chbind) +{ + VALUE_PAIR *vp; + + if (!chbind) return NULL; /* don't produce garbage */ + + vp = fr_pair_afrom_num(packet, PW_UKERNA_CHBIND, VENDORPEC_UKERNA); + if (!vp) return NULL; + fr_pair_value_memcpy(vp, (uint8_t *) chbind, talloc_array_length((uint8_t *)chbind)); + + return vp; +} diff --git a/src/modules/rlm_eap/libeap/eap_chbind.h b/src/modules/rlm_eap/libeap/eap_chbind.h new file mode 100644 index 0000000..346b712 --- /dev/null +++ b/src/modules/rlm_eap/libeap/eap_chbind.h @@ -0,0 +1,64 @@ +/* + * eap_chbind.c + * + * Version: $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * Copyright 2014 Network RADIUS SARL + * Copyright 2014 The FreeRADIUS server project + */ + +#ifndef _EAP_CHBIND_H +#define _EAP_CHBIND_H + +RCSIDH(eap_chbind_h, "$Id$") + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <ctype.h> + +#include <freeradius-devel/radiusd.h> + +#include "eap.h" + +/* Structure to represent eap channel binding packet format */ +typedef struct chbind_packet_t { + uint8_t code; + uint8_t data[1]; +} chbind_packet_t; + +/* Structure to hold channel bindings req/resp information */ +typedef struct CHBIND_REQ { + VALUE_PAIR *username; /* the username */ + chbind_packet_t *request; /* channel binding request buffer */ + chbind_packet_t *response; /* channel binding response buffer */ +} CHBIND_REQ; + +/* Protocol constants */ +#define CHBIND_NSID_RADIUS 1 + +#define CHBIND_CODE_REQUEST 1 +#define CHBIND_CODE_SUCCESS 2 +#define CHBIND_CODE_FAILURE 3 + +/* Channel binding function prototypes */ +PW_CODE chbind_process(REQUEST *request, CHBIND_REQ *chbind_req); + +VALUE_PAIR *eap_chbind_packet2vp(RADIUS_PACKET *packet, chbind_packet_t *chbind); +chbind_packet_t *eap_chbind_vp2packet(TALLOC_CTX *ctx, VALUE_PAIR *vps); + +#endif /*_EAP_CHBIND_H*/ diff --git a/src/modules/rlm_eap/libeap/eap_sim.h b/src/modules/rlm_eap/libeap/eap_sim.h new file mode 100644 index 0000000..74dda25 --- /dev/null +++ b/src/modules/rlm_eap/libeap/eap_sim.h @@ -0,0 +1,122 @@ +/* + * eap_sim.h Header file containing the EAP-SIM types + * + * Version: $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * Copyright 2003 Michael Richardson <mcr@sandelman.ottawa.on.ca> + * Copyright 2006 The FreeRADIUS server project + * + */ +#ifndef _EAP_SIM_H +#define _EAP_SIM_H + +RCSIDH(eap_sim_h, "$Id$") + +#include "eap_types.h" + +#define EAP_SIM_VERSION 0x0001 + +enum eapsim_subtype { + EAPSIM_START = 10, + EAPSIM_CHALLENGE = 11, + EAPSIM_NOTIFICATION = 12, + EAPSIM_REAUTH = 13, + EAPSIM_CLIENT_ERROR = 14, + EAPSIM_MAX_SUBTYPE = 15 +}; + +enum eapsim_clientstates { + EAPSIM_CLIENT_INIT = 0, + EAPSIM_CLIENT_START = 1, + EAPSIM_CLIENT_MAXSTATES +}; + +/* server states + * + * in server_start, we send a EAP-SIM Start message. + * + */ +enum eapsim_serverstates { + EAPSIM_SERVER_START = 0, + EAPSIM_SERVER_CHALLENGE = 1, + EAPSIM_SERVER_SUCCESS = 10, + EAPSIM_SERVER_MAXSTATES +}; + + +/* + * interfaces in eapsimlib.c + */ +int map_eapsim_basictypes(RADIUS_PACKET *r, eap_packet_t *ep); +char const *sim_state2name(enum eapsim_clientstates state, char *buf, int buflen); +char const *sim_subtype2name(enum eapsim_subtype subtype, char *buf, int buflen); +int unmap_eapsim_basictypes(RADIUS_PACKET *r, uint8_t *attr, unsigned int attrlen); + + +/************************/ +/* CRYPTO FUNCTIONS */ +/************************/ + +/* + * key derivation functions/structures + * + */ + +#define EAPSIM_SRES_SIZE 4 +#define EAPSIM_RAND_SIZE 16 +#define EAPSIM_KC_SIZE 8 +#define EAPSIM_CALCMAC_SIZE 20 +#define EAPSIM_NONCEMT_SIZE 16 +#define EAPSIM_AUTH_SIZE 16 + +struct eapsim_keys { + /* inputs */ + uint8_t identity[MAX_STRING_LEN]; + unsigned int identitylen; + uint8_t nonce_mt[EAPSIM_NONCEMT_SIZE]; + uint8_t rand[3][EAPSIM_RAND_SIZE]; + uint8_t sres[3][EAPSIM_SRES_SIZE]; + uint8_t Kc[3][EAPSIM_KC_SIZE]; + uint8_t versionlist[MAX_STRING_LEN]; + uint8_t versionlistlen; + uint8_t versionselect[2]; + + /* outputs */ + uint8_t master_key[20]; + uint8_t K_aut[EAPSIM_AUTH_SIZE]; + uint8_t K_encr[16]; + uint8_t msk[64]; + uint8_t emsk[64]; +}; + + +/* + * interfaces in eapsimlib.c + */ +int eapsim_checkmac(TALLOC_CTX *ctx, VALUE_PAIR *rvps, + uint8_t key[8], + uint8_t *extra, int extralen, + uint8_t calcmac[20]); + +/* + * in eapcrypto.c + */ +void eapsim_calculate_keys(struct eapsim_keys *ek); +void eapsim_dump_mk(struct eapsim_keys *ek); + + +#endif /* _EAP_SIM_H */ diff --git a/src/modules/rlm_eap/libeap/eap_tls.c b/src/modules/rlm_eap/libeap/eap_tls.c new file mode 100644 index 0000000..2f37663 --- /dev/null +++ b/src/modules/rlm_eap/libeap/eap_tls.c @@ -0,0 +1,1206 @@ + +/* + * eap_tls.c + * + * Version: $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * Copyright 2001 hereUare Communications, Inc. <raghud@hereuare.com> + * Copyright 2003 Alan DeKok <aland@freeradius.org> + * Copyright 2006 The FreeRADIUS server project + */ + +/* + * + * TLS Packet Format in EAP + * --- ------ ------ -- --- + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Code | Identifier | Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type | Flags | TLS Message Length + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | TLS Message Length | TLS Data... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + */ + +RCSID("$Id$") +USES_APPLE_DEPRECATED_API /* OpenSSL API has been deprecated by Apple */ + +#include <assert.h> + +#include "eap_tls.h" +/* + * Send an initial eap-tls request to the peer. + * + * Frame eap reply packet. + * len = header + type + tls_typedata + * tls_typedata = flags(Start (S) bit set, and no data) + * + * Once having received the peer's Identity, the EAP server MUST + * respond with an EAP-TLS/Start packet, which is an + * EAP-Request packet with EAP-Type=EAP-TLS, the Start (S) bit + * set, and no data. The EAP-TLS conversation will then begin, + * with the peer sending an EAP-Response packet with + * EAP-Type = EAP-TLS. The data field of that packet will + * be the TLS data. + * + * Fragment length is Framed-MTU - 4. + */ +tls_session_t *eaptls_session(eap_handler_t *handler, fr_tls_server_conf_t *tls_conf, bool client_cert, bool allow_tls13) +{ + tls_session_t *ssn; + REQUEST *request = handler->request; + + handler->tls = true; + + /* + * Every new session is started only from EAP-TLS-START. + * Before Sending EAP-TLS-START, open a new SSL session. + * Create all the required data structures & store them + * in Opaque. So that we can use these data structures + * when we get the response + */ + ssn = tls_new_session(handler, tls_conf, request, client_cert, allow_tls13); + if (!ssn) { + return NULL; + } + + /* + * Create a structure for all the items required to be + * verified for each client and set that as opaque data + * structure. + * + * NOTE: If we want to set each item sepearately then + * this index should be global. + */ + SSL_set_ex_data(ssn->ssl, FR_TLS_EX_INDEX_HANDLER, (void *)handler); + SSL_set_ex_data(ssn->ssl, FR_TLS_EX_INDEX_CONF, (void *)tls_conf); + SSL_set_ex_data(ssn->ssl, fr_tls_ex_index_certs, (void *)&(handler->certs)); + SSL_set_ex_data(ssn->ssl, FR_TLS_EX_INDEX_IDENTITY, (void *)&(handler->identity)); +#ifdef HAVE_OPENSSL_OCSP_H + SSL_set_ex_data(ssn->ssl, FR_TLS_EX_INDEX_STORE, (void *)tls_conf->ocsp_store); +#endif + SSL_set_ex_data(ssn->ssl, FR_TLS_EX_INDEX_SSN, (void *)ssn); + SSL_set_ex_data(ssn->ssl, FR_TLS_EX_INDEX_TALLOC, handler); + + return talloc_steal(handler, ssn); /* ssn */ +} + +/* + The S flag is set only within the EAP-TLS start message + sent from the EAP server to the peer. +*/ +int eaptls_start(EAP_DS *eap_ds, int peap_flag) +{ + EAPTLS_PACKET reply; + + reply.code = FR_TLS_START; + reply.length = TLS_HEADER_LEN + 1/*flags*/; + + reply.flags = peap_flag; + reply.flags = SET_START(reply.flags); + + reply.data = NULL; + reply.dlen = 0; + + eaptls_compose(eap_ds, &reply); + + return 1; +} + + +/** Send an EAP-TLS success + * + * Composes an EAP-TLS-Success. This is a message with code EAP_TLS_ESTABLISHED. + * It contains no cryptographic material, and is not protected. + * + * We add the MPPE keys here. These are used by the NAS. The supplicant + * will derive the same keys separately. + * + * @param handler handler of eap session that completed successfully. + * @param peap_flag to indicate PEAP version + * @return + * - 1 on success. + */ +int eaptls_success(eap_handler_t *handler, int peap_flag) +{ + EAPTLS_PACKET reply; + REQUEST *request = handler->request; + tls_session_t *tls_session = handler->opaque; + + handler->finished = true; + reply.code = FR_TLS_SUCCESS; + reply.length = TLS_HEADER_LEN; + reply.flags = peap_flag; + reply.data = NULL; + reply.dlen = 0; + + tls_success(tls_session, request); + + /* + * Call compose AFTER checking for cached data. + */ + eaptls_compose(handler->eap_ds, &reply); + + /* + * Automatically generate MPPE keying material. + */ + if (tls_session->label) { + uint8_t const *context = NULL; + size_t context_size = 0; +#ifdef TLS1_3_VERSION + uint8_t const context_tls13[] = { handler->type }; +#endif + + switch (SSL_version(tls_session->ssl)) { +#ifdef TLS1_3_VERSION + case TLS1_3_VERSION: + context = context_tls13; + context_size = sizeof(context_tls13); + tls_session->label = "EXPORTER_EAP_TLS_Key_Material"; + break; +#endif + case TLS1_2_VERSION: + case TLS1_1_VERSION: + case TLS1_VERSION: + break; + case SSL2_VERSION: + case SSL3_VERSION: + default: + /* Should never happen */ + rad_assert(0); + return 0; + break; + } + eaptls_gen_mppe_keys(request, + tls_session->ssl, tls_session->label, + context, context_size); + } else if (handler->type != PW_EAP_FAST) { + RWDEBUG("(TLS) EAP Not adding MPPE keys because there is no PRF label"); + } + + eaptls_gen_eap_key(handler); + + return 1; +} + +int eaptls_fail(eap_handler_t *handler, int peap_flag) +{ + EAPTLS_PACKET reply; + tls_session_t *tls_session = handler->opaque; + + handler->finished = true; + reply.code = FR_TLS_FAIL; + reply.length = TLS_HEADER_LEN; + reply.flags = peap_flag; + reply.data = NULL; + reply.dlen = 0; + + tls_fail(tls_session); + + eaptls_compose(handler->eap_ds, &reply); + + return 1; +} + +/* + A single TLS record may be up to 16384 octets in length, but a TLS + message may span multiple TLS records, and a TLS certificate message + may in principle be as long as 16MB. +*/ + +/* + * Frame the Dirty data that needs to be send to the client in an + * EAP-Request. We always embed the TLS-length in all EAP-TLS + * packets that we send, for easy reference purpose. Handle + * fragmentation and sending the next fragment etc. + */ +int eaptls_request(EAP_DS *eap_ds, tls_session_t *ssn) +{ + EAPTLS_PACKET reply; + unsigned int size; + unsigned int nlen; + unsigned int lbit = 0; + + /* This value determines whether we set (L)ength flag for + EVERY packet we send and add corresponding + "TLS Message Length" field. + + length_flag = true; + This means we include L flag and "TLS Msg Len" in EVERY + packet we send out. + + length_flag = false; + This means we include L flag and "TLS Msg Len" **ONLY** + in First packet of a fragment series. We do not use + it anywhere else. + + Having L flag in every packet is prefered. + + */ + if (ssn->length_flag) { + lbit = 4; + } + if (ssn->fragment == 0) { + ssn->tls_msg_len = ssn->dirty_out.used; + } + + reply.code = FR_TLS_REQUEST; + reply.flags = ssn->peap_flag; + + /* Send data, NOT more than the FRAGMENT size */ + if (ssn->dirty_out.used > ssn->mtu) { + size = ssn->mtu; + reply.flags = SET_MORE_FRAGMENTS(reply.flags); + /* Length MUST be included if it is the First Fragment */ + if (ssn->fragment == 0) { + lbit = 4; + } + ssn->fragment = 1; + } else { + size = ssn->dirty_out.used; + ssn->fragment = 0; + } + + reply.dlen = lbit + size; + reply.length = TLS_HEADER_LEN + 1/*flags*/ + reply.dlen; + + reply.data = talloc_array(eap_ds, uint8_t, reply.length); + if (!reply.data) return 0; + + if (lbit) { + nlen = htonl(ssn->tls_msg_len); + memcpy(reply.data, &nlen, lbit); + reply.flags = SET_LENGTH_INCLUDED(reply.flags); + } + (ssn->record_minus)(&ssn->dirty_out, reply.data + lbit, size); + + eaptls_compose(eap_ds, &reply); + talloc_free(reply.data); + reply.data = NULL; + + return 1; +} + + +/* + * Similarly, when the EAP server receives an EAP-Response with + * the M bit set, it MUST respond with an EAP-Request with + * EAP-Type=EAP-TLS and no data. This serves as a fragment ACK. + * + * In order to prevent errors in the processing of fragments, the + * EAP server MUST use increment the Identifier value for each + * fragment ACK contained within an EAP-Request, and the peer + * MUST include this Identifier value in the subsequent fragment + * contained within an EAP- Reponse. + * + * EAP server sends an ACK when it determines there are More + * fragments to receive to make the complete + * TLS-record/TLS-Message + */ +static int eaptls_send_ack(eap_handler_t *handler, int peap_flag) +{ + EAPTLS_PACKET reply; + REQUEST *request = handler->request; + + RDEBUG2("(TLS) EAP ACKing fragment, the peer should send more data."); + reply.code = FR_TLS_ACK; + reply.length = TLS_HEADER_LEN + 1/*flags*/; + reply.flags = peap_flag; + reply.data = NULL; + reply.dlen = 0; + + eaptls_compose(handler->eap_ds, &reply); + + return 1; +} + +/* + * The S flag is set only within the EAP-TLS start message sent + * from the EAP server to the peer. + * + * Similarly, when the EAP server receives an EAP-Response with + * the M bit set, it MUST respond with an EAP-Request with + * EAP-Type=EAP-TLS and no data. This serves as a fragment + * ACK. The EAP peer MUST wait. + */ +static fr_tls_status_t eaptls_verify(eap_handler_t *handler) +{ + EAP_DS *eap_ds = handler->eap_ds; + tls_session_t *tls_session = handler->opaque; + EAP_DS *prev_eap_ds = handler->prev_eapds; + eaptls_packet_t *eaptls_packet, *eaptls_prev = NULL; + REQUEST *request = handler->request; + size_t frag_len; + + /* + * We don't check ANY of the input parameters. It's all + * code which works together, so if something is wrong, + * we SHOULD core dump. + * + * e.g. if eap_ds is NULL, of if eap_ds->response is + * NULL, of if it's NOT an EAP-Response, or if the packet + * is too short. See eap_validation()., in ../../eap.c + * + * Also, eap_method_select() takes care of selecting the + * appropriate type, so we don't need to check + * eap_ds->response->type.num == PW_EAP_TLS, or anything + * else. + */ + eaptls_packet = (eaptls_packet_t *)eap_ds->response->type.data; + if (prev_eap_ds && prev_eap_ds->response) + eaptls_prev = (eaptls_packet_t *)prev_eap_ds->response->type.data; + + if (eaptls_packet) { + /* + * First output the flags (for debugging) + */ + RDEBUG3("(TLS) EAP Peer sent flags %c%c%c", + TLS_START(eaptls_packet->flags) ? 'S' : '-', + TLS_MORE_FRAGMENTS(eaptls_packet->flags) ? 'M' : '-', + TLS_LENGTH_INCLUDED(eaptls_packet->flags) ? 'L' : '-'); + } + + /* + * check for ACK + * + * If there's no TLS data, or there's 1 byte of TLS data, + * with the flags set to zero, then it's an ACK. + * + * Find if this is a reply to the previous request sent + */ + if ((!eaptls_packet) || + ((eap_ds->response->length == EAP_HEADER_LEN + 2) && + ((eaptls_packet->flags & 0xc0) == 0x00))) { + + if (prev_eap_ds && (prev_eap_ds->request->id == eap_ds->response->id)) { + return tls_ack_handler(handler->opaque, request); + } else { + REDEBUG("(TLS) EAP Received Unexpected ACK - rejection the connection"); + return FR_TLS_INVALID; + } + } + + /* + * We send TLS_START, but do not receive it. + */ + if (TLS_START(eaptls_packet->flags)) { + REDEBUG("(TLS) EAP Peer sent EAP-TLS Start message (only the server is allowed to do this)"); + return FR_TLS_INVALID; + } + + /* + * Calculate this fragment's length + */ + frag_len = eap_ds->response->length - + (EAP_HEADER_LEN + (TLS_LENGTH_INCLUDED(eaptls_packet->flags) ? 6 : 2)); + + /* + * The L bit (length included) is set to indicate the + * presence of the four octet TLS Message Length field, + * and MUST be set for the first fragment of a fragmented + * TLS message or set of messages. + * + * The M bit (more fragments) is set on all but the last + * fragment. + * + * The S bit (EAP-TLS start) is set in an EAP-TLS Start + * message. This differentiates the EAP-TLS Start message + * from a fragment acknowledgement. + */ + if (TLS_LENGTH_INCLUDED(eaptls_packet->flags)) { + size_t total_len = eaptls_packet->data[2] * 256 | eaptls_packet->data[3]; + + if (frag_len > total_len) { + RWDEBUG("(TLS) EAP Fragment length (%zu bytes) is greater than TLS record length (%zu bytes)", frag_len, + total_len); + } + + RDEBUG2("(TLS) EAP Peer says that the final record size will be %zu bytes", total_len); + if (TLS_MORE_FRAGMENTS(eaptls_packet->flags)) { + /* + * The supplicant is free to send fragments of wildly varying + * lengths, but the vast majority won't. + * + * In this calculation we take into account the fact that the future + * fragments are likely to be 4 bytes larger than the initial one + * as they won't contain the length field. + */ + if (frag_len + 4) { /* check for wrap, else clang scan gets excited */ + RDEBUG2("(TLS) EAP Expecting %i fragments", + (int)((((total_len - frag_len) + ((frag_len + 4) - 1)) / (frag_len + 4)) + 1)); + } + + /* + * FIRST_FRAGMENT is identified + * 1. If there is no previous EAP-response received. + * 2. If EAP-response received, then its M bit not set. + * (It is because Last fragment will not have M bit set) + */ + if (!prev_eap_ds || (!prev_eap_ds->response) || (!eaptls_prev) || + !TLS_MORE_FRAGMENTS(eaptls_prev->flags)) { + RDEBUG2("(TLS) EAP Got first TLS fragment (%zu bytes). Peer says more fragments " + "will follow", frag_len); + tls_session->tls_record_in_total_len = total_len; + tls_session->tls_record_in_recvd_len = frag_len; + + return FR_TLS_FIRST_FRAGMENT; + } + + RDEBUG2("(TLS) EAP Got additional fragment with length (%zu bytes). " + "Peer says more fragments will follow", frag_len); + + /* + * Check we've not exceeded the originally indicated TLS record size. + */ + tls_session->tls_record_in_recvd_len += frag_len; + if (tls_session->tls_record_in_recvd_len > tls_session->tls_record_in_total_len) { + RWDEBUG("(TLS) EAP Total received fragments (%zu bytes), exceeds " + "total data length (%zu bytes)", frag_len, total_len); + } + + return FR_TLS_MORE_FRAGMENTS_WITH_LENGTH; + } + + /* + * If it's a complete record, our fragment size should match the + * value of the four octet TLS length field. + */ + if (total_len != frag_len) { + RWDEBUG("(TLS) EAP Peer says no more fragments, but expected data length (%zu bytes) " + "does not match expected data length (%zu bytes)", total_len, frag_len); + } + + tls_session->tls_record_in_total_len = total_len; + tls_session->tls_record_in_recvd_len = frag_len; + RDEBUG2("(TLS) EAP Got all data (%zu bytes)", frag_len); + return FR_TLS_LENGTH_INCLUDED; + } + + /* + * The previous packet had the M flags set, but this one doesn't, + * this must be the final record fragment + */ + if ((eaptls_prev && TLS_MORE_FRAGMENTS(eaptls_prev->flags)) && !TLS_MORE_FRAGMENTS(eaptls_packet->flags)) { + RDEBUG2("(TLS) EAP Got final fragment (%zu bytes)", frag_len); + tls_session->tls_record_in_recvd_len += frag_len; + if (tls_session->tls_record_in_recvd_len != tls_session->tls_record_in_total_len) { + RWDEBUG("(TLS) EAP Total received record fragments (%zu bytes), does not equal expected " + "expected data length (%zu bytes)", + tls_session->tls_record_in_recvd_len, tls_session->tls_record_in_total_len); + } + } + + if (TLS_MORE_FRAGMENTS(eaptls_packet->flags)) { + RDEBUG2("(TLS) EAP Got additional fragment (%zu bytes). Peer says more fragments will follow", + frag_len); + tls_session->tls_record_in_recvd_len += frag_len; + if (tls_session->tls_record_in_recvd_len > tls_session->tls_record_in_total_len) { + RWDEBUG("(TLS) EAP Total received fragments (%zu bytes), exceeds " + "expected length (%zu bytes)", + tls_session->tls_record_in_recvd_len, tls_session->tls_record_in_total_len); + } + return FR_TLS_MORE_FRAGMENTS; + } + + /* + * None of the flags are set, but it's still a valid EAP-TLS packet. + */ + return FR_TLS_OK; +} + +/* + * EAPTLS_PACKET + * code = EAP-code + * id = EAP-id + * length = code + id + length + flags + tlsdata + * = 1 + 1 + 2 + 1 + X + * length = EAP-length - 1(EAP-Type = 1 octet) + * flags = EAP-typedata[0] (1 octet) + * dlen = EAP-typedata[1-4] (4 octets), if L flag set + * = length - 5(code+id+length+flags), otherwise + * data = EAP-typedata[5-n], if L flag set + * = EAP-typedata[1-n], otherwise + * packet = EAP-typedata (complete typedata) + * + * Points to consider during EAP-TLS data extraction + * 1. In the received packet, No data will be present incase of ACK-NAK + * 2. Incase if more fragments need to be received then ACK after retreiving this fragment. + * + * RFC 2716 Section 4.2. PPP EAP TLS Request Packet + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Code | Identifier | Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type | Flags | TLS Message Length + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | TLS Message Length | TLS Data... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * The Length field is two octets and indicates the length of the EAP + * packet including the Code, Identifir, Length, Type, and TLS data + * fields. + */ +static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, fr_tls_status_t status) +{ + EAPTLS_PACKET *tlspacket; + uint32_t data_len = 0; + uint32_t len = 0; + uint8_t *data = NULL; + + if (status == FR_TLS_INVALID) return NULL; + + /* + * The main EAP code & eaptls_verify() take care of + * ensuring that the packet is OK, and that we can + * extract the various fields we want. + * + * e.g. a TLS packet with zero data is allowed as an ACK, + * but we will never see it here, as we will simply + * send another fragment, instead of trying to extract + * the data. + * + * MUST have TLS type octet, followed by flags, followed + * by data. + */ + assert(eap_ds->response->length > 2); + + tlspacket = talloc(eap_ds, EAPTLS_PACKET); + if (!tlspacket) return NULL; + + /* + * Code & id for EAPTLS & EAP are same + * but eaptls_length = eap_length - 1(EAP-Type = 1 octet) + * + * length = code + id + length + type + tlsdata + * = 1 + 1 + 2 + 1 + X + */ + tlspacket->code = eap_ds->response->code; + tlspacket->id = eap_ds->response->id; + tlspacket->length = eap_ds->response->length - 1; /* EAP type */ + tlspacket->flags = eap_ds->response->type.data[0]; + + /* + * A quick sanity check of the flags. If we've been told + * that there's a length, and there isn't one, then stop. + */ + if (TLS_LENGTH_INCLUDED(tlspacket->flags) && + (tlspacket->length < 5)) { /* flags + TLS message length */ + REDEBUG("(TLS) EAP Invalid packet received: Length bit is set," + "but packet too short to contain length field"); + talloc_free(tlspacket); + return NULL; + } + + /* + * If the final TLS packet is larger than we can handle, die + * now. + * + * Likewise, if the EAP packet says N bytes, and the TLS + * packet says there's fewer bytes, it's a problem. + */ + if (TLS_LENGTH_INCLUDED(tlspacket->flags)) { + memcpy(&data_len, &eap_ds->response->type.data[1], 4); + data_len = ntohl(data_len); + if (data_len > MAX_RECORD_SIZE) { + REDEBUG("(TLS) EAP Reassembled data will be %u bytes, " + "greater than the size that we can handle (" STRINGIFY(MAX_RECORD_SIZE) " bytes)", + data_len); + talloc_free(tlspacket); + return NULL; + } + } + + switch (status) { + /* + * The TLS Message Length field is four octets, and + * provides the total length of the TLS message or set of + * messages that is being fragmented; this simplifies + * buffer allocation. + * + * Dynamic allocation of buffers as & when we know the + * length should solve the problem. + */ + case FR_TLS_FIRST_FRAGMENT: + case FR_TLS_LENGTH_INCLUDED: + case FR_TLS_MORE_FRAGMENTS_WITH_LENGTH: + if (tlspacket->length < 5) { /* flags + TLS message length */ + REDEBUG("(TLS) EAP Invalid packet received: Expected length, got none"); + talloc_free(tlspacket); + return NULL; + } + + /* + * Extract all the TLS fragments from the + * previous eap_ds Start appending this + * fragment to the above ds + */ + memcpy(&data_len, &eap_ds->response->type.data[1], sizeof(uint32_t)); + data_len = ntohl(data_len); + data = (eap_ds->response->type.data + 5/*flags+TLS-Length*/); + len = eap_ds->response->type.length - 5/*flags+TLS-Length*/; + + /* + * Hmm... this should be an error, too. + */ + if (data_len > len) { + data_len = len; + } + break; + + /* + * Data length is implicit, from the EAP header. + */ + case FR_TLS_MORE_FRAGMENTS: + case FR_TLS_OK: + data_len = eap_ds->response->type.length - 1/*flags*/; + data = eap_ds->response->type.data + 1/*flags*/; + break; + + default: + REDEBUG("(TLS) EAP Invalid packet received"); + talloc_free(tlspacket); + return NULL; + } + + tlspacket->dlen = data_len; + if (data_len) { + tlspacket->data = talloc_array(tlspacket, uint8_t, + data_len); + if (!tlspacket->data) { + talloc_free(tlspacket); + return NULL; + } + memcpy(tlspacket->data, data, data_len); + } + + return tlspacket; +} + + + +/* + * To process the TLS, + * INCOMING DATA: + * 1. EAP-TLS should get the compelete TLS data from the peer. + * 2. Store that data in a data structure with any other required info + * 3. Handle that data structure to the TLS module. + * 4. TLS module will perform its operations on the data and + * handle back to EAP-TLS + * + * OUTGOING DATA: + * 1. EAP-TLS if necessary will fragment it and send it to the + * destination. + * + * During EAP-TLS initialization, TLS Context object will be + * initialized and stored. For every new authentication + * requests, TLS will open a new session object and that session + * object should be maintained even after the session is + * completed for session resumption. (Probably later as a feature + * as we donot know who maintains these session objects ie, + * SSL_CTX (internally) or TLS module(explicitly). If TLS module, + * then how to let SSL API know about these sessions.) + */ +static fr_tls_status_t eaptls_operation(fr_tls_status_t status, eap_handler_t *handler) +{ + REQUEST *request = handler->request; + tls_session_t *tls_session = handler->opaque; + + if ((status == FR_TLS_MORE_FRAGMENTS) || + (status == FR_TLS_MORE_FRAGMENTS_WITH_LENGTH) || + (status == FR_TLS_FIRST_FRAGMENT)) { + /* + * Send the ACK. + */ + eaptls_send_ack(handler, tls_session->peap_flag); + return FR_TLS_HANDLED; + + } + + /* + * We have the complete TLS-data or TLS-message. + * + * Clean the dirty message. + * + * Authenticate the user and send + * Success/Failure. + * + * If more info + * is required then send another request. + */ + if (!tls_handshake_recv(handler->request, tls_session)) { + REDEBUG("(TLS) EAP Receive handshake failed during operation"); + tls_fail(tls_session); + return FR_TLS_FAIL; + } + +#ifdef TLS1_3_VERSION + /* + * https://tools.ietf.org/html/draft-ietf-emu-eap-tls13#section-2.5 + * + * We need to signal the other end that TLS negotiation + * is done. We can't send a zero-length application data + * message, so we send application data which is one byte + * of zero. + * + * Note this is only done for when there is no application + * data to be sent. So this is done always for EAP-TLS but + * notibly not for PEAP even on resumption. + */ + if ((SSL_version(tls_session->ssl) == TLS1_3_VERSION) && + (tls_session->client_cert_ok || tls_session->authentication_success || SSL_session_reused(tls_session->ssl))) { + if ((handler->type == PW_EAP_TLS) || SSL_session_reused(tls_session->ssl)) { + tls_session->authentication_success = true; + + RDEBUG("(TLS) EAP Sending final Commitment Message."); + tls_session->record_plus(&tls_session->clean_in, "\0", 1); + } + + tls_handshake_send(request, tls_session); + } +#endif + + /* + * FIXME: return success/fail. + * + * TLS proper can decide what to do, then. + */ + if (tls_session->dirty_out.used > 0) { + eaptls_request(handler->eap_ds, tls_session); + return FR_TLS_HANDLED; + } + + /* + * If there is no data to send i.e + * dirty_out.used <=0 and if the SSL + * handshake is finished. + */ + if (tls_session->is_init_finished) return FR_TLS_SUCCESS; + + /* + * If session is established, skip round-trip and + * try to process any inner tunnel data if present. + * + * This occurs for EAP-TTLS/PAP with TLSv1.3. + */ + if (!tls_session->is_init_finished && SSL_is_init_finished(tls_session->ssl)) { + /* + * Don't set is_init_finished, as that causes the + * rest of the code to make too many assumptions. + */ + return FR_TLS_OK; + } + + /* + * Who knows what happened... + */ + REDEBUG("(TLS) Cannot continue, as the peer is misbehaving."); + return FR_TLS_FAIL; +} + + +/* + * In the actual authentication first verify the packet and then create the data structure + */ +/* + * To process the TLS, + * INCOMING DATA: + * 1. EAP-TLS should get the compelete TLS data from the peer. + * 2. Store that data in a data structure with any other required info + * 3. Hand this data structure to the TLS module. + * 4. TLS module will perform its operations on the data and hands back to EAP-TLS + * OUTGOING DATA: + * 1. EAP-TLS if necessary will fragment it and send it to the destination. + * + * During EAP-TLS initialization, TLS Context object will be + * initialized and stored. For every new authentication + * requests, TLS will open a new session object and that + * session object SHOULD be maintained even after the session + * is completed, for session resumption. (Probably later as a + * feature, as we do not know who maintains these session + * objects ie, SSL_CTX (internally) or TLS module (explicitly). If + * TLS module, then how to let SSL API know about these + * sessions.) + */ + +/* + * Process an EAP request + */ +fr_tls_status_t eaptls_process(eap_handler_t *handler) +{ + tls_session_t *tls_session = (tls_session_t *) handler->opaque; + EAPTLS_PACKET *tlspacket; + fr_tls_status_t status; + REQUEST *request = handler->request; + + if (!request) return FR_TLS_FAIL; + + RDEBUG3("(TLS) EAP Continuing ..."); + + SSL_set_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_REQUEST, request); + + if (handler->certs) fr_pair_add(&request->packet->vps, + fr_pair_list_copy(request->packet, handler->certs)); + + /* + * This case is when SSL generates Alert then we + * send that alert to the client and then send the EAP-Failure + */ + status = eaptls_verify(handler); + if ((status == FR_TLS_INVALID) || (status == FR_TLS_FAIL)) { + REDEBUG("(TLS) EAP Verification failed with %s", fr_int2str(fr_tls_status_table, status, "<INVALID>")); + } else { + RDEBUG3("(TLS) EAP Verification says %s", fr_int2str(fr_tls_status_table, status, "<INVALID>")); + } + + switch (status) { + default: + case FR_TLS_INVALID: + case FR_TLS_FAIL: + + /* + * Success means that we're done the initial + * handshake. For TTLS, this means send stuff + * back to the client, and the client sends us + * more tunneled data. + */ + case FR_TLS_SUCCESS: + goto done; + + /* + * Normal TLS request, continue with the "get rest + * of fragments" phase. + */ + case FR_TLS_REQUEST: + eaptls_request(handler->eap_ds, tls_session); + status = FR_TLS_HANDLED; + goto done; + + /* + * The handshake is done, and we're in the "tunnel + * data" phase. + */ + case FR_TLS_OK: + RDEBUG2("(TLS) EAP Done initial handshake"); + + /* + * Get the rest of the fragments. + */ + case FR_TLS_FIRST_FRAGMENT: + case FR_TLS_MORE_FRAGMENTS: + case FR_TLS_LENGTH_INCLUDED: + case FR_TLS_MORE_FRAGMENTS_WITH_LENGTH: + break; + } + + /* + * Extract the TLS packet from the buffer. + */ + if ((tlspacket = eaptls_extract(request, handler->eap_ds, status)) == NULL) { + REDEBUG("(TLS) EAP Failed extracting TLS packet from EAP-Message"); + status = FR_TLS_FAIL; + goto done; + } + + /* + * Get the session struct from the handler + * + * update the dirty_in buffer + * + * NOTE: This buffer will contain partial data when M bit is set. + * + * CAUTION while reinitializing this buffer, it should be + * reinitialized only when this M bit is NOT set. + */ + if (tlspacket->dlen != + (tls_session->record_plus)(&tls_session->dirty_in, tlspacket->data, tlspacket->dlen)) { + talloc_free(tlspacket); + REDEBUG("(TLS) EAP Exceeded maximum record size"); + status = FR_TLS_FAIL; + goto done; + } + + /* + * No longer needed. + */ + talloc_free(tlspacket); + + /* + * SSL initalization is done. Return. + * + * The TLS data will be in the tls_session structure. + */ + if (tls_session->is_init_finished) { + /* + * The initialization may be finished, but if + * there more fragments coming, then send ACK, + * and get the caller to continue the + * conversation. + */ + if ((status == FR_TLS_MORE_FRAGMENTS) || + (status == FR_TLS_MORE_FRAGMENTS_WITH_LENGTH) || + (status == FR_TLS_FIRST_FRAGMENT)) { + /* + * Send the ACK. + */ + eaptls_send_ack(handler, tls_session->peap_flag); + RDEBUG2("(TLS) EAP Init is done, but tunneled data is fragmented"); + status = FR_TLS_HANDLED; + goto done; + } + + status = tls_application_data(tls_session, request); + goto done; + } + + /* + * Continue the handshake. + */ + status = eaptls_operation(status, handler); + if (status == FR_TLS_SUCCESS) { +#define MAX_SESSION_SIZE (256) + VALUE_PAIR *vps; + char buffer[2 * MAX_SESSION_SIZE + 1]; + + /* + * Restore the cached VPs before processing the + * application data. + */ + tls_session_id(tls_session->ssl_session, buffer, MAX_SESSION_SIZE); + + vps = SSL_SESSION_get_ex_data(tls_session->ssl_session, fr_tls_ex_index_vps); + if (!vps) { + RWDEBUG("(TLS) EAP No information in cached session %s", buffer); + } else { + vp_cursor_t cursor; + VALUE_PAIR *vp; + fr_tls_server_conf_t *conf; + + RDEBUG("(TLS) EAP Adding cached attributes from session %s", buffer); + + conf = (fr_tls_server_conf_t *)SSL_get_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_CONF); + rad_assert(conf != NULL); + + /* + * The cbtls_get_session() function doesn't have + * access to sock->certs or handler->certs, which + * is where the certificates normally live. So + * the certs are all in the VPS list here, and + * have to be manually extracted. + */ + RINDENT(); + for (vp = fr_cursor_init(&cursor, &vps); + vp; + vp = fr_cursor_next(&cursor)) { + if (conf->cache_ht && fr_hash_table_finddata(conf->cache_ht, vp->da)) { + rdebug_pair(L_DBG_LVL_2, request, vp, "&session-state:"); + fr_pair_add(&request->state, fr_pair_copy(request->state_ctx, vp)); + continue; + } + + /* + * TLS-* attrs get added back to + * the request list. + */ + if ((vp->da->vendor == 0) && + (vp->da->attr >= PW_TLS_CERT_SERIAL) && + (vp->da->attr <= PW_TLS_CLIENT_CERT_SUBJECT_ALT_NAME_UPN)) { + /* + * Certs already exist. Don't re-add them. + */ + if (!handler->certs) { + rdebug_pair(L_DBG_LVL_2, request, vp, "&request:"); + fr_pair_add(&request->packet->vps, fr_pair_copy(request->packet, vp)); + } + + } else if ((vp->da->vendor == 0) && + (vp->da->attr == PW_EAP_TYPE)) { + /* + * EAP-Type gets added to + * the control list, so + * that we can sanity check it. + */ + rdebug_pair(L_DBG_LVL_2, request, vp, "&control:"); + fr_pair_add(&request->config, fr_pair_copy(request, vp)); + + } else { + + rdebug_pair(L_DBG_LVL_2, request, vp, "&reply:"); + fr_pair_add(&request->reply->vps, fr_pair_copy(request->reply, vp)); + } + } + REXDENT(); + } + } + + done: + SSL_set_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_REQUEST, NULL); + + return status; +} + + +/* + * compose the TLS reply packet in the EAP reply typedata + */ +int eaptls_compose(EAP_DS *eap_ds, EAPTLS_PACKET *reply) +{ + uint8_t *ptr; + + /* + * Don't set eap_ds->request->type.num, as the main EAP + * handler will do that for us. This allows the TLS + * module to be called from TTLS & PEAP. + */ + + /* + * When the EAP server receives an EAP-Response with the + * M bit set, it MUST respond with an EAP-Request with + * EAP-Type=EAP-TLS and no data. This serves as a + * fragment ACK. The EAP peer MUST wait until it receives + * the EAP-Request before sending another fragment. + * + * In order to prevent errors in the processing of + * fragments, the EAP server MUST use increment the + * Identifier value for each fragment ACK contained + * within an EAP-Request, and the peer MUST include this + * Identifier value in the subsequent fragment contained + * within an EAP- Reponse. + */ + eap_ds->request->type.data = talloc_array(eap_ds->request, uint8_t, + reply->length - TLS_HEADER_LEN + 1); + if (!eap_ds->request->type.data) return 0; + + /* EAPTLS Header length is excluded while computing EAP typelen */ + eap_ds->request->type.length = reply->length - TLS_HEADER_LEN; + + ptr = eap_ds->request->type.data; + *ptr++ = (uint8_t)(reply->flags & 0xFF); + + if (reply->dlen) memcpy(ptr, reply->data, reply->dlen); + + switch (reply->code) { + case FR_TLS_ACK: + case FR_TLS_START: + case FR_TLS_REQUEST: + eap_ds->request->code = PW_EAP_REQUEST; + break; + + case FR_TLS_SUCCESS: + eap_ds->request->code = PW_EAP_SUCCESS; + break; + + case FR_TLS_FAIL: + eap_ds->request->code = PW_EAP_FAILURE; + break; + + default: + /* Should never enter here */ + rad_assert(0); + break; + } + + return 1; +} + +/* + * Parse TLS configuration + * + * If the option given by 'attr' is set, we find the config section + * of that name and use that for the TLS configuration. If not, we + * fall back to compatibility mode and read the TLS options from + * the 'tls' section. + */ +fr_tls_server_conf_t *eaptls_conf_parse(CONF_SECTION *cs, char const *attr) +{ + char const *tls_conf_name; + CONF_PAIR *cp; + CONF_SECTION *parent; + CONF_SECTION *tls_cs; + fr_tls_server_conf_t *tls_conf; + + if (!cs) + return NULL; + + rad_assert(attr != NULL); + + parent = cf_item_parent(cf_section_to_item(cs)); + + cp = cf_pair_find(cs, attr); + if (cp) { + tls_conf_name = cf_pair_value(cp); + + tls_cs = cf_section_sub_find_name2(parent, TLS_CONFIG_SECTION, tls_conf_name); + + if (!tls_cs) { + ERROR("Cannot find tls config \"%s\"", tls_conf_name); + return NULL; + } + } else { + /* + * If we can't find the section given by the 'attr', we + * fall-back to looking for the "tls" section, as in + * previous versions. + * + * We don't fall back if the 'attr' is specified, but we can't + * find the section - that is just a config error. + */ + INFO("TLS section \"%s\" missing, trying to use legacy configuration", attr); + tls_cs = cf_section_sub_find(parent, "tls"); + } + + if (!tls_cs) + return NULL; + + tls_conf = tls_server_conf_parse(tls_cs); + + if (!tls_conf) + return NULL; + + /* + * The EAP RFC's say 1020, but we're less picky. + */ + if (tls_conf->fragment_size < 100) { + ERROR("Configured fragment size is too small, must be >= 100"); + return NULL; + } + + /* + * The maximum size for a RADIUS packet is 4096, + * minus the header (20), Message-Authenticator (18), + * and State (18), etc. results in about 4000 bytes of data + * that can be devoted *solely* to EAP. + */ + if (tls_conf->fragment_size > 4000) { + ERROR("Configured fragment size is too large, must be <= 4000"); + return NULL; + } + + /* + * Account for the EAP header (4), and the EAP-TLS header + * (6), as per Section 4.2 of RFC 2716. What's left is + * the maximum amount of data we read from a TLS buffer. + */ + tls_conf->fragment_size -= 10; + + return tls_conf; +} + diff --git a/src/modules/rlm_eap/libeap/eap_tls.h b/src/modules/rlm_eap/libeap/eap_tls.h new file mode 100644 index 0000000..8e5fc77 --- /dev/null +++ b/src/modules/rlm_eap/libeap/eap_tls.h @@ -0,0 +1,109 @@ +/* + * eap_tls.h + * + * Version: $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * Copyright 2001 hereUare Communications, Inc. <raghud@hereuare.com> + * Copyright 2003 Alan DeKok <aland@freeradius.org> + * Copyright 2006 The FreeRADIUS server project + */ +#ifndef _EAP_TLS_H +#define _EAP_TLS_H + +RCSIDH(eap_tls_h, "$Id$") +USES_APPLE_DEPRECATED_API /* OpenSSL API has been deprecated by Apple */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include <fcntl.h> +#include <signal.h> + +#include <ctype.h> +#include <sys/time.h> +#include <arpa/inet.h> + +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +#include <freeradius-devel/radiusd.h> +#include <freeradius-devel/tls.h> + +#include "eap.h" + +/* + * Externally exported TLS functions. + */ +fr_tls_status_t eaptls_process(eap_handler_t *handler); + +int eaptls_success(eap_handler_t *handler, int peap_flag) CC_HINT(nonnull); +int eaptls_fail(eap_handler_t *handler, int peap_flag) CC_HINT(nonnull); +int eaptls_request(EAP_DS *eap_ds, tls_session_t *ssn) CC_HINT(nonnull); + + +void T_PRF(unsigned char const *secret, unsigned int secret_len, char const *prf_label, unsigned char const *seed, unsigned int seed_len, unsigned char *out, unsigned int out_len) CC_HINT(nonnull(1,3,6)); +void eaptls_gen_mppe_keys(REQUEST *request, SSL *s, char const *label, uint8_t const *context, size_t context_size); +void eapttls_gen_challenge(SSL *s, uint8_t *buffer, size_t size); +void eaptls_gen_eap_key(eap_handler_t *handler); +void eap_fast_tls_gen_challenge(SSL *ssl, int version, uint8_t *buffer, size_t size, char const *prf_label) CC_HINT(nonnull); + +#define BUFFER_SIZE 1024 + +typedef enum tls_op { + EAP_TLS_START = 1, + EAP_TLS_ACK = 2, + EAP_TLS_SUCCESS = 3, + EAP_TLS_FAIL = 4, + EAP_TLS_ALERT = 9 +} tls_op_t; + +#define TLS_HEADER_LEN 4 + +typedef struct tls_packet_t { + uint8_t flags; + uint8_t data[1]; +} eaptls_packet_t; + +typedef struct tls_packet { + uint8_t code; + uint8_t id; + uint32_t length; + uint8_t flags; + uint8_t *data; + uint32_t dlen; + + //uint8_t *packet; /* Wired EAP-TLS packet as found in typdedata of eap_packet_t */ +} EAPTLS_PACKET; + + +/* EAP-TLS framework */ +EAPTLS_PACKET *eaptls_alloc(void); +void eaptls_free(EAPTLS_PACKET **eaptls_packet_ptr); +tls_session_t *eaptls_session(eap_handler_t *handler, fr_tls_server_conf_t *tls_conf, bool client_cert, bool allow_tls13); +int eaptls_start(EAP_DS *eap_ds, int peap); +int eaptls_compose(EAP_DS *eap_ds, EAPTLS_PACKET *reply); + +fr_tls_server_conf_t *eaptls_conf_parse(CONF_SECTION *cs, char const *key); + +#endif /*_EAP_TLS_H*/ diff --git a/src/modules/rlm_eap/libeap/eap_types.h b/src/modules/rlm_eap/libeap/eap_types.h new file mode 100644 index 0000000..c6568ff --- /dev/null +++ b/src/modules/rlm_eap/libeap/eap_types.h @@ -0,0 +1,162 @@ +/* + * eap_types.h Header file containing the interfaces for all EAP types. + * + * most contents moved from modules/rlm_eap/eap.h + * + * Version: $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * Copyright 2001 hereUare Communications, Inc. <raghud@hereuare.com> + * Copyright 2003 Alan DeKok <aland@freeradius.org> + * Copyright 2006 The FreeRADIUS server project + */ +#ifndef _EAP_TYPES_H +#define _EAP_TYPES_H + +RCSIDH(eap_methods_h, "$Id$") + +#include <freeradius-devel/radiusd.h> +#include <freeradius-devel/modules.h> + +/* Code (1) + Identifier (1) + Length (2) */ +#define EAP_HEADER_LEN 4 + +typedef enum eap_code { + PW_EAP_REQUEST = 1, + PW_EAP_RESPONSE, + PW_EAP_SUCCESS, + PW_EAP_FAILURE, + PW_EAP_MAX_CODES +} eap_code_t; + +typedef enum eap_method { + PW_EAP_INVALID = 0, /* 0 */ + PW_EAP_IDENTITY, /* 1 */ + PW_EAP_NOTIFICATION, /* 2 */ + PW_EAP_NAK, /* 3 */ + PW_EAP_MD5, /* 4 */ + PW_EAP_OTP, /* 5 */ + PW_EAP_GTC, /* 6 */ + PW_EAP_7, /* 7 - unused */ + PW_EAP_8, /* 8 - unused */ + PW_EAP_RSA_PUBLIC_KEY, /* 9 */ + PW_EAP_DSS_UNILATERAL, /* 10 */ + PW_EAP_KEA, /* 11 */ + PW_EAP_KEA_VALIDATE, /* 12 */ + PW_EAP_TLS, /* 13 */ + PW_EAP_DEFENDER_TOKEN, /* 14 */ + PW_EAP_RSA_SECURID, /* 15 */ + PW_EAP_ARCOT_SYSTEMS, /* 16 */ + PW_EAP_LEAP, /* 17 */ + PW_EAP_SIM, /* 18 */ + PW_EAP_SRP_SHA1, /* 19 */ + PW_EAP_20, /* 20 - unassigned */ + PW_EAP_TTLS, /* 21 */ + PW_EAP_REMOTE_ACCESS_SERVICE, /* 22 */ + PW_EAP_AKA, /* 23 */ + PW_EAP_3COM, /* 24 - should this be EAP-HP now? */ + PW_EAP_PEAP, /* 25 */ + PW_EAP_MSCHAPV2, /* 26 */ + PW_EAP_MAKE, /* 27 */ + PW_EAP_CRYPTOCARD, /* 28 */ + PW_EAP_CISCO_MSCHAPV2, /* 29 */ + PW_EAP_DYNAMID, /* 30 */ + PW_EAP_ROB, /* 31 */ + PW_EAP_POTP, /* 32 */ + PW_EAP_MS_ATLV, /* 33 */ + PW_EAP_SENTRINET, /* 34 */ + PW_EAP_ACTIONTEC, /* 35 */ + PW_EAP_COGENT_BIOMETRIC, /* 36 */ + PW_EAP_AIRFORTRESS, /* 37 */ + PW_EAP_TNC, /* 38 - fixme conflicts with HTTP DIGEST */ +// PW_EAP_HTTP_DIGEST, /* 38 */ + PW_EAP_SECURISUITE, /* 39 */ + PW_EAP_DEVICECONNECT, /* 40 */ + PW_EAP_SPEKE, /* 41 */ + PW_EAP_MOBAC, /* 42 */ + PW_EAP_FAST, /* 43 */ + PW_EAP_ZONELABS, /* 44 */ + PW_EAP_LINK, /* 45 */ + PW_EAP_PAX, /* 46 */ + PW_EAP_PSK, /* 47 */ + PW_EAP_SAKE, /* 48 */ + PW_EAP_IKEV2, /* 49 */ + PW_EAP_AKA2, /* 50 */ + PW_EAP_GPSK, /* 51 */ + PW_EAP_PWD, /* 52 */ + PW_EAP_EKE, /* 53 */ + PW_EAP_MAX_TYPES /* 54 - for validation */ +} eap_type_t; + +#define PW_EAP_EXPANDED_TYPE (254) + +typedef enum eap_rcode { + EAP_NOTFOUND, //!< EAP handler data not found. + EAP_FOUND, //!< EAP handler data found, continue. + EAP_OK, //!< Ok, continue. + EAP_FAIL, //!< Failed, don't reply. + EAP_NOOP, //!< Succeeded without doing anything. + EAP_INVALID, //!< Invalid, don't reply. + EAP_VALID, //!< Valid, continue. + EAP_MAX_RCODES +} eap_rcode_t; + +extern const FR_NAME_NUMBER eap_rcode_table[]; + +/** EAP-Type specific data + */ +typedef struct eap_type_data { + eap_type_t num; + size_t length; + uint8_t *data; +} eap_type_data_t; + +/** Structure to hold EAP data + * + * length = code + id + length + type + type.data + * = 1 + 1 + 2 + 1 + X + */ +typedef struct eap_packet { + eap_code_t code; + uint8_t id; + size_t length; + eap_type_data_t type; + + uint8_t *packet; +} eap_packet_t; + +/** Structure to represent packet format of eap *on wire* + */ +typedef struct eap_packet_raw { + uint8_t code; + uint8_t id; + uint8_t length[2]; + uint8_t data[1]; +} eap_packet_raw_t; + + +/* + * interfaces in eapcommon.c + */ +eap_type_t eap_name2type(char const *name); +char const *eap_type2name(eap_type_t method); +int eap_wireformat(eap_packet_t *reply); +int eap_basic_compose(RADIUS_PACKET *packet, eap_packet_t *reply); +VALUE_PAIR *eap_packet2vp(RADIUS_PACKET *packet, eap_packet_raw_t const *reply); +eap_packet_raw_t *eap_vp2packet(TALLOC_CTX *ctx, VALUE_PAIR *vps); +void eap_add_reply(REQUEST *request, char const *name, uint8_t const *value, int len); + +#endif /* _EAP_TYPES_H */ diff --git a/src/modules/rlm_eap/libeap/eapclient.h b/src/modules/rlm_eap/libeap/eapclient.h new file mode 100644 index 0000000..594007f --- /dev/null +++ b/src/modules/rlm_eap/libeap/eapclient.h @@ -0,0 +1,8 @@ +/* + * some of this seems like a repeat of rlm_eap, and needs to be better + * integrated, but as a client library, it deals with Request/Replies + * rather than with Replies -> new requests. + * + * Bare with me for a bit. + * + */ diff --git a/src/modules/rlm_eap/libeap/eapcommon.c b/src/modules/rlm_eap/libeap/eapcommon.c new file mode 100644 index 0000000..96db30b --- /dev/null +++ b/src/modules/rlm_eap/libeap/eapcommon.c @@ -0,0 +1,401 @@ +/* + * eapcommon.c rfc2284 & rfc2869 implementation + * + * code common to clients and to servers. + * + * Version: $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * Copyright 2000-2003,2006 The FreeRADIUS server project + * Copyright 2001 hereUare Communications, Inc. <raghud@hereuare.com> + * Copyright 2003 Alan DeKok <aland@freeradius.org> + * Copyright 2003 Michael Richardson <mcr@sandelman.ottawa.on.ca> + */ +/* + * EAP PACKET FORMAT + * --- ------ ------ + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Code | Identifier | Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Data ... + * +-+-+-+-+ + * + * + * EAP Request and Response Packet Format + * --- ------- --- -------- ------ ------ + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Code | Identifier | Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type | Type-Data ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + * + * + * EAP Success and Failure Packet Format + * --- ------- --- ------- ------ ------ + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Code | Identifier | Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + */ + +RCSID("$Id$") + +#include <freeradius-devel/libradius.h> +#include <freeradius-devel/rad_assert.h> +#include "eap_types.h" + +const FR_NAME_NUMBER eap_rcode_table[] = { + { "notfound", EAP_NOTFOUND }, + { "found", EAP_OK }, + { "ok", EAP_FAIL }, + { "fail", EAP_NOOP }, + { "noop", EAP_INVALID }, + { "invalid", EAP_VALID }, + { "valid", EAP_MAX_RCODES }, + + { NULL , -1 } +}; + +/** Return an EAP-Type for a particular name + * + * Converts a name into an IANA EAP type. + * + * @param name to convert. + * @return The IANA EAP type or PW_EAP_INVALID if the name doesn't match any + * known types. + */ +eap_type_t eap_name2type(char const *name) +{ + DICT_VALUE *dv; + + dv = dict_valbyname(PW_EAP_TYPE, 0, name); + if (!dv) return PW_EAP_INVALID; + + if (dv->value >= PW_EAP_MAX_TYPES) return PW_EAP_INVALID; + + return dv->value; +} + +/** Return an EAP-name for a particular type + * + * Resolve + */ +char const *eap_type2name(eap_type_t method) +{ + DICT_VALUE *dv; + + dv = dict_valbyattr(PW_EAP_TYPE, 0, method); + if (dv) { + return dv->name; + } + + return "unknown"; +} + +/* + * EAP packet format to be sent over the wire + * + * i.e. code+id+length+data where data = null/type+typedata + * based on code. + * + * INPUT to function is reply->code + * reply->id + * reply->type - setup with data + * + * OUTPUT reply->packet is setup with wire format, and will + * be allocated to the right size. + * + */ +int eap_wireformat(eap_packet_t *reply) +{ + eap_packet_raw_t *header; + uint16_t total_length = 0; + + if (!reply) return EAP_INVALID; + + /* + * If reply->packet is set, then the wire format + * has already been calculated, just succeed. + */ + if(reply->packet != NULL) return EAP_VALID; + + total_length = EAP_HEADER_LEN; + if (reply->code < 3) { + total_length += 1/* EAP Method */; + if (reply->type.data && reply->type.length > 0) { + total_length += reply->type.length; + } + } + + reply->packet = talloc_array(reply, uint8_t, total_length); + header = (eap_packet_raw_t *)reply->packet; + if (!header) { + return EAP_INVALID; + } + + header->code = (reply->code & 0xFF); + header->id = (reply->id & 0xFF); + + total_length = htons(total_length); + memcpy(header->length, &total_length, sizeof(total_length)); + + /* + * Request and Response packets are special. + */ + if ((reply->code == PW_EAP_REQUEST) || + (reply->code == PW_EAP_RESPONSE)) { + header->data[0] = (reply->type.num & 0xFF); + + /* + * Here since we cannot know the typedata format and length + * + * Type_data is expected to be wired by each EAP-Type + * + * Zero length/No typedata is supported as long as + * type is defined + */ + if (reply->type.data && reply->type.length > 0) { + memcpy(&header->data[1], reply->type.data, reply->type.length); + talloc_free(reply->type.data); + reply->type.data = reply->packet + EAP_HEADER_LEN + 1/*EAPtype*/; + } + } + + return EAP_VALID; +} + + +/* + * compose EAP reply packet in EAP-Message attr of RADIUS. If + * EAP exceeds 253, frame it in multiple EAP-Message attrs. + */ +int eap_basic_compose(RADIUS_PACKET *packet, eap_packet_t *reply) +{ + VALUE_PAIR *vp; + eap_packet_raw_t *eap_packet; + int rcode; + + if (eap_wireformat(reply) == EAP_INVALID) { + return RLM_MODULE_INVALID; + } + eap_packet = (eap_packet_raw_t *)reply->packet; + + fr_pair_delete_by_num(&(packet->vps), PW_EAP_MESSAGE, 0, TAG_ANY); + + vp = eap_packet2vp(packet, eap_packet); + if (!vp) return RLM_MODULE_INVALID; + fr_pair_add(&(packet->vps), vp); + + /* + * EAP-Message is always associated with + * Message-Authenticator but not vice-versa. + * + * Don't add a Message-Authenticator if it's already + * there. + */ + vp = fr_pair_find_by_num(packet->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY); + if (!vp) { + vp = fr_pair_afrom_num(packet, PW_MESSAGE_AUTHENTICATOR, 0); + vp->vp_length = AUTH_VECTOR_LEN; + vp->vp_octets = talloc_zero_array(vp, uint8_t, vp->vp_length); + + fr_pair_add(&(packet->vps), vp); + } + + /* Set request reply code, but only if it's not already set. */ + rcode = RLM_MODULE_OK; + if (!packet->code) switch (reply->code) { + case PW_EAP_RESPONSE: + case PW_EAP_SUCCESS: + packet->code = PW_CODE_ACCESS_ACCEPT; + rcode = RLM_MODULE_HANDLED; + break; + case PW_EAP_FAILURE: + packet->code = PW_CODE_ACCESS_REJECT; + rcode = RLM_MODULE_REJECT; + break; + case PW_EAP_REQUEST: + packet->code = PW_CODE_ACCESS_CHALLENGE; + rcode = RLM_MODULE_HANDLED; + break; + default: + /* Should never enter here */ + ERROR("rlm_eap: reply code %d is unknown, Rejecting the request.", reply->code); + packet->code = PW_CODE_ACCESS_REJECT; + break; + } + + return rcode; +} + + +VALUE_PAIR *eap_packet2vp(RADIUS_PACKET *packet, eap_packet_raw_t const *eap) +{ + int total, size; + uint8_t const *ptr; + VALUE_PAIR *head = NULL; + VALUE_PAIR *vp; + vp_cursor_t out; + + total = eap->length[0] * 256 + eap->length[1]; + + if (total == 0) { + DEBUG("Asked to encode empty EAP-Message!"); + return NULL; + } + + ptr = (uint8_t const *) eap; + + fr_cursor_init(&out, &head); + do { + size = total; + if (size > 253) size = 253; + + vp = fr_pair_afrom_num(packet, PW_EAP_MESSAGE, 0); + if (!vp) { + fr_pair_list_free(&head); + return NULL; + } + fr_pair_value_memcpy(vp, ptr, size); + + fr_cursor_insert(&out, vp); + + ptr += size; + total -= size; + } while (total > 0); + + return head; +} + + +/* + * Handles multiple EAP-Message attrs + * ie concatenates all to get the complete EAP packet. + * + * NOTE: Sometimes Framed-MTU might contain the length of EAP-Message, + * refer fragmentation in rfc2869. + */ +eap_packet_raw_t *eap_vp2packet(TALLOC_CTX *ctx, VALUE_PAIR *vps) +{ + VALUE_PAIR *first, *i; + eap_packet_raw_t *eap_packet; + unsigned char *ptr; + uint16_t len; + int total_len; + vp_cursor_t cursor; + + /* + * Get only EAP-Message attribute list + */ + first = fr_pair_find_by_num(vps, PW_EAP_MESSAGE, 0, TAG_ANY); + if (!first) { + fr_strerror_printf("EAP-Message not found"); + return NULL; + } + + /* + * Sanity check the length before doing anything. + */ + if (first->vp_length < 4) { + fr_strerror_printf("EAP packet is too short"); + return NULL; + } + + /* + * Get the Actual length from the EAP packet + * First EAP-Message contains the EAP packet header + */ + memcpy(&len, first->vp_strvalue + 2, sizeof(len)); + len = ntohs(len); + + /* + * Take out even more weird things. + */ + if (len < 4) { + fr_strerror_printf("EAP packet has invalid length (less than 4 bytes)"); + return NULL; + } + + /* + * Sanity check the length, BEFORE allocating memory. + */ + total_len = 0; + fr_cursor_init(&cursor, &first); + while ((i = fr_cursor_next_by_num(&cursor, PW_EAP_MESSAGE, 0, TAG_ANY))) { + total_len += i->vp_length; + + if (total_len > len) { + fr_strerror_printf("Malformed EAP packet. Length in packet header %i, " + "does not match actual length %i", len, total_len); + return NULL; + } + } + + /* + * If the length is SMALLER, die, too. + */ + if (total_len < len) { + fr_strerror_printf("Malformed EAP packet. Length in packet header does not " + "match actual length"); + return NULL; + } + + /* + * Now that we know the lengths are OK, allocate memory. + */ + eap_packet = (eap_packet_raw_t *) talloc_zero_array(ctx, uint8_t, len); + if (!eap_packet) { + return NULL; + } + + /* + * Copy the data from EAP-Message's over to our EAP packet. + */ + ptr = (unsigned char *)eap_packet; + + /* RADIUS ensures order of attrs, so just concatenate all */ + fr_cursor_first(&cursor); + while ((i = fr_cursor_next_by_num(&cursor, PW_EAP_MESSAGE, 0, TAG_ANY))) { + memcpy(ptr, i->vp_strvalue, i->vp_length); + ptr += i->vp_length; + } + + return eap_packet; +} + +/* + * Add raw hex data to the reply. + */ +void eap_add_reply(REQUEST *request, + char const *name, uint8_t const *value, int len) +{ + VALUE_PAIR *vp; + + vp = pair_make_reply(name, NULL, T_OP_EQ); + if (!vp) { + REDEBUG("Did not create attribute %s: %s\n", + name, fr_strerror()); + return; + } + + fr_pair_value_memcpy(vp, value, len); +} diff --git a/src/modules/rlm_eap/libeap/eapcrypto.c b/src/modules/rlm_eap/libeap/eapcrypto.c new file mode 100644 index 0000000..f57714b --- /dev/null +++ b/src/modules/rlm_eap/libeap/eapcrypto.c @@ -0,0 +1,301 @@ +/* + * eapcrypto.c Common key derivation routines for EAP/SIM. + * + * The development of the EAP/SIM support was funded by Internet Foundation + * Austria (http://www.nic.at/ipa). + * + * Version: $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * Copyright 2003 Michael Richardson <mcr@sandelman.ottawa.on.ca> + * Copyright 2003,2006 The FreeRADIUS server project + * + */ + +RCSID("$Id$") + +#include <stdio.h> +#include <stdlib.h> + +#include "eap_types.h" +#include "eap_sim.h" +#include <freeradius-devel/sha1.h> + +void eapsim_calculate_keys(struct eapsim_keys *ek) +{ + fr_sha1_ctx context; + uint8_t fk[160]; + unsigned char buf[256]; + unsigned char *p; + unsigned int blen; + + p = buf; + memcpy(p, ek->identity, ek->identitylen); p = p+ek->identitylen; + memcpy(p, ek->Kc[0], EAPSIM_KC_SIZE); p = p+EAPSIM_KC_SIZE; + memcpy(p, ek->Kc[1], EAPSIM_KC_SIZE); p = p+EAPSIM_KC_SIZE; + memcpy(p, ek->Kc[2], EAPSIM_KC_SIZE); p = p+EAPSIM_KC_SIZE; + memcpy(p, ek->nonce_mt, sizeof(ek->nonce_mt)); p=p+sizeof(ek->nonce_mt); + memcpy(p, ek->versionlist, ek->versionlistlen);p=p+ek->versionlistlen; + memcpy(p, ek->versionselect, sizeof(ek->versionselect)); p=p+sizeof(ek->versionselect); + /* *p++ = ek->versionselect[1]; */ + + blen = p - buf; + +#if defined(TEST_CASE) || defined(DUMP_EAPSIM_KEYS) + { + unsigned int i, j, k; + + j=0; k=0; + + printf("SHA1buffer was: "); + for (i = 0; i < blen; i++) { + if(j==4) { + printf("_"); + j=0; + } + if(k==20) { + printf("\n "); + k=0; + j=0; + } + j++; + k++; + + printf("%02x", buf[i]); + } + printf("\n"); + } +#endif + + + /* do the master key first */ + fr_sha1_init(&context); + fr_sha1_update(&context, buf, blen); + fr_sha1_final(ek->master_key, &context); + + /* + * now use the PRF to expand it, generated K_aut, K_encr, + * MSK and EMSK. + */ + fips186_2prf(ek->master_key, fk); + + /* split up the result */ + memcpy(ek->K_encr, fk + 0, 16); /* 128 bits for encryption */ + memcpy(ek->K_aut, fk + 16, EAPSIM_AUTH_SIZE); /*128 bits for auth */ + memcpy(ek->msk, fk + 32, 64); /* 64 bytes for Master Session Key */ + memcpy(ek->emsk, fk + 96, 64); /* 64- extended Master Session Key */ +} + + +void eapsim_dump_mk(struct eapsim_keys *ek) +{ + unsigned int i, j, k; + + printf("Input was: \n"); + printf(" identity: (len=%u)", ek->identitylen); + for (i = 0; i < ek->identitylen; i++) { + printf("%02x", ek->identity[i]); + } + + printf("\n nonce_mt: "); + for (i = 0; i < EAPSIM_NONCEMT_SIZE; i++) { + printf("%02x", ek->nonce_mt[i]); + } + + for (k = 0; k<3; k++) { + printf("\n rand%u: ", k); + for (i = 0; i < EAPSIM_RAND_SIZE; i++) { + printf("%02x", ek->rand[k][i]); + } + } + + for (k = 0; k<3; k++) { + printf("\n sres%u: ", k); + for (i = 0; i < EAPSIM_SRES_SIZE; i++) { + printf("%02x", ek->sres[k][i]); + } + } + + for (k = 0; k<3; k++) { + printf("\n Kc%u: ", k); + for (i = 0; i < EAPSIM_KC_SIZE; i++) { + printf("%02x", ek->Kc[k][i]); + } + } + + printf("\n versionlist[%d]: ",ek->versionlistlen); + for (i = 0; i < ek->versionlistlen; i++) { + printf("%02x", ek->versionlist[i]); + } + + printf("\n select %02x %02x\n", + ek->versionselect[0], + ek->versionselect[1]); + + printf("\n\nOutput\n"); + + printf("mk: "); + j=0; + for (i = 0; i < sizeof(ek->master_key); i++) { + if(j==4) { + printf("_"); + j=0; + } + j++; + + printf("%02x", ek->master_key[i]); + } + + printf("\nK_aut: "); + j=0; + for (i = 0; i < sizeof(ek->K_aut); i++) { + if(j==4) { + printf("_"); + j=0; + } + j++; + + printf("%02x", ek->K_aut[i]); + } + + printf("\nK_encr: "); + j=0; + for (i = 0; i < sizeof(ek->K_encr); i++) { + if(j==4) { + printf("_"); + j=0; + } + j++; + + printf("%02x", ek->K_encr[i]); + } + + printf("\nmsk: "); + j=0; k=0; + for (i = 0; i < sizeof(ek->msk); i++) { + if(k==20) { + printf("\n "); + k=0; + j=0; + } + if(j==4) { + printf("_"); + j=0; + } + k++; + j++; + + printf("%02x", ek->msk[i]); + } + printf("\nemsk: "); + j=0; k=0; + for (i = 0; i < sizeof(ek->emsk); i++) { + if(k==20) { + printf("\n "); + k=0; + j=0; + } + if(j==4) { + printf("_"); + j=0; + } + k++; + j++; + + printf("%02x", ek->emsk[i]); + } + printf("\n"); +} + +#ifdef TEST_CASE + +#include <assert.h> + +struct eapsim_keys inputkey1 = { + {'e', 'a', 'p', 's','i','m' }, + 6, + 0x4d, 0x6c, 0x40, 0xde, 0x48, 0x3a, 0xdd, 0x99, /* nonce_mt */ + 0x50, 0x90, 0x2c, 0x40, 0x24, 0xce, 0x76, 0x5e, + 0x89, 0xab, 0xcd, 0xef, 0x89, 0xab, 0xcd, 0xef, /* chalX */ + 0x89, 0xab, 0xcd, 0xef, 0x89, 0xab, 0xcd, 0xef, + 0x9a, 0xbc, 0xde, 0xf8, 0x9a, 0xbc, 0xde, 0xf8, + 0x9a, 0xbc, 0xde, 0xf8, 0x9a, 0xbc, 0xde, 0xf8, + 0xab, 0xcd, 0xef, 0x89, 0xab, 0xcd, 0xef, 0x89, + 0xab, 0xcd, 0xef, 0x89, 0xab, 0xcd, 0xef, 0x89, + 0x12, 0x34, 0xab, 0xcd, /* sresX */ + 0x12, 0x34, 0xab, 0xcd, + 0x23, 0x4a, 0xbc, 0xd1, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, /* Kc */ + 0x10, 0x21, 0x32, 0x43, 0x54, 0x65, 0x76, 0x87, + 0x30, 0x41, 0x52, 0x63, 0x74, 0x85, 0x96, 0xa7, + {0x00, 0x02, 0x00, 0x01}, + 4, + 0x00, 0x01 , +}; + +struct eapsim_keys inputkey2 = { + {'1','2','4','4','0','7','0','1','0','0','0','0','0','0','0','1','@','e','a','p','s','i','m','.','f','o','o'}, + 27, + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, /* nonce_mt */ + 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* chalX */ + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + + 0xd1, 0xd2, 0xd3, 0xd4, /* SRES 1 */ + 0xe1, 0xe2, 0xe3, 0xe4, + 0xf1, 0xf2, 0xf3, 0xf4, + + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* Kc */ + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + /* {0x00, 0x02, 0x00, 0x01}, */ + {0x00, 0x01}, + 2, + 0x00, 0x01 , +}; + + + +main(int argc, char *argv[]) +{ + struct eapsim_keys *ek; + + ek = &inputkey1; + + eapsim_calculate_keys(ek); + eapsim_dump_mk(ek); + + ek = &inputkey2; + + eapsim_calculate_keys(ek); + eapsim_dump_mk(ek); +} +#endif + + + + + + +/* + * Local Variables: + * c-style: bsd + * End: + */ diff --git a/src/modules/rlm_eap/libeap/eapsimlib.c b/src/modules/rlm_eap/libeap/eapsimlib.c new file mode 100644 index 0000000..67e21b2 --- /dev/null +++ b/src/modules/rlm_eap/libeap/eapsimlib.c @@ -0,0 +1,508 @@ +/* + * eapsimlib.c based upon draft-haverinen-pppext-eap-sim-11.txt. + * + * The development of the EAP/SIM support was funded by Internet Foundation + * Austria (http://www.nic.at/ipa). + * + * code common to EAP-SIM clients and to servers. + * + * Version: $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * Copyright 2000-2003,2006 The FreeRADIUS server project + * Copyright 2003 Michael Richardson <mcr@sandelman.ottawa.on.ca> + */ + +/* + * EAP-SIM PACKET FORMAT + * ------- ------ ------ + * + * EAP Request and Response Packet Format + * --- ------- --- -------- ------ ------ + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Code | Identifier | Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type | SIM-Type | SIM-Length | value ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * with SIM-Type/SIM-Length/Value... repeating. SIM-Length is in units + * of 32 bits, and includes the Sim-Type/Sim-Length fields. + * + * The SIM-Type's are mapped to PW_EAP_SIM_BASE+Sim-type and + * unmapped by these functions. + * + */ + +RCSID("$Id$") + +#include <freeradius-devel/libradius.h> +#include "eap_types.h" +#include "eap_sim.h" +#include <freeradius-devel/sha1.h> + +/* + * given a radius request with many attributes in the EAP-SIM range, build + * them all into a single EAP-SIM body. + * + */ +int map_eapsim_basictypes(RADIUS_PACKET *r, eap_packet_t *ep) +{ + VALUE_PAIR *vp; + int encoded_size; + uint8_t *encodedmsg, *attr; + unsigned int id, eapcode; + uint8_t *macspace; + uint8_t const *append; + int appendlen; + unsigned char subtype; + vp_cursor_t cursor; + + macspace = NULL; + append = NULL; + appendlen = 0; + + /* + * encodedmsg is now an EAP-SIM message. + * it might be too big for putting into an EAP-Type-SIM + * + */ + subtype = (vp = fr_pair_find_by_num(r->vps, PW_EAP_SIM_SUBTYPE, 0, TAG_ANY)) ? + vp->vp_integer : EAPSIM_START; + + id = (vp = fr_pair_find_by_num(r->vps, PW_EAP_ID, 0, TAG_ANY)) ? + vp->vp_integer : ((int)getpid() & 0xff); + + eapcode = (vp = fr_pair_find_by_num(r->vps, PW_EAP_CODE, 0, TAG_ANY)) ? + vp->vp_integer : PW_EAP_REQUEST; + + /* + * take a walk through the attribute list to see how much space + * that we need to encode all of this. + */ + encoded_size = 0; + for (vp = fr_cursor_init(&cursor, &r->vps); + vp; + vp = fr_cursor_next(&cursor)) { + int roundedlen; + int vplen; + + if ((vp->da->attr < PW_EAP_SIM_BASE) || (vp->da->attr >= (PW_EAP_SIM_BASE + 256))) { + continue; + } + + vplen = vp->vp_length; + + /* + * the AT_MAC attribute is a bit different, when we get to this + * attribute, we pull the contents out, save it for later + * processing, set the size to 16 bytes (plus 2 bytes padding). + * + * At this point, we only care about the size. + */ + if(vp->da->attr == PW_EAP_SIM_MAC) { + vplen = 18; + } + + /* round up to next multiple of 4, after taking in + * account the type and length bytes + */ + roundedlen = (vplen + 2 + 3) & ~3; + encoded_size += roundedlen; + } + + if (ep->code != PW_EAP_SUCCESS) { + ep->code = eapcode; + } + + ep->id = (id & 0xff); + ep->type.num = PW_EAP_SIM; + + /* + * if no attributes were found, do very little. + * + */ + if (encoded_size == 0) { + encodedmsg = talloc_array(ep, uint8_t, 3); + /* FIX: could be NULL */ + + encodedmsg[0] = subtype; + encodedmsg[1] = 0; + encodedmsg[2] = 0; + + ep->type.length = 3; + ep->type.data = encodedmsg; + + return 1; + } + + + /* + * figured out the length, so allocate some space for the results. + * + * Note that we do not bother going through an "EAP" stage, which + * is a bit strange compared to the unmap, which expects to see + * an EAP-SIM virtual attributes. + * + * EAP is 1-code, 1-identifier, 2-length, 1-type = 5 overhead. + * + * SIM code adds a subtype, and 2 bytes of reserved = 3. + * + */ + encoded_size += 3; + encodedmsg = talloc_array(ep, uint8_t, encoded_size); + if (!encodedmsg) { + return 0; + } + memset(encodedmsg, 0, encoded_size); + + /* + * now walk the attributes again, sticking them in. + * + * we go three bytes into the encoded message, because there are two + * bytes of reserved, and we will fill the "subtype" in later. + * + */ + attr = encodedmsg+3; + + for (vp = fr_cursor_first(&cursor); vp; vp = fr_cursor_next(&cursor)) { + int roundedlen; + + if(vp->da->attr < PW_EAP_SIM_BASE || + vp->da->attr >= PW_EAP_SIM_BASE + 256) { + continue; + } + + /* + * the AT_MAC attribute is a bit different, when we get to this + * attribute, we pull the contents out, save it for later + * processing, set the size to 16 bytes (plus 2 bytes padding). + * + * At this point, we put in zeros, and remember where the + * sixteen bytes go. + */ + if(vp->da->attr == PW_EAP_SIM_MAC) { + roundedlen = 20; + memset(&attr[2], 0, 18); + macspace = &attr[4]; + append = vp->vp_octets; + appendlen = vp->vp_length; + } else { + roundedlen = (vp->vp_length + 2 + 3) & ~3; + memset(attr, 0, roundedlen); + memcpy(&attr[2], vp->vp_strvalue, vp->vp_length); + } + attr[0] = vp->da->attr - PW_EAP_SIM_BASE; + attr[1] = roundedlen >> 2; + + attr += roundedlen; + } + + encodedmsg[0] = subtype; + + ep->type.length = encoded_size; + ep->type.data = encodedmsg; + + /* + * if macspace was set and we have a key, + * then we should calculate the HMAC-SHA1 of the resulting EAP-SIM + * packet, appended with the value of append. + */ + vp = fr_pair_find_by_num(r->vps, PW_EAP_SIM_KEY, 0, TAG_ANY); + if(macspace != NULL && vp != NULL) { + unsigned char *buffer; + eap_packet_raw_t *hdr; + uint16_t hmaclen, total_length = 0; + unsigned char sha1digest[20]; + + total_length = EAP_HEADER_LEN + 1 + encoded_size; + hmaclen = total_length + appendlen; + buffer = talloc_array(r, uint8_t, hmaclen); + hdr = (eap_packet_raw_t *) buffer; + if (!hdr) { + talloc_free(encodedmsg); + return 0; + } + + hdr->code = eapcode & 0xFF; + hdr->id = (id & 0xFF); + total_length = htons(total_length); + memcpy(hdr->length, &total_length, sizeof(total_length)); + + hdr->data[0] = PW_EAP_SIM; + + /* copy the data */ + memcpy(&hdr->data[1], encodedmsg, encoded_size); + + /* copy the nonce */ + memcpy(&hdr->data[encoded_size+1], append, appendlen); + + /* HMAC it! */ + fr_hmac_sha1(sha1digest, buffer, hmaclen, vp->vp_octets, vp->vp_length); + + /* done with the buffer, free it */ + talloc_free(buffer); + + /* now copy the digest to where it belongs in the AT_MAC */ + /* note that it is truncated to 128-bits */ + memcpy(macspace, sha1digest, 16); + } + + /* if we had an AT_MAC and no key, then fail */ + if ((macspace != NULL) && !vp) { + if (encodedmsg != NULL) { + talloc_free(encodedmsg); + } + + return 0; + } + + return 1; +} + +/* + * given a radius request with an EAP-SIM body, decode it into TLV pairs + * + * return value is true if it succeeded, false if there was something + * wrong and the packet should be discarded. + * + */ +int unmap_eapsim_basictypes(RADIUS_PACKET *r, + uint8_t *attr, unsigned int attrlen) +{ + VALUE_PAIR *newvp; + int eapsim_attribute; + unsigned int eapsim_len; + int es_attribute_count; + + es_attribute_count = 0; + + /* big enough to have even a single attribute */ + if (attrlen < 5) { + fr_strerror_printf("EAP-Sim attribute too short: %d < 5", attrlen); + return 0; + } + + newvp = fr_pair_afrom_num(r, PW_EAP_SIM_SUBTYPE, 0); + if (!newvp) { + fr_strerror_printf("Failed creating EAP-SIM-Subtype"); + return 0; + } + + newvp->vp_integer = attr[0]; + newvp->vp_length = 1; + fr_pair_add(&(r->vps), newvp); + + /* + * EAP-SIM has a 1 octet of subtype, and 2 octets + * reserved. + */ + attr += 3; + attrlen -= 3; + + /* + * Loop over each attribute. The format is: + * + * 1 octet of type + * 1 octet of length (value 1..255) + * ((4 * length) - 2) octets of data. + */ + while (attrlen > 0) { + uint8_t *p; + + if (attrlen < 2) { + fr_strerror_printf("EAP-Sim attribute %d too short: %d < 2", es_attribute_count, attrlen); + return 0; + } + + if (!attr[1]) { + fr_strerror_printf("EAP-Sim attribute %d (no.%d) has no data", attr[0], + es_attribute_count); + return 0; + } + + eapsim_attribute = attr[0]; + eapsim_len = attr[1] * 4; + + /* + * The length includes the 2-byte header. + */ + if (eapsim_len > attrlen) { + fr_strerror_printf("EAP-Sim attribute %d (no.%d) has length longer than data (%d > %d)", + eapsim_attribute, es_attribute_count, eapsim_len, attrlen); + return 0; + } + + newvp = fr_pair_afrom_num(r, eapsim_attribute + PW_EAP_SIM_BASE, 0); + if (!newvp) { + /* + * RFC 4186 Section 8.1 says 0..127 are + * "non-skippable". If one such + * attribute is found and we don't + * understand it, the server has to send: + * + * EAP-Request/SIM/Notification packet with an + * (AT_NOTIFICATION code, which implies general failure ("General + * failure after authentication" (0), or "General failure" (16384), + * depending on the phase of the exchange), which terminates the + * authentication exchange. + */ + if (eapsim_attribute <= 127) { + fr_strerror_printf("Unknown mandatory attribute %d, failing", + eapsim_attribute); + return 0; + } + + } else { + /* + * It's known, ccount for header, and + * copy the value over. + */ + newvp->vp_length = eapsim_len - 2; + + newvp->vp_octets = p = talloc_array(newvp, uint8_t, newvp->vp_length); + memcpy(p, &attr[2], newvp->vp_length); + fr_pair_add(&(r->vps), newvp); + } + + /* advance pointers, decrement length */ + attr += eapsim_len; + attrlen -= eapsim_len; + es_attribute_count++; + } + + return 1; +} + +/* + * calculate the MAC for the EAP message, given the key. + * The "extra" will be appended to the EAP message and included in the + * HMAC. + * + */ +int eapsim_checkmac(TALLOC_CTX *ctx, VALUE_PAIR *rvps, uint8_t key[EAPSIM_AUTH_SIZE], uint8_t *extra, int extralen, + uint8_t calcmac[20]) +{ + int ret; + eap_packet_raw_t *e; + uint8_t *buffer; + int elen,len; + VALUE_PAIR *mac; + + mac = fr_pair_find_by_num(rvps, PW_EAP_SIM_MAC, 0, TAG_ANY); + + if(!mac || mac->vp_length != 18) { + /* can't check a packet with no AT_MAC attribute */ + return 0; + } + + /* get original copy of EAP message, note that it was sanitized + * to have a valid length, which we depend upon. + */ + e = eap_vp2packet(ctx, rvps); + if (!e) return 0; + + /* make copy big enough for everything */ + elen = (e->length[0] * 256) + e->length[1]; + len = elen + extralen; + + buffer = talloc_array(ctx, uint8_t, len); + if (!buffer) { + talloc_free(e); + return 0; + } + + memcpy(buffer, e, elen); + memcpy(buffer + elen, extra, extralen); + + /* + * now look for the AT_MAC attribute in the copy of the buffer + * and make sure that the checksum is zero. + * + */ + { + uint8_t *attr; + + /* first attribute is 8 bytes into the EAP packet. + * 4 bytes for EAP, 1 for type, 1 for subtype, 2 reserved. + */ + attr = buffer+8; + while(attr < (buffer+elen)) { + if (attr[0] == (PW_EAP_SIM_MAC - PW_EAP_SIM_BASE)) { + /* zero the data portion, after making sure + * the size is >=5. Maybe future versions. + * will use more bytes, so be liberal. + */ + if(attr[1] < 5) { + ret = 0; + goto done; + } + memset(&attr[4], 0, (attr[1]-1)*4); + } + /* advance the pointer */ + attr += attr[1]*4; + } + } + + /* now, HMAC-SHA1 it with the key. */ + fr_hmac_sha1(calcmac, buffer, len, key, 16); + + ret = memcmp(&mac->vp_strvalue[2], calcmac, 16) == 0 ? 1 : 0; + done: + talloc_free(e); + talloc_free(buffer); + return(ret); +} + +/* + * definitions changed to take a buffer for unknowns + * as this is more thread safe. + */ +static char const *simstates[] = { "init", "start", NULL }; + +char const *sim_state2name(enum eapsim_clientstates state, + char *statenamebuf, + int statenamebuflen) +{ + if(state >= EAPSIM_CLIENT_MAXSTATES) { + snprintf(statenamebuf, statenamebuflen, "eapstate:%d", state); + return statenamebuf; + } + + return simstates[state]; +} + +static char const *subtypes[] = { "subtype0", "subtype1", "subtype2", "subtype3", + "subtype4", "subtype5", "subtype6", "subtype7", + "subtype8", "subtype9", + "start", + "challenge", + "notification", + "reauth", + "client-error", + NULL }; + +char const *sim_subtype2name(enum eapsim_subtype subtype, char *subtypenamebuf, int subtypenamebuflen) +{ + if (subtype >= EAPSIM_MAX_SUBTYPE) { + snprintf(subtypenamebuf, subtypenamebuflen, "illegal-subtype:%d", subtype); + + return subtypenamebuf; + } + + return subtypes[subtype]; +} diff --git a/src/modules/rlm_eap/libeap/fips186prf.c b/src/modules/rlm_eap/libeap/fips186prf.c new file mode 100644 index 0000000..2002c62 --- /dev/null +++ b/src/modules/rlm_eap/libeap/fips186prf.c @@ -0,0 +1,270 @@ +/* + * fips186prf.c An implementation of the FIPS-186-2 SHA1-based PRF. + * + * The development of the EAP/SIM support was funded by Internet Foundation + * Austria (http://www.nic.at/ipa). + * + * This code was written from scratch by Michael Richardson, and it is + * dual licensed under both GPL and BSD. + * + * Version: $Id$ + * + * GPL notice: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * BSD notice: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Copyright 2003 Michael Richardson <mcr@sandelman.ottawa.on.ca> + * Copyright 2006 The FreeRADIUS server project + * + */ + +RCSID("$Id$") + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif + +#include <freeradius-devel/sha1.h> + +/* + * we do it in 8-bit chunks, because we have to keep the numbers + * in network byte order (i.e. MSB) + * + * make it a structure so that we can do structure assignments. + */ +typedef struct onesixty { + uint8_t p[20]; +} onesixty; + +static void onesixty_add_mod(onesixty *sum, onesixty *a, onesixty *b) +{ + uint32_t s; + int i, carry; + + carry = 0; + for(i=19; i>=0; i--) { +/* for(i=0; i<20; i++) { */ + s = a->p[i] + b->p[i] + carry; + sum->p[i] = s & 0xff; + carry = s >> 8; + } +} + +/* + * run the FIPS-186-2 PRF on the given Master Key (160 bits) + * in order to derive 1280 bits (160 bytes) of keying data from + * it. + * + * Given that in EAP-SIM, this is coming from a 64-bit Kc it seems + * like an awful lot of "randomness" to pull out.. (MCR) + * + */ +void fips186_2prf(uint8_t mk[20], uint8_t finalkey[160]) +{ + fr_sha1_ctx context; + int j; + onesixty xval, xkey, w_0, w_1, sum, one; + uint8_t *f; + uint8_t zeros[64]; + + /* + * let XKEY := MK, + * + * Step 3: For j = 0 to 3 do + * a. XVAL = XKEY + * b. w_0 = SHA1(XVAL) + * c. XKEY = (1 + XKEY + w_0) mod 2^160 + * d. XVAL = XKEY + * e. w_1 = SHA1(XVAL) + * f. XKEY = (1 + XKEY + w_1) mod 2^160 + * 3.3 x_j = w_0|w_1 + * + */ + memcpy(&xkey, mk, sizeof(xkey)); + + /* make the value 1 */ + memset(&one, 0, sizeof(one)); + one.p[19]=1; + + f=finalkey; + + for(j=0; j<4; j++) { + /* a. XVAL = XKEY */ + xval = xkey; + + /* b. w_0 = SHA1(XVAL) */ + fr_sha1_init(&context); + + memset(zeros + 20, 0, sizeof(zeros) - 20); + memcpy(zeros, xval.p, 20); +#ifndef WITH_OPENSSL_SHA1 + fr_sha1_transform(context.state, zeros); +#else + fr_sha1_transform(&context, zeros); +#endif + fr_sha1_final_no_len(w_0.p, &context); + + /* c. XKEY = (1 + XKEY + w_0) mod 2^160 */ + onesixty_add_mod(&sum, &xkey, &w_0); + onesixty_add_mod(&xkey, &sum, &one); + + /* d. XVAL = XKEY */ + xval = xkey; + + /* e. w_1 = SHA1(XVAL) */ + fr_sha1_init(&context); + + memset(zeros + 20, 0, sizeof(zeros) - 20); + memcpy(zeros, xval.p, 20); +#ifndef WITH_OPENSSL_SHA1 + fr_sha1_transform(context.state, zeros); +#else + fr_sha1_transform(&context, zeros); +#endif + fr_sha1_final_no_len(w_1.p, &context); + + /* f. XKEY = (1 + XKEY + w_1) mod 2^160 */ + onesixty_add_mod(&sum, &xkey, &w_1); + onesixty_add_mod(&xkey, &sum, &one); + + /* now store it away */ + memcpy(f, &w_0, 20); + f += 20; + + memcpy(f, &w_1, 20); + f += 20; + } +} + +/* + * test vectors + * from http://csrc.nist.gov/CryptoToolkit/dss/Examples-1024bit.pdf + * + * page 5 + * + * XKEY= bd029bbe 7f51960b cf9edb2b 61f06f0f eb5a38b6 + * XSEED= 00000000 00000000 00000000 00000000 00000000 + * + * + * The first loop through step 3.2 provides: + * + * XVAL= bd029bbe 7f51960b cf9edb2b 61f06f0f eb5a38b6 + * + * Using the routine in Appendix 3.3, Constructing The Function G From SHA-1, + * in step 3.2.b of the Change Notice algorithm for computing values of x + * provides: + * + * w[0]= 2070b322 3dba372f de1c0ffc 7b2e3b49 8b260614 + * + * + * The following value is the updated XKEY value from step 3.2.c: + * + * XKEY= dd734ee0 bd0bcd3b adbaeb27 dd1eaa59 76803ecb + * + * The second loop through step 3.2 provides: + * + * XVAL= dd734ee0 bd0bcd3b adbaeb27 dd1eaa59 76803ecb + * + * Using the routine in Appendix 3.3, Constructing The Function G From SHA-1, + * in step 3.2.b of the Change Notice algorithm for computing values of x + * provides: + * + * w[1]= 3c6c18ba cb0f6c55 babb1378 8e20d737 a3275116 + * + * The following value is the updated XKEY value from step 3.2.c: + * + * + * XKEY= 19df679b 881b3991 6875fea0 6b3f8191 19a78fe2 + * + * Step 3.3 provides the following values: + * + * w[0] || w[1]= 2070b322 3dba372f de1c0ffc 7b2e3b49 8b260614 + * 3c6c18ba cb0f6c55 babb1378 8e20d737 a3275116 + * + */ + +#ifdef TEST_CASE + +#include <assert.h> + +uint8_t mk[20]={ 0xbd, 0x02, 0x9b, 0xbe, 0x7f, 0x51, 0x96, 0x0b, + 0xcf, 0x9e, 0xdb, 0x2b, 0x61, 0xf0, 0x6f, 0x0f, + 0xeb, 0x5a, 0x38, 0xb6 }; + +main(int argc, char *argv[]) +{ + uint8_t finalkey[160]; + int i, j, k; + + fips186_2prf(mk, finalkey); + + printf("Input was: |"); + j=0; + for (i = 0; i < 20; i++) { + if(j==4) { + printf("_"); + j=0; + } + j++; + + printf("%02x", mk[i]); + } + + printf("|\nOutput was: "); + j=0; k=0; + for (i = 0; i < 160; i++) { + if(k==20) { + printf("\n "); + k=0; + j=0; + } + if(j==4) { + printf("_"); + j=0; + } + k++; + j++; + + printf("%02x", finalkey[i]); + } + printf("\n"); +} +#endif diff --git a/src/modules/rlm_eap/libeap/mppe_keys.c b/src/modules/rlm_eap/libeap/mppe_keys.c new file mode 100644 index 0000000..385441c --- /dev/null +++ b/src/modules/rlm_eap/libeap/mppe_keys.c @@ -0,0 +1,384 @@ +/* + * mppe_keys.c + * + * Version: $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * Copyright 2002 Axis Communications AB + * Copyright 2006 The FreeRADIUS server project + * Authors: Henrik Eriksson <henriken@axis.com> & Lars Viklund <larsv@axis.com> + */ + +RCSID("$Id$") +USES_APPLE_DEPRECATED_API /* OpenSSL API has been deprecated by Apple */ + +#include "eap_tls.h" +#include <openssl/ssl.h> +#include <openssl/hmac.h> +#include <freeradius-devel/openssl3.h> + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#include <openssl/provider.h> +#endif + +/* + * TLS P_hash from RFC 2246/5246 section 5 + */ +static void P_hash(EVP_MD const *evp_md, + unsigned char const *secret, unsigned int secret_len, + unsigned char const *seed, unsigned int seed_len, + unsigned char *out, unsigned int out_len) +{ + HMAC_CTX *ctx_a, *ctx_out; + unsigned char a[EVP_MAX_MD_SIZE]; + unsigned int size = EVP_MAX_MD_SIZE; + unsigned int digest_len; + + ctx_a = HMAC_CTX_new(); + ctx_out = HMAC_CTX_new(); + HMAC_Init_ex(ctx_a, secret, secret_len, evp_md, NULL); + HMAC_Init_ex(ctx_out, secret, secret_len, evp_md, NULL); + + /* Calculate A(1) */ + HMAC_Update(ctx_a, seed, seed_len); + HMAC_Final(ctx_a, a, &size); + + while (1) { + /* Calculate next part of output */ + HMAC_Update(ctx_out, a, size); + HMAC_Update(ctx_out, seed, seed_len); + + /* Check if last part */ + if (out_len < size) { + digest_len = EVP_MAX_MD_SIZE; + HMAC_Final(ctx_out, a, &digest_len); + memcpy(out, a, out_len); + break; + } + + /* Place digest in output buffer */ + digest_len = EVP_MAX_MD_SIZE; + HMAC_Final(ctx_out, out, &digest_len); + HMAC_Init_ex(ctx_out, NULL, 0, NULL, NULL); + out += size; + out_len -= size; + + /* Calculate next A(i) */ + HMAC_Init_ex(ctx_a, NULL, 0, NULL, NULL); + HMAC_Update(ctx_a, a, size); + digest_len = EVP_MAX_MD_SIZE; + HMAC_Final(ctx_a, a, &digest_len); + } + + HMAC_CTX_free(ctx_a); + HMAC_CTX_free(ctx_out); + memset(a, 0, sizeof(a)); +} + +/* + * TLS PRF from RFC 2246 section 5 + */ +static void PRF(unsigned char const *secret, unsigned int secret_len, + unsigned char const *seed, unsigned int seed_len, + unsigned char *out, unsigned int out_len) +{ + uint8_t buf[out_len + (out_len % SHA_DIGEST_LENGTH)]; + unsigned int i; + + unsigned int len = (secret_len + 1) / 2; + uint8_t const *s1 = secret; + uint8_t const *s2 = secret + (secret_len - len); + + EVP_MD const *md5 = NULL; + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + EVP_MD *md5_to_free = NULL; + + /* + * If we are using OpenSSL >= 3.0 and FIPS mode is + * enabled, we need to load the default provider in a + * standalone context in order to access MD5. + */ + OSSL_LIB_CTX *libctx = NULL; + OSSL_PROVIDER *default_provider = NULL; + + if (EVP_default_properties_is_fips_enabled(NULL)) { + libctx = OSSL_LIB_CTX_new(); + default_provider = OSSL_PROVIDER_load(libctx, "default"); + + if (!default_provider) { + ERROR("Failed loading OpenSSL default provider."); + return; + } + + md5_to_free = EVP_MD_fetch(libctx, "MD5", NULL); + if (!md5_to_free) { + ERROR("Failed loading OpenSSL MD5 function."); + return; + } + + md5 = md5_to_free; + } else { + md5 = EVP_md5(); + } +#else + md5 = EVP_md5(); +#endif + + P_hash(md5, s1, len, seed, seed_len, out, out_len); + P_hash(EVP_sha1(), s2, len, seed, seed_len, buf, out_len); + + for (i = 0; i < out_len; i++) { + out[i] ^= buf[i]; + } + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + if (libctx) { + OSSL_PROVIDER_unload(default_provider); + OSSL_LIB_CTX_free(libctx); + EVP_MD_free(md5_to_free); + } +#endif +} + +/* + * TLS 1.2 PRF from RFC 5246 section 5 + */ +static void PRFv12(unsigned char const *secret, unsigned int secret_len, + unsigned char const *seed, unsigned int seed_len, + unsigned char *out, unsigned int out_len) +{ + P_hash(EVP_sha256(), secret, secret_len, seed, seed_len, out, out_len); +} + +/* EAP-FAST Pseudo-Random Function (T-PRF): RFC 4851, Section 5.5 */ +void T_PRF(unsigned char const *secret, unsigned int secret_len, + char const *prf_label, + unsigned char const *seed, unsigned int seed_len, + unsigned char *out, unsigned int out_len) +{ + size_t prf_size = strlen(prf_label); + size_t pos; + uint8_t *buf; + + if (prf_size > 128) prf_size = 128; + prf_size++; /* include trailing zero */ + + buf = talloc_size(NULL, SHA1_DIGEST_LENGTH + prf_size + seed_len + 2 + 1); + + memcpy(buf + SHA1_DIGEST_LENGTH, prf_label, prf_size); + if (seed) memcpy(buf + SHA1_DIGEST_LENGTH + prf_size, seed, seed_len); + *(uint16_t *)&buf[SHA1_DIGEST_LENGTH + prf_size + seed_len] = htons(out_len); + buf[SHA1_DIGEST_LENGTH + prf_size + seed_len + 2] = 1; + + // T1 is just the seed + fr_hmac_sha1(buf, buf + SHA1_DIGEST_LENGTH, prf_size + seed_len + 2 + 1, secret, secret_len); + +#define MIN(a,b) (((a)>(b)) ? (b) : (a)) + memcpy(out, buf, MIN(out_len, SHA1_DIGEST_LENGTH)); + + pos = SHA1_DIGEST_LENGTH; + while (pos < out_len) { + buf[SHA1_DIGEST_LENGTH + prf_size + seed_len + 2]++; + + fr_hmac_sha1(buf, buf, SHA1_DIGEST_LENGTH + prf_size + seed_len + 2 + 1, secret, secret_len); + memcpy(&out[pos], buf, MIN(out_len - pos, SHA1_DIGEST_LENGTH)); + + if (out_len - pos <= SHA1_DIGEST_LENGTH) + break; + + pos += SHA1_DIGEST_LENGTH; + } + + memset(buf, 0, SHA1_DIGEST_LENGTH + prf_size + seed_len + 2 + 1); + talloc_free(buf); +} + +#define EAPTLS_MPPE_KEY_LEN 32 + +/* + * Generate keys according to RFC 2716 and add to reply + */ +void eaptls_gen_mppe_keys(REQUEST *request, SSL *s, char const *label, uint8_t const *context, UNUSED size_t context_size) +{ + uint8_t out[4 * EAPTLS_MPPE_KEY_LEN]; + uint8_t *p; + size_t len; + + len = strlen(label); + +#if OPENSSL_VERSION_NUMBER >= 0x10001000L + if (SSL_export_keying_material(s, out, sizeof(out), label, len, context, context_size, context != NULL) != 1) { + ERROR("Failed generating keying material"); + return; + } +#else + { + uint8_t seed[64 + (2 * SSL3_RANDOM_SIZE) + (context ? 2 + context_size : 0)]; + uint8_t buf[4 * EAPTLS_MPPE_KEY_LEN]; + + p = seed; + + memcpy(p, label, len); + p += len; + + memcpy(p, s->s3->client_random, SSL3_RANDOM_SIZE); + p += SSL3_RANDOM_SIZE; + len += SSL3_RANDOM_SIZE; + + memcpy(p, s->s3->server_random, SSL3_RANDOM_SIZE); + p += SSL3_RANDOM_SIZE; + len += SSL3_RANDOM_SIZE; + + if (context) { + /* cloned and reversed FR_PUT_LE16 */ + p[0] = ((uint16_t) (context_size)) >> 8; + p[1] = ((uint16_t) (context_size)) & 0xff; + p += 2; + len += 2; + memcpy(p, context, context_size); + p += context_size; + len += context_size; + } + + PRF(s->session->master_key, s->session->master_key_length, + seed, len, out, buf, sizeof(out)); + } +#endif + + p = out; + eap_add_reply(request, "MS-MPPE-Recv-Key", p, EAPTLS_MPPE_KEY_LEN); + p += EAPTLS_MPPE_KEY_LEN; + eap_add_reply(request, "MS-MPPE-Send-Key", p, EAPTLS_MPPE_KEY_LEN); + + eap_add_reply(request, "EAP-MSK", out, 64); + eap_add_reply(request, "EAP-EMSK", out + 64, 64); +} + + +#define FR_TLS_PRF_CHALLENGE "ttls challenge" + +/* + * Generate the TTLS challenge + * + * It's in the TLS module simply because it's only a few lines + * of code, and it needs access to the TLS PRF functions. + */ +void eapttls_gen_challenge(SSL *s, uint8_t *buffer, size_t size) +{ +#if OPENSSL_VERSION_NUMBER >= 0x10001000L + if (SSL_export_keying_material(s, buffer, size, FR_TLS_PRF_CHALLENGE, + sizeof(FR_TLS_PRF_CHALLENGE)-1, NULL, 0, 0) != 1) { + ERROR("Failed generating keying material"); + } +#else + uint8_t out[32], buf[32]; + uint8_t seed[sizeof(FR_TLS_PRF_CHALLENGE)-1 + 2*SSL3_RANDOM_SIZE]; + uint8_t *p = seed; + + memcpy(p, FR_TLS_PRF_CHALLENGE, sizeof(FR_TLS_PRF_CHALLENGE)-1); + p += sizeof(FR_TLS_PRF_CHALLENGE)-1; + memcpy(p, s->s3->client_random, SSL3_RANDOM_SIZE); + p += SSL3_RANDOM_SIZE; + memcpy(p, s->s3->server_random, SSL3_RANDOM_SIZE); + + PRF(s->session->master_key, s->session->master_key_length, + seed, sizeof(seed), out, buf, sizeof(out)); + memcpy(buffer, out, size); +#endif +} + +#define FR_TLS_EXPORTER_METHOD_ID "EXPORTER_EAP_TLS_Method-Id" + +/* + * Actually generates EAP-Session-Id, which is an internal server + * attribute. Not all systems want to send EAP-Key-Name. + */ +void eaptls_gen_eap_key(eap_handler_t *handler) +{ + RADIUS_PACKET *packet = handler->request->reply; + tls_session_t *tls_session = handler->opaque; + SSL *s = tls_session->ssl; + VALUE_PAIR *vp; + uint8_t *buff, *p; + uint8_t type = handler->type & 0xff; + + vp = fr_pair_afrom_num(packet, PW_EAP_SESSION_ID, 0); + if (!vp) return; + + vp->vp_length = 1 + 2 * SSL3_RANDOM_SIZE; + buff = p = talloc_array(vp, uint8_t, vp->vp_length); + + *p++ = type; + + switch (SSL_version(tls_session->ssl)) { + case TLS1_VERSION: + case TLS1_1_VERSION: + case TLS1_2_VERSION: + SSL_get_client_random(s, p, SSL3_RANDOM_SIZE); + p += SSL3_RANDOM_SIZE; + SSL_get_server_random(s, p, SSL3_RANDOM_SIZE); + break; +#if OPENSSL_VERSION_NUMBER >= 0x10100000L +#ifdef TLS1_3_VERSION + case TLS1_3_VERSION: +#endif + default: + { + uint8_t const context[] = { type }; + + if (SSL_export_keying_material(s, p, 2 * SSL3_RANDOM_SIZE, + FR_TLS_EXPORTER_METHOD_ID, sizeof(FR_TLS_EXPORTER_METHOD_ID)-1, + context, sizeof(context), 1) != 1) { + ERROR("Failed generating keying material"); + return; + } + } +#endif + } + + vp->vp_octets = buff; + fr_pair_add(&packet->vps, vp); +} + +/* + * Same as before, but for EAP-FAST the order of {server,client}_random is flipped + */ +void eap_fast_tls_gen_challenge(SSL *s, int version, uint8_t *buffer, size_t size, char const *prf_label) +{ + uint8_t *p; + size_t len, master_key_len; + uint8_t seed[128 + 2*SSL3_RANDOM_SIZE]; + uint8_t master_key[SSL_MAX_MASTER_KEY_LENGTH]; + + len = strlen(prf_label); + if (len > 128) len = 128; + + p = seed; + memcpy(p, prf_label, len); + p += len; + SSL_get_server_random(s, p, SSL3_RANDOM_SIZE); + p += SSL3_RANDOM_SIZE; + SSL_get_client_random(s, p, SSL3_RANDOM_SIZE); + p += SSL3_RANDOM_SIZE; + + master_key_len = SSL_SESSION_get_master_key(SSL_get_session(s), master_key, sizeof(master_key)); + + if (version == TLS1_2_VERSION) + PRFv12(master_key, master_key_len, seed, p - seed, buffer, size); + else + PRF(master_key, master_key_len, seed, p - seed, buffer, size); +} |