Re-initialize hostapd/wpa_supplicant git repository based on 0.6.3 release
[libeap.git] / src / eap_server / eap_tls_common.c
1 /*
2  * hostapd / EAP-TLS/PEAP/TTLS/FAST common functions
3  * Copyright (c) 2004-2007, 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 "sha1.h"
21 #include "tls.h"
22
23
24 int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
25                             int verify_peer)
26 {
27         data->eap = sm;
28         data->phase2 = sm->init_phase2;
29
30         data->conn = tls_connection_init(sm->ssl_ctx);
31         if (data->conn == NULL) {
32                 wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS "
33                            "connection");
34                 return -1;
35         }
36
37         if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer)) {
38                 wpa_printf(MSG_INFO, "SSL: Failed to configure verification "
39                            "of TLS peer certificate");
40                 tls_connection_deinit(sm->ssl_ctx, data->conn);
41                 data->conn = NULL;
42                 return -1;
43         }
44
45         /* TODO: make this configurable */
46         data->tls_out_limit = 1398;
47         if (data->phase2) {
48                 /* Limit the fragment size in the inner TLS authentication
49                  * since the outer authentication with EAP-PEAP does not yet
50                  * support fragmentation */
51                 if (data->tls_out_limit > 100)
52                         data->tls_out_limit -= 100;
53         }
54         return 0;
55 }
56
57
58 void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data)
59 {
60         tls_connection_deinit(sm->ssl_ctx, data->conn);
61         os_free(data->tls_in);
62         os_free(data->tls_out);
63 }
64
65
66 u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
67                                char *label, size_t len)
68 {
69         struct tls_keys keys;
70         u8 *rnd = NULL, *out;
71
72         out = os_malloc(len);
73         if (out == NULL)
74                 return NULL;
75
76         if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, out, len) ==
77             0)
78                 return out;
79
80         if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
81                 goto fail;
82
83         if (keys.client_random == NULL || keys.server_random == NULL ||
84             keys.master_key == NULL)
85                 goto fail;
86
87         rnd = os_malloc(keys.client_random_len + keys.server_random_len);
88         if (rnd == NULL)
89                 goto fail;
90         os_memcpy(rnd, keys.client_random, keys.client_random_len);
91         os_memcpy(rnd + keys.client_random_len, keys.server_random,
92                   keys.server_random_len);
93
94         if (tls_prf(keys.master_key, keys.master_key_len,
95                     label, rnd, keys.client_random_len +
96                     keys.server_random_len, out, len))
97                 goto fail;
98
99         os_free(rnd);
100         return out;
101
102 fail:
103         os_free(out);
104         os_free(rnd);
105         return NULL;
106 }
107
108
109 int eap_server_tls_data_reassemble(struct eap_sm *sm,
110                                    struct eap_ssl_data *data,
111                                    u8 **in_data, size_t *in_len)
112 {
113         u8 *buf;
114
115         if (data->tls_in_left > *in_len || data->tls_in) {
116                 if (*in_len == 0) {
117                         wpa_printf(MSG_INFO, "SSL: Empty fragment when trying "
118                                    "to reassemble");
119                         return -1;
120                 }
121                 if (data->tls_in_len + *in_len > 65536) {
122                         /* Limit length to avoid rogue peers from causing large
123                          * memory allocations. */
124                         os_free(data->tls_in);
125                         data->tls_in = NULL;
126                         data->tls_in_len = 0;
127                         wpa_printf(MSG_INFO, "SSL: Too long TLS fragment (size"
128                                    " over 64 kB)");
129                         return -1;
130                 }
131                 buf = os_realloc(data->tls_in, data->tls_in_len + *in_len);
132                 if (buf == NULL) {
133                         os_free(data->tls_in);
134                         data->tls_in = NULL;
135                         data->tls_in_len = 0;
136                         wpa_printf(MSG_INFO, "SSL: Could not allocate memory "
137                                    "for TLS data");
138                         return -1;
139                 }
140                 os_memcpy(buf + data->tls_in_len, *in_data, *in_len);
141                 data->tls_in = buf;
142                 data->tls_in_len += *in_len;
143                 if (*in_len > data->tls_in_left) {
144                         wpa_printf(MSG_INFO, "SSL: more data than TLS message "
145                                    "length indicated");
146                         data->tls_in_left = 0;
147                         return -1;
148                 }
149                 data->tls_in_left -= *in_len;
150                 if (data->tls_in_left > 0) {
151                         wpa_printf(MSG_DEBUG, "SSL: Need %lu bytes more input "
152                                    "data", (unsigned long) data->tls_in_left);
153                         return 1;
154                 }
155
156                 *in_data = data->tls_in;
157                 *in_len = data->tls_in_len;
158         } else
159                 data->tls_in_left = 0;
160
161         return 0;
162 }
163
164
165 int eap_server_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
166                                   const u8 *in_data, size_t in_len)
167 {
168         WPA_ASSERT(data->tls_out_len == 0 || in_len == 0);
169
170         if (data->tls_out_len == 0) {
171                 u8 *_in_data = (u8 *) in_data; /* FIX: get rid of the typecast
172                                                 */
173                 /* No more data to send out - expect to receive more data from
174                  * the peer. */
175                 int res = eap_server_tls_data_reassemble(sm, data, &_in_data,
176                                                          &in_len);
177                 if (res < 0 || res == 1) {
178                         wpa_printf(MSG_DEBUG, "SSL: data reassembly failed");
179                         return res;
180                 }
181                 /* Full TLS message reassembled - continue handshake processing
182                  */
183                 if (data->tls_out) {
184                         /* This should not happen.. */
185                         wpa_printf(MSG_INFO, "SSL: eap_tls_process_helper - "
186                                    "pending tls_out data even though "
187                                    "tls_out_len = 0");
188                         os_free(data->tls_out);
189                         WPA_ASSERT(data->tls_out == NULL);
190                 }
191                 data->tls_out = tls_connection_server_handshake(
192                         sm->ssl_ctx, data->conn, _in_data, in_len,
193                         &data->tls_out_len);
194
195                 /* Clear reassembled input data (if the buffer was needed). */
196                 data->tls_in_left = data->tls_in_total = data->tls_in_len = 0;
197                 os_free(data->tls_in);
198                 data->tls_in = NULL;
199         }
200
201         if (data->tls_out == NULL) {
202                 wpa_printf(MSG_DEBUG, "SSL: failed to generate output data");
203                 data->tls_out_len = 0;
204                 return -1;
205         }
206         if (data->tls_out_len == 0) {
207                 /* TLS negotiation should now be complete since all other cases
208                  * needing more that should have been catched above based on
209                  * the TLS Message Length field. */
210                 wpa_printf(MSG_DEBUG, "SSL: No data to be sent out");
211                 os_free(data->tls_out);
212                 data->tls_out = NULL;
213
214                 if (tls_connection_get_read_alerts(sm->ssl_ctx, data->conn)) {
215                         wpa_printf(MSG_DEBUG, "SSL: Remote end sent a fatal "
216                                    "alert - abort handshake");
217                         return -1;
218                 }
219
220                 return 1;
221         }
222
223         wpa_printf(MSG_DEBUG, "SSL: %lu bytes left to be sent out (of total "
224                    "%lu bytes)",
225                    (unsigned long) data->tls_out_len - data->tls_out_pos,
226                    (unsigned long) data->tls_out_len);
227
228         return 0;
229 }
230
231
232 int eap_server_tls_buildReq_helper(struct eap_sm *sm,
233                                    struct eap_ssl_data *data,
234                                    int eap_type, int peap_version, u8 id,
235                                    struct wpabuf **out_data)
236 {
237         size_t len;
238         u8 *flags;
239         struct wpabuf *req;
240         int incl_len;
241
242         incl_len = data->tls_out_pos == 0 &&
243                 data->tls_out_len > data->tls_out_limit;
244         req = eap_msg_alloc(EAP_VENDOR_IETF, eap_type,
245                             1 + (incl_len ? 4 : 0) + data->tls_out_limit,
246                             EAP_CODE_REQUEST, id);
247         if (req == NULL) {
248                 *out_data = NULL;
249                 return -1;
250         }
251         flags = wpabuf_put(req, 1);
252         *flags = peap_version;
253         if (incl_len) {
254                 *flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED;
255                 wpabuf_put_be32(req, data->tls_out_len);
256         }
257
258         len = data->tls_out_len - data->tls_out_pos;
259         if (len > data->tls_out_limit) {
260                 *flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS;
261                 len = data->tls_out_limit;
262                 wpa_printf(MSG_DEBUG, "SSL: sending %lu bytes, more fragments "
263                            "will follow", (unsigned long) len);
264         }
265         wpabuf_put_data(req, &data->tls_out[data->tls_out_pos], len);
266         data->tls_out_pos += len;
267
268         eap_update_len(req);
269         *out_data = req;
270
271         if (!(*flags & EAP_TLS_FLAGS_MORE_FRAGMENTS)) {
272                 data->tls_out_len = 0;
273                 data->tls_out_pos = 0;
274                 os_free(data->tls_out);
275                 data->tls_out = NULL;
276         }
277
278         return 0;
279 }
280
281
282 struct wpabuf * eap_server_tls_build_ack(u8 id, int eap_type, int peap_version)
283 {
284         struct wpabuf *req;
285
286         req = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, 1, EAP_CODE_REQUEST,
287                             id);
288         if (req == NULL)
289                 return NULL;
290         wpa_printf(MSG_DEBUG, "SSL: Building ACK");
291         wpabuf_put_u8(req, peap_version); /* Flags */
292         return req;
293 }