Merge branch 'vendor/LESS'
[dragonfly.git] / contrib / hostapd / src / eap_server / eap_server_tls.c
1 /*
2  * hostapd / EAP-TLS (RFC 2716)
3  * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
4  *
5  * This software may be distributed under the terms of the BSD license.
6  * See README for more details.
7  */
8
9 #include "includes.h"
10
11 #include "common.h"
12 #include "eap_i.h"
13 #include "eap_tls_common.h"
14 #include "crypto/tls.h"
15
16
17 static void eap_tls_reset(struct eap_sm *sm, void *priv);
18
19
20 struct eap_tls_data {
21         struct eap_ssl_data ssl;
22         enum { START, CONTINUE, SUCCESS, FAILURE } state;
23         int established;
24         u8 eap_type;
25 };
26
27
28 static const char * eap_tls_state_txt(int state)
29 {
30         switch (state) {
31         case START:
32                 return "START";
33         case CONTINUE:
34                 return "CONTINUE";
35         case SUCCESS:
36                 return "SUCCESS";
37         case FAILURE:
38                 return "FAILURE";
39         default:
40                 return "Unknown?!";
41         }
42 }
43
44
45 static void eap_tls_state(struct eap_tls_data *data, int state)
46 {
47         wpa_printf(MSG_DEBUG, "EAP-TLS: %s -> %s",
48                    eap_tls_state_txt(data->state),
49                    eap_tls_state_txt(state));
50         data->state = state;
51 }
52
53
54 static void * eap_tls_init(struct eap_sm *sm)
55 {
56         struct eap_tls_data *data;
57
58         data = os_zalloc(sizeof(*data));
59         if (data == NULL)
60                 return NULL;
61         data->state = START;
62
63         if (eap_server_tls_ssl_init(sm, &data->ssl, 1)) {
64                 wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
65                 eap_tls_reset(sm, data);
66                 return NULL;
67         }
68
69         data->eap_type = EAP_TYPE_TLS;
70
71         return data;
72 }
73
74
75 #ifdef EAP_SERVER_UNAUTH_TLS
76 static void * eap_unauth_tls_init(struct eap_sm *sm)
77 {
78         struct eap_tls_data *data;
79
80         data = os_zalloc(sizeof(*data));
81         if (data == NULL)
82                 return NULL;
83         data->state = START;
84
85         if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
86                 wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
87                 eap_tls_reset(sm, data);
88                 return NULL;
89         }
90
91         data->eap_type = EAP_UNAUTH_TLS_TYPE;
92         return data;
93 }
94 #endif /* EAP_SERVER_UNAUTH_TLS */
95
96
97 static void eap_tls_reset(struct eap_sm *sm, void *priv)
98 {
99         struct eap_tls_data *data = priv;
100         if (data == NULL)
101                 return;
102         eap_server_tls_ssl_deinit(sm, &data->ssl);
103         os_free(data);
104 }
105
106
107 static struct wpabuf * eap_tls_build_start(struct eap_sm *sm,
108                                            struct eap_tls_data *data, u8 id)
109 {
110         struct wpabuf *req;
111
112         req = eap_tls_msg_alloc(data->eap_type, 1, EAP_CODE_REQUEST, id);
113         if (req == NULL) {
114                 wpa_printf(MSG_ERROR, "EAP-TLS: Failed to allocate memory for "
115                            "request");
116                 eap_tls_state(data, FAILURE);
117                 return NULL;
118         }
119
120         wpabuf_put_u8(req, EAP_TLS_FLAGS_START);
121
122         eap_tls_state(data, CONTINUE);
123
124         return req;
125 }
126
127
128 static struct wpabuf * eap_tls_buildReq(struct eap_sm *sm, void *priv, u8 id)
129 {
130         struct eap_tls_data *data = priv;
131         struct wpabuf *res;
132
133         if (data->ssl.state == FRAG_ACK) {
134                 return eap_server_tls_build_ack(id, data->eap_type, 0);
135         }
136
137         if (data->ssl.state == WAIT_FRAG_ACK) {
138                 res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0,
139                                                id);
140                 goto check_established;
141         }
142
143         switch (data->state) {
144         case START:
145                 return eap_tls_build_start(sm, data, id);
146         case CONTINUE:
147                 if (tls_connection_established(sm->ssl_ctx, data->ssl.conn))
148                         data->established = 1;
149                 break;
150         default:
151                 wpa_printf(MSG_DEBUG, "EAP-TLS: %s - unexpected state %d",
152                            __func__, data->state);
153                 return NULL;
154         }
155
156         res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0, id);
157
158 check_established:
159         if (data->established && data->ssl.state != WAIT_FRAG_ACK) {
160                 /* TLS handshake has been completed and there are no more
161                  * fragments waiting to be sent out. */
162                 wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
163                 eap_tls_state(data, SUCCESS);
164         }
165
166         return res;
167 }
168
169
170 static Boolean eap_tls_check(struct eap_sm *sm, void *priv,
171                              struct wpabuf *respData)
172 {
173         struct eap_tls_data *data = priv;
174         const u8 *pos;
175         size_t len;
176
177         if (data->eap_type == EAP_UNAUTH_TLS_TYPE)
178                 pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS,
179                                        EAP_VENDOR_TYPE_UNAUTH_TLS, respData,
180                                        &len);
181         else
182                 pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_type,
183                                        respData, &len);
184         if (pos == NULL || len < 1) {
185                 wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame");
186                 return TRUE;
187         }
188
189         return FALSE;
190 }
191
192
193 static void eap_tls_process_msg(struct eap_sm *sm, void *priv,
194                                 const struct wpabuf *respData)
195 {
196         struct eap_tls_data *data = priv;
197         if (data->state == SUCCESS && wpabuf_len(data->ssl.tls_in) == 0) {
198                 wpa_printf(MSG_DEBUG, "EAP-TLS: Client acknowledged final TLS "
199                            "handshake message");
200                 return;
201         }
202         if (eap_server_tls_phase1(sm, &data->ssl) < 0)
203                 eap_tls_state(data, FAILURE);
204 }
205
206
207 static void eap_tls_process(struct eap_sm *sm, void *priv,
208                             struct wpabuf *respData)
209 {
210         struct eap_tls_data *data = priv;
211         if (eap_server_tls_process(sm, &data->ssl, respData, data,
212                                    data->eap_type, NULL, eap_tls_process_msg) <
213             0)
214                 eap_tls_state(data, FAILURE);
215 }
216
217
218 static Boolean eap_tls_isDone(struct eap_sm *sm, void *priv)
219 {
220         struct eap_tls_data *data = priv;
221         return data->state == SUCCESS || data->state == FAILURE;
222 }
223
224
225 static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
226 {
227         struct eap_tls_data *data = priv;
228         u8 *eapKeyData;
229
230         if (data->state != SUCCESS)
231                 return NULL;
232
233         eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
234                                                "client EAP encryption",
235                                                EAP_TLS_KEY_LEN);
236         if (eapKeyData) {
237                 *len = EAP_TLS_KEY_LEN;
238                 wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived key",
239                             eapKeyData, EAP_TLS_KEY_LEN);
240         } else {
241                 wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive key");
242         }
243
244         return eapKeyData;
245 }
246
247
248 static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
249 {
250         struct eap_tls_data *data = priv;
251         u8 *eapKeyData, *emsk;
252
253         if (data->state != SUCCESS)
254                 return NULL;
255
256         eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
257                                                "client EAP encryption",
258                                                EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
259         if (eapKeyData) {
260                 emsk = os_malloc(EAP_EMSK_LEN);
261                 if (emsk)
262                         os_memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN,
263                                   EAP_EMSK_LEN);
264                 os_free(eapKeyData);
265         } else
266                 emsk = NULL;
267
268         if (emsk) {
269                 *len = EAP_EMSK_LEN;
270                 wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived EMSK",
271                             emsk, EAP_EMSK_LEN);
272         } else {
273                 wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive EMSK");
274         }
275
276         return emsk;
277 }
278
279
280 static Boolean eap_tls_isSuccess(struct eap_sm *sm, void *priv)
281 {
282         struct eap_tls_data *data = priv;
283         return data->state == SUCCESS;
284 }
285
286
287 int eap_server_tls_register(void)
288 {
289         struct eap_method *eap;
290         int ret;
291
292         eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
293                                       EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS");
294         if (eap == NULL)
295                 return -1;
296
297         eap->init = eap_tls_init;
298         eap->reset = eap_tls_reset;
299         eap->buildReq = eap_tls_buildReq;
300         eap->check = eap_tls_check;
301         eap->process = eap_tls_process;
302         eap->isDone = eap_tls_isDone;
303         eap->getKey = eap_tls_getKey;
304         eap->isSuccess = eap_tls_isSuccess;
305         eap->get_emsk = eap_tls_get_emsk;
306
307         ret = eap_server_method_register(eap);
308         if (ret)
309                 eap_server_method_free(eap);
310         return ret;
311 }
312
313
314 #ifdef EAP_SERVER_UNAUTH_TLS
315 int eap_server_unauth_tls_register(void)
316 {
317         struct eap_method *eap;
318         int ret;
319
320         eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
321                                       EAP_VENDOR_UNAUTH_TLS,
322                                       EAP_VENDOR_TYPE_UNAUTH_TLS,
323                                       "UNAUTH-TLS");
324         if (eap == NULL)
325                 return -1;
326
327         eap->init = eap_unauth_tls_init;
328         eap->reset = eap_tls_reset;
329         eap->buildReq = eap_tls_buildReq;
330         eap->check = eap_tls_check;
331         eap->process = eap_tls_process;
332         eap->isDone = eap_tls_isDone;
333         eap->getKey = eap_tls_getKey;
334         eap->isSuccess = eap_tls_isSuccess;
335         eap->get_emsk = eap_tls_get_emsk;
336
337         ret = eap_server_method_register(eap);
338         if (ret)
339                 eap_server_method_free(eap);
340         return ret;
341 }
342 #endif /* EAP_SERVER_UNAUTH_TLS */