]>
Commit | Line | Data |
---|---|---|
6fc6879b | 1 | /* |
a9141cff | 2 | * hostapd / EAP-TTLS (RFC 5281) |
fd2f2d04 | 3 | * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi> |
6fc6879b | 4 | * |
0f3d578e JM |
5 | * This software may be distributed under the terms of the BSD license. |
6 | * See README for more details. | |
6fc6879b JM |
7 | */ |
8 | ||
9 | #include "includes.h" | |
10 | ||
11 | #include "common.h" | |
03da66bd JM |
12 | #include "crypto/ms_funcs.h" |
13 | #include "crypto/sha1.h" | |
14 | #include "crypto/tls.h" | |
6fc6879b JM |
15 | #include "eap_server/eap_i.h" |
16 | #include "eap_server/eap_tls_common.h" | |
6fc6879b | 17 | #include "eap_common/chap.h" |
6fc6879b JM |
18 | #include "eap_common/eap_ttls.h" |
19 | ||
20 | ||
fd2f2d04 | 21 | #define EAP_TTLS_VERSION 0 |
6fc6879b JM |
22 | |
23 | ||
24 | static void eap_ttls_reset(struct eap_sm *sm, void *priv); | |
25 | ||
26 | ||
27 | struct eap_ttls_data { | |
28 | struct eap_ssl_data ssl; | |
29 | enum { | |
30 | START, PHASE1, PHASE2_START, PHASE2_METHOD, | |
fd2f2d04 | 31 | PHASE2_MSCHAPV2_RESP, SUCCESS, FAILURE |
6fc6879b JM |
32 | } state; |
33 | ||
34 | int ttls_version; | |
6fc6879b JM |
35 | const struct eap_method *phase2_method; |
36 | void *phase2_priv; | |
37 | int mschapv2_resp_ok; | |
38 | u8 mschapv2_auth_response[20]; | |
39 | u8 mschapv2_ident; | |
6fc6879b | 40 | struct wpabuf *pending_phase2_eap_resp; |
c80a74d7 | 41 | int tnc_started; |
6fc6879b JM |
42 | }; |
43 | ||
44 | ||
45 | static const char * eap_ttls_state_txt(int state) | |
46 | { | |
47 | switch (state) { | |
48 | case START: | |
49 | return "START"; | |
50 | case PHASE1: | |
51 | return "PHASE1"; | |
52 | case PHASE2_START: | |
53 | return "PHASE2_START"; | |
54 | case PHASE2_METHOD: | |
55 | return "PHASE2_METHOD"; | |
56 | case PHASE2_MSCHAPV2_RESP: | |
57 | return "PHASE2_MSCHAPV2_RESP"; | |
6fc6879b JM |
58 | case SUCCESS: |
59 | return "SUCCESS"; | |
60 | case FAILURE: | |
61 | return "FAILURE"; | |
62 | default: | |
63 | return "Unknown?!"; | |
64 | } | |
65 | } | |
66 | ||
67 | ||
68 | static void eap_ttls_state(struct eap_ttls_data *data, int state) | |
69 | { | |
70 | wpa_printf(MSG_DEBUG, "EAP-TTLS: %s -> %s", | |
71 | eap_ttls_state_txt(data->state), | |
72 | eap_ttls_state_txt(state)); | |
73 | data->state = state; | |
b6f2ae3b JM |
74 | if (state == FAILURE) |
75 | tls_connection_remove_session(data->ssl.conn); | |
76 | } | |
77 | ||
78 | ||
79 | static void eap_ttls_valid_session(struct eap_sm *sm, | |
80 | struct eap_ttls_data *data) | |
81 | { | |
82 | struct wpabuf *buf; | |
83 | ||
84 | if (!sm->tls_session_lifetime) | |
85 | return; | |
86 | ||
87 | buf = wpabuf_alloc(1 + 1 + sm->identity_len); | |
88 | if (!buf) | |
89 | return; | |
90 | wpabuf_put_u8(buf, EAP_TYPE_TTLS); | |
91 | if (sm->identity) { | |
92 | u8 id_len; | |
93 | ||
94 | if (sm->identity_len <= 255) | |
95 | id_len = sm->identity_len; | |
96 | else | |
97 | id_len = 255; | |
98 | wpabuf_put_u8(buf, id_len); | |
99 | wpabuf_put_data(buf, sm->identity, id_len); | |
100 | } else { | |
101 | wpabuf_put_u8(buf, 0); | |
102 | } | |
103 | tls_connection_set_success_data(data->ssl.conn, buf); | |
6fc6879b JM |
104 | } |
105 | ||
106 | ||
107 | static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id, | |
108 | int mandatory, size_t len) | |
109 | { | |
110 | struct ttls_avp_vendor *avp; | |
111 | u8 flags; | |
112 | size_t hdrlen; | |
113 | ||
114 | avp = (struct ttls_avp_vendor *) avphdr; | |
115 | flags = mandatory ? AVP_FLAGS_MANDATORY : 0; | |
116 | if (vendor_id) { | |
117 | flags |= AVP_FLAGS_VENDOR; | |
118 | hdrlen = sizeof(*avp); | |
119 | avp->vendor_id = host_to_be32(vendor_id); | |
120 | } else { | |
121 | hdrlen = sizeof(struct ttls_avp); | |
122 | } | |
123 | ||
124 | avp->avp_code = host_to_be32(avp_code); | |
c8894a31 JM |
125 | avp->avp_length = host_to_be32(((u32) flags << 24) | |
126 | ((u32) (hdrlen + len))); | |
6fc6879b JM |
127 | |
128 | return avphdr + hdrlen; | |
129 | } | |
130 | ||
131 | ||
132 | static struct wpabuf * eap_ttls_avp_encapsulate(struct wpabuf *resp, | |
133 | u32 avp_code, int mandatory) | |
134 | { | |
135 | struct wpabuf *avp; | |
136 | u8 *pos; | |
137 | ||
138 | avp = wpabuf_alloc(sizeof(struct ttls_avp) + wpabuf_len(resp) + 4); | |
139 | if (avp == NULL) { | |
140 | wpabuf_free(resp); | |
141 | return NULL; | |
142 | } | |
143 | ||
144 | pos = eap_ttls_avp_hdr(wpabuf_mhead(avp), avp_code, 0, mandatory, | |
145 | wpabuf_len(resp)); | |
146 | os_memcpy(pos, wpabuf_head(resp), wpabuf_len(resp)); | |
147 | pos += wpabuf_len(resp); | |
148 | AVP_PAD((const u8 *) wpabuf_head(avp), pos); | |
149 | wpabuf_free(resp); | |
150 | wpabuf_put(avp, pos - (u8 *) wpabuf_head(avp)); | |
151 | return avp; | |
152 | } | |
153 | ||
154 | ||
155 | struct eap_ttls_avp { | |
156 | /* Note: eap is allocated memory; caller is responsible for freeing | |
157 | * it. All the other pointers are pointing to the packet data, i.e., | |
158 | * they must not be freed separately. */ | |
159 | u8 *eap; | |
160 | size_t eap_len; | |
161 | u8 *user_name; | |
162 | size_t user_name_len; | |
163 | u8 *user_password; | |
164 | size_t user_password_len; | |
165 | u8 *chap_challenge; | |
166 | size_t chap_challenge_len; | |
167 | u8 *chap_password; | |
168 | size_t chap_password_len; | |
169 | u8 *mschap_challenge; | |
170 | size_t mschap_challenge_len; | |
171 | u8 *mschap_response; | |
172 | size_t mschap_response_len; | |
173 | u8 *mschap2_response; | |
174 | size_t mschap2_response_len; | |
175 | }; | |
176 | ||
177 | ||
81c85c06 | 178 | static int eap_ttls_avp_parse(struct wpabuf *buf, struct eap_ttls_avp *parse) |
6fc6879b JM |
179 | { |
180 | struct ttls_avp *avp; | |
181 | u8 *pos; | |
182 | int left; | |
183 | ||
81c85c06 JM |
184 | pos = wpabuf_mhead(buf); |
185 | left = wpabuf_len(buf); | |
6fc6879b JM |
186 | os_memset(parse, 0, sizeof(*parse)); |
187 | ||
188 | while (left > 0) { | |
189 | u32 avp_code, avp_length, vendor_id = 0; | |
190 | u8 avp_flags, *dpos; | |
191 | size_t pad, dlen; | |
192 | avp = (struct ttls_avp *) pos; | |
193 | avp_code = be_to_host32(avp->avp_code); | |
194 | avp_length = be_to_host32(avp->avp_length); | |
195 | avp_flags = (avp_length >> 24) & 0xff; | |
196 | avp_length &= 0xffffff; | |
197 | wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP: code=%d flags=0x%02x " | |
198 | "length=%d", (int) avp_code, avp_flags, | |
199 | (int) avp_length); | |
200 | if ((int) avp_length > left) { | |
201 | wpa_printf(MSG_WARNING, "EAP-TTLS: AVP overflow " | |
202 | "(len=%d, left=%d) - dropped", | |
203 | (int) avp_length, left); | |
204 | goto fail; | |
205 | } | |
206 | if (avp_length < sizeof(*avp)) { | |
207 | wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid AVP length " | |
208 | "%d", avp_length); | |
209 | goto fail; | |
210 | } | |
211 | dpos = (u8 *) (avp + 1); | |
212 | dlen = avp_length - sizeof(*avp); | |
213 | if (avp_flags & AVP_FLAGS_VENDOR) { | |
214 | if (dlen < 4) { | |
215 | wpa_printf(MSG_WARNING, "EAP-TTLS: vendor AVP " | |
216 | "underflow"); | |
217 | goto fail; | |
218 | } | |
219 | vendor_id = be_to_host32(* (be32 *) dpos); | |
220 | wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d", | |
221 | (int) vendor_id); | |
222 | dpos += 4; | |
223 | dlen -= 4; | |
224 | } | |
225 | ||
226 | wpa_hexdump(MSG_DEBUG, "EAP-TTLS: AVP data", dpos, dlen); | |
227 | ||
228 | if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) { | |
229 | wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message"); | |
230 | if (parse->eap == NULL) { | |
231 | parse->eap = os_malloc(dlen); | |
232 | if (parse->eap == NULL) { | |
233 | wpa_printf(MSG_WARNING, "EAP-TTLS: " | |
234 | "failed to allocate memory " | |
235 | "for Phase 2 EAP data"); | |
236 | goto fail; | |
237 | } | |
238 | os_memcpy(parse->eap, dpos, dlen); | |
239 | parse->eap_len = dlen; | |
240 | } else { | |
241 | u8 *neweap = os_realloc(parse->eap, | |
242 | parse->eap_len + dlen); | |
243 | if (neweap == NULL) { | |
244 | wpa_printf(MSG_WARNING, "EAP-TTLS: " | |
245 | "failed to allocate memory " | |
246 | "for Phase 2 EAP data"); | |
247 | goto fail; | |
248 | } | |
249 | os_memcpy(neweap + parse->eap_len, dpos, dlen); | |
250 | parse->eap = neweap; | |
251 | parse->eap_len += dlen; | |
252 | } | |
253 | } else if (vendor_id == 0 && | |
254 | avp_code == RADIUS_ATTR_USER_NAME) { | |
255 | wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: User-Name", | |
256 | dpos, dlen); | |
257 | parse->user_name = dpos; | |
258 | parse->user_name_len = dlen; | |
259 | } else if (vendor_id == 0 && | |
260 | avp_code == RADIUS_ATTR_USER_PASSWORD) { | |
261 | u8 *password = dpos; | |
262 | size_t password_len = dlen; | |
263 | while (password_len > 0 && | |
264 | password[password_len - 1] == '\0') { | |
265 | password_len--; | |
266 | } | |
267 | wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: " | |
268 | "User-Password (PAP)", | |
269 | password, password_len); | |
270 | parse->user_password = password; | |
271 | parse->user_password_len = password_len; | |
272 | } else if (vendor_id == 0 && | |
273 | avp_code == RADIUS_ATTR_CHAP_CHALLENGE) { | |
274 | wpa_hexdump(MSG_DEBUG, | |
275 | "EAP-TTLS: CHAP-Challenge (CHAP)", | |
276 | dpos, dlen); | |
277 | parse->chap_challenge = dpos; | |
278 | parse->chap_challenge_len = dlen; | |
279 | } else if (vendor_id == 0 && | |
280 | avp_code == RADIUS_ATTR_CHAP_PASSWORD) { | |
281 | wpa_hexdump(MSG_DEBUG, | |
282 | "EAP-TTLS: CHAP-Password (CHAP)", | |
283 | dpos, dlen); | |
284 | parse->chap_password = dpos; | |
285 | parse->chap_password_len = dlen; | |
286 | } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && | |
287 | avp_code == RADIUS_ATTR_MS_CHAP_CHALLENGE) { | |
288 | wpa_hexdump(MSG_DEBUG, | |
289 | "EAP-TTLS: MS-CHAP-Challenge", | |
290 | dpos, dlen); | |
291 | parse->mschap_challenge = dpos; | |
292 | parse->mschap_challenge_len = dlen; | |
293 | } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && | |
294 | avp_code == RADIUS_ATTR_MS_CHAP_RESPONSE) { | |
295 | wpa_hexdump(MSG_DEBUG, | |
296 | "EAP-TTLS: MS-CHAP-Response (MSCHAP)", | |
297 | dpos, dlen); | |
298 | parse->mschap_response = dpos; | |
299 | parse->mschap_response_len = dlen; | |
300 | } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && | |
301 | avp_code == RADIUS_ATTR_MS_CHAP2_RESPONSE) { | |
302 | wpa_hexdump(MSG_DEBUG, | |
303 | "EAP-TTLS: MS-CHAP2-Response (MSCHAPV2)", | |
304 | dpos, dlen); | |
305 | parse->mschap2_response = dpos; | |
306 | parse->mschap2_response_len = dlen; | |
307 | } else if (avp_flags & AVP_FLAGS_MANDATORY) { | |
308 | wpa_printf(MSG_WARNING, "EAP-TTLS: Unsupported " | |
309 | "mandatory AVP code %d vendor_id %d - " | |
310 | "dropped", (int) avp_code, (int) vendor_id); | |
311 | goto fail; | |
312 | } else { | |
313 | wpa_printf(MSG_DEBUG, "EAP-TTLS: Ignoring unsupported " | |
314 | "AVP code %d vendor_id %d", | |
315 | (int) avp_code, (int) vendor_id); | |
316 | } | |
317 | ||
318 | pad = (4 - (avp_length & 3)) & 3; | |
319 | pos += avp_length + pad; | |
320 | left -= avp_length + pad; | |
321 | } | |
322 | ||
323 | return 0; | |
324 | ||
325 | fail: | |
326 | os_free(parse->eap); | |
327 | parse->eap = NULL; | |
328 | return -1; | |
329 | } | |
330 | ||
331 | ||
332 | static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm, | |
333 | struct eap_ttls_data *data, size_t len) | |
334 | { | |
fd2f2d04 JM |
335 | return eap_server_tls_derive_key(sm, &data->ssl, "ttls challenge", |
336 | len); | |
6fc6879b JM |
337 | } |
338 | ||
339 | ||
340 | static void * eap_ttls_init(struct eap_sm *sm) | |
341 | { | |
342 | struct eap_ttls_data *data; | |
343 | ||
344 | data = os_zalloc(sizeof(*data)); | |
345 | if (data == NULL) | |
346 | return NULL; | |
347 | data->ttls_version = EAP_TTLS_VERSION; | |
6fc6879b JM |
348 | data->state = START; |
349 | ||
3f1b792f | 350 | if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_TYPE_TTLS)) { |
6fc6879b JM |
351 | wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL."); |
352 | eap_ttls_reset(sm, data); | |
353 | return NULL; | |
354 | } | |
355 | ||
356 | return data; | |
357 | } | |
358 | ||
359 | ||
360 | static void eap_ttls_reset(struct eap_sm *sm, void *priv) | |
361 | { | |
362 | struct eap_ttls_data *data = priv; | |
363 | if (data == NULL) | |
364 | return; | |
365 | if (data->phase2_priv && data->phase2_method) | |
366 | data->phase2_method->reset(sm, data->phase2_priv); | |
367 | eap_server_tls_ssl_deinit(sm, &data->ssl); | |
368 | wpabuf_free(data->pending_phase2_eap_resp); | |
0a13e06b | 369 | bin_clear_free(data, sizeof(*data)); |
6fc6879b JM |
370 | } |
371 | ||
372 | ||
373 | static struct wpabuf * eap_ttls_build_start(struct eap_sm *sm, | |
374 | struct eap_ttls_data *data, u8 id) | |
375 | { | |
376 | struct wpabuf *req; | |
377 | ||
378 | req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS, 1, | |
379 | EAP_CODE_REQUEST, id); | |
380 | if (req == NULL) { | |
381 | wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to allocate memory for" | |
382 | " request"); | |
383 | eap_ttls_state(data, FAILURE); | |
384 | return NULL; | |
385 | } | |
386 | ||
387 | wpabuf_put_u8(req, EAP_TLS_FLAGS_START | data->ttls_version); | |
388 | ||
389 | eap_ttls_state(data, PHASE1); | |
390 | ||
391 | return req; | |
392 | } | |
393 | ||
394 | ||
6fc6879b JM |
395 | static struct wpabuf * eap_ttls_build_phase2_eap_req( |
396 | struct eap_sm *sm, struct eap_ttls_data *data, u8 id) | |
397 | { | |
398 | struct wpabuf *buf, *encr_req; | |
6fc6879b JM |
399 | |
400 | ||
401 | buf = data->phase2_method->buildReq(sm, data->phase2_priv, id); | |
402 | if (buf == NULL) | |
403 | return NULL; | |
404 | ||
405 | wpa_hexdump_buf_key(MSG_DEBUG, | |
406 | "EAP-TTLS/EAP: Encapsulate Phase 2 data", buf); | |
407 | ||
408 | buf = eap_ttls_avp_encapsulate(buf, RADIUS_ATTR_EAP_MESSAGE, 1); | |
409 | if (buf == NULL) { | |
410 | wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Failed to encapsulate " | |
411 | "packet"); | |
412 | return NULL; | |
413 | } | |
414 | ||
81c85c06 JM |
415 | wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS/EAP: Encrypt encapsulated " |
416 | "Phase 2 data", buf); | |
6fc6879b | 417 | |
81c85c06 | 418 | encr_req = eap_server_tls_encrypt(sm, &data->ssl, buf); |
6fc6879b JM |
419 | wpabuf_free(buf); |
420 | ||
421 | return encr_req; | |
422 | } | |
423 | ||
424 | ||
425 | static struct wpabuf * eap_ttls_build_phase2_mschapv2( | |
34f564db | 426 | struct eap_sm *sm, struct eap_ttls_data *data) |
6fc6879b | 427 | { |
81c85c06 | 428 | struct wpabuf *encr_req, msgbuf; |
6fc6879b JM |
429 | u8 *req, *pos, *end; |
430 | int ret; | |
6fc6879b JM |
431 | |
432 | pos = req = os_malloc(100); | |
433 | if (req == NULL) | |
434 | return NULL; | |
435 | end = req + 100; | |
436 | ||
437 | if (data->mschapv2_resp_ok) { | |
438 | pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_SUCCESS, | |
439 | RADIUS_VENDOR_ID_MICROSOFT, 1, 43); | |
440 | *pos++ = data->mschapv2_ident; | |
441 | ret = os_snprintf((char *) pos, end - pos, "S="); | |
a80ba67a | 442 | if (!os_snprintf_error(end - pos, ret)) |
6fc6879b JM |
443 | pos += ret; |
444 | pos += wpa_snprintf_hex_uppercase( | |
445 | (char *) pos, end - pos, data->mschapv2_auth_response, | |
446 | sizeof(data->mschapv2_auth_response)); | |
447 | } else { | |
448 | pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_ERROR, | |
449 | RADIUS_VENDOR_ID_MICROSOFT, 1, 6); | |
450 | os_memcpy(pos, "Failed", 6); | |
451 | pos += 6; | |
452 | AVP_PAD(req, pos); | |
453 | } | |
454 | ||
81c85c06 JM |
455 | wpabuf_set(&msgbuf, req, pos - req); |
456 | wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Encrypting Phase 2 " | |
457 | "data", &msgbuf); | |
6fc6879b | 458 | |
81c85c06 | 459 | encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf); |
6fc6879b JM |
460 | os_free(req); |
461 | ||
462 | return encr_req; | |
463 | } | |
464 | ||
465 | ||
6fc6879b JM |
466 | static struct wpabuf * eap_ttls_buildReq(struct eap_sm *sm, void *priv, u8 id) |
467 | { | |
468 | struct eap_ttls_data *data = priv; | |
469 | ||
34f564db JM |
470 | if (data->ssl.state == FRAG_ACK) { |
471 | return eap_server_tls_build_ack(id, EAP_TYPE_TTLS, | |
472 | data->ttls_version); | |
473 | } | |
474 | ||
475 | if (data->ssl.state == WAIT_FRAG_ACK) { | |
476 | return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TTLS, | |
477 | data->ttls_version, id); | |
478 | } | |
479 | ||
6fc6879b JM |
480 | switch (data->state) { |
481 | case START: | |
482 | return eap_ttls_build_start(sm, data, id); | |
483 | case PHASE1: | |
34f564db JM |
484 | if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { |
485 | wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase1 done, " | |
486 | "starting Phase2"); | |
487 | eap_ttls_state(data, PHASE2_START); | |
488 | } | |
489 | break; | |
6fc6879b | 490 | case PHASE2_METHOD: |
2a29f0d4 JM |
491 | wpabuf_free(data->ssl.tls_out); |
492 | data->ssl.tls_out_pos = 0; | |
493 | data->ssl.tls_out = eap_ttls_build_phase2_eap_req(sm, data, | |
34f564db JM |
494 | id); |
495 | break; | |
6fc6879b | 496 | case PHASE2_MSCHAPV2_RESP: |
2a29f0d4 JM |
497 | wpabuf_free(data->ssl.tls_out); |
498 | data->ssl.tls_out_pos = 0; | |
499 | data->ssl.tls_out = eap_ttls_build_phase2_mschapv2(sm, data); | |
34f564db | 500 | break; |
6fc6879b JM |
501 | default: |
502 | wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d", | |
503 | __func__, data->state); | |
504 | return NULL; | |
505 | } | |
34f564db JM |
506 | |
507 | return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TTLS, | |
508 | data->ttls_version, id); | |
6fc6879b JM |
509 | } |
510 | ||
511 | ||
512 | static Boolean eap_ttls_check(struct eap_sm *sm, void *priv, | |
513 | struct wpabuf *respData) | |
514 | { | |
515 | const u8 *pos; | |
516 | size_t len; | |
517 | ||
518 | pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TTLS, respData, &len); | |
519 | if (pos == NULL || len < 1) { | |
520 | wpa_printf(MSG_INFO, "EAP-TTLS: Invalid frame"); | |
521 | return TRUE; | |
522 | } | |
523 | ||
524 | return FALSE; | |
525 | } | |
526 | ||
527 | ||
6fc6879b JM |
528 | static void eap_ttls_process_phase2_pap(struct eap_sm *sm, |
529 | struct eap_ttls_data *data, | |
530 | const u8 *user_password, | |
531 | size_t user_password_len) | |
532 | { | |
533 | if (!sm->user || !sm->user->password || sm->user->password_hash || | |
534 | !(sm->user->ttls_auth & EAP_TTLS_AUTH_PAP)) { | |
535 | wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: No plaintext user " | |
536 | "password configured"); | |
537 | eap_ttls_state(data, FAILURE); | |
538 | return; | |
539 | } | |
540 | ||
541 | if (sm->user->password_len != user_password_len || | |
30411b35 JM |
542 | os_memcmp_const(sm->user->password, user_password, |
543 | user_password_len) != 0) { | |
6fc6879b JM |
544 | wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Invalid user password"); |
545 | eap_ttls_state(data, FAILURE); | |
546 | return; | |
547 | } | |
548 | ||
549 | wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Correct user password"); | |
fd2f2d04 | 550 | eap_ttls_state(data, SUCCESS); |
b6f2ae3b | 551 | eap_ttls_valid_session(sm, data); |
6fc6879b JM |
552 | } |
553 | ||
554 | ||
555 | static void eap_ttls_process_phase2_chap(struct eap_sm *sm, | |
556 | struct eap_ttls_data *data, | |
557 | const u8 *challenge, | |
558 | size_t challenge_len, | |
559 | const u8 *password, | |
560 | size_t password_len) | |
561 | { | |
562 | u8 *chal, hash[CHAP_MD5_LEN]; | |
563 | ||
564 | if (challenge == NULL || password == NULL || | |
565 | challenge_len != EAP_TTLS_CHAP_CHALLENGE_LEN || | |
566 | password_len != 1 + EAP_TTLS_CHAP_PASSWORD_LEN) { | |
567 | wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid CHAP attributes " | |
568 | "(challenge len %lu password len %lu)", | |
569 | (unsigned long) challenge_len, | |
570 | (unsigned long) password_len); | |
571 | eap_ttls_state(data, FAILURE); | |
572 | return; | |
573 | } | |
574 | ||
575 | if (!sm->user || !sm->user->password || sm->user->password_hash || | |
576 | !(sm->user->ttls_auth & EAP_TTLS_AUTH_CHAP)) { | |
577 | wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: No plaintext user " | |
578 | "password configured"); | |
579 | eap_ttls_state(data, FAILURE); | |
580 | return; | |
581 | } | |
582 | ||
583 | chal = eap_ttls_implicit_challenge(sm, data, | |
584 | EAP_TTLS_CHAP_CHALLENGE_LEN + 1); | |
585 | if (chal == NULL) { | |
586 | wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Failed to generate " | |
587 | "challenge from TLS data"); | |
588 | eap_ttls_state(data, FAILURE); | |
589 | return; | |
590 | } | |
591 | ||
30411b35 JM |
592 | if (os_memcmp_const(challenge, chal, EAP_TTLS_CHAP_CHALLENGE_LEN) |
593 | != 0 || | |
6fc6879b JM |
594 | password[0] != chal[EAP_TTLS_CHAP_CHALLENGE_LEN]) { |
595 | wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Challenge mismatch"); | |
596 | os_free(chal); | |
597 | eap_ttls_state(data, FAILURE); | |
598 | return; | |
599 | } | |
600 | os_free(chal); | |
601 | ||
602 | /* MD5(Ident + Password + Challenge) */ | |
603 | chap_md5(password[0], sm->user->password, sm->user->password_len, | |
604 | challenge, challenge_len, hash); | |
605 | ||
30411b35 JM |
606 | if (os_memcmp_const(hash, password + 1, EAP_TTLS_CHAP_PASSWORD_LEN) == |
607 | 0) { | |
6fc6879b | 608 | wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Correct user password"); |
fd2f2d04 | 609 | eap_ttls_state(data, SUCCESS); |
b6f2ae3b | 610 | eap_ttls_valid_session(sm, data); |
6fc6879b JM |
611 | } else { |
612 | wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid user password"); | |
613 | eap_ttls_state(data, FAILURE); | |
614 | } | |
615 | } | |
616 | ||
617 | ||
618 | static void eap_ttls_process_phase2_mschap(struct eap_sm *sm, | |
619 | struct eap_ttls_data *data, | |
620 | u8 *challenge, size_t challenge_len, | |
621 | u8 *response, size_t response_len) | |
622 | { | |
623 | u8 *chal, nt_response[24]; | |
624 | ||
625 | if (challenge == NULL || response == NULL || | |
626 | challenge_len != EAP_TTLS_MSCHAP_CHALLENGE_LEN || | |
627 | response_len != EAP_TTLS_MSCHAP_RESPONSE_LEN) { | |
628 | wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid MS-CHAP " | |
629 | "attributes (challenge len %lu response len %lu)", | |
630 | (unsigned long) challenge_len, | |
631 | (unsigned long) response_len); | |
632 | eap_ttls_state(data, FAILURE); | |
633 | return; | |
634 | } | |
635 | ||
636 | if (!sm->user || !sm->user->password || | |
637 | !(sm->user->ttls_auth & EAP_TTLS_AUTH_MSCHAP)) { | |
638 | wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: No user password " | |
639 | "configured"); | |
640 | eap_ttls_state(data, FAILURE); | |
641 | return; | |
642 | } | |
643 | ||
644 | chal = eap_ttls_implicit_challenge(sm, data, | |
645 | EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1); | |
646 | if (chal == NULL) { | |
647 | wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Failed to generate " | |
648 | "challenge from TLS data"); | |
649 | eap_ttls_state(data, FAILURE); | |
650 | return; | |
651 | } | |
652 | ||
1d0f42a0 JM |
653 | #ifdef CONFIG_TESTING_OPTIONS |
654 | eap_server_mschap_rx_callback(sm, "TTLS-MSCHAP", | |
655 | sm->identity, sm->identity_len, | |
656 | challenge, response + 2 + 24); | |
657 | #endif /* CONFIG_TESTING_OPTIONS */ | |
658 | ||
30411b35 JM |
659 | if (os_memcmp_const(challenge, chal, EAP_TTLS_MSCHAP_CHALLENGE_LEN) |
660 | != 0 || | |
6fc6879b JM |
661 | response[0] != chal[EAP_TTLS_MSCHAP_CHALLENGE_LEN]) { |
662 | wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Challenge mismatch"); | |
663 | os_free(chal); | |
664 | eap_ttls_state(data, FAILURE); | |
665 | return; | |
666 | } | |
667 | os_free(chal); | |
668 | ||
669 | if (sm->user->password_hash) | |
670 | challenge_response(challenge, sm->user->password, nt_response); | |
671 | else | |
672 | nt_challenge_response(challenge, sm->user->password, | |
673 | sm->user->password_len, nt_response); | |
674 | ||
30411b35 | 675 | if (os_memcmp_const(nt_response, response + 2 + 24, 24) == 0) { |
6fc6879b | 676 | wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Correct response"); |
fd2f2d04 | 677 | eap_ttls_state(data, SUCCESS); |
b6f2ae3b | 678 | eap_ttls_valid_session(sm, data); |
6fc6879b JM |
679 | } else { |
680 | wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid NT-Response"); | |
681 | wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Received", | |
682 | response + 2 + 24, 24); | |
683 | wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Expected", | |
684 | nt_response, 24); | |
685 | eap_ttls_state(data, FAILURE); | |
686 | } | |
687 | } | |
688 | ||
689 | ||
690 | static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, | |
691 | struct eap_ttls_data *data, | |
692 | u8 *challenge, | |
693 | size_t challenge_len, | |
694 | u8 *response, size_t response_len) | |
695 | { | |
696 | u8 *chal, *username, nt_response[24], *rx_resp, *peer_challenge, | |
697 | *auth_challenge; | |
698 | size_t username_len, i; | |
699 | ||
700 | if (challenge == NULL || response == NULL || | |
701 | challenge_len != EAP_TTLS_MSCHAPV2_CHALLENGE_LEN || | |
702 | response_len != EAP_TTLS_MSCHAPV2_RESPONSE_LEN) { | |
703 | wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Invalid MS-CHAP2 " | |
704 | "attributes (challenge len %lu response len %lu)", | |
705 | (unsigned long) challenge_len, | |
706 | (unsigned long) response_len); | |
707 | eap_ttls_state(data, FAILURE); | |
708 | return; | |
709 | } | |
710 | ||
711 | if (!sm->user || !sm->user->password || | |
712 | !(sm->user->ttls_auth & EAP_TTLS_AUTH_MSCHAPV2)) { | |
713 | wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: No user password " | |
714 | "configured"); | |
715 | eap_ttls_state(data, FAILURE); | |
716 | return; | |
717 | } | |
718 | ||
a2f94dbe JM |
719 | if (sm->identity == NULL) { |
720 | wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: No user identity " | |
721 | "known"); | |
722 | eap_ttls_state(data, FAILURE); | |
723 | return; | |
724 | } | |
725 | ||
6fc6879b JM |
726 | /* MSCHAPv2 does not include optional domain name in the |
727 | * challenge-response calculation, so remove domain prefix | |
728 | * (if present). */ | |
729 | username = sm->identity; | |
730 | username_len = sm->identity_len; | |
731 | for (i = 0; i < username_len; i++) { | |
732 | if (username[i] == '\\') { | |
733 | username_len -= i + 1; | |
734 | username += i + 1; | |
735 | break; | |
736 | } | |
737 | } | |
738 | ||
739 | chal = eap_ttls_implicit_challenge( | |
740 | sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1); | |
741 | if (chal == NULL) { | |
742 | wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Failed to generate " | |
743 | "challenge from TLS data"); | |
744 | eap_ttls_state(data, FAILURE); | |
745 | return; | |
746 | } | |
747 | ||
30411b35 JM |
748 | if (os_memcmp_const(challenge, chal, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) |
749 | != 0 || | |
6fc6879b JM |
750 | response[0] != chal[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN]) { |
751 | wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Challenge mismatch"); | |
752 | os_free(chal); | |
753 | eap_ttls_state(data, FAILURE); | |
754 | return; | |
755 | } | |
756 | os_free(chal); | |
757 | ||
758 | auth_challenge = challenge; | |
759 | peer_challenge = response + 2; | |
760 | ||
761 | wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: User", | |
762 | username, username_len); | |
763 | wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: auth_challenge", | |
764 | auth_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); | |
765 | wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: peer_challenge", | |
766 | peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); | |
767 | ||
768 | if (sm->user->password_hash) { | |
769 | generate_nt_response_pwhash(auth_challenge, peer_challenge, | |
770 | username, username_len, | |
771 | sm->user->password, | |
772 | nt_response); | |
773 | } else { | |
774 | generate_nt_response(auth_challenge, peer_challenge, | |
775 | username, username_len, | |
776 | sm->user->password, | |
777 | sm->user->password_len, | |
778 | nt_response); | |
779 | } | |
780 | ||
781 | rx_resp = response + 2 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 8; | |
1d0f42a0 JM |
782 | #ifdef CONFIG_TESTING_OPTIONS |
783 | { | |
784 | u8 challenge2[8]; | |
785 | ||
786 | if (challenge_hash(peer_challenge, auth_challenge, | |
787 | username, username_len, challenge2) == 0) { | |
788 | eap_server_mschap_rx_callback(sm, "TTLS-MSCHAPV2", | |
789 | username, username_len, | |
790 | challenge2, rx_resp); | |
791 | } | |
792 | } | |
793 | #endif /* CONFIG_TESTING_OPTIONS */ | |
30411b35 | 794 | if (os_memcmp_const(nt_response, rx_resp, 24) == 0) { |
6fc6879b JM |
795 | wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Correct " |
796 | "NT-Response"); | |
797 | data->mschapv2_resp_ok = 1; | |
6fc6879b JM |
798 | |
799 | if (sm->user->password_hash) { | |
800 | generate_authenticator_response_pwhash( | |
801 | sm->user->password, | |
802 | peer_challenge, auth_challenge, | |
803 | username, username_len, nt_response, | |
804 | data->mschapv2_auth_response); | |
805 | } else { | |
806 | generate_authenticator_response( | |
807 | sm->user->password, sm->user->password_len, | |
808 | peer_challenge, auth_challenge, | |
809 | username, username_len, nt_response, | |
810 | data->mschapv2_auth_response); | |
811 | } | |
812 | } else { | |
813 | wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Invalid " | |
814 | "NT-Response"); | |
815 | wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: Received", | |
816 | rx_resp, 24); | |
817 | wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: Expected", | |
818 | nt_response, 24); | |
819 | data->mschapv2_resp_ok = 0; | |
820 | } | |
821 | eap_ttls_state(data, PHASE2_MSCHAPV2_RESP); | |
822 | data->mschapv2_ident = response[0]; | |
823 | } | |
824 | ||
825 | ||
826 | static int eap_ttls_phase2_eap_init(struct eap_sm *sm, | |
827 | struct eap_ttls_data *data, | |
828 | EapType eap_type) | |
829 | { | |
830 | if (data->phase2_priv && data->phase2_method) { | |
831 | data->phase2_method->reset(sm, data->phase2_priv); | |
832 | data->phase2_method = NULL; | |
833 | data->phase2_priv = NULL; | |
834 | } | |
835 | data->phase2_method = eap_server_get_eap_method(EAP_VENDOR_IETF, | |
836 | eap_type); | |
837 | if (!data->phase2_method) | |
838 | return -1; | |
839 | ||
840 | sm->init_phase2 = 1; | |
841 | data->phase2_priv = data->phase2_method->init(sm); | |
842 | sm->init_phase2 = 0; | |
99bff843 | 843 | return data->phase2_priv == NULL ? -1 : 0; |
6fc6879b JM |
844 | } |
845 | ||
846 | ||
847 | static void eap_ttls_process_phase2_eap_response(struct eap_sm *sm, | |
848 | struct eap_ttls_data *data, | |
849 | u8 *in_data, size_t in_len) | |
850 | { | |
851 | u8 next_type = EAP_TYPE_NONE; | |
852 | struct eap_hdr *hdr; | |
853 | u8 *pos; | |
854 | size_t left; | |
855 | struct wpabuf buf; | |
856 | const struct eap_method *m = data->phase2_method; | |
857 | void *priv = data->phase2_priv; | |
858 | ||
859 | if (priv == NULL) { | |
860 | wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: %s - Phase2 not " | |
861 | "initialized?!", __func__); | |
862 | return; | |
863 | } | |
864 | ||
865 | hdr = (struct eap_hdr *) in_data; | |
866 | pos = (u8 *) (hdr + 1); | |
867 | ||
868 | if (in_len > sizeof(*hdr) && *pos == EAP_TYPE_NAK) { | |
869 | left = in_len - sizeof(*hdr); | |
870 | wpa_hexdump(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 type Nak'ed; " | |
871 | "allowed types", pos + 1, left - 1); | |
872 | eap_sm_process_nak(sm, pos + 1, left - 1); | |
873 | if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && | |
874 | sm->user->methods[sm->user_eap_method_index].method != | |
875 | EAP_TYPE_NONE) { | |
876 | next_type = sm->user->methods[ | |
877 | sm->user_eap_method_index++].method; | |
878 | wpa_printf(MSG_DEBUG, "EAP-TTLS: try EAP type %d", | |
879 | next_type); | |
510c02d4 JM |
880 | if (eap_ttls_phase2_eap_init(sm, data, next_type)) { |
881 | wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to " | |
882 | "initialize EAP type %d", | |
883 | next_type); | |
884 | eap_ttls_state(data, FAILURE); | |
885 | return; | |
886 | } | |
6fc6879b JM |
887 | } else { |
888 | eap_ttls_state(data, FAILURE); | |
889 | } | |
890 | return; | |
891 | } | |
892 | ||
893 | wpabuf_set(&buf, in_data, in_len); | |
894 | ||
895 | if (m->check(sm, priv, &buf)) { | |
896 | wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 check() asked to " | |
897 | "ignore the packet"); | |
898 | return; | |
899 | } | |
900 | ||
901 | m->process(sm, priv, &buf); | |
902 | ||
903 | if (sm->method_pending == METHOD_PENDING_WAIT) { | |
904 | wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 method is in " | |
905 | "pending wait state - save decrypted response"); | |
906 | wpabuf_free(data->pending_phase2_eap_resp); | |
907 | data->pending_phase2_eap_resp = wpabuf_dup(&buf); | |
908 | } | |
909 | ||
910 | if (!m->isDone(sm, priv)) | |
911 | return; | |
912 | ||
913 | if (!m->isSuccess(sm, priv)) { | |
914 | wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 method failed"); | |
915 | eap_ttls_state(data, FAILURE); | |
916 | return; | |
917 | } | |
918 | ||
919 | switch (data->state) { | |
920 | case PHASE2_START: | |
921 | if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { | |
922 | wpa_hexdump_ascii(MSG_DEBUG, "EAP_TTLS: Phase2 " | |
923 | "Identity not found in the user " | |
924 | "database", | |
925 | sm->identity, sm->identity_len); | |
926 | eap_ttls_state(data, FAILURE); | |
927 | break; | |
928 | } | |
929 | ||
930 | eap_ttls_state(data, PHASE2_METHOD); | |
931 | next_type = sm->user->methods[0].method; | |
932 | sm->user_eap_method_index = 1; | |
933 | wpa_printf(MSG_DEBUG, "EAP-TTLS: try EAP type %d", next_type); | |
51853c89 JM |
934 | if (eap_ttls_phase2_eap_init(sm, data, next_type)) { |
935 | wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to initialize " | |
936 | "EAP type %d", next_type); | |
937 | eap_ttls_state(data, FAILURE); | |
938 | } | |
6fc6879b JM |
939 | break; |
940 | case PHASE2_METHOD: | |
fd2f2d04 | 941 | eap_ttls_state(data, SUCCESS); |
b6f2ae3b | 942 | eap_ttls_valid_session(sm, data); |
6fc6879b JM |
943 | break; |
944 | case FAILURE: | |
945 | break; | |
946 | default: | |
947 | wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d", | |
948 | __func__, data->state); | |
949 | break; | |
950 | } | |
6fc6879b JM |
951 | } |
952 | ||
953 | ||
954 | static void eap_ttls_process_phase2_eap(struct eap_sm *sm, | |
955 | struct eap_ttls_data *data, | |
956 | const u8 *eap, size_t eap_len) | |
957 | { | |
958 | struct eap_hdr *hdr; | |
959 | size_t len; | |
960 | ||
961 | if (data->state == PHASE2_START) { | |
962 | wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: initializing Phase 2"); | |
963 | if (eap_ttls_phase2_eap_init(sm, data, EAP_TYPE_IDENTITY) < 0) | |
964 | { | |
965 | wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: failed to " | |
966 | "initialize EAP-Identity"); | |
967 | return; | |
968 | } | |
969 | } | |
970 | ||
971 | if (eap_len < sizeof(*hdr)) { | |
972 | wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: too short Phase 2 EAP " | |
973 | "packet (len=%lu)", (unsigned long) eap_len); | |
974 | return; | |
975 | } | |
976 | ||
977 | hdr = (struct eap_hdr *) eap; | |
978 | len = be_to_host16(hdr->length); | |
979 | wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: received Phase 2 EAP: code=%d " | |
980 | "identifier=%d length=%lu", hdr->code, hdr->identifier, | |
981 | (unsigned long) len); | |
982 | if (len > eap_len) { | |
983 | wpa_printf(MSG_INFO, "EAP-TTLS/EAP: Length mismatch in Phase 2" | |
984 | " EAP frame (hdr len=%lu, data len in AVP=%lu)", | |
985 | (unsigned long) len, (unsigned long) eap_len); | |
986 | return; | |
987 | } | |
988 | ||
989 | switch (hdr->code) { | |
990 | case EAP_CODE_RESPONSE: | |
991 | eap_ttls_process_phase2_eap_response(sm, data, (u8 *) hdr, | |
992 | len); | |
993 | break; | |
994 | default: | |
995 | wpa_printf(MSG_INFO, "EAP-TTLS/EAP: Unexpected code=%d in " | |
996 | "Phase 2 EAP header", hdr->code); | |
997 | break; | |
998 | } | |
999 | } | |
1000 | ||
1001 | ||
1002 | static void eap_ttls_process_phase2(struct eap_sm *sm, | |
1003 | struct eap_ttls_data *data, | |
34f564db | 1004 | struct wpabuf *in_buf) |
6fc6879b | 1005 | { |
81c85c06 | 1006 | struct wpabuf *in_decrypted; |
6fc6879b | 1007 | struct eap_ttls_avp parse; |
6fc6879b JM |
1008 | |
1009 | wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for" | |
81c85c06 | 1010 | " Phase 2", (unsigned long) wpabuf_len(in_buf)); |
6fc6879b JM |
1011 | |
1012 | if (data->pending_phase2_eap_resp) { | |
1013 | wpa_printf(MSG_DEBUG, "EAP-TTLS: Pending Phase 2 EAP response " | |
1014 | "- skip decryption and use old data"); | |
1015 | eap_ttls_process_phase2_eap( | |
1016 | sm, data, wpabuf_head(data->pending_phase2_eap_resp), | |
1017 | wpabuf_len(data->pending_phase2_eap_resp)); | |
1018 | wpabuf_free(data->pending_phase2_eap_resp); | |
1019 | data->pending_phase2_eap_resp = NULL; | |
1020 | return; | |
1021 | } | |
1022 | ||
81c85c06 JM |
1023 | in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, |
1024 | in_buf); | |
6fc6879b | 1025 | if (in_decrypted == NULL) { |
6fc6879b JM |
1026 | wpa_printf(MSG_INFO, "EAP-TTLS: Failed to decrypt Phase 2 " |
1027 | "data"); | |
6fc6879b JM |
1028 | eap_ttls_state(data, FAILURE); |
1029 | return; | |
1030 | } | |
1031 | ||
81c85c06 JM |
1032 | wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 EAP", |
1033 | in_decrypted); | |
6fc6879b | 1034 | |
81c85c06 | 1035 | if (eap_ttls_avp_parse(in_decrypted, &parse) < 0) { |
6fc6879b | 1036 | wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to parse AVPs"); |
81c85c06 | 1037 | wpabuf_free(in_decrypted); |
6fc6879b JM |
1038 | eap_ttls_state(data, FAILURE); |
1039 | return; | |
1040 | } | |
1041 | ||
1042 | if (parse.user_name) { | |
01f7fe10 | 1043 | char *nbuf; |
95f6f6a4 | 1044 | nbuf = os_malloc(parse.user_name_len * 4 + 1); |
01f7fe10 | 1045 | if (nbuf) { |
95f6f6a4 | 1046 | printf_encode(nbuf, parse.user_name_len * 4 + 1, |
01f7fe10 JM |
1047 | parse.user_name, |
1048 | parse.user_name_len); | |
1049 | eap_log_msg(sm, "TTLS-User-Name '%s'", nbuf); | |
1050 | os_free(nbuf); | |
1051 | } | |
1052 | ||
6fc6879b JM |
1053 | os_free(sm->identity); |
1054 | sm->identity = os_malloc(parse.user_name_len); | |
a2f94dbe JM |
1055 | if (sm->identity == NULL) { |
1056 | eap_ttls_state(data, FAILURE); | |
1057 | goto done; | |
6fc6879b | 1058 | } |
a2f94dbe JM |
1059 | os_memcpy(sm->identity, parse.user_name, parse.user_name_len); |
1060 | sm->identity_len = parse.user_name_len; | |
6fc6879b JM |
1061 | if (eap_user_get(sm, parse.user_name, parse.user_name_len, 1) |
1062 | != 0) { | |
1063 | wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 Identity not " | |
1064 | "found in the user database"); | |
1065 | eap_ttls_state(data, FAILURE); | |
1066 | goto done; | |
1067 | } | |
1068 | } | |
1069 | ||
1e5839e0 | 1070 | #ifdef EAP_SERVER_TNC |
c80a74d7 JM |
1071 | if (data->tnc_started && parse.eap == NULL) { |
1072 | wpa_printf(MSG_DEBUG, "EAP-TTLS: TNC started but no EAP " | |
1073 | "response from peer"); | |
1074 | eap_ttls_state(data, FAILURE); | |
1075 | goto done; | |
1076 | } | |
1e5839e0 | 1077 | #endif /* EAP_SERVER_TNC */ |
c80a74d7 | 1078 | |
6fc6879b JM |
1079 | if (parse.eap) { |
1080 | eap_ttls_process_phase2_eap(sm, data, parse.eap, | |
1081 | parse.eap_len); | |
1082 | } else if (parse.user_password) { | |
1083 | eap_ttls_process_phase2_pap(sm, data, parse.user_password, | |
1084 | parse.user_password_len); | |
1085 | } else if (parse.chap_password) { | |
1086 | eap_ttls_process_phase2_chap(sm, data, | |
1087 | parse.chap_challenge, | |
1088 | parse.chap_challenge_len, | |
1089 | parse.chap_password, | |
1090 | parse.chap_password_len); | |
1091 | } else if (parse.mschap_response) { | |
1092 | eap_ttls_process_phase2_mschap(sm, data, | |
1093 | parse.mschap_challenge, | |
1094 | parse.mschap_challenge_len, | |
1095 | parse.mschap_response, | |
1096 | parse.mschap_response_len); | |
1097 | } else if (parse.mschap2_response) { | |
1098 | eap_ttls_process_phase2_mschapv2(sm, data, | |
1099 | parse.mschap_challenge, | |
1100 | parse.mschap_challenge_len, | |
1101 | parse.mschap2_response, | |
1102 | parse.mschap2_response_len); | |
1103 | } | |
1104 | ||
1105 | done: | |
81c85c06 | 1106 | wpabuf_free(in_decrypted); |
6fc6879b JM |
1107 | os_free(parse.eap); |
1108 | } | |
1109 | ||
1110 | ||
c80a74d7 JM |
1111 | static void eap_ttls_start_tnc(struct eap_sm *sm, struct eap_ttls_data *data) |
1112 | { | |
1e5839e0 | 1113 | #ifdef EAP_SERVER_TNC |
c80a74d7 JM |
1114 | if (!sm->tnc || data->state != SUCCESS || data->tnc_started) |
1115 | return; | |
1116 | ||
1117 | wpa_printf(MSG_DEBUG, "EAP-TTLS: Initialize TNC"); | |
1118 | if (eap_ttls_phase2_eap_init(sm, data, EAP_TYPE_TNC)) { | |
1119 | wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to initialize TNC"); | |
1120 | eap_ttls_state(data, FAILURE); | |
1121 | return; | |
1122 | } | |
1123 | ||
1124 | data->tnc_started = 1; | |
1125 | eap_ttls_state(data, PHASE2_METHOD); | |
1e5839e0 | 1126 | #endif /* EAP_SERVER_TNC */ |
c80a74d7 JM |
1127 | } |
1128 | ||
1129 | ||
cda97d11 JM |
1130 | static int eap_ttls_process_version(struct eap_sm *sm, void *priv, |
1131 | int peer_version) | |
34f564db | 1132 | { |
cda97d11 | 1133 | struct eap_ttls_data *data = priv; |
34f564db JM |
1134 | if (peer_version < data->ttls_version) { |
1135 | wpa_printf(MSG_DEBUG, "EAP-TTLS: peer ver=%d, own ver=%d; " | |
1136 | "use version %d", | |
1137 | peer_version, data->ttls_version, peer_version); | |
1138 | data->ttls_version = peer_version; | |
1139 | } | |
1140 | ||
cda97d11 | 1141 | return 0; |
34f564db JM |
1142 | } |
1143 | ||
1144 | ||
cda97d11 JM |
1145 | static void eap_ttls_process_msg(struct eap_sm *sm, void *priv, |
1146 | const struct wpabuf *respData) | |
6fc6879b JM |
1147 | { |
1148 | struct eap_ttls_data *data = priv; | |
6fc6879b JM |
1149 | |
1150 | switch (data->state) { | |
1151 | case PHASE1: | |
34f564db | 1152 | if (eap_server_tls_phase1(sm, &data->ssl) < 0) |
6fc6879b | 1153 | eap_ttls_state(data, FAILURE); |
6fc6879b JM |
1154 | break; |
1155 | case PHASE2_START: | |
1156 | case PHASE2_METHOD: | |
2a29f0d4 | 1157 | eap_ttls_process_phase2(sm, data, data->ssl.tls_in); |
c80a74d7 | 1158 | eap_ttls_start_tnc(sm, data); |
6fc6879b JM |
1159 | break; |
1160 | case PHASE2_MSCHAPV2_RESP: | |
2a29f0d4 | 1161 | if (data->mschapv2_resp_ok && wpabuf_len(data->ssl.tls_in) == |
cda97d11 | 1162 | 0) { |
6fc6879b JM |
1163 | wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer " |
1164 | "acknowledged response"); | |
fd2f2d04 | 1165 | eap_ttls_state(data, SUCCESS); |
b6f2ae3b | 1166 | eap_ttls_valid_session(sm, data); |
6fc6879b JM |
1167 | } else if (!data->mschapv2_resp_ok) { |
1168 | wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer " | |
1169 | "acknowledged error"); | |
1170 | eap_ttls_state(data, FAILURE); | |
1171 | } else { | |
1172 | wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Unexpected " | |
1173 | "frame from peer (payload len %lu, " | |
1174 | "expected empty frame)", | |
cda97d11 | 1175 | (unsigned long) |
2a29f0d4 | 1176 | wpabuf_len(data->ssl.tls_in)); |
6fc6879b JM |
1177 | eap_ttls_state(data, FAILURE); |
1178 | } | |
c80a74d7 | 1179 | eap_ttls_start_tnc(sm, data); |
6fc6879b JM |
1180 | break; |
1181 | default: | |
1182 | wpa_printf(MSG_DEBUG, "EAP-TTLS: Unexpected state %d in %s", | |
1183 | data->state, __func__); | |
1184 | break; | |
1185 | } | |
cda97d11 | 1186 | } |
6fc6879b | 1187 | |
34f564db | 1188 | |
cda97d11 JM |
1189 | static void eap_ttls_process(struct eap_sm *sm, void *priv, |
1190 | struct wpabuf *respData) | |
1191 | { | |
1192 | struct eap_ttls_data *data = priv; | |
b6f2ae3b JM |
1193 | const struct wpabuf *buf; |
1194 | const u8 *pos; | |
1195 | u8 id_len; | |
1196 | ||
cda97d11 JM |
1197 | if (eap_server_tls_process(sm, &data->ssl, respData, data, |
1198 | EAP_TYPE_TTLS, eap_ttls_process_version, | |
b6f2ae3b JM |
1199 | eap_ttls_process_msg) < 0) { |
1200 | eap_ttls_state(data, FAILURE); | |
1201 | return; | |
1202 | } | |
1203 | ||
1204 | if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) || | |
1205 | !tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) | |
1206 | return; | |
1207 | ||
1208 | buf = tls_connection_get_success_data(data->ssl.conn); | |
1209 | if (!buf || wpabuf_len(buf) < 1) { | |
1210 | wpa_printf(MSG_DEBUG, | |
1211 | "EAP-TTLS: No success data in resumed session - reject attempt"); | |
cda97d11 | 1212 | eap_ttls_state(data, FAILURE); |
b6f2ae3b JM |
1213 | return; |
1214 | } | |
1215 | ||
1216 | pos = wpabuf_head(buf); | |
1217 | if (*pos != EAP_TYPE_TTLS) { | |
1218 | wpa_printf(MSG_DEBUG, | |
1219 | "EAP-TTLS: Resumed session for another EAP type (%u) - reject attempt", | |
1220 | *pos); | |
1221 | eap_ttls_state(data, FAILURE); | |
1222 | return; | |
1223 | } | |
1224 | ||
1225 | pos++; | |
1226 | id_len = *pos++; | |
1227 | wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: Identity from cached session", | |
1228 | pos, id_len); | |
1229 | os_free(sm->identity); | |
1230 | sm->identity = os_malloc(id_len ? id_len : 1); | |
1231 | if (!sm->identity) { | |
1232 | sm->identity_len = 0; | |
1233 | eap_ttls_state(data, FAILURE); | |
1234 | return; | |
1235 | } | |
1236 | ||
1237 | os_memcpy(sm->identity, pos, id_len); | |
1238 | sm->identity_len = id_len; | |
1239 | ||
1240 | if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { | |
1241 | wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: Phase2 Identity not found in the user database", | |
1242 | sm->identity, sm->identity_len); | |
1243 | eap_ttls_state(data, FAILURE); | |
1244 | return; | |
1245 | } | |
1246 | ||
1247 | wpa_printf(MSG_DEBUG, | |
1248 | "EAP-TTLS: Resuming previous session - skip Phase2"); | |
1249 | eap_ttls_state(data, SUCCESS); | |
1250 | tls_connection_set_success_data_resumed(data->ssl.conn); | |
6fc6879b JM |
1251 | } |
1252 | ||
1253 | ||
1254 | static Boolean eap_ttls_isDone(struct eap_sm *sm, void *priv) | |
1255 | { | |
1256 | struct eap_ttls_data *data = priv; | |
1257 | return data->state == SUCCESS || data->state == FAILURE; | |
1258 | } | |
1259 | ||
1260 | ||
6fc6879b JM |
1261 | static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) |
1262 | { | |
1263 | struct eap_ttls_data *data = priv; | |
1264 | u8 *eapKeyData; | |
1265 | ||
1266 | if (data->state != SUCCESS) | |
1267 | return NULL; | |
1268 | ||
fd2f2d04 JM |
1269 | eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, |
1270 | "ttls keying material", | |
1271 | EAP_TLS_KEY_LEN); | |
6fc6879b JM |
1272 | if (eapKeyData) { |
1273 | *len = EAP_TLS_KEY_LEN; | |
1274 | wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key", | |
1275 | eapKeyData, EAP_TLS_KEY_LEN); | |
1276 | } else { | |
1277 | wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key"); | |
1278 | } | |
1279 | ||
1280 | return eapKeyData; | |
1281 | } | |
1282 | ||
1283 | ||
1284 | static Boolean eap_ttls_isSuccess(struct eap_sm *sm, void *priv) | |
1285 | { | |
1286 | struct eap_ttls_data *data = priv; | |
1287 | return data->state == SUCCESS; | |
1288 | } | |
1289 | ||
1290 | ||
d1f89dd7 JM |
1291 | static u8 * eap_ttls_get_session_id(struct eap_sm *sm, void *priv, size_t *len) |
1292 | { | |
1293 | struct eap_ttls_data *data = priv; | |
1294 | ||
1295 | if (data->state != SUCCESS) | |
1296 | return NULL; | |
1297 | ||
1298 | return eap_server_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_TTLS, | |
1299 | len); | |
1300 | } | |
1301 | ||
1302 | ||
a7bec9e7 JM |
1303 | static u8 * eap_ttls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) |
1304 | { | |
1305 | struct eap_ttls_data *data = priv; | |
1306 | u8 *eapKeyData, *emsk; | |
1307 | ||
1308 | if (data->state != SUCCESS) | |
1309 | return NULL; | |
1310 | ||
1311 | eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, | |
1312 | "ttls keying material", | |
1313 | EAP_TLS_KEY_LEN + EAP_EMSK_LEN); | |
1314 | if (eapKeyData) { | |
1315 | emsk = os_malloc(EAP_EMSK_LEN); | |
1316 | if (emsk) | |
1317 | os_memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN, | |
1318 | EAP_EMSK_LEN); | |
1319 | bin_clear_free(eapKeyData, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); | |
1320 | } else | |
1321 | emsk = NULL; | |
1322 | ||
1323 | if (emsk) { | |
1324 | *len = EAP_EMSK_LEN; | |
1325 | wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Derived EMSK", | |
1326 | emsk, EAP_EMSK_LEN); | |
1327 | } else { | |
1328 | wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive EMSK"); | |
1329 | } | |
1330 | ||
1331 | return emsk; | |
1332 | } | |
1333 | ||
1334 | ||
6fc6879b JM |
1335 | int eap_server_ttls_register(void) |
1336 | { | |
1337 | struct eap_method *eap; | |
6fc6879b JM |
1338 | |
1339 | eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, | |
1340 | EAP_VENDOR_IETF, EAP_TYPE_TTLS, "TTLS"); | |
1341 | if (eap == NULL) | |
1342 | return -1; | |
1343 | ||
1344 | eap->init = eap_ttls_init; | |
1345 | eap->reset = eap_ttls_reset; | |
1346 | eap->buildReq = eap_ttls_buildReq; | |
1347 | eap->check = eap_ttls_check; | |
1348 | eap->process = eap_ttls_process; | |
1349 | eap->isDone = eap_ttls_isDone; | |
1350 | eap->getKey = eap_ttls_getKey; | |
1351 | eap->isSuccess = eap_ttls_isSuccess; | |
d1f89dd7 | 1352 | eap->getSessionId = eap_ttls_get_session_id; |
a7bec9e7 | 1353 | eap->get_emsk = eap_ttls_get_emsk; |
6fc6879b | 1354 | |
814f43cf | 1355 | return eap_server_method_register(eap); |
6fc6879b | 1356 | } |