f18f2c1da9d61643871b53300ef39bb4c8e768bd
[libeap.git] / src / eap_server / eap_tls.c
1 /*
2  * hostapd / EAP-TLS (RFC 2716)
3  * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  *
9  * Alternatively, this software may be distributed under the terms of BSD
10  * license.
11  *
12  * See README and COPYING for more details.
13  */
14
15 #include "includes.h"
16
17 #include "common.h"
18 #include "eap_i.h"
19 #include "eap_tls_common.h"
20 #include "tls.h"
21
22
23 static void eap_tls_reset(struct eap_sm *sm, void *priv);
24
25
26 struct eap_tls_data {
27         struct eap_ssl_data ssl;
28         enum { START, CONTINUE, SUCCESS, FAILURE } state;
29 };
30
31
32 static void * eap_tls_init(struct eap_sm *sm)
33 {
34         struct eap_tls_data *data;
35
36         data = os_zalloc(sizeof(*data));
37         if (data == NULL)
38                 return NULL;
39         data->state = START;
40
41         if (eap_server_tls_ssl_init(sm, &data->ssl, 1)) {
42                 wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
43                 eap_tls_reset(sm, data);
44                 return NULL;
45         }
46
47         return data;
48 }
49
50
51 static void eap_tls_reset(struct eap_sm *sm, void *priv)
52 {
53         struct eap_tls_data *data = priv;
54         if (data == NULL)
55                 return;
56         eap_server_tls_ssl_deinit(sm, &data->ssl);
57         os_free(data);
58 }
59
60
61 static struct wpabuf * eap_tls_build_start(struct eap_sm *sm,
62                                            struct eap_tls_data *data, u8 id)
63 {
64         struct wpabuf *req;
65
66         req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLS, 1, EAP_CODE_REQUEST,
67                             id);
68         if (req == NULL) {
69                 wpa_printf(MSG_ERROR, "EAP-TLS: Failed to allocate memory for "
70                            "request");
71                 data->state = FAILURE;
72                 return NULL;
73         }
74
75         wpabuf_put_u8(req, EAP_TLS_FLAGS_START);
76
77         data->state = CONTINUE;
78
79         return req;
80 }
81
82
83 static struct wpabuf * eap_tls_buildReq(struct eap_sm *sm, void *priv, u8 id)
84 {
85         struct eap_tls_data *data = priv;
86
87
88         if (data->ssl.state == FRAG_ACK) {
89                 return eap_server_tls_build_ack(id, EAP_TYPE_TLS, 0);
90         }
91
92         if (data->ssl.state == WAIT_FRAG_ACK) {
93                 return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0,
94                                                 id);
95         }
96
97         switch (data->state) {
98         case START:
99                 return eap_tls_build_start(sm, data, id);
100         case CONTINUE:
101                 if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
102                         wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
103                         data->state = SUCCESS;
104                 }
105                 break;
106         default:
107                 wpa_printf(MSG_DEBUG, "EAP-TLS: %s - unexpected state %d",
108                            __func__, data->state);
109                 return NULL;
110         }
111
112         return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0, id);
113 }
114
115
116 static Boolean eap_tls_check(struct eap_sm *sm, void *priv,
117                              struct wpabuf *respData)
118 {
119         const u8 *pos;
120         size_t len;
121
122         pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLS, respData, &len);
123         if (pos == NULL || len < 1) {
124                 wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame");
125                 return TRUE;
126         }
127
128         return FALSE;
129 }
130
131
132 static void eap_tls_process_msg(struct eap_sm *sm, void *priv,
133                                 const struct wpabuf *respData)
134 {
135         struct eap_tls_data *data = priv;
136         if (eap_server_tls_phase1(sm, &data->ssl) < 0)
137                 data->state = FAILURE;
138 }
139
140
141 static void eap_tls_process(struct eap_sm *sm, void *priv,
142                             struct wpabuf *respData)
143 {
144         struct eap_tls_data *data = priv;
145         if (eap_server_tls_process(sm, &data->ssl, respData, data,
146                                    EAP_TYPE_TLS, NULL, eap_tls_process_msg) <
147             0)
148                 data->state = FAILURE;
149 }
150
151
152 static Boolean eap_tls_isDone(struct eap_sm *sm, void *priv)
153 {
154         struct eap_tls_data *data = priv;
155         return data->state == SUCCESS || data->state == FAILURE;
156 }
157
158
159 static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
160 {
161         struct eap_tls_data *data = priv;
162         u8 *eapKeyData;
163
164         if (data->state != SUCCESS)
165                 return NULL;
166
167         eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
168                                                "client EAP encryption",
169                                                EAP_TLS_KEY_LEN);
170         if (eapKeyData) {
171                 *len = EAP_TLS_KEY_LEN;
172                 wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived key",
173                             eapKeyData, EAP_TLS_KEY_LEN);
174         } else {
175                 wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive key");
176         }
177
178         return eapKeyData;
179 }
180
181
182 static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
183 {
184         struct eap_tls_data *data = priv;
185         u8 *eapKeyData, *emsk;
186
187         if (data->state != SUCCESS)
188                 return NULL;
189
190         eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
191                                                "client EAP encryption",
192                                                EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
193         if (eapKeyData) {
194                 emsk = os_malloc(EAP_EMSK_LEN);
195                 if (emsk)
196                         os_memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN,
197                                   EAP_EMSK_LEN);
198                 os_free(eapKeyData);
199         } else
200                 emsk = NULL;
201
202         if (emsk) {
203                 *len = EAP_EMSK_LEN;
204                 wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived EMSK",
205                             emsk, EAP_EMSK_LEN);
206         } else {
207                 wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive EMSK");
208         }
209
210         return emsk;
211 }
212
213
214 static Boolean eap_tls_isSuccess(struct eap_sm *sm, void *priv)
215 {
216         struct eap_tls_data *data = priv;
217         return data->state == SUCCESS;
218 }
219
220
221 int eap_server_tls_register(void)
222 {
223         struct eap_method *eap;
224         int ret;
225
226         eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
227                                       EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS");
228         if (eap == NULL)
229                 return -1;
230
231         eap->init = eap_tls_init;
232         eap->reset = eap_tls_reset;
233         eap->buildReq = eap_tls_buildReq;
234         eap->check = eap_tls_check;
235         eap->process = eap_tls_process;
236         eap->isDone = eap_tls_isDone;
237         eap->getKey = eap_tls_getKey;
238         eap->isSuccess = eap_tls_isSuccess;
239         eap->get_emsk = eap_tls_get_emsk;
240
241         ret = eap_server_method_register(eap);
242         if (ret)
243                 eap_server_method_free(eap);
244         return ret;
245 }