1b168c582b3af1e57f1c7d9908e9748c10095f1e
[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 (data->state == SUCCESS && wpabuf_len(data->ssl.in_buf) == 0) {
163                 wpa_printf(MSG_DEBUG, "EAP-TLS: Client acknowledged final TLS "
164                            "handshake message");
165                 return;
166         }
167         if (eap_server_tls_phase1(sm, &data->ssl) < 0)
168                 eap_tls_state(data, FAILURE);
169 }
170
171
172 static void eap_tls_process(struct eap_sm *sm, void *priv,
173                             struct wpabuf *respData)
174 {
175         struct eap_tls_data *data = priv;
176         if (eap_server_tls_process(sm, &data->ssl, respData, data,
177                                    EAP_TYPE_TLS, NULL, eap_tls_process_msg) <
178             0)
179                 eap_tls_state(data, FAILURE);
180 }
181
182
183 static Boolean eap_tls_isDone(struct eap_sm *sm, void *priv)
184 {
185         struct eap_tls_data *data = priv;
186         return data->state == SUCCESS || data->state == FAILURE;
187 }
188
189
190 static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
191 {
192         struct eap_tls_data *data = priv;
193         u8 *eapKeyData;
194
195         if (data->state != SUCCESS)
196                 return NULL;
197
198         eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
199                                                "client EAP encryption",
200                                                EAP_TLS_KEY_LEN);
201         if (eapKeyData) {
202                 *len = EAP_TLS_KEY_LEN;
203                 wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived key",
204                             eapKeyData, EAP_TLS_KEY_LEN);
205         } else {
206                 wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive key");
207         }
208
209         return eapKeyData;
210 }
211
212
213 static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
214 {
215         struct eap_tls_data *data = priv;
216         u8 *eapKeyData, *emsk;
217
218         if (data->state != SUCCESS)
219                 return NULL;
220
221         eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
222                                                "client EAP encryption",
223                                                EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
224         if (eapKeyData) {
225                 emsk = os_malloc(EAP_EMSK_LEN);
226                 if (emsk)
227                         os_memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN,
228                                   EAP_EMSK_LEN);
229                 os_free(eapKeyData);
230         } else
231                 emsk = NULL;
232
233         if (emsk) {
234                 *len = EAP_EMSK_LEN;
235                 wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived EMSK",
236                             emsk, EAP_EMSK_LEN);
237         } else {
238                 wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive EMSK");
239         }
240
241         return emsk;
242 }
243
244
245 static Boolean eap_tls_isSuccess(struct eap_sm *sm, void *priv)
246 {
247         struct eap_tls_data *data = priv;
248         return data->state == SUCCESS;
249 }
250
251
252 int eap_server_tls_register(void)
253 {
254         struct eap_method *eap;
255         int ret;
256
257         eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
258                                       EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS");
259         if (eap == NULL)
260                 return -1;
261
262         eap->init = eap_tls_init;
263         eap->reset = eap_tls_reset;
264         eap->buildReq = eap_tls_buildReq;
265         eap->check = eap_tls_check;
266         eap->process = eap_tls_process;
267         eap->isDone = eap_tls_isDone;
268         eap->getKey = eap_tls_getKey;
269         eap->isSuccess = eap_tls_isSuccess;
270         eap->get_emsk = eap_tls_get_emsk;
271
272         ret = eap_server_method_register(eap);
273         if (ret)
274                 eap_server_method_free(eap);
275         return ret;
276 }