e5f1931e6185168c0fe6553e1b20eca578e1a462
[libeap.git] / src / eap_server / eap_tls.c
1 /*
2  * hostapd / EAP-TLS (RFC 2716)
3  * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  *
9  * Alternatively, this software may be distributed under the terms of BSD
10  * license.
11  *
12  * See README and COPYING for more details.
13  */
14
15 #include "includes.h"
16
17 #include "common.h"
18 #include "eap_i.h"
19 #include "eap_tls_common.h"
20 #include "tls.h"
21
22
23 static void eap_tls_reset(struct eap_sm *sm, void *priv);
24
25
26 struct eap_tls_data {
27         struct eap_ssl_data ssl;
28         enum { START, CONTINUE, SUCCESS, FAILURE } state;
29 };
30
31
32 static const char * eap_tls_state_txt(int state)
33 {
34         switch (state) {
35         case START:
36                 return "START";
37         case CONTINUE:
38                 return "CONTINUE";
39         case SUCCESS:
40                 return "SUCCESS";
41         case FAILURE:
42                 return "FAILURE";
43         default:
44                 return "Unknown?!";
45         }
46 }
47
48
49 static void eap_tls_state(struct eap_tls_data *data, int state)
50 {
51         wpa_printf(MSG_DEBUG, "EAP-TLS: %s -> %s",
52                    eap_tls_state_txt(data->state),
53                    eap_tls_state_txt(state));
54         data->state = state;
55 }
56
57
58 static void * eap_tls_init(struct eap_sm *sm)
59 {
60         struct eap_tls_data *data;
61
62         data = os_zalloc(sizeof(*data));
63         if (data == NULL)
64                 return NULL;
65         data->state = START;
66
67         if (eap_server_tls_ssl_init(sm, &data->ssl, 1)) {
68                 wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
69                 eap_tls_reset(sm, data);
70                 return NULL;
71         }
72
73         return data;
74 }
75
76
77 static void eap_tls_reset(struct eap_sm *sm, void *priv)
78 {
79         struct eap_tls_data *data = priv;
80         if (data == NULL)
81                 return;
82         eap_server_tls_ssl_deinit(sm, &data->ssl);
83         os_free(data);
84 }
85
86
87 static struct wpabuf * eap_tls_build_start(struct eap_sm *sm,
88                                            struct eap_tls_data *data, u8 id)
89 {
90         struct wpabuf *req;
91
92         req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLS, 1, EAP_CODE_REQUEST,
93                             id);
94         if (req == NULL) {
95                 wpa_printf(MSG_ERROR, "EAP-TLS: Failed to allocate memory for "
96                            "request");
97                 eap_tls_state(data, FAILURE);
98                 return NULL;
99         }
100
101         wpabuf_put_u8(req, EAP_TLS_FLAGS_START);
102
103         eap_tls_state(data, CONTINUE);
104
105         return req;
106 }
107
108
109 static struct wpabuf * eap_tls_buildReq(struct eap_sm *sm, void *priv, u8 id)
110 {
111         struct eap_tls_data *data = priv;
112
113
114         if (data->ssl.state == FRAG_ACK) {
115                 return eap_server_tls_build_ack(id, EAP_TYPE_TLS, 0);
116         }
117
118         if (data->ssl.state == WAIT_FRAG_ACK) {
119                 return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0,
120                                                 id);
121         }
122
123         switch (data->state) {
124         case START:
125                 return eap_tls_build_start(sm, data, id);
126         case CONTINUE:
127                 if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
128                         wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
129                         eap_tls_state(data, SUCCESS);
130                 }
131                 break;
132         default:
133                 wpa_printf(MSG_DEBUG, "EAP-TLS: %s - unexpected state %d",
134                            __func__, data->state);
135                 return NULL;
136         }
137
138         return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0, id);
139 }
140
141
142 static Boolean eap_tls_check(struct eap_sm *sm, void *priv,
143                              struct wpabuf *respData)
144 {
145         const u8 *pos;
146         size_t len;
147
148         pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLS, respData, &len);
149         if (pos == NULL || len < 1) {
150                 wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame");
151                 return TRUE;
152         }
153
154         return FALSE;
155 }
156
157
158 static void eap_tls_process_msg(struct eap_sm *sm, void *priv,
159                                 const struct wpabuf *respData)
160 {
161         struct eap_tls_data *data = priv;
162         if (eap_server_tls_phase1(sm, &data->ssl) < 0)
163                 eap_tls_state(data, FAILURE);
164 }
165
166
167 static void eap_tls_process(struct eap_sm *sm, void *priv,
168                             struct wpabuf *respData)
169 {
170         struct eap_tls_data *data = priv;
171         if (eap_server_tls_process(sm, &data->ssl, respData, data,
172                                    EAP_TYPE_TLS, NULL, eap_tls_process_msg) <
173             0)
174                 eap_tls_state(data, FAILURE);
175 }
176
177
178 static Boolean eap_tls_isDone(struct eap_sm *sm, void *priv)
179 {
180         struct eap_tls_data *data = priv;
181         return data->state == SUCCESS || data->state == FAILURE;
182 }
183
184
185 static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
186 {
187         struct eap_tls_data *data = priv;
188         u8 *eapKeyData;
189
190         if (data->state != SUCCESS)
191                 return NULL;
192
193         eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
194                                                "client EAP encryption",
195                                                EAP_TLS_KEY_LEN);
196         if (eapKeyData) {
197                 *len = EAP_TLS_KEY_LEN;
198                 wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived key",
199                             eapKeyData, EAP_TLS_KEY_LEN);
200         } else {
201                 wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive key");
202         }
203
204         return eapKeyData;
205 }
206
207
208 static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
209 {
210         struct eap_tls_data *data = priv;
211         u8 *eapKeyData, *emsk;
212
213         if (data->state != SUCCESS)
214                 return NULL;
215
216         eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
217                                                "client EAP encryption",
218                                                EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
219         if (eapKeyData) {
220                 emsk = os_malloc(EAP_EMSK_LEN);
221                 if (emsk)
222                         os_memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN,
223                                   EAP_EMSK_LEN);
224                 os_free(eapKeyData);
225         } else
226                 emsk = NULL;
227
228         if (emsk) {
229                 *len = EAP_EMSK_LEN;
230                 wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived EMSK",
231                             emsk, EAP_EMSK_LEN);
232         } else {
233                 wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive EMSK");
234         }
235
236         return emsk;
237 }
238
239
240 static Boolean eap_tls_isSuccess(struct eap_sm *sm, void *priv)
241 {
242         struct eap_tls_data *data = priv;
243         return data->state == SUCCESS;
244 }
245
246
247 int eap_server_tls_register(void)
248 {
249         struct eap_method *eap;
250         int ret;
251
252         eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
253                                       EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS");
254         if (eap == NULL)
255                 return -1;
256
257         eap->init = eap_tls_init;
258         eap->reset = eap_tls_reset;
259         eap->buildReq = eap_tls_buildReq;
260         eap->check = eap_tls_check;
261         eap->process = eap_tls_process;
262         eap->isDone = eap_tls_isDone;
263         eap->getKey = eap_tls_getKey;
264         eap->isSuccess = eap_tls_isSuccess;
265         eap->get_emsk = eap_tls_get_emsk;
266
267         ret = eap_server_method_register(eap);
268         if (ret)
269                 eap_server_method_free(eap);
270         return ret;
271 }