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