Commit | Line | Data |
---|---|---|
ebfa2275 SZ |
1 | /* |
2 | * hostapd / EAP-TLS (RFC 2716) | |
a875087d | 3 | * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> |
ebfa2275 SZ |
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 | ||
ebfa2275 SZ |
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; | |
a875087d | 29 | int established; |
ebfa2275 SZ |
30 | }; |
31 | ||
32 | ||
a875087d JL |
33 | static const char * eap_tls_state_txt(int state) |
34 | { | |
35 | switch (state) { | |
36 | case START: | |
37 | return "START"; | |
38 | case CONTINUE: | |
39 | return "CONTINUE"; | |
40 | case SUCCESS: | |
41 | return "SUCCESS"; | |
42 | case FAILURE: | |
43 | return "FAILURE"; | |
44 | default: | |
45 | return "Unknown?!"; | |
46 | } | |
47 | } | |
48 | ||
49 | ||
50 | static void eap_tls_state(struct eap_tls_data *data, int state) | |
51 | { | |
52 | wpa_printf(MSG_DEBUG, "EAP-TLS: %s -> %s", | |
53 | eap_tls_state_txt(data->state), | |
54 | eap_tls_state_txt(state)); | |
55 | data->state = state; | |
56 | } | |
57 | ||
58 | ||
ebfa2275 SZ |
59 | static void * eap_tls_init(struct eap_sm *sm) |
60 | { | |
61 | struct eap_tls_data *data; | |
62 | ||
a875087d | 63 | data = os_zalloc(sizeof(*data)); |
ebfa2275 SZ |
64 | if (data == NULL) |
65 | return NULL; | |
66 | data->state = START; | |
67 | ||
a875087d | 68 | if (eap_server_tls_ssl_init(sm, &data->ssl, 1)) { |
ebfa2275 SZ |
69 | wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); |
70 | eap_tls_reset(sm, data); | |
71 | return NULL; | |
72 | } | |
73 | ||
74 | return data; | |
75 | } | |
76 | ||
77 | ||
78 | static void eap_tls_reset(struct eap_sm *sm, void *priv) | |
79 | { | |
80 | struct eap_tls_data *data = priv; | |
81 | if (data == NULL) | |
82 | return; | |
a875087d JL |
83 | eap_server_tls_ssl_deinit(sm, &data->ssl); |
84 | os_free(data); | |
ebfa2275 SZ |
85 | } |
86 | ||
87 | ||
a875087d JL |
88 | static struct wpabuf * eap_tls_build_start(struct eap_sm *sm, |
89 | struct eap_tls_data *data, u8 id) | |
ebfa2275 | 90 | { |
a875087d | 91 | struct wpabuf *req; |
ebfa2275 | 92 | |
a875087d JL |
93 | req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLS, 1, EAP_CODE_REQUEST, |
94 | id); | |
ebfa2275 SZ |
95 | if (req == NULL) { |
96 | wpa_printf(MSG_ERROR, "EAP-TLS: Failed to allocate memory for " | |
97 | "request"); | |
a875087d | 98 | eap_tls_state(data, FAILURE); |
ebfa2275 SZ |
99 | return NULL; |
100 | } | |
101 | ||
a875087d | 102 | wpabuf_put_u8(req, EAP_TLS_FLAGS_START); |
ebfa2275 | 103 | |
a875087d | 104 | eap_tls_state(data, CONTINUE); |
ebfa2275 | 105 | |
a875087d | 106 | return req; |
ebfa2275 SZ |
107 | } |
108 | ||
109 | ||
a875087d | 110 | static struct wpabuf * eap_tls_buildReq(struct eap_sm *sm, void *priv, u8 id) |
ebfa2275 | 111 | { |
a875087d JL |
112 | struct eap_tls_data *data = priv; |
113 | struct wpabuf *res; | |
ebfa2275 | 114 | |
a875087d JL |
115 | if (data->ssl.state == FRAG_ACK) { |
116 | return eap_server_tls_build_ack(id, EAP_TYPE_TLS, 0); | |
ebfa2275 SZ |
117 | } |
118 | ||
a875087d JL |
119 | if (data->ssl.state == WAIT_FRAG_ACK) { |
120 | res = eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0, | |
121 | id); | |
122 | goto check_established; | |
123 | } | |
ebfa2275 SZ |
124 | |
125 | switch (data->state) { | |
126 | case START: | |
a875087d | 127 | return eap_tls_build_start(sm, data, id); |
ebfa2275 | 128 | case CONTINUE: |
a875087d JL |
129 | if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) |
130 | data->established = 1; | |
131 | break; | |
ebfa2275 SZ |
132 | default: |
133 | wpa_printf(MSG_DEBUG, "EAP-TLS: %s - unexpected state %d", | |
134 | __func__, data->state); | |
135 | return NULL; | |
136 | } | |
a875087d JL |
137 | |
138 | res = eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0, id); | |
139 | ||
140 | check_established: | |
141 | if (data->established && data->ssl.state != WAIT_FRAG_ACK) { | |
142 | /* TLS handshake has been completed and there are no more | |
143 | * fragments waiting to be sent out. */ | |
144 | wpa_printf(MSG_DEBUG, "EAP-TLS: Done"); | |
145 | eap_tls_state(data, SUCCESS); | |
146 | } | |
147 | ||
148 | return res; | |
ebfa2275 SZ |
149 | } |
150 | ||
151 | ||
152 | static Boolean eap_tls_check(struct eap_sm *sm, void *priv, | |
a875087d | 153 | struct wpabuf *respData) |
ebfa2275 | 154 | { |
a875087d JL |
155 | const u8 *pos; |
156 | size_t len; | |
ebfa2275 | 157 | |
a875087d JL |
158 | pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLS, respData, &len); |
159 | if (pos == NULL || len < 1) { | |
ebfa2275 SZ |
160 | wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame"); |
161 | return TRUE; | |
162 | } | |
163 | ||
164 | return FALSE; | |
165 | } | |
166 | ||
167 | ||
a875087d JL |
168 | static void eap_tls_process_msg(struct eap_sm *sm, void *priv, |
169 | const struct wpabuf *respData) | |
ebfa2275 SZ |
170 | { |
171 | struct eap_tls_data *data = priv; | |
a875087d JL |
172 | if (data->state == SUCCESS && wpabuf_len(data->ssl.in_buf) == 0) { |
173 | wpa_printf(MSG_DEBUG, "EAP-TLS: Client acknowledged final TLS " | |
174 | "handshake message"); | |
ebfa2275 SZ |
175 | return; |
176 | } | |
a875087d JL |
177 | if (eap_server_tls_phase1(sm, &data->ssl) < 0) |
178 | eap_tls_state(data, FAILURE); | |
179 | } | |
ebfa2275 | 180 | |
a875087d JL |
181 | |
182 | static void eap_tls_process(struct eap_sm *sm, void *priv, | |
183 | struct wpabuf *respData) | |
184 | { | |
185 | struct eap_tls_data *data = priv; | |
186 | if (eap_server_tls_process(sm, &data->ssl, respData, data, | |
187 | EAP_TYPE_TLS, NULL, eap_tls_process_msg) < | |
188 | 0) | |
189 | eap_tls_state(data, FAILURE); | |
ebfa2275 SZ |
190 | } |
191 | ||
192 | ||
193 | static Boolean eap_tls_isDone(struct eap_sm *sm, void *priv) | |
194 | { | |
195 | struct eap_tls_data *data = priv; | |
196 | return data->state == SUCCESS || data->state == FAILURE; | |
197 | } | |
198 | ||
199 | ||
200 | static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len) | |
201 | { | |
202 | struct eap_tls_data *data = priv; | |
203 | u8 *eapKeyData; | |
204 | ||
205 | if (data->state != SUCCESS) | |
206 | return NULL; | |
207 | ||
a875087d JL |
208 | eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, |
209 | "client EAP encryption", | |
210 | EAP_TLS_KEY_LEN); | |
ebfa2275 SZ |
211 | if (eapKeyData) { |
212 | *len = EAP_TLS_KEY_LEN; | |
213 | wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived key", | |
214 | eapKeyData, EAP_TLS_KEY_LEN); | |
215 | } else { | |
216 | wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive key"); | |
217 | } | |
218 | ||
219 | return eapKeyData; | |
220 | } | |
221 | ||
222 | ||
223 | static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) | |
224 | { | |
225 | struct eap_tls_data *data = priv; | |
226 | u8 *eapKeyData, *emsk; | |
227 | ||
228 | if (data->state != SUCCESS) | |
229 | return NULL; | |
230 | ||
a875087d JL |
231 | eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, |
232 | "client EAP encryption", | |
233 | EAP_TLS_KEY_LEN + EAP_EMSK_LEN); | |
ebfa2275 | 234 | if (eapKeyData) { |
a875087d | 235 | emsk = os_malloc(EAP_EMSK_LEN); |
ebfa2275 | 236 | if (emsk) |
a875087d JL |
237 | os_memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN, |
238 | EAP_EMSK_LEN); | |
239 | os_free(eapKeyData); | |
ebfa2275 SZ |
240 | } else |
241 | emsk = NULL; | |
242 | ||
243 | if (emsk) { | |
244 | *len = EAP_EMSK_LEN; | |
245 | wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived EMSK", | |
246 | emsk, EAP_EMSK_LEN); | |
247 | } else { | |
248 | wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive EMSK"); | |
249 | } | |
250 | ||
251 | return emsk; | |
252 | } | |
253 | ||
254 | ||
255 | static Boolean eap_tls_isSuccess(struct eap_sm *sm, void *priv) | |
256 | { | |
257 | struct eap_tls_data *data = priv; | |
258 | return data->state == SUCCESS; | |
259 | } | |
260 | ||
261 | ||
262 | int eap_server_tls_register(void) | |
263 | { | |
264 | struct eap_method *eap; | |
265 | int ret; | |
266 | ||
267 | eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, | |
268 | EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS"); | |
269 | if (eap == NULL) | |
270 | return -1; | |
271 | ||
272 | eap->init = eap_tls_init; | |
273 | eap->reset = eap_tls_reset; | |
274 | eap->buildReq = eap_tls_buildReq; | |
275 | eap->check = eap_tls_check; | |
276 | eap->process = eap_tls_process; | |
277 | eap->isDone = eap_tls_isDone; | |
278 | eap->getKey = eap_tls_getKey; | |
279 | eap->isSuccess = eap_tls_isSuccess; | |
280 | eap->get_emsk = eap_tls_get_emsk; | |
281 | ||
282 | ret = eap_server_method_register(eap); | |
283 | if (ret) | |
284 | eap_server_method_free(eap); | |
285 | return ret; | |
286 | } |