summaryrefslogtreecommitdiffstats
path: root/src/modules/rlm_eap/libeap
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/rlm_eap/libeap')
-rw-r--r--src/modules/rlm_eap/libeap/all.mk10
-rw-r--r--src/modules/rlm_eap/libeap/comp128.c460
-rw-r--r--src/modules/rlm_eap/libeap/comp128.h11
-rw-r--r--src/modules/rlm_eap/libeap/eap_chbind.c290
-rw-r--r--src/modules/rlm_eap/libeap/eap_chbind.h64
-rw-r--r--src/modules/rlm_eap/libeap/eap_sim.h122
-rw-r--r--src/modules/rlm_eap/libeap/eap_tls.c1206
-rw-r--r--src/modules/rlm_eap/libeap/eap_tls.h109
-rw-r--r--src/modules/rlm_eap/libeap/eap_types.h162
-rw-r--r--src/modules/rlm_eap/libeap/eapclient.h8
-rw-r--r--src/modules/rlm_eap/libeap/eapcommon.c401
-rw-r--r--src/modules/rlm_eap/libeap/eapcrypto.c301
-rw-r--r--src/modules/rlm_eap/libeap/eapsimlib.c508
-rw-r--r--src/modules/rlm_eap/libeap/fips186prf.c270
-rw-r--r--src/modules/rlm_eap/libeap/mppe_keys.c384
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..0d92f67
--- /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[EAPSIM_AUTH_SIZE],
+ 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);
+}