]>
Commit | Line | Data |
---|---|---|
0ed57c5e JM |
1 | /* |
2 | * EAP-TEAP server (RFC 7170) | |
3 | * Copyright (c) 2004-2019, 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 "crypto/aes_wrap.h" | |
13 | #include "crypto/tls.h" | |
14 | #include "crypto/random.h" | |
15 | #include "eap_common/eap_teap_common.h" | |
16 | #include "eap_i.h" | |
17 | #include "eap_tls_common.h" | |
18 | ||
19 | ||
20 | static void eap_teap_reset(struct eap_sm *sm, void *priv); | |
21 | ||
22 | ||
23 | /* Private PAC-Opaque TLV types */ | |
24 | #define PAC_OPAQUE_TYPE_PAD 0 | |
25 | #define PAC_OPAQUE_TYPE_KEY 1 | |
26 | #define PAC_OPAQUE_TYPE_LIFETIME 2 | |
27 | #define PAC_OPAQUE_TYPE_IDENTITY 3 | |
28 | ||
29 | struct eap_teap_data { | |
30 | struct eap_ssl_data ssl; | |
31 | enum { | |
32 | START, PHASE1, PHASE1B, PHASE2_START, PHASE2_ID, | |
33 | PHASE2_BASIC_AUTH, PHASE2_METHOD, CRYPTO_BINDING, REQUEST_PAC, | |
34 | FAILURE_SEND_RESULT, SUCCESS, FAILURE | |
35 | } state; | |
36 | ||
37 | u8 teap_version; | |
38 | u8 peer_version; | |
39 | u16 tls_cs; | |
40 | ||
41 | const struct eap_method *phase2_method; | |
42 | void *phase2_priv; | |
43 | ||
44 | u8 crypto_binding_nonce[32]; | |
45 | int final_result; | |
46 | ||
47 | u8 simck_msk[EAP_TEAP_SIMCK_LEN]; | |
48 | u8 cmk_msk[EAP_TEAP_CMK_LEN]; | |
49 | u8 simck_emsk[EAP_TEAP_SIMCK_LEN]; | |
50 | u8 cmk_emsk[EAP_TEAP_CMK_LEN]; | |
51 | int simck_idx; | |
52 | int cmk_emsk_available; | |
53 | ||
54 | u8 pac_opaque_encr[16]; | |
55 | u8 *srv_id; | |
56 | size_t srv_id_len; | |
57 | char *srv_id_info; | |
58 | ||
59 | int anon_provisioning; | |
60 | int send_new_pac; /* server triggered re-keying of Tunnel PAC */ | |
61 | struct wpabuf *pending_phase2_resp; | |
62 | struct wpabuf *server_outer_tlvs; | |
63 | struct wpabuf *peer_outer_tlvs; | |
64 | u8 *identity; /* from PAC-Opaque */ | |
65 | size_t identity_len; | |
66 | int eap_seq; | |
67 | int tnc_started; | |
68 | ||
69 | int pac_key_lifetime; | |
70 | int pac_key_refresh_time; | |
71 | ||
72 | enum teap_error_codes error_code; | |
73 | }; | |
74 | ||
75 | ||
76 | static int eap_teap_process_phase2_start(struct eap_sm *sm, | |
77 | struct eap_teap_data *data); | |
78 | ||
79 | ||
80 | static const char * eap_teap_state_txt(int state) | |
81 | { | |
82 | switch (state) { | |
83 | case START: | |
84 | return "START"; | |
85 | case PHASE1: | |
86 | return "PHASE1"; | |
87 | case PHASE1B: | |
88 | return "PHASE1B"; | |
89 | case PHASE2_START: | |
90 | return "PHASE2_START"; | |
91 | case PHASE2_ID: | |
92 | return "PHASE2_ID"; | |
93 | case PHASE2_BASIC_AUTH: | |
94 | return "PHASE2_BASIC_AUTH"; | |
95 | case PHASE2_METHOD: | |
96 | return "PHASE2_METHOD"; | |
97 | case CRYPTO_BINDING: | |
98 | return "CRYPTO_BINDING"; | |
99 | case REQUEST_PAC: | |
100 | return "REQUEST_PAC"; | |
101 | case FAILURE_SEND_RESULT: | |
102 | return "FAILURE_SEND_RESULT"; | |
103 | case SUCCESS: | |
104 | return "SUCCESS"; | |
105 | case FAILURE: | |
106 | return "FAILURE"; | |
107 | default: | |
108 | return "Unknown?!"; | |
109 | } | |
110 | } | |
111 | ||
112 | ||
113 | static void eap_teap_state(struct eap_teap_data *data, int state) | |
114 | { | |
115 | wpa_printf(MSG_DEBUG, "EAP-TEAP: %s -> %s", | |
116 | eap_teap_state_txt(data->state), | |
117 | eap_teap_state_txt(state)); | |
118 | data->state = state; | |
119 | } | |
120 | ||
121 | ||
122 | static EapType eap_teap_req_failure(struct eap_teap_data *data, | |
123 | enum teap_error_codes error) | |
124 | { | |
125 | eap_teap_state(data, FAILURE_SEND_RESULT); | |
126 | return EAP_TYPE_NONE; | |
127 | } | |
128 | ||
129 | ||
130 | static int eap_teap_session_ticket_cb(void *ctx, const u8 *ticket, size_t len, | |
131 | const u8 *client_random, | |
132 | const u8 *server_random, | |
133 | u8 *master_secret) | |
134 | { | |
135 | struct eap_teap_data *data = ctx; | |
136 | const u8 *pac_opaque; | |
137 | size_t pac_opaque_len; | |
138 | u8 *buf, *pos, *end, *pac_key = NULL; | |
139 | os_time_t lifetime = 0; | |
140 | struct os_time now; | |
141 | u8 *identity = NULL; | |
142 | size_t identity_len = 0; | |
143 | ||
144 | wpa_printf(MSG_DEBUG, "EAP-TEAP: SessionTicket callback"); | |
145 | wpa_hexdump(MSG_DEBUG, "EAP-TEAP: SessionTicket (PAC-Opaque)", | |
146 | ticket, len); | |
147 | ||
148 | if (len < 4 || WPA_GET_BE16(ticket) != PAC_TYPE_PAC_OPAQUE) { | |
149 | wpa_printf(MSG_DEBUG, "EAP-TEAP: Ignore invalid SessionTicket"); | |
150 | return 0; | |
151 | } | |
152 | ||
153 | pac_opaque_len = WPA_GET_BE16(ticket + 2); | |
154 | pac_opaque = ticket + 4; | |
155 | if (pac_opaque_len < 8 || pac_opaque_len % 8 || | |
156 | pac_opaque_len > len - 4) { | |
157 | wpa_printf(MSG_DEBUG, | |
158 | "EAP-TEAP: Ignore invalid PAC-Opaque (len=%lu left=%lu)", | |
159 | (unsigned long) pac_opaque_len, | |
160 | (unsigned long) len); | |
161 | return 0; | |
162 | } | |
163 | wpa_hexdump(MSG_DEBUG, "EAP-TEAP: Received PAC-Opaque", | |
164 | pac_opaque, pac_opaque_len); | |
165 | ||
166 | buf = os_malloc(pac_opaque_len - 8); | |
167 | if (!buf) { | |
168 | wpa_printf(MSG_DEBUG, | |
169 | "EAP-TEAP: Failed to allocate memory for decrypting PAC-Opaque"); | |
170 | return 0; | |
171 | } | |
172 | ||
173 | if (aes_unwrap(data->pac_opaque_encr, sizeof(data->pac_opaque_encr), | |
174 | (pac_opaque_len - 8) / 8, pac_opaque, buf) < 0) { | |
175 | wpa_printf(MSG_DEBUG, "EAP-TEAP: Failed to decrypt PAC-Opaque"); | |
176 | os_free(buf); | |
177 | /* | |
178 | * This may have been caused by server changing the PAC-Opaque | |
179 | * encryption key, so just ignore this PAC-Opaque instead of | |
180 | * failing the authentication completely. Provisioning can now | |
181 | * be used to provision a new PAC. | |
182 | */ | |
183 | return 0; | |
184 | } | |
185 | ||
186 | end = buf + pac_opaque_len - 8; | |
187 | wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: Decrypted PAC-Opaque", | |
188 | buf, end - buf); | |
189 | ||
190 | pos = buf; | |
191 | while (end - pos > 1) { | |
192 | u8 id, elen; | |
193 | ||
194 | id = *pos++; | |
195 | elen = *pos++; | |
196 | if (elen > end - pos) | |
197 | break; | |
198 | ||
199 | switch (id) { | |
200 | case PAC_OPAQUE_TYPE_PAD: | |
201 | goto done; | |
202 | case PAC_OPAQUE_TYPE_KEY: | |
203 | if (elen != EAP_TEAP_PAC_KEY_LEN) { | |
204 | wpa_printf(MSG_DEBUG, | |
205 | "EAP-TEAP: Invalid PAC-Key length %d", | |
206 | elen); | |
207 | os_free(buf); | |
208 | return -1; | |
209 | } | |
210 | pac_key = pos; | |
211 | wpa_hexdump_key(MSG_DEBUG, | |
212 | "EAP-TEAP: PAC-Key from decrypted PAC-Opaque", | |
213 | pac_key, EAP_TEAP_PAC_KEY_LEN); | |
214 | break; | |
215 | case PAC_OPAQUE_TYPE_LIFETIME: | |
216 | if (elen != 4) { | |
217 | wpa_printf(MSG_DEBUG, | |
218 | "EAP-TEAP: Invalid PAC-Key lifetime length %d", | |
219 | elen); | |
220 | os_free(buf); | |
221 | return -1; | |
222 | } | |
223 | lifetime = WPA_GET_BE32(pos); | |
224 | break; | |
225 | case PAC_OPAQUE_TYPE_IDENTITY: | |
226 | identity = pos; | |
227 | identity_len = elen; | |
228 | break; | |
229 | } | |
230 | ||
231 | pos += elen; | |
232 | } | |
233 | done: | |
234 | ||
235 | if (!pac_key) { | |
236 | wpa_printf(MSG_DEBUG, | |
237 | "EAP-TEAP: No PAC-Key included in PAC-Opaque"); | |
238 | os_free(buf); | |
239 | return -1; | |
240 | } | |
241 | ||
242 | if (identity) { | |
243 | wpa_hexdump_ascii(MSG_DEBUG, | |
244 | "EAP-TEAP: Identity from PAC-Opaque", | |
245 | identity, identity_len); | |
246 | os_free(data->identity); | |
247 | data->identity = os_malloc(identity_len); | |
248 | if (data->identity) { | |
249 | os_memcpy(data->identity, identity, identity_len); | |
250 | data->identity_len = identity_len; | |
251 | } | |
252 | } | |
253 | ||
254 | if (os_get_time(&now) < 0 || lifetime <= 0 || now.sec > lifetime) { | |
255 | wpa_printf(MSG_DEBUG, | |
256 | "EAP-TEAP: PAC-Key not valid anymore (lifetime=%ld now=%ld)", | |
257 | lifetime, now.sec); | |
258 | data->send_new_pac = 2; | |
259 | /* | |
260 | * Allow PAC to be used to allow a PAC update with some level | |
261 | * of server authentication (i.e., do not fall back to full TLS | |
262 | * handshake since we cannot be sure that the peer would be | |
263 | * able to validate server certificate now). However, reject | |
264 | * the authentication since the PAC was not valid anymore. Peer | |
265 | * can connect again with the newly provisioned PAC after this. | |
266 | */ | |
267 | } else if (lifetime - now.sec < data->pac_key_refresh_time) { | |
268 | wpa_printf(MSG_DEBUG, | |
269 | "EAP-TEAP: PAC-Key soft timeout; send an update if authentication succeeds"); | |
270 | data->send_new_pac = 1; | |
271 | } | |
272 | ||
273 | /* EAP-TEAP uses PAC-Key as the TLS master_secret */ | |
274 | os_memcpy(master_secret, pac_key, EAP_TEAP_PAC_KEY_LEN); | |
275 | ||
276 | os_free(buf); | |
277 | ||
278 | return 1; | |
279 | } | |
280 | ||
281 | ||
282 | static int eap_teap_derive_key_auth(struct eap_sm *sm, | |
283 | struct eap_teap_data *data) | |
284 | { | |
285 | int res; | |
286 | ||
287 | /* RFC 7170, Section 5.1 */ | |
288 | res = tls_connection_export_key(sm->ssl_ctx, data->ssl.conn, | |
289 | TEAP_TLS_EXPORTER_LABEL_SKS, NULL, 0, | |
290 | data->simck_msk, EAP_TEAP_SIMCK_LEN); | |
291 | if (res) | |
292 | return res; | |
293 | wpa_hexdump_key(MSG_DEBUG, | |
294 | "EAP-TEAP: session_key_seed (S-IMCK[0])", | |
295 | data->simck_msk, EAP_TEAP_SIMCK_LEN); | |
296 | os_memcpy(data->simck_emsk, data->simck_msk, EAP_TEAP_SIMCK_LEN); | |
297 | data->simck_idx = 0; | |
298 | return 0; | |
299 | } | |
300 | ||
301 | ||
302 | static int eap_teap_update_icmk(struct eap_sm *sm, struct eap_teap_data *data) | |
303 | { | |
304 | u8 *msk = NULL, *emsk = NULL; | |
305 | size_t msk_len = 0, emsk_len = 0; | |
306 | int res; | |
307 | ||
308 | wpa_printf(MSG_DEBUG, "EAP-TEAP: Deriving ICMK[%d] (S-IMCK and CMK)", | |
309 | data->simck_idx + 1); | |
310 | ||
311 | if (sm->eap_teap_auth == 1) | |
a66e53c4 JM |
312 | return eap_teap_derive_cmk_basic_pw_auth(data->tls_cs, |
313 | data->simck_msk, | |
0ed57c5e JM |
314 | data->cmk_msk); |
315 | ||
316 | if (!data->phase2_method || !data->phase2_priv) { | |
317 | wpa_printf(MSG_INFO, "EAP-TEAP: Phase 2 method not available"); | |
318 | return -1; | |
319 | } | |
320 | ||
321 | if (data->phase2_method->getKey) { | |
322 | msk = data->phase2_method->getKey(sm, data->phase2_priv, | |
323 | &msk_len); | |
324 | if (!msk) { | |
325 | wpa_printf(MSG_INFO, | |
326 | "EAP-TEAP: Could not fetch Phase 2 MSK"); | |
327 | return -1; | |
328 | } | |
329 | } | |
330 | ||
331 | if (data->phase2_method->get_emsk) { | |
332 | emsk = data->phase2_method->get_emsk(sm, data->phase2_priv, | |
333 | &emsk_len); | |
334 | } | |
335 | ||
a66e53c4 JM |
336 | res = eap_teap_derive_imck(data->tls_cs, |
337 | data->simck_msk, data->simck_emsk, | |
0ed57c5e JM |
338 | msk, msk_len, emsk, emsk_len, |
339 | data->simck_msk, data->cmk_msk, | |
340 | data->simck_emsk, data->cmk_emsk); | |
341 | bin_clear_free(msk, msk_len); | |
342 | bin_clear_free(emsk, emsk_len); | |
343 | if (res == 0) { | |
344 | data->simck_idx++; | |
345 | if (emsk) | |
346 | data->cmk_emsk_available = 1; | |
347 | } | |
348 | return 0; | |
349 | } | |
350 | ||
351 | ||
352 | static void * eap_teap_init(struct eap_sm *sm) | |
353 | { | |
354 | struct eap_teap_data *data; | |
355 | ||
356 | data = os_zalloc(sizeof(*data)); | |
357 | if (!data) | |
358 | return NULL; | |
359 | data->teap_version = EAP_TEAP_VERSION; | |
360 | data->state = START; | |
361 | ||
362 | if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_TYPE_TEAP)) { | |
363 | wpa_printf(MSG_INFO, "EAP-TEAP: Failed to initialize SSL."); | |
364 | eap_teap_reset(sm, data); | |
365 | return NULL; | |
366 | } | |
367 | ||
368 | /* TODO: Add anon-DH TLS cipher suites (and if one is negotiated, | |
369 | * enforce inner EAP with mutual authentication to be used) */ | |
370 | ||
371 | if (tls_connection_set_session_ticket_cb(sm->ssl_ctx, data->ssl.conn, | |
372 | eap_teap_session_ticket_cb, | |
373 | data) < 0) { | |
374 | wpa_printf(MSG_INFO, | |
375 | "EAP-TEAP: Failed to set SessionTicket callback"); | |
376 | eap_teap_reset(sm, data); | |
377 | return NULL; | |
378 | } | |
379 | ||
380 | if (!sm->pac_opaque_encr_key) { | |
381 | wpa_printf(MSG_INFO, | |
382 | "EAP-TEAP: No PAC-Opaque encryption key configured"); | |
383 | eap_teap_reset(sm, data); | |
384 | return NULL; | |
385 | } | |
386 | os_memcpy(data->pac_opaque_encr, sm->pac_opaque_encr_key, | |
387 | sizeof(data->pac_opaque_encr)); | |
388 | ||
389 | if (!sm->eap_fast_a_id) { | |
390 | wpa_printf(MSG_INFO, "EAP-TEAP: No A-ID configured"); | |
391 | eap_teap_reset(sm, data); | |
392 | return NULL; | |
393 | } | |
394 | data->srv_id = os_malloc(sm->eap_fast_a_id_len); | |
395 | if (!data->srv_id) { | |
396 | eap_teap_reset(sm, data); | |
397 | return NULL; | |
398 | } | |
399 | os_memcpy(data->srv_id, sm->eap_fast_a_id, sm->eap_fast_a_id_len); | |
400 | data->srv_id_len = sm->eap_fast_a_id_len; | |
401 | ||
402 | if (!sm->eap_fast_a_id_info) { | |
403 | wpa_printf(MSG_INFO, "EAP-TEAP: No A-ID-Info configured"); | |
404 | eap_teap_reset(sm, data); | |
405 | return NULL; | |
406 | } | |
407 | data->srv_id_info = os_strdup(sm->eap_fast_a_id_info); | |
408 | if (!data->srv_id_info) { | |
409 | eap_teap_reset(sm, data); | |
410 | return NULL; | |
411 | } | |
412 | ||
413 | /* PAC-Key lifetime in seconds (hard limit) */ | |
414 | data->pac_key_lifetime = sm->pac_key_lifetime; | |
415 | ||
416 | /* | |
417 | * PAC-Key refresh time in seconds (soft limit on remaining hard | |
418 | * limit). The server will generate a new PAC-Key when this number of | |
419 | * seconds (or fewer) of the lifetime remains. | |
420 | */ | |
421 | data->pac_key_refresh_time = sm->pac_key_refresh_time; | |
422 | ||
423 | return data; | |
424 | } | |
425 | ||
426 | ||
427 | static void eap_teap_reset(struct eap_sm *sm, void *priv) | |
428 | { | |
429 | struct eap_teap_data *data = priv; | |
430 | ||
431 | if (!data) | |
432 | return; | |
433 | if (data->phase2_priv && data->phase2_method) | |
434 | data->phase2_method->reset(sm, data->phase2_priv); | |
435 | eap_server_tls_ssl_deinit(sm, &data->ssl); | |
436 | os_free(data->srv_id); | |
437 | os_free(data->srv_id_info); | |
438 | wpabuf_free(data->pending_phase2_resp); | |
439 | wpabuf_free(data->server_outer_tlvs); | |
440 | wpabuf_free(data->peer_outer_tlvs); | |
441 | os_free(data->identity); | |
442 | forced_memzero(data->simck_msk, EAP_TEAP_SIMCK_LEN); | |
443 | forced_memzero(data->simck_emsk, EAP_TEAP_SIMCK_LEN); | |
444 | forced_memzero(data->cmk_msk, EAP_TEAP_CMK_LEN); | |
445 | forced_memzero(data->cmk_emsk, EAP_TEAP_CMK_LEN); | |
446 | forced_memzero(data->pac_opaque_encr, sizeof(data->pac_opaque_encr)); | |
447 | bin_clear_free(data, sizeof(*data)); | |
448 | } | |
449 | ||
450 | ||
451 | static struct wpabuf * eap_teap_build_start(struct eap_sm *sm, | |
452 | struct eap_teap_data *data, u8 id) | |
453 | { | |
454 | struct wpabuf *req; | |
455 | size_t outer_tlv_len = sizeof(struct teap_tlv_hdr) + data->srv_id_len; | |
456 | const u8 *start, *end; | |
457 | ||
458 | req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TEAP, | |
459 | 1 + 4 + outer_tlv_len, EAP_CODE_REQUEST, id); | |
460 | if (!req) { | |
461 | wpa_printf(MSG_ERROR, | |
462 | "EAP-TEAP: Failed to allocate memory for request"); | |
463 | eap_teap_state(data, FAILURE); | |
464 | return NULL; | |
465 | } | |
466 | ||
467 | wpabuf_put_u8(req, EAP_TLS_FLAGS_START | EAP_TEAP_FLAGS_OUTER_TLV_LEN | | |
468 | data->teap_version); | |
469 | wpabuf_put_be32(req, outer_tlv_len); | |
470 | ||
471 | start = wpabuf_put(req, 0); | |
472 | ||
473 | /* RFC 7170, Section 4.2.2: Authority-ID TLV */ | |
474 | eap_teap_put_tlv(req, TEAP_TLV_AUTHORITY_ID, | |
475 | data->srv_id, data->srv_id_len); | |
476 | ||
477 | end = wpabuf_put(req, 0); | |
478 | wpabuf_free(data->server_outer_tlvs); | |
479 | data->server_outer_tlvs = wpabuf_alloc_copy(start, end - start); | |
480 | if (!data->server_outer_tlvs) { | |
481 | eap_teap_state(data, FAILURE); | |
482 | return NULL; | |
483 | } | |
484 | ||
485 | eap_teap_state(data, PHASE1); | |
486 | ||
487 | return req; | |
488 | } | |
489 | ||
490 | ||
491 | static int eap_teap_phase1_done(struct eap_sm *sm, struct eap_teap_data *data) | |
492 | { | |
493 | char cipher[64]; | |
494 | ||
495 | wpa_printf(MSG_DEBUG, "EAP-TEAP: Phase 1 done, starting Phase 2"); | |
496 | ||
497 | data->tls_cs = tls_connection_get_cipher_suite(data->ssl.conn); | |
498 | wpa_printf(MSG_DEBUG, "EAP-TEAP: TLS cipher suite 0x%04x", | |
499 | data->tls_cs); | |
500 | ||
501 | if (tls_get_cipher(sm->ssl_ctx, data->ssl.conn, cipher, sizeof(cipher)) | |
502 | < 0) { | |
503 | wpa_printf(MSG_DEBUG, | |
504 | "EAP-TEAP: Failed to get cipher information"); | |
505 | eap_teap_state(data, FAILURE); | |
506 | return -1; | |
507 | } | |
508 | data->anon_provisioning = os_strstr(cipher, "ADH") != NULL; | |
509 | ||
510 | if (data->anon_provisioning) | |
511 | wpa_printf(MSG_DEBUG, "EAP-TEAP: Anonymous provisioning"); | |
512 | ||
513 | if (eap_teap_derive_key_auth(sm, data) < 0) { | |
514 | eap_teap_state(data, FAILURE); | |
515 | return -1; | |
516 | } | |
517 | ||
518 | eap_teap_state(data, PHASE2_START); | |
519 | ||
520 | return 0; | |
521 | } | |
522 | ||
523 | ||
524 | static struct wpabuf * eap_teap_build_phase2_req(struct eap_sm *sm, | |
525 | struct eap_teap_data *data, | |
526 | u8 id) | |
527 | { | |
528 | struct wpabuf *req; | |
529 | ||
530 | if (sm->eap_teap_auth == 1) { | |
531 | wpa_printf(MSG_DEBUG, "EAP-TEAP: Initiate Basic-Password-Auth"); | |
532 | req = wpabuf_alloc(sizeof(struct teap_tlv_hdr)); | |
533 | if (!req) | |
534 | return NULL; | |
535 | eap_teap_put_tlv_hdr(req, TEAP_TLV_BASIC_PASSWORD_AUTH_REQ, 0); | |
536 | return req; | |
537 | } | |
538 | ||
539 | wpa_printf(MSG_DEBUG, "EAP-TEAP: Initiate inner EAP method"); | |
540 | if (!data->phase2_priv) { | |
541 | wpa_printf(MSG_DEBUG, | |
542 | "EAP-TEAP: Phase 2 method not initialized"); | |
543 | return NULL; | |
544 | } | |
545 | ||
546 | req = data->phase2_method->buildReq(sm, data->phase2_priv, id); | |
547 | if (!req) | |
548 | return NULL; | |
549 | ||
550 | wpa_hexdump_buf_key(MSG_MSGDUMP, "EAP-TEAP: Phase 2 EAP-Request", req); | |
551 | return eap_teap_tlv_eap_payload(req); | |
552 | } | |
553 | ||
554 | ||
555 | static struct wpabuf * eap_teap_build_crypto_binding( | |
556 | struct eap_sm *sm, struct eap_teap_data *data) | |
557 | { | |
558 | struct wpabuf *buf; | |
559 | struct teap_tlv_result *result; | |
560 | struct teap_tlv_crypto_binding *cb; | |
561 | u8 subtype, flags; | |
562 | ||
563 | buf = wpabuf_alloc(2 * sizeof(*result) + sizeof(*cb)); | |
564 | if (!buf) | |
565 | return NULL; | |
566 | ||
567 | if (data->send_new_pac || data->anon_provisioning || | |
568 | data->phase2_method) | |
569 | data->final_result = 0; | |
570 | else | |
571 | data->final_result = 1; | |
572 | ||
573 | if (!data->final_result || data->eap_seq > 0) { | |
574 | /* Intermediate-Result */ | |
575 | wpa_printf(MSG_DEBUG, | |
576 | "EAP-TEAP: Add Intermediate-Result TLV (status=SUCCESS)"); | |
577 | result = wpabuf_put(buf, sizeof(*result)); | |
578 | result->tlv_type = host_to_be16(TEAP_TLV_MANDATORY | | |
579 | TEAP_TLV_INTERMEDIATE_RESULT); | |
580 | result->length = host_to_be16(2); | |
581 | result->status = host_to_be16(TEAP_STATUS_SUCCESS); | |
582 | } | |
583 | ||
584 | if (data->final_result) { | |
585 | /* Result TLV */ | |
586 | wpa_printf(MSG_DEBUG, | |
587 | "EAP-TEAP: Add Result TLV (status=SUCCESS)"); | |
588 | result = wpabuf_put(buf, sizeof(*result)); | |
589 | result->tlv_type = host_to_be16(TEAP_TLV_MANDATORY | | |
590 | TEAP_TLV_RESULT); | |
591 | result->length = host_to_be16(2); | |
592 | result->status = host_to_be16(TEAP_STATUS_SUCCESS); | |
593 | } | |
594 | ||
595 | /* Crypto-Binding TLV */ | |
596 | cb = wpabuf_put(buf, sizeof(*cb)); | |
597 | cb->tlv_type = host_to_be16(TEAP_TLV_MANDATORY | | |
598 | TEAP_TLV_CRYPTO_BINDING); | |
599 | cb->length = host_to_be16(sizeof(*cb) - sizeof(struct teap_tlv_hdr)); | |
600 | cb->version = EAP_TEAP_VERSION; | |
601 | cb->received_version = data->peer_version; | |
602 | /* FIX: RFC 7170 is not clear on which Flags value to use when | |
603 | * Crypto-Binding TLV is used with Basic-Password-Auth */ | |
604 | flags = data->cmk_emsk_available ? | |
605 | TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC : | |
606 | TEAP_CRYPTO_BINDING_MSK_CMAC; | |
607 | subtype = TEAP_CRYPTO_BINDING_SUBTYPE_REQUEST; | |
608 | cb->subtype = (flags << 4) | subtype; | |
609 | if (random_get_bytes(cb->nonce, sizeof(cb->nonce)) < 0) { | |
610 | wpabuf_free(buf); | |
611 | return NULL; | |
612 | } | |
613 | ||
614 | /* | |
615 | * RFC 7170, Section 4.2.13: | |
616 | * The nonce in a request MUST have its least significant bit set to 0. | |
617 | */ | |
618 | cb->nonce[sizeof(cb->nonce) - 1] &= ~0x01; | |
619 | ||
620 | os_memcpy(data->crypto_binding_nonce, cb->nonce, sizeof(cb->nonce)); | |
621 | ||
622 | if (eap_teap_compound_mac(data->tls_cs, cb, data->server_outer_tlvs, | |
623 | data->peer_outer_tlvs, data->cmk_msk, | |
624 | cb->msk_compound_mac) < 0) { | |
625 | wpabuf_free(buf); | |
626 | return NULL; | |
627 | } | |
628 | ||
629 | if (data->cmk_emsk_available && | |
630 | eap_teap_compound_mac(data->tls_cs, cb, data->server_outer_tlvs, | |
631 | data->peer_outer_tlvs, data->cmk_emsk, | |
632 | cb->emsk_compound_mac) < 0) { | |
633 | wpabuf_free(buf); | |
634 | return NULL; | |
635 | } | |
636 | ||
637 | wpa_printf(MSG_DEBUG, | |
638 | "EAP-TEAP: Add Crypto-Binding TLV: Version %u Received Version %u Flags %u Sub-Type %u", | |
639 | cb->version, cb->received_version, flags, subtype); | |
640 | wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Nonce", | |
641 | cb->nonce, sizeof(cb->nonce)); | |
642 | wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: EMSK Compound MAC", | |
643 | cb->emsk_compound_mac, sizeof(cb->emsk_compound_mac)); | |
644 | wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: MSK Compound MAC", | |
645 | cb->msk_compound_mac, sizeof(cb->msk_compound_mac)); | |
646 | ||
647 | return buf; | |
648 | } | |
649 | ||
650 | ||
651 | static struct wpabuf * eap_teap_build_pac(struct eap_sm *sm, | |
652 | struct eap_teap_data *data) | |
653 | { | |
654 | u8 pac_key[EAP_TEAP_PAC_KEY_LEN]; | |
655 | u8 *pac_buf, *pac_opaque; | |
656 | struct wpabuf *buf; | |
657 | u8 *pos; | |
658 | size_t buf_len, srv_id_info_len, pac_len; | |
659 | struct teap_tlv_hdr *pac_tlv; | |
660 | struct pac_attr_hdr *pac_info; | |
661 | struct teap_tlv_result *result; | |
662 | struct os_time now; | |
663 | ||
664 | wpa_printf(MSG_DEBUG, "EAP-TEAP: Build a new PAC"); | |
665 | ||
666 | if (random_get_bytes(pac_key, EAP_TEAP_PAC_KEY_LEN) < 0 || | |
667 | os_get_time(&now) < 0) | |
668 | return NULL; | |
669 | wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: Generated PAC-Key", | |
670 | pac_key, EAP_TEAP_PAC_KEY_LEN); | |
671 | ||
672 | pac_len = (2 + EAP_TEAP_PAC_KEY_LEN) + (2 + 4) + | |
673 | (2 + sm->identity_len) + 8; | |
674 | pac_buf = os_malloc(pac_len); | |
675 | if (!pac_buf) | |
676 | return NULL; | |
677 | ||
678 | srv_id_info_len = os_strlen(data->srv_id_info); | |
679 | ||
680 | pos = pac_buf; | |
681 | *pos++ = PAC_OPAQUE_TYPE_KEY; | |
682 | *pos++ = EAP_TEAP_PAC_KEY_LEN; | |
683 | os_memcpy(pos, pac_key, EAP_TEAP_PAC_KEY_LEN); | |
684 | pos += EAP_TEAP_PAC_KEY_LEN; | |
685 | ||
686 | wpa_printf(MSG_DEBUG, "EAP-TEAP: PAC-Key lifetime: %u seconds", | |
687 | data->pac_key_lifetime); | |
688 | *pos++ = PAC_OPAQUE_TYPE_LIFETIME; | |
689 | *pos++ = 4; | |
690 | WPA_PUT_BE32(pos, now.sec + data->pac_key_lifetime); | |
691 | pos += 4; | |
692 | ||
693 | if (sm->identity) { | |
694 | wpa_hexdump_ascii(MSG_DEBUG, "EAP-TEAP: PAC-Opaque Identity", | |
695 | sm->identity, sm->identity_len); | |
696 | *pos++ = PAC_OPAQUE_TYPE_IDENTITY; | |
697 | *pos++ = sm->identity_len; | |
698 | os_memcpy(pos, sm->identity, sm->identity_len); | |
699 | pos += sm->identity_len; | |
700 | } | |
701 | ||
702 | pac_len = pos - pac_buf; | |
703 | while (pac_len % 8) { | |
704 | *pos++ = PAC_OPAQUE_TYPE_PAD; | |
705 | pac_len++; | |
706 | } | |
707 | ||
708 | pac_opaque = os_malloc(pac_len + 8); | |
709 | if (!pac_opaque) { | |
710 | os_free(pac_buf); | |
711 | return NULL; | |
712 | } | |
713 | if (aes_wrap(data->pac_opaque_encr, sizeof(data->pac_opaque_encr), | |
714 | pac_len / 8, pac_buf, pac_opaque) < 0) { | |
715 | os_free(pac_buf); | |
716 | os_free(pac_opaque); | |
717 | return NULL; | |
718 | } | |
719 | os_free(pac_buf); | |
720 | ||
721 | pac_len += 8; | |
722 | wpa_hexdump(MSG_DEBUG, "EAP-TEAP: PAC-Opaque", pac_opaque, pac_len); | |
723 | ||
724 | buf_len = sizeof(*pac_tlv) + | |
725 | sizeof(struct pac_attr_hdr) + EAP_TEAP_PAC_KEY_LEN + | |
726 | sizeof(struct pac_attr_hdr) + pac_len + | |
727 | data->srv_id_len + srv_id_info_len + 100 + sizeof(*result); | |
728 | buf = wpabuf_alloc(buf_len); | |
729 | if (!buf) { | |
730 | os_free(pac_opaque); | |
731 | return NULL; | |
732 | } | |
733 | ||
734 | /* Result TLV */ | |
735 | wpa_printf(MSG_DEBUG, "EAP-TEAP: Add Result TLV (status=SUCCESS)"); | |
736 | result = wpabuf_put(buf, sizeof(*result)); | |
737 | WPA_PUT_BE16((u8 *) &result->tlv_type, | |
738 | TEAP_TLV_MANDATORY | TEAP_TLV_RESULT); | |
739 | WPA_PUT_BE16((u8 *) &result->length, 2); | |
740 | WPA_PUT_BE16((u8 *) &result->status, TEAP_STATUS_SUCCESS); | |
741 | ||
742 | /* PAC TLV */ | |
743 | wpa_printf(MSG_DEBUG, "EAP-TEAP: Add PAC TLV"); | |
744 | pac_tlv = wpabuf_put(buf, sizeof(*pac_tlv)); | |
745 | pac_tlv->tlv_type = host_to_be16(TEAP_TLV_MANDATORY | TEAP_TLV_PAC); | |
746 | ||
747 | /* PAC-Key */ | |
748 | eap_teap_put_tlv(buf, PAC_TYPE_PAC_KEY, pac_key, EAP_TEAP_PAC_KEY_LEN); | |
749 | ||
750 | /* PAC-Opaque */ | |
751 | eap_teap_put_tlv(buf, PAC_TYPE_PAC_OPAQUE, pac_opaque, pac_len); | |
752 | os_free(pac_opaque); | |
753 | ||
754 | /* PAC-Info */ | |
755 | pac_info = wpabuf_put(buf, sizeof(*pac_info)); | |
756 | pac_info->type = host_to_be16(PAC_TYPE_PAC_INFO); | |
757 | ||
758 | /* PAC-Lifetime (inside PAC-Info) */ | |
759 | eap_teap_put_tlv_hdr(buf, PAC_TYPE_CRED_LIFETIME, 4); | |
760 | wpabuf_put_be32(buf, now.sec + data->pac_key_lifetime); | |
761 | ||
762 | /* A-ID (inside PAC-Info) */ | |
763 | eap_teap_put_tlv(buf, PAC_TYPE_A_ID, data->srv_id, data->srv_id_len); | |
764 | ||
765 | /* Note: headers may be misaligned after A-ID */ | |
766 | ||
767 | if (sm->identity) { | |
768 | eap_teap_put_tlv(buf, PAC_TYPE_I_ID, sm->identity, | |
769 | sm->identity_len); | |
770 | } | |
771 | ||
772 | /* A-ID-Info (inside PAC-Info) */ | |
773 | eap_teap_put_tlv(buf, PAC_TYPE_A_ID_INFO, data->srv_id_info, | |
774 | srv_id_info_len); | |
775 | ||
776 | /* PAC-Type (inside PAC-Info) */ | |
777 | eap_teap_put_tlv_hdr(buf, PAC_TYPE_PAC_TYPE, 2); | |
778 | wpabuf_put_be16(buf, PAC_TYPE_TUNNEL_PAC); | |
779 | ||
780 | /* Update PAC-Info and PAC TLV Length fields */ | |
781 | pos = wpabuf_put(buf, 0); | |
782 | pac_info->len = host_to_be16(pos - (u8 *) (pac_info + 1)); | |
783 | pac_tlv->length = host_to_be16(pos - (u8 *) (pac_tlv + 1)); | |
784 | ||
785 | return buf; | |
786 | } | |
787 | ||
788 | ||
789 | static int eap_teap_encrypt_phase2(struct eap_sm *sm, | |
790 | struct eap_teap_data *data, | |
791 | struct wpabuf *plain, int piggyback) | |
792 | { | |
793 | struct wpabuf *encr; | |
794 | ||
795 | wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TEAP: Encrypting Phase 2 TLVs", | |
796 | plain); | |
797 | encr = eap_server_tls_encrypt(sm, &data->ssl, plain); | |
798 | wpabuf_free(plain); | |
799 | ||
800 | if (!encr) | |
801 | return -1; | |
802 | ||
803 | if (data->ssl.tls_out && piggyback) { | |
804 | wpa_printf(MSG_DEBUG, | |
805 | "EAP-TEAP: Piggyback Phase 2 data (len=%d) with last Phase 1 Message (len=%d used=%d)", | |
806 | (int) wpabuf_len(encr), | |
807 | (int) wpabuf_len(data->ssl.tls_out), | |
808 | (int) data->ssl.tls_out_pos); | |
809 | if (wpabuf_resize(&data->ssl.tls_out, wpabuf_len(encr)) < 0) { | |
810 | wpa_printf(MSG_WARNING, | |
811 | "EAP-TEAP: Failed to resize output buffer"); | |
812 | wpabuf_free(encr); | |
813 | return -1; | |
814 | } | |
815 | wpabuf_put_buf(data->ssl.tls_out, encr); | |
816 | wpabuf_free(encr); | |
817 | } else { | |
818 | wpabuf_free(data->ssl.tls_out); | |
819 | data->ssl.tls_out_pos = 0; | |
820 | data->ssl.tls_out = encr; | |
821 | } | |
822 | ||
823 | return 0; | |
824 | } | |
825 | ||
826 | ||
827 | static struct wpabuf * eap_teap_buildReq(struct eap_sm *sm, void *priv, u8 id) | |
828 | { | |
829 | struct eap_teap_data *data = priv; | |
830 | struct wpabuf *req = NULL; | |
831 | int piggyback = 0; | |
832 | ||
833 | if (data->ssl.state == FRAG_ACK) { | |
834 | return eap_server_tls_build_ack(id, EAP_TYPE_TEAP, | |
835 | data->teap_version); | |
836 | } | |
837 | ||
838 | if (data->ssl.state == WAIT_FRAG_ACK) { | |
839 | return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TEAP, | |
840 | data->teap_version, id); | |
841 | } | |
842 | ||
843 | switch (data->state) { | |
844 | case START: | |
845 | return eap_teap_build_start(sm, data, id); | |
846 | case PHASE1B: | |
847 | if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { | |
848 | if (eap_teap_phase1_done(sm, data) < 0) | |
849 | return NULL; | |
850 | if (data->state == PHASE2_START) { | |
851 | int res; | |
852 | ||
853 | /* | |
854 | * Try to generate Phase 2 data to piggyback | |
855 | * with the end of Phase 1 to avoid extra | |
856 | * roundtrip. | |
857 | */ | |
858 | wpa_printf(MSG_DEBUG, | |
859 | "EAP-TEAP: Try to start Phase 2"); | |
860 | res = eap_teap_process_phase2_start(sm, data); | |
861 | if (res == 1) { | |
862 | req = eap_teap_build_crypto_binding( | |
863 | sm, data); | |
864 | piggyback = 1; | |
865 | break; | |
866 | } | |
867 | ||
868 | if (res) | |
869 | break; | |
870 | req = eap_teap_build_phase2_req(sm, data, id); | |
871 | piggyback = 1; | |
872 | } | |
873 | } | |
874 | break; | |
875 | case PHASE2_ID: | |
876 | case PHASE2_BASIC_AUTH: | |
877 | case PHASE2_METHOD: | |
878 | req = eap_teap_build_phase2_req(sm, data, id); | |
879 | break; | |
880 | case CRYPTO_BINDING: | |
881 | req = eap_teap_build_crypto_binding(sm, data); | |
882 | if (data->phase2_method) { | |
883 | /* | |
884 | * Include the start of the next EAP method in the | |
885 | * sequence in the same message with Crypto-Binding to | |
886 | * save a round-trip. | |
887 | */ | |
888 | struct wpabuf *eap; | |
889 | ||
890 | eap = eap_teap_build_phase2_req(sm, data, id); | |
891 | req = wpabuf_concat(req, eap); | |
892 | eap_teap_state(data, PHASE2_METHOD); | |
893 | } | |
894 | break; | |
895 | case REQUEST_PAC: | |
896 | req = eap_teap_build_pac(sm, data); | |
897 | break; | |
898 | case FAILURE_SEND_RESULT: | |
899 | req = eap_teap_tlv_result(TEAP_STATUS_FAILURE, 0); | |
900 | if (data->error_code) | |
901 | req = wpabuf_concat( | |
902 | req, eap_teap_tlv_error(data->error_code)); | |
903 | break; | |
904 | default: | |
905 | wpa_printf(MSG_DEBUG, "EAP-TEAP: %s - unexpected state %d", | |
906 | __func__, data->state); | |
907 | return NULL; | |
908 | } | |
909 | ||
910 | if (req && eap_teap_encrypt_phase2(sm, data, req, piggyback) < 0) | |
911 | return NULL; | |
912 | ||
913 | return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TEAP, | |
914 | data->teap_version, id); | |
915 | } | |
916 | ||
917 | ||
918 | static Boolean eap_teap_check(struct eap_sm *sm, void *priv, | |
919 | struct wpabuf *respData) | |
920 | { | |
921 | const u8 *pos; | |
922 | size_t len; | |
923 | ||
924 | pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TEAP, respData, &len); | |
925 | if (!pos || len < 1) { | |
926 | wpa_printf(MSG_INFO, "EAP-TEAP: Invalid frame"); | |
927 | return TRUE; | |
928 | } | |
929 | ||
930 | return FALSE; | |
931 | } | |
932 | ||
933 | ||
934 | static int eap_teap_phase2_init(struct eap_sm *sm, struct eap_teap_data *data, | |
935 | EapType eap_type) | |
936 | { | |
937 | if (data->phase2_priv && data->phase2_method) { | |
938 | data->phase2_method->reset(sm, data->phase2_priv); | |
939 | data->phase2_method = NULL; | |
940 | data->phase2_priv = NULL; | |
941 | } | |
942 | data->phase2_method = eap_server_get_eap_method(EAP_VENDOR_IETF, | |
943 | eap_type); | |
944 | if (!data->phase2_method) | |
945 | return -1; | |
946 | ||
947 | sm->init_phase2 = 1; | |
948 | data->phase2_priv = data->phase2_method->init(sm); | |
949 | sm->init_phase2 = 0; | |
950 | ||
951 | return data->phase2_priv ? 0 : -1; | |
952 | } | |
953 | ||
954 | ||
955 | static void eap_teap_process_phase2_response(struct eap_sm *sm, | |
956 | struct eap_teap_data *data, | |
957 | u8 *in_data, size_t in_len) | |
958 | { | |
959 | u8 next_type = EAP_TYPE_NONE; | |
960 | struct eap_hdr *hdr; | |
961 | u8 *pos; | |
962 | size_t left; | |
963 | struct wpabuf buf; | |
964 | const struct eap_method *m = data->phase2_method; | |
965 | void *priv = data->phase2_priv; | |
966 | ||
967 | if (!priv) { | |
968 | wpa_printf(MSG_DEBUG, | |
969 | "EAP-TEAP: %s - Phase 2 not initialized?!", | |
970 | __func__); | |
971 | return; | |
972 | } | |
973 | ||
974 | hdr = (struct eap_hdr *) in_data; | |
975 | pos = (u8 *) (hdr + 1); | |
976 | ||
977 | if (in_len > sizeof(*hdr) && *pos == EAP_TYPE_NAK) { | |
978 | left = in_len - sizeof(*hdr); | |
979 | wpa_hexdump(MSG_DEBUG, | |
980 | "EAP-TEAP: Phase 2 type Nak'ed; allowed types", | |
981 | pos + 1, left - 1); | |
982 | #ifdef EAP_SERVER_TNC | |
983 | if (m && m->vendor == EAP_VENDOR_IETF && | |
984 | m->method == EAP_TYPE_TNC) { | |
985 | wpa_printf(MSG_DEBUG, | |
986 | "EAP-TEAP: Peer Nak'ed required TNC negotiation"); | |
987 | next_type = eap_teap_req_failure(data, 0); | |
988 | eap_teap_phase2_init(sm, data, next_type); | |
989 | return; | |
990 | } | |
991 | #endif /* EAP_SERVER_TNC */ | |
992 | eap_sm_process_nak(sm, pos + 1, left - 1); | |
993 | if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && | |
994 | sm->user->methods[sm->user_eap_method_index].method != | |
995 | EAP_TYPE_NONE) { | |
996 | next_type = sm->user->methods[ | |
997 | sm->user_eap_method_index++].method; | |
998 | wpa_printf(MSG_DEBUG, "EAP-TEAP: try EAP type %d", | |
999 | next_type); | |
1000 | } else { | |
1001 | next_type = eap_teap_req_failure(data, 0); | |
1002 | } | |
1003 | eap_teap_phase2_init(sm, data, next_type); | |
1004 | return; | |
1005 | } | |
1006 | ||
1007 | wpabuf_set(&buf, in_data, in_len); | |
1008 | ||
1009 | if (m->check(sm, priv, &buf)) { | |
1010 | wpa_printf(MSG_DEBUG, | |
1011 | "EAP-TEAP: Phase 2 check() asked to ignore the packet"); | |
1012 | eap_teap_req_failure(data, TEAP_ERROR_INNER_METHOD); | |
1013 | return; | |
1014 | } | |
1015 | ||
1016 | m->process(sm, priv, &buf); | |
1017 | ||
1018 | if (!m->isDone(sm, priv)) | |
1019 | return; | |
1020 | ||
1021 | if (!m->isSuccess(sm, priv)) { | |
1022 | wpa_printf(MSG_DEBUG, "EAP-TEAP: Phase 2 method failed"); | |
1023 | next_type = eap_teap_req_failure(data, TEAP_ERROR_INNER_METHOD); | |
1024 | eap_teap_phase2_init(sm, data, next_type); | |
1025 | return; | |
1026 | } | |
1027 | ||
1028 | switch (data->state) { | |
1029 | case PHASE2_ID: | |
1030 | if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { | |
1031 | wpa_hexdump_ascii(MSG_DEBUG, | |
1032 | "EAP-TEAP: Phase 2 Identity not found in the user database", | |
1033 | sm->identity, sm->identity_len); | |
1034 | next_type = eap_teap_req_failure( | |
1035 | data, TEAP_ERROR_INNER_METHOD); | |
1036 | break; | |
1037 | } | |
1038 | ||
1039 | eap_teap_state(data, PHASE2_METHOD); | |
1040 | if (data->anon_provisioning) { | |
1041 | /* TODO: Allow any inner EAP method that provides | |
1042 | * mutual authentication and EMSK derivation (i.e., | |
1043 | * EAP-pwd or EAP-EKE). */ | |
1044 | next_type = EAP_TYPE_PWD; | |
1045 | sm->user_eap_method_index = 0; | |
1046 | } else { | |
1047 | next_type = sm->user->methods[0].method; | |
1048 | sm->user_eap_method_index = 1; | |
1049 | } | |
1050 | wpa_printf(MSG_DEBUG, "EAP-TEAP: Try EAP type %d", next_type); | |
1051 | break; | |
1052 | case PHASE2_METHOD: | |
1053 | case CRYPTO_BINDING: | |
1054 | eap_teap_update_icmk(sm, data); | |
1055 | eap_teap_state(data, CRYPTO_BINDING); | |
1056 | data->eap_seq++; | |
1057 | next_type = EAP_TYPE_NONE; | |
1058 | #ifdef EAP_SERVER_TNC | |
1059 | if (sm->tnc && !data->tnc_started) { | |
1060 | wpa_printf(MSG_DEBUG, "EAP-TEAP: Initialize TNC"); | |
1061 | next_type = EAP_TYPE_TNC; | |
1062 | data->tnc_started = 1; | |
1063 | } | |
1064 | #endif /* EAP_SERVER_TNC */ | |
1065 | break; | |
1066 | case FAILURE: | |
1067 | break; | |
1068 | default: | |
1069 | wpa_printf(MSG_DEBUG, "EAP-TEAP: %s - unexpected state %d", | |
1070 | __func__, data->state); | |
1071 | break; | |
1072 | } | |
1073 | ||
1074 | eap_teap_phase2_init(sm, data, next_type); | |
1075 | } | |
1076 | ||
1077 | ||
1078 | static void eap_teap_process_phase2_eap(struct eap_sm *sm, | |
1079 | struct eap_teap_data *data, | |
1080 | u8 *in_data, size_t in_len) | |
1081 | { | |
1082 | struct eap_hdr *hdr; | |
1083 | size_t len; | |
1084 | ||
1085 | hdr = (struct eap_hdr *) in_data; | |
1086 | if (in_len < (int) sizeof(*hdr)) { | |
1087 | wpa_printf(MSG_INFO, | |
1088 | "EAP-TEAP: Too short Phase 2 EAP frame (len=%lu)", | |
1089 | (unsigned long) in_len); | |
1090 | eap_teap_req_failure(data, TEAP_ERROR_INNER_METHOD); | |
1091 | return; | |
1092 | } | |
1093 | len = be_to_host16(hdr->length); | |
1094 | if (len > in_len) { | |
1095 | wpa_printf(MSG_INFO, | |
1096 | "EAP-TEAP: Length mismatch in Phase 2 EAP frame (len=%lu hdr->length=%lu)", | |
1097 | (unsigned long) in_len, (unsigned long) len); | |
1098 | eap_teap_req_failure(data, TEAP_ERROR_INNER_METHOD); | |
1099 | return; | |
1100 | } | |
1101 | wpa_printf(MSG_DEBUG, | |
1102 | "EAP-TEAP: Received Phase 2: code=%d identifier=%d length=%lu", | |
1103 | hdr->code, hdr->identifier, | |
1104 | (unsigned long) len); | |
1105 | switch (hdr->code) { | |
1106 | case EAP_CODE_RESPONSE: | |
1107 | eap_teap_process_phase2_response(sm, data, (u8 *) hdr, len); | |
1108 | break; | |
1109 | default: | |
1110 | wpa_printf(MSG_INFO, | |
1111 | "EAP-TEAP: Unexpected code=%d in Phase 2 EAP header", | |
1112 | hdr->code); | |
1113 | break; | |
1114 | } | |
1115 | } | |
1116 | ||
1117 | ||
1118 | static void eap_teap_process_basic_auth_resp(struct eap_sm *sm, | |
1119 | struct eap_teap_data *data, | |
1120 | u8 *in_data, size_t in_len) | |
1121 | { | |
1122 | u8 *pos, *end, *username, *password, *new_id; | |
1123 | u8 userlen, passlen; | |
1124 | ||
1125 | pos = in_data; | |
1126 | end = pos + in_len; | |
1127 | ||
1128 | if (end - pos < 1) { | |
1129 | wpa_printf(MSG_DEBUG, | |
1130 | "EAP-TEAP: No room for Basic-Password-Auth-Resp Userlen field"); | |
1131 | eap_teap_req_failure(data, 0); | |
1132 | return; | |
1133 | } | |
1134 | userlen = *pos++; | |
1135 | if (end - pos < userlen) { | |
1136 | wpa_printf(MSG_DEBUG, | |
1137 | "EAP-TEAP: Truncated Basic-Password-Auth-Resp Username field"); | |
1138 | eap_teap_req_failure(data, 0); | |
1139 | return; | |
1140 | } | |
1141 | username = pos; | |
1142 | pos += userlen; | |
1143 | wpa_hexdump_ascii(MSG_DEBUG, | |
1144 | "EAP-TEAP: Basic-Password-Auth-Resp Username", | |
1145 | username, userlen); | |
1146 | ||
1147 | if (end - pos < 1) { | |
1148 | wpa_printf(MSG_DEBUG, | |
1149 | "EAP-TEAP: No room for Basic-Password-Auth-Resp Passlen field"); | |
1150 | eap_teap_req_failure(data, 0); | |
1151 | return; | |
1152 | } | |
1153 | passlen = *pos++; | |
1154 | if (end - pos < passlen) { | |
1155 | wpa_printf(MSG_DEBUG, | |
1156 | "EAP-TEAP: Truncated Basic-Password-Auth-Resp Password field"); | |
1157 | eap_teap_req_failure(data, 0); | |
1158 | return; | |
1159 | } | |
1160 | password = pos; | |
1161 | pos += passlen; | |
1162 | wpa_hexdump_ascii_key(MSG_DEBUG, | |
1163 | "EAP-TEAP: Basic-Password-Auth-Resp Password", | |
1164 | password, passlen); | |
1165 | ||
1166 | if (end > pos) { | |
1167 | wpa_printf(MSG_DEBUG, | |
1168 | "EAP-TEAP: Unexpected %d extra octet(s) at the end of Basic-Password-Auth-Resp TLV", | |
1169 | (int) (end - pos)); | |
1170 | eap_teap_req_failure(data, 0); | |
1171 | return; | |
1172 | } | |
1173 | ||
1174 | if (eap_user_get(sm, username, userlen, 1) != 0) { | |
1175 | wpa_printf(MSG_DEBUG, | |
1176 | "EAP-TEAP: Username not found in the user database"); | |
1177 | eap_teap_req_failure(data, 0); | |
1178 | return; | |
1179 | } | |
1180 | ||
1181 | if (!sm->user || !sm->user->password || sm->user->password_hash) { | |
1182 | wpa_printf(MSG_DEBUG, | |
1183 | "EAP-TEAP: No plaintext user password configured"); | |
1184 | eap_teap_req_failure(data, 0); | |
1185 | return; | |
1186 | } | |
1187 | ||
1188 | if (sm->user->password_len != passlen || | |
1189 | os_memcmp_const(sm->user->password, password, passlen) != 0) { | |
1190 | wpa_printf(MSG_DEBUG, "EAP-TEAP: Invalid password"); | |
1191 | eap_teap_req_failure(data, 0); | |
1192 | return; | |
1193 | } | |
1194 | ||
1195 | wpa_printf(MSG_DEBUG, "EAP-TEAP: Correct password"); | |
1196 | new_id = os_memdup(username, userlen); | |
1197 | if (new_id) { | |
1198 | os_free(sm->identity); | |
1199 | sm->identity = new_id; | |
1200 | sm->identity_len = userlen; | |
1201 | } | |
1202 | eap_teap_state(data, CRYPTO_BINDING); | |
1203 | eap_teap_update_icmk(sm, data); | |
1204 | } | |
1205 | ||
1206 | ||
1207 | static int eap_teap_parse_tlvs(struct wpabuf *data, | |
1208 | struct eap_teap_tlv_parse *tlv) | |
1209 | { | |
1210 | u16 tlv_type; | |
1211 | int mandatory, res; | |
1212 | size_t len; | |
1213 | u8 *pos, *end; | |
1214 | ||
1215 | os_memset(tlv, 0, sizeof(*tlv)); | |
1216 | ||
1217 | pos = wpabuf_mhead(data); | |
1218 | end = pos + wpabuf_len(data); | |
1219 | while (end - pos > 4) { | |
1220 | mandatory = pos[0] & 0x80; | |
1221 | tlv_type = WPA_GET_BE16(pos) & 0x3fff; | |
1222 | pos += 2; | |
1223 | len = WPA_GET_BE16(pos); | |
1224 | pos += 2; | |
1225 | if (len > (size_t) (end - pos)) { | |
1226 | wpa_printf(MSG_INFO, "EAP-TEAP: TLV overflow"); | |
1227 | return -1; | |
1228 | } | |
1229 | wpa_printf(MSG_DEBUG, | |
1230 | "EAP-TEAP: Received Phase 2: TLV type %u (%s) length %u%s", | |
1231 | tlv_type, eap_teap_tlv_type_str(tlv_type), | |
1232 | (unsigned int) len, | |
1233 | mandatory ? " (mandatory)" : ""); | |
1234 | ||
1235 | res = eap_teap_parse_tlv(tlv, tlv_type, pos, len); | |
1236 | if (res == -2) | |
1237 | break; | |
1238 | if (res < 0) { | |
1239 | if (mandatory) { | |
1240 | wpa_printf(MSG_DEBUG, | |
1241 | "EAP-TEAP: NAK unknown mandatory TLV type %u", | |
1242 | tlv_type); | |
1243 | /* TODO: generate NAK TLV */ | |
1244 | break; | |
1245 | } | |
1246 | ||
1247 | wpa_printf(MSG_DEBUG, | |
1248 | "EAP-TEAP: Ignore unknown optional TLV type %u", | |
1249 | tlv_type); | |
1250 | } | |
1251 | ||
1252 | pos += len; | |
1253 | } | |
1254 | ||
1255 | return 0; | |
1256 | } | |
1257 | ||
1258 | ||
1259 | static int eap_teap_validate_crypto_binding( | |
1260 | struct eap_teap_data *data, const struct teap_tlv_crypto_binding *cb, | |
1261 | size_t bind_len) | |
1262 | { | |
1263 | u8 flags, subtype; | |
1264 | ||
1265 | subtype = cb->subtype & 0x0f; | |
1266 | flags = cb->subtype >> 4; | |
1267 | ||
1268 | wpa_printf(MSG_DEBUG, | |
1269 | "EAP-TEAP: Reply Crypto-Binding TLV: Version %u Received Version %u Flags %u Sub-Type %u", | |
1270 | cb->version, cb->received_version, flags, subtype); | |
1271 | wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Nonce", | |
1272 | cb->nonce, sizeof(cb->nonce)); | |
1273 | wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: EMSK Compound MAC", | |
1274 | cb->emsk_compound_mac, sizeof(cb->emsk_compound_mac)); | |
1275 | wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: MSK Compound MAC", | |
1276 | cb->msk_compound_mac, sizeof(cb->msk_compound_mac)); | |
1277 | ||
1278 | if (cb->version != EAP_TEAP_VERSION || | |
1279 | cb->received_version != data->peer_version) { | |
1280 | wpa_printf(MSG_DEBUG, | |
1281 | "EAP-TEAP: Unexpected version in Crypto-Binding: Version %u Received Version %u", | |
1282 | cb->version, cb->received_version); | |
1283 | return -1; | |
1284 | } | |
1285 | ||
1286 | if (flags < 1 || flags > 3) { | |
1287 | wpa_printf(MSG_DEBUG, | |
1288 | "EAP-TEAP: Unexpected Flags in Crypto-Binding: %u", | |
1289 | flags); | |
1290 | return -1; | |
1291 | } | |
1292 | ||
1293 | if (subtype != TEAP_CRYPTO_BINDING_SUBTYPE_RESPONSE) { | |
1294 | wpa_printf(MSG_DEBUG, | |
1295 | "EAP-TEAP: Unexpected Sub-Type in Crypto-Binding: %u", | |
1296 | subtype); | |
1297 | return -1; | |
1298 | } | |
1299 | ||
1300 | if (os_memcmp_const(data->crypto_binding_nonce, cb->nonce, | |
1301 | EAP_TEAP_NONCE_LEN - 1) != 0 || | |
1302 | (data->crypto_binding_nonce[EAP_TEAP_NONCE_LEN - 1] | 1) != | |
1303 | cb->nonce[EAP_TEAP_NONCE_LEN - 1]) { | |
1304 | wpa_printf(MSG_DEBUG, | |
1305 | "EAP-TEAP: Invalid Nonce in Crypto-Binding"); | |
1306 | return -1; | |
1307 | } | |
1308 | ||
1309 | if (flags == TEAP_CRYPTO_BINDING_MSK_CMAC || | |
1310 | flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC) { | |
1311 | u8 msk_compound_mac[EAP_TEAP_COMPOUND_MAC_LEN]; | |
1312 | ||
1313 | if (eap_teap_compound_mac(data->tls_cs, cb, | |
1314 | data->server_outer_tlvs, | |
1315 | data->peer_outer_tlvs, data->cmk_msk, | |
1316 | msk_compound_mac) < 0) | |
1317 | return -1; | |
1318 | if (os_memcmp_const(msk_compound_mac, cb->msk_compound_mac, | |
1319 | EAP_TEAP_COMPOUND_MAC_LEN) != 0) { | |
1320 | wpa_hexdump(MSG_DEBUG, | |
1321 | "EAP-TEAP: Calculated MSK Compound MAC", | |
1322 | msk_compound_mac, | |
1323 | EAP_TEAP_COMPOUND_MAC_LEN); | |
1324 | wpa_printf(MSG_INFO, | |
1325 | "EAP-TEAP: MSK Compound MAC did not match"); | |
1326 | return -1; | |
1327 | } | |
1328 | } | |
1329 | ||
1330 | if ((flags == TEAP_CRYPTO_BINDING_EMSK_CMAC || | |
1331 | flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC) && | |
1332 | data->cmk_emsk_available) { | |
1333 | u8 emsk_compound_mac[EAP_TEAP_COMPOUND_MAC_LEN]; | |
1334 | ||
1335 | if (eap_teap_compound_mac(data->tls_cs, cb, | |
1336 | data->server_outer_tlvs, | |
1337 | data->peer_outer_tlvs, data->cmk_emsk, | |
1338 | emsk_compound_mac) < 0) | |
1339 | return -1; | |
1340 | if (os_memcmp_const(emsk_compound_mac, cb->emsk_compound_mac, | |
1341 | EAP_TEAP_COMPOUND_MAC_LEN) != 0) { | |
1342 | wpa_hexdump(MSG_DEBUG, | |
1343 | "EAP-TEAP: Calculated EMSK Compound MAC", | |
1344 | emsk_compound_mac, | |
1345 | EAP_TEAP_COMPOUND_MAC_LEN); | |
1346 | wpa_printf(MSG_INFO, | |
1347 | "EAP-TEAP: EMSK Compound MAC did not match"); | |
1348 | return -1; | |
1349 | } | |
1350 | } | |
1351 | ||
1352 | if (flags == TEAP_CRYPTO_BINDING_EMSK_CMAC && | |
1353 | !data->cmk_emsk_available) { | |
1354 | wpa_printf(MSG_INFO, | |
1355 | "EAP-TEAP: Peer included only EMSK Compound MAC, but no locally generated inner EAP EMSK to validate this"); | |
1356 | return -1; | |
1357 | } | |
1358 | ||
1359 | return 0; | |
1360 | } | |
1361 | ||
1362 | ||
1363 | static int eap_teap_pac_type(u8 *pac, size_t len, u16 type) | |
1364 | { | |
1365 | struct teap_attr_pac_type *tlv; | |
1366 | ||
1367 | if (!pac || len != sizeof(*tlv)) | |
1368 | return 0; | |
1369 | ||
1370 | tlv = (struct teap_attr_pac_type *) pac; | |
1371 | ||
1372 | return be_to_host16(tlv->type) == PAC_TYPE_PAC_TYPE && | |
1373 | be_to_host16(tlv->length) == 2 && | |
1374 | be_to_host16(tlv->pac_type) == type; | |
1375 | } | |
1376 | ||
1377 | ||
1378 | static void eap_teap_process_phase2_tlvs(struct eap_sm *sm, | |
1379 | struct eap_teap_data *data, | |
1380 | struct wpabuf *in_data) | |
1381 | { | |
1382 | struct eap_teap_tlv_parse tlv; | |
1383 | int check_crypto_binding = data->state == CRYPTO_BINDING; | |
1384 | ||
1385 | if (eap_teap_parse_tlvs(in_data, &tlv) < 0) { | |
1386 | wpa_printf(MSG_DEBUG, | |
1387 | "EAP-TEAP: Failed to parse received Phase 2 TLVs"); | |
1388 | return; | |
1389 | } | |
1390 | ||
1391 | if (tlv.result == TEAP_STATUS_FAILURE) { | |
1392 | wpa_printf(MSG_DEBUG, "EAP-TEAP: Result TLV indicated failure"); | |
1393 | eap_teap_state(data, FAILURE); | |
1394 | return; | |
1395 | } | |
1396 | ||
1397 | if (tlv.nak) { | |
1398 | wpa_printf(MSG_DEBUG, | |
1399 | "EAP-TEAP: Peer NAK'ed Vendor-Id %u NAK-Type %u", | |
1400 | WPA_GET_BE32(tlv.nak), WPA_GET_BE16(tlv.nak + 4)); | |
1401 | eap_teap_state(data, FAILURE_SEND_RESULT); | |
1402 | return; | |
1403 | } | |
1404 | ||
1405 | if (data->state == REQUEST_PAC) { | |
1406 | u16 type, len, res; | |
1407 | ||
1408 | if (!tlv.pac || tlv.pac_len < 6) { | |
1409 | wpa_printf(MSG_DEBUG, | |
1410 | "EAP-TEAP: No PAC Acknowledgement received"); | |
1411 | eap_teap_state(data, FAILURE); | |
1412 | return; | |
1413 | } | |
1414 | ||
1415 | type = WPA_GET_BE16(tlv.pac); | |
1416 | len = WPA_GET_BE16(tlv.pac + 2); | |
1417 | res = WPA_GET_BE16(tlv.pac + 4); | |
1418 | ||
1419 | if (type != PAC_TYPE_PAC_ACKNOWLEDGEMENT || len != 2 || | |
1420 | res != TEAP_STATUS_SUCCESS) { | |
1421 | wpa_printf(MSG_DEBUG, | |
1422 | "EAP-TEAP: PAC TLV did not contain acknowledgement"); | |
1423 | eap_teap_state(data, FAILURE); | |
1424 | return; | |
1425 | } | |
1426 | ||
1427 | wpa_printf(MSG_DEBUG, | |
1428 | "EAP-TEAP: PAC-Acknowledgement received - PAC provisioning succeeded"); | |
1429 | eap_teap_state(data, SUCCESS); | |
1430 | return; | |
1431 | } | |
1432 | ||
1433 | if (check_crypto_binding) { | |
1434 | if (!tlv.crypto_binding) { | |
1435 | wpa_printf(MSG_DEBUG, | |
1436 | "EAP-TEAP: No Crypto-Binding TLV received"); | |
1437 | eap_teap_state(data, FAILURE); | |
1438 | return; | |
1439 | } | |
1440 | ||
1441 | if (data->final_result && | |
1442 | tlv.result != TEAP_STATUS_SUCCESS) { | |
1443 | wpa_printf(MSG_DEBUG, | |
1444 | "EAP-TEAP: Crypto-Binding TLV without Success Result"); | |
1445 | eap_teap_state(data, FAILURE); | |
1446 | return; | |
1447 | } | |
1448 | ||
234489ef | 1449 | if (sm->eap_teap_auth != 1 && |
0ed57c5e JM |
1450 | tlv.iresult != TEAP_STATUS_SUCCESS) { |
1451 | wpa_printf(MSG_DEBUG, | |
1452 | "EAP-TEAP: Crypto-Binding TLV without intermediate Success Result"); | |
1453 | eap_teap_state(data, FAILURE); | |
1454 | return; | |
1455 | } | |
1456 | ||
1457 | if (eap_teap_validate_crypto_binding(data, tlv.crypto_binding, | |
1458 | tlv.crypto_binding_len)) { | |
1459 | eap_teap_state(data, FAILURE); | |
1460 | return; | |
1461 | } | |
1462 | ||
1463 | wpa_printf(MSG_DEBUG, | |
1464 | "EAP-TEAP: Valid Crypto-Binding TLV received"); | |
1465 | if (data->final_result) { | |
1466 | wpa_printf(MSG_DEBUG, | |
1467 | "EAP-TEAP: Authentication completed successfully"); | |
1468 | } | |
1469 | ||
1470 | if (data->anon_provisioning && | |
1471 | sm->eap_fast_prov != ANON_PROV && | |
1472 | sm->eap_fast_prov != BOTH_PROV) { | |
1473 | wpa_printf(MSG_DEBUG, | |
1474 | "EAP-TEAP: Client is trying to use unauthenticated provisioning which is disabled"); | |
1475 | eap_teap_state(data, FAILURE); | |
1476 | return; | |
1477 | } | |
1478 | ||
1479 | if (sm->eap_fast_prov != AUTH_PROV && | |
1480 | sm->eap_fast_prov != BOTH_PROV && | |
1481 | tlv.request_action == TEAP_REQUEST_ACTION_PROCESS_TLV && | |
1482 | eap_teap_pac_type(tlv.pac, tlv.pac_len, | |
1483 | PAC_TYPE_TUNNEL_PAC)) { | |
1484 | wpa_printf(MSG_DEBUG, | |
1485 | "EAP-TEAP: Client is trying to use authenticated provisioning which is disabled"); | |
1486 | eap_teap_state(data, FAILURE); | |
1487 | return; | |
1488 | } | |
1489 | ||
1490 | if (data->anon_provisioning || | |
1491 | (tlv.request_action == TEAP_REQUEST_ACTION_PROCESS_TLV && | |
1492 | eap_teap_pac_type(tlv.pac, tlv.pac_len, | |
1493 | PAC_TYPE_TUNNEL_PAC))) { | |
1494 | wpa_printf(MSG_DEBUG, | |
1495 | "EAP-TEAP: Requested a new Tunnel PAC"); | |
1496 | eap_teap_state(data, REQUEST_PAC); | |
1497 | } else if (data->send_new_pac) { | |
1498 | wpa_printf(MSG_DEBUG, | |
1499 | "EAP-TEAP: Server triggered re-keying of Tunnel PAC"); | |
1500 | eap_teap_state(data, REQUEST_PAC); | |
1501 | } else if (data->final_result) | |
1502 | eap_teap_state(data, SUCCESS); | |
1503 | } | |
1504 | ||
1505 | if (tlv.basic_auth_resp) { | |
1506 | if (sm->eap_teap_auth != 1) { | |
1507 | wpa_printf(MSG_DEBUG, | |
1508 | "EAP-TEAP: Unexpected Basic-Password-Auth-Resp when trying to use inner EAP"); | |
1509 | eap_teap_state(data, FAILURE); | |
1510 | return; | |
1511 | } | |
1512 | eap_teap_process_basic_auth_resp(sm, data, tlv.basic_auth_resp, | |
1513 | tlv.basic_auth_resp_len); | |
1514 | } | |
1515 | ||
1516 | if (tlv.eap_payload_tlv) { | |
1517 | if (sm->eap_teap_auth == 1) { | |
1518 | wpa_printf(MSG_DEBUG, | |
1519 | "EAP-TEAP: Unexpected EAP Payload TLV when trying to use Basic-Password-Auth"); | |
1520 | eap_teap_state(data, FAILURE); | |
1521 | return; | |
1522 | } | |
1523 | eap_teap_process_phase2_eap(sm, data, tlv.eap_payload_tlv, | |
1524 | tlv.eap_payload_tlv_len); | |
1525 | } | |
1526 | } | |
1527 | ||
1528 | ||
1529 | static void eap_teap_process_phase2(struct eap_sm *sm, | |
1530 | struct eap_teap_data *data, | |
1531 | struct wpabuf *in_buf) | |
1532 | { | |
1533 | struct wpabuf *in_decrypted; | |
1534 | ||
1535 | wpa_printf(MSG_DEBUG, | |
1536 | "EAP-TEAP: Received %lu bytes encrypted data for Phase 2", | |
1537 | (unsigned long) wpabuf_len(in_buf)); | |
1538 | ||
1539 | if (data->pending_phase2_resp) { | |
1540 | wpa_printf(MSG_DEBUG, | |
1541 | "EAP-TEAP: Pending Phase 2 response - skip decryption and use old data"); | |
1542 | eap_teap_process_phase2_tlvs(sm, data, | |
1543 | data->pending_phase2_resp); | |
1544 | wpabuf_free(data->pending_phase2_resp); | |
1545 | data->pending_phase2_resp = NULL; | |
1546 | return; | |
1547 | } | |
1548 | ||
1549 | in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, | |
1550 | in_buf); | |
1551 | if (!in_decrypted) { | |
1552 | wpa_printf(MSG_INFO, | |
1553 | "EAP-TEAP: Failed to decrypt Phase 2 data"); | |
1554 | eap_teap_state(data, FAILURE); | |
1555 | return; | |
1556 | } | |
1557 | ||
1558 | wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TEAP: Decrypted Phase 2 TLVs", | |
1559 | in_decrypted); | |
1560 | ||
1561 | eap_teap_process_phase2_tlvs(sm, data, in_decrypted); | |
1562 | ||
1563 | if (sm->method_pending == METHOD_PENDING_WAIT) { | |
1564 | wpa_printf(MSG_DEBUG, | |
1565 | "EAP-TEAP: Phase 2 method is in pending wait state - save decrypted response"); | |
1566 | wpabuf_free(data->pending_phase2_resp); | |
1567 | data->pending_phase2_resp = in_decrypted; | |
1568 | return; | |
1569 | } | |
1570 | ||
1571 | wpabuf_free(in_decrypted); | |
1572 | } | |
1573 | ||
1574 | ||
1575 | static int eap_teap_process_version(struct eap_sm *sm, void *priv, | |
1576 | int peer_version) | |
1577 | { | |
1578 | struct eap_teap_data *data = priv; | |
1579 | ||
1580 | if (peer_version < 1) { | |
1581 | /* Version 1 was the first defined version, so reject 0 */ | |
1582 | wpa_printf(MSG_INFO, | |
1583 | "EAP-TEAP: Peer used unknown TEAP version %u", | |
1584 | peer_version); | |
1585 | return -1; | |
1586 | } | |
1587 | ||
1588 | if (peer_version < data->teap_version) { | |
1589 | wpa_printf(MSG_DEBUG, "EAP-TEAP: peer ver=%u, own ver=%u; " | |
1590 | "use version %u", | |
1591 | peer_version, data->teap_version, peer_version); | |
1592 | data->teap_version = peer_version; | |
1593 | } | |
1594 | ||
1595 | data->peer_version = peer_version; | |
1596 | ||
1597 | return 0; | |
1598 | } | |
1599 | ||
1600 | ||
1601 | static int eap_teap_process_phase1(struct eap_sm *sm, | |
1602 | struct eap_teap_data *data) | |
1603 | { | |
1604 | if (eap_server_tls_phase1(sm, &data->ssl) < 0) { | |
1605 | wpa_printf(MSG_INFO, "EAP-TEAP: TLS processing failed"); | |
1606 | eap_teap_state(data, FAILURE); | |
1607 | return -1; | |
1608 | } | |
1609 | ||
1610 | if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) || | |
1611 | wpabuf_len(data->ssl.tls_out) > 0) | |
1612 | return 1; | |
1613 | ||
1614 | /* | |
1615 | * Phase 1 was completed with the received message (e.g., when using | |
1616 | * abbreviated handshake), so Phase 2 can be started immediately | |
1617 | * without having to send through an empty message to the peer. | |
1618 | */ | |
1619 | ||
1620 | return eap_teap_phase1_done(sm, data); | |
1621 | } | |
1622 | ||
1623 | ||
1624 | static int eap_teap_process_phase2_start(struct eap_sm *sm, | |
1625 | struct eap_teap_data *data) | |
1626 | { | |
1627 | u8 next_type; | |
1628 | ||
1629 | if (data->identity) { | |
1630 | /* Used PAC and identity is from PAC-Opaque */ | |
1631 | os_free(sm->identity); | |
1632 | sm->identity = data->identity; | |
1633 | data->identity = NULL; | |
1634 | sm->identity_len = data->identity_len; | |
1635 | data->identity_len = 0; | |
1636 | if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { | |
1637 | wpa_hexdump_ascii(MSG_DEBUG, | |
1638 | "EAP-TEAP: Phase 2 Identity not found in the user database", | |
1639 | sm->identity, sm->identity_len); | |
1640 | next_type = EAP_TYPE_NONE; | |
1641 | eap_teap_state(data, PHASE2_METHOD); | |
1642 | } else if (sm->eap_teap_pac_no_inner) { | |
1643 | wpa_printf(MSG_DEBUG, | |
1644 | "EAP-TEAP: Used PAC and identity already known - skip inner auth"); | |
1645 | /* FIX: Need to derive CMK here. However, how is that | |
1646 | * supposed to be done? RFC 7170 does not tell that for | |
1647 | * the no-inner-auth case. */ | |
a66e53c4 JM |
1648 | eap_teap_derive_cmk_basic_pw_auth(data->tls_cs, |
1649 | data->simck_msk, | |
0ed57c5e JM |
1650 | data->cmk_msk); |
1651 | eap_teap_state(data, CRYPTO_BINDING); | |
1652 | return 1; | |
1653 | } else if (sm->eap_teap_auth == 1) { | |
1654 | eap_teap_state(data, PHASE2_BASIC_AUTH); | |
1655 | return 1; | |
1656 | } else { | |
1657 | wpa_printf(MSG_DEBUG, | |
1658 | "EAP-TEAP: Identity already known - skip Phase 2 Identity Request"); | |
1659 | next_type = sm->user->methods[0].method; | |
1660 | sm->user_eap_method_index = 1; | |
1661 | eap_teap_state(data, PHASE2_METHOD); | |
1662 | } | |
1663 | ||
1664 | } else if (sm->eap_teap_auth == 1) { | |
1665 | eap_teap_state(data, PHASE2_BASIC_AUTH); | |
1666 | return 0; | |
1667 | } else { | |
1668 | eap_teap_state(data, PHASE2_ID); | |
1669 | next_type = EAP_TYPE_IDENTITY; | |
1670 | } | |
1671 | ||
1672 | return eap_teap_phase2_init(sm, data, next_type); | |
1673 | } | |
1674 | ||
1675 | ||
1676 | static void eap_teap_process_msg(struct eap_sm *sm, void *priv, | |
1677 | const struct wpabuf *respData) | |
1678 | { | |
1679 | struct eap_teap_data *data = priv; | |
1680 | ||
1681 | switch (data->state) { | |
1682 | case PHASE1: | |
1683 | case PHASE1B: | |
1684 | if (eap_teap_process_phase1(sm, data)) | |
1685 | break; | |
1686 | ||
1687 | /* fall through */ | |
1688 | case PHASE2_START: | |
1689 | eap_teap_process_phase2_start(sm, data); | |
1690 | break; | |
1691 | case PHASE2_ID: | |
1692 | case PHASE2_BASIC_AUTH: | |
1693 | case PHASE2_METHOD: | |
1694 | case CRYPTO_BINDING: | |
1695 | case REQUEST_PAC: | |
1696 | eap_teap_process_phase2(sm, data, data->ssl.tls_in); | |
1697 | break; | |
1698 | case FAILURE_SEND_RESULT: | |
1699 | /* Protected failure result indication completed. Ignore the | |
1700 | * received message (which is supposed to include Result TLV | |
1701 | * indicating failure) and terminate exchange with cleartext | |
1702 | * EAP-Failure. */ | |
1703 | eap_teap_state(data, FAILURE); | |
1704 | break; | |
1705 | default: | |
1706 | wpa_printf(MSG_DEBUG, "EAP-TEAP: Unexpected state %d in %s", | |
1707 | data->state, __func__); | |
1708 | break; | |
1709 | } | |
1710 | } | |
1711 | ||
1712 | ||
1713 | static void eap_teap_process(struct eap_sm *sm, void *priv, | |
1714 | struct wpabuf *respData) | |
1715 | { | |
1716 | struct eap_teap_data *data = priv; | |
1717 | const u8 *pos; | |
1718 | size_t len; | |
1719 | struct wpabuf *resp = respData; | |
1720 | u8 flags; | |
1721 | ||
1722 | pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TEAP, respData, &len); | |
1723 | if (!pos || len < 1) | |
1724 | return; | |
1725 | ||
1726 | flags = *pos++; | |
1727 | len--; | |
1728 | ||
1729 | if (flags & EAP_TEAP_FLAGS_OUTER_TLV_LEN) { | |
1730 | /* Extract Outer TLVs from the message before common TLS | |
1731 | * processing */ | |
1732 | u32 message_len = 0, outer_tlv_len; | |
1733 | const u8 *hdr; | |
1734 | ||
1735 | if (data->state != PHASE1) { | |
1736 | wpa_printf(MSG_INFO, | |
1737 | "EAP-TEAP: Unexpected Outer TLVs in a message that is not the first message from the peer"); | |
1738 | return; | |
1739 | } | |
1740 | ||
1741 | if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { | |
1742 | if (len < 4) { | |
1743 | wpa_printf(MSG_INFO, | |
1744 | "EAP-TEAP: Too short message to include Message Length field"); | |
1745 | return; | |
1746 | } | |
1747 | ||
1748 | message_len = WPA_GET_BE32(pos); | |
1749 | pos += 4; | |
1750 | len -= 4; | |
1751 | if (message_len < 4) { | |
1752 | wpa_printf(MSG_INFO, | |
1753 | "EAP-TEAP: Message Length field has too msall value to include Outer TLV Length field"); | |
1754 | return; | |
1755 | } | |
1756 | } | |
1757 | ||
1758 | if (len < 4) { | |
1759 | wpa_printf(MSG_INFO, | |
1760 | "EAP-TEAP: Too short message to include Outer TLVs Length field"); | |
1761 | return; | |
1762 | } | |
1763 | ||
1764 | outer_tlv_len = WPA_GET_BE32(pos); | |
1765 | pos += 4; | |
1766 | len -= 4; | |
1767 | ||
1768 | wpa_printf(MSG_DEBUG, | |
1769 | "EAP-TEAP: Message Length %u Outer TLV Length %u", | |
1770 | message_len, outer_tlv_len); | |
1771 | if (len < outer_tlv_len) { | |
1772 | wpa_printf(MSG_INFO, | |
1773 | "EAP-TEAP: Too short message to include Outer TLVs field"); | |
1774 | return; | |
1775 | } | |
1776 | ||
1777 | if (message_len && | |
1778 | (message_len < outer_tlv_len || | |
1779 | message_len < 4 + outer_tlv_len)) { | |
1780 | wpa_printf(MSG_INFO, | |
1781 | "EAP-TEAP: Message Length field has too small value to include Outer TLVs"); | |
1782 | return; | |
1783 | } | |
1784 | ||
1785 | if (wpabuf_len(respData) < 4 + outer_tlv_len || | |
1786 | len < outer_tlv_len) | |
1787 | return; | |
1788 | resp = wpabuf_alloc(wpabuf_len(respData) - 4 - outer_tlv_len); | |
1789 | if (!resp) | |
1790 | return; | |
1791 | hdr = wpabuf_head(respData); | |
1792 | wpabuf_put_u8(resp, *hdr++); /* Code */ | |
1793 | wpabuf_put_u8(resp, *hdr++); /* Identifier */ | |
1794 | wpabuf_put_be16(resp, WPA_GET_BE16(hdr) - 4 - outer_tlv_len); | |
1795 | hdr += 2; | |
1796 | wpabuf_put_u8(resp, *hdr++); /* Type */ | |
1797 | /* Flags | Ver */ | |
1798 | wpabuf_put_u8(resp, flags & ~EAP_TEAP_FLAGS_OUTER_TLV_LEN); | |
1799 | ||
1800 | if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) | |
1801 | wpabuf_put_be32(resp, message_len - 4 - outer_tlv_len); | |
1802 | ||
1803 | wpabuf_put_data(resp, pos, len - outer_tlv_len); | |
1804 | pos += len - outer_tlv_len; | |
1805 | wpabuf_free(data->peer_outer_tlvs); | |
1806 | data->peer_outer_tlvs = wpabuf_alloc_copy(pos, outer_tlv_len); | |
1807 | if (!data->peer_outer_tlvs) | |
1808 | return; | |
1809 | wpa_hexdump_buf(MSG_DEBUG, "EAP-TEAP: Outer TLVs", | |
1810 | data->peer_outer_tlvs); | |
1811 | ||
1812 | wpa_hexdump_buf(MSG_DEBUG, | |
1813 | "EAP-TEAP: TLS Data message after Outer TLV removal", | |
1814 | resp); | |
1815 | pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TEAP, resp, | |
1816 | &len); | |
1817 | if (!pos || len < 1) { | |
1818 | wpa_printf(MSG_INFO, | |
1819 | "EAP-TEAP: Invalid frame after Outer TLV removal"); | |
1820 | return; | |
1821 | } | |
1822 | } | |
1823 | ||
1824 | if (data->state == PHASE1) | |
1825 | eap_teap_state(data, PHASE1B); | |
1826 | ||
1827 | if (eap_server_tls_process(sm, &data->ssl, resp, data, | |
1828 | EAP_TYPE_TEAP, eap_teap_process_version, | |
1829 | eap_teap_process_msg) < 0) | |
1830 | eap_teap_state(data, FAILURE); | |
1831 | ||
1832 | if (resp != respData) | |
1833 | wpabuf_free(resp); | |
1834 | } | |
1835 | ||
1836 | ||
1837 | static Boolean eap_teap_isDone(struct eap_sm *sm, void *priv) | |
1838 | { | |
1839 | struct eap_teap_data *data = priv; | |
1840 | ||
1841 | return data->state == SUCCESS || data->state == FAILURE; | |
1842 | } | |
1843 | ||
1844 | ||
1845 | static u8 * eap_teap_getKey(struct eap_sm *sm, void *priv, size_t *len) | |
1846 | { | |
1847 | struct eap_teap_data *data = priv; | |
1848 | u8 *eapKeyData; | |
1849 | ||
1850 | if (data->state != SUCCESS) | |
1851 | return NULL; | |
1852 | ||
1853 | eapKeyData = os_malloc(EAP_TEAP_KEY_LEN); | |
1854 | if (!eapKeyData) | |
1855 | return NULL; | |
1856 | ||
1857 | /* FIX: RFC 7170 does not describe whether MSK or EMSK based S-IMCK[j] | |
1858 | * is used in this derivation */ | |
a66e53c4 JM |
1859 | if (eap_teap_derive_eap_msk(data->tls_cs, data->simck_msk, |
1860 | eapKeyData) < 0) { | |
0ed57c5e JM |
1861 | os_free(eapKeyData); |
1862 | return NULL; | |
1863 | } | |
1864 | *len = EAP_TEAP_KEY_LEN; | |
1865 | ||
1866 | return eapKeyData; | |
1867 | } | |
1868 | ||
1869 | ||
1870 | static u8 * eap_teap_get_emsk(struct eap_sm *sm, void *priv, size_t *len) | |
1871 | { | |
1872 | struct eap_teap_data *data = priv; | |
1873 | u8 *eapKeyData; | |
1874 | ||
1875 | if (data->state != SUCCESS) | |
1876 | return NULL; | |
1877 | ||
1878 | eapKeyData = os_malloc(EAP_EMSK_LEN); | |
1879 | if (!eapKeyData) | |
1880 | return NULL; | |
1881 | ||
1882 | /* FIX: RFC 7170 does not describe whether MSK or EMSK based S-IMCK[j] | |
1883 | * is used in this derivation */ | |
a66e53c4 JM |
1884 | if (eap_teap_derive_eap_emsk(data->tls_cs, data->simck_msk, |
1885 | eapKeyData) < 0) { | |
0ed57c5e JM |
1886 | os_free(eapKeyData); |
1887 | return NULL; | |
1888 | } | |
1889 | *len = EAP_EMSK_LEN; | |
1890 | ||
1891 | return eapKeyData; | |
1892 | } | |
1893 | ||
1894 | ||
1895 | static Boolean eap_teap_isSuccess(struct eap_sm *sm, void *priv) | |
1896 | { | |
1897 | struct eap_teap_data *data = priv; | |
1898 | ||
1899 | return data->state == SUCCESS; | |
1900 | } | |
1901 | ||
1902 | ||
1903 | static u8 * eap_teap_get_session_id(struct eap_sm *sm, void *priv, size_t *len) | |
1904 | { | |
1905 | struct eap_teap_data *data = priv; | |
1906 | const size_t max_id_len = 100; | |
1907 | int res; | |
1908 | u8 *id; | |
1909 | ||
1910 | if (data->state != SUCCESS) | |
1911 | return NULL; | |
1912 | ||
1913 | id = os_malloc(max_id_len); | |
1914 | if (!id) | |
1915 | return NULL; | |
1916 | ||
1917 | id[0] = EAP_TYPE_TEAP; | |
1918 | res = tls_get_tls_unique(data->ssl.conn, id + 1, max_id_len - 1); | |
1919 | if (res < 0) { | |
1920 | os_free(id); | |
1921 | wpa_printf(MSG_ERROR, "EAP-TEAP: Failed to derive Session-Id"); | |
1922 | return NULL; | |
1923 | } | |
1924 | ||
1925 | *len = 1 + res; | |
1926 | wpa_hexdump(MSG_DEBUG, "EAP-TEAP: Derived Session-Id", id, *len); | |
1927 | return id; | |
1928 | } | |
1929 | ||
1930 | ||
1931 | int eap_server_teap_register(void) | |
1932 | { | |
1933 | struct eap_method *eap; | |
1934 | ||
1935 | eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, | |
1936 | EAP_VENDOR_IETF, EAP_TYPE_TEAP, "TEAP"); | |
1937 | if (!eap) | |
1938 | return -1; | |
1939 | ||
1940 | eap->init = eap_teap_init; | |
1941 | eap->reset = eap_teap_reset; | |
1942 | eap->buildReq = eap_teap_buildReq; | |
1943 | eap->check = eap_teap_check; | |
1944 | eap->process = eap_teap_process; | |
1945 | eap->isDone = eap_teap_isDone; | |
1946 | eap->getKey = eap_teap_getKey; | |
1947 | eap->get_emsk = eap_teap_get_emsk; | |
1948 | eap->isSuccess = eap_teap_isSuccess; | |
1949 | eap->getSessionId = eap_teap_get_session_id; | |
1950 | ||
1951 | return eap_server_method_register(eap); | |
1952 | } |