2 * state.c To generate and verify State attribute
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright 2001 hereUare Communications, Inc. <raghud@hereuare.com>
21 * Copyright 2006 The FreeRADIUS server project
24 #include <freeradius-devel/ident.h>
33 * Global key to generate & verify State
35 * This is needed only once per instance of the server,
36 * and putting it in the rlm_eap_t is just too much effort.
38 * Putting it here is ugly, but it works.
40 static int key_initialized = 0;
41 static unsigned char state_key[AUTH_VECTOR_LEN];
44 * Generate & Verify the State attribute
46 * In the simplest implementation, we would just use the
47 * challenge as state. Unfortunately, the RADIUS secret protects
48 * only the User-Password attribute; an attacker that can remove
49 * packets from the wire and insert new ones can simply insert a
50 * replayed state without having to know the secret.
52 * However, RADIUS packets containing EAP conversations MUST be
53 * signed with Message-Authenticator, at which point, they MUST
54 * know the secret, in order to get to the EAP module. And if
55 * they know the secret, they can do many worse things than
56 * re-playing a State attribute. Their only alternative is to
57 * re-play entire packets, which is caught by the server core.
59 * In any case, we sign our state with data unique to this
60 * specific request. A NAS would use the Request Authenticator,
61 * we don't know what that will be when the State is returned to
62 * us, so we'll use a time stamp.
64 * Our replay prevention is limited to a time interval
65 * (inst->maxdelay). We could keep track of all challenges
66 * issued over that time interval, to ensure that the challenges
67 * were unique. However, they're 8-bytes of data from a good
68 * PRNG, which means that it's pretty damn unlikely that they'll
71 * Our state, then, is (challenge + hmac(challenge + time, key)),
72 * where '+' denotes concatentation, 'challenge' is the octets
73 * of the challenge, 'time' is the 'time_t' in host byte order,
74 * and 'key' is a random key, generated once in eap_init().
76 * This means that only the server which generates a challenge
77 * can verify it; this should be OK if your NAS's load balance
78 * across RADIUS servers by a "first available" algorithm. If
79 * your NAS's round-robin (ugh), you could use the RADIUS
80 * secret instead, but read RFC 2104 first, and make very sure
81 * you really want to do this.
83 void generate_key(void)
87 if (key_initialized) return;
90 * Use a cryptographically strong method to generate
91 * pseudo-random numbers.
93 for (i = 0; i < sizeof(state_key); i++) {
94 state_key[i] = lrad_rand();
101 * For clarity. Also, to avoid giving away
102 * too much information, we only put 8 octets of the HMAC
103 * into the State attribute, instead of all 16.
105 * As a security feature, it's a little hokey, but WTF.
107 * Also, ensure that EAP_CHALLENGE_LEN + EAP_USE_OF_HMAC = EAP_STATE_LEN
109 #define EAP_CHALLENGE_LEN (8)
110 #define EAP_HMAC_SIZE (16)
111 #define EAP_USE_OF_HMAC (8)
114 * Our state, is (challenge + time + hmac(challenge + time, key))
116 * If it's too long, then some clients chop it (sigh)
118 VALUE_PAIR *generate_state(time_t timestamp)
121 unsigned char challenge[EAP_CHALLENGE_LEN];
122 unsigned char hmac[EAP_HMAC_SIZE];
123 unsigned char value[EAP_CHALLENGE_LEN + sizeof(timestamp)];
126 /* Generate challenge (a random value). */
127 for (i = 0; i < sizeof(challenge); i++) {
128 challenge[i] = lrad_rand();
131 memcpy(value, challenge, sizeof(challenge));
132 memcpy(value + sizeof(challenge), ×tamp, sizeof(timestamp));
135 * hmac(challenge + timestamp)
137 lrad_hmac_md5(value, sizeof(value),
138 state_key, sizeof(state_key), hmac);
141 * Create the state attribute.
143 * Note that the timestamp is used internally, but is NOT
144 * sent to the client!
146 state = paircreate(PW_STATE, PW_TYPE_OCTETS);
148 radlog(L_ERR, "rlm_eap: out of memory");
151 memcpy(state->vp_strvalue, challenge, sizeof(challenge));
152 memcpy(state->vp_strvalue + sizeof(challenge), hmac,
155 state->length = sizeof(challenge) + EAP_USE_OF_HMAC;
161 * Returns 0 on success, non-zero otherwise.
163 int verify_state(VALUE_PAIR *state, time_t timestamp)
165 unsigned char hmac[EAP_HMAC_SIZE];
166 unsigned char value[EAP_CHALLENGE_LEN + sizeof(timestamp)];
169 * The length is wrong. Don't do anything.
171 if (state->length != EAP_STATE_LEN) {
176 * The first 16 octets of the State attribute constains
177 * the random challenge.
179 memcpy(value, state->vp_strvalue, EAP_CHALLENGE_LEN);
180 memcpy(value + EAP_CHALLENGE_LEN, ×tamp, sizeof(timestamp));
183 lrad_hmac_md5(value, sizeof(value),
184 state_key, sizeof(state_key), hmac);
187 * Compare the hmac we calculated to the one in the
190 return memcmp(hmac, state->vp_strvalue + EAP_CHALLENGE_LEN,