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