import from branch_1_1:
[freeradius.git] / src / modules / rlm_eap / state.c
1 /*
2  * state.c  To generate and verify State attribute
3  *
4  * $Id$
5  *
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.
10  *
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.
15  *
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
19  *
20  * Copyright 2001  hereUare Communications, Inc. <raghud@hereuare.com>
21  * Copyright 2006  The FreeRADIUS server project
22  */
23
24 #include <freeradius-devel/ident.h>
25 RCSID("$Id$")
26
27 #include <fcntl.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include "rlm_eap.h"
31
32 /*
33  *      Global key to generate & verify State
34  *
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.
37  *
38  *      Putting it here is ugly, but it works.
39  */
40 static int key_initialized = 0;
41 static unsigned char state_key[AUTH_VECTOR_LEN];
42
43 /*
44  *      Generate & Verify the State attribute
45  *
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.
51  *
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.
58  *
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.
63  *
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
69  *      be re-used.
70  *
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().
75  *
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.
82  */
83 void generate_key(void)
84 {
85         unsigned int i;
86
87         if (key_initialized) return;
88
89         /*
90          *      Use a cryptographically strong method to generate
91          *      pseudo-random numbers.
92          */
93         for (i = 0; i < sizeof(state_key); i++) {
94                 state_key[i] = lrad_rand();
95         }
96
97         key_initialized = 1;
98 }
99
100 /*
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.
104  *
105  *      As a security feature, it's a little hokey, but WTF.
106  *
107  *      Also, ensure that EAP_CHALLENGE_LEN + EAP_USE_OF_HMAC = EAP_STATE_LEN
108  */
109 #define EAP_CHALLENGE_LEN (8)
110 #define EAP_HMAC_SIZE (16)
111 #define EAP_USE_OF_HMAC (8)
112
113 /*
114  * Our state, is (challenge + time + hmac(challenge + time, key))
115  *
116  *  If it's too long, then some clients chop it (sigh)
117  */
118 VALUE_PAIR *generate_state(time_t timestamp)
119 {
120         unsigned int i;
121         unsigned char challenge[EAP_CHALLENGE_LEN];
122         unsigned char hmac[EAP_HMAC_SIZE];
123         unsigned char value[EAP_CHALLENGE_LEN + sizeof(timestamp)];
124         VALUE_PAIR    *state;
125
126         /* Generate challenge (a random value).  */
127         for (i = 0; i < sizeof(challenge); i++) {
128                 challenge[i] = lrad_rand();
129         }
130
131         memcpy(value, challenge, sizeof(challenge));
132         memcpy(value + sizeof(challenge), &timestamp, sizeof(timestamp));
133
134         /*
135          *      hmac(challenge + timestamp)
136          */
137         lrad_hmac_md5(value, sizeof(value),
138                       state_key, sizeof(state_key), hmac);
139
140         /*
141          *      Create the state attribute.
142          *
143          *      Note that the timestamp is used internally, but is NOT
144          *      sent to the client!
145          */
146         state = paircreate(PW_STATE, PW_TYPE_OCTETS);
147         if (state == NULL) {
148                 radlog(L_ERR, "rlm_eap: out of memory");
149                 return NULL;
150         }
151         memcpy(state->vp_strvalue, challenge, sizeof(challenge));
152         memcpy(state->vp_strvalue + sizeof(challenge), hmac,
153                EAP_USE_OF_HMAC);
154
155         state->length = sizeof(challenge) + EAP_USE_OF_HMAC;
156
157         return state;
158 }
159
160 /*
161  * Returns 0 on success, non-zero otherwise.
162  */
163 int verify_state(VALUE_PAIR *state, time_t timestamp)
164 {
165         unsigned char hmac[EAP_HMAC_SIZE];
166         unsigned char value[EAP_CHALLENGE_LEN + sizeof(timestamp)];
167
168         /*
169          *      The length is wrong.  Don't do anything.
170          */
171         if (state->length != EAP_STATE_LEN) {
172                 return -1;
173         }
174
175         /*
176          *      The first 16 octets of the State attribute constains
177          *      the random challenge.
178          */
179         memcpy(value, state->vp_strvalue, EAP_CHALLENGE_LEN);
180         memcpy(value + EAP_CHALLENGE_LEN, &timestamp, sizeof(timestamp));
181
182         /* Generate hmac.  */
183         lrad_hmac_md5(value, sizeof(value),
184                       state_key, sizeof(state_key), hmac);
185
186         /*
187          *      Compare the hmac we calculated to the one in the
188          *      packet.
189          */
190         return memcmp(hmac, state->vp_strvalue + EAP_CHALLENGE_LEN,
191                       EAP_USE_OF_HMAC);
192 }
193