]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - contrib/hostapd/eap_tls_common.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / contrib / hostapd / eap_tls_common.c
1 /*
2  * hostapd / EAP-TLS/PEAP/TTLS common functions
3  * Copyright (c) 2004-2006, 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 "hostapd.h"
18 #include "common.h"
19 #include "eap_i.h"
20 #include "eap_tls_common.h"
21 #include "sha1.h"
22 #include "tls.h"
23
24
25 int eap_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
26                      int verify_peer)
27 {
28         data->eap = sm;
29         data->phase2 = sm->init_phase2;
30
31         data->conn = tls_connection_init(sm->ssl_ctx);
32         if (data->conn == NULL) {
33                 wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS "
34                            "connection");
35                 return -1;
36         }
37
38         if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer)) {
39                 wpa_printf(MSG_INFO, "SSL: Failed to configure verification "
40                            "of TLS peer certificate");
41                 tls_connection_deinit(sm->ssl_ctx, data->conn);
42                 data->conn = NULL;
43                 return -1;
44         }
45
46         /* TODO: make this configurable */
47         data->tls_out_limit = 1398;
48         if (data->phase2) {
49                 /* Limit the fragment size in the inner TLS authentication
50                  * since the outer authentication with EAP-PEAP does not yet
51                  * support fragmentation */
52                 if (data->tls_out_limit > 100)
53                         data->tls_out_limit -= 100;
54         }
55         return 0;
56 }
57
58
59 void eap_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data)
60 {
61         tls_connection_deinit(sm->ssl_ctx, data->conn);
62         free(data->tls_in);
63         free(data->tls_out);
64 }
65
66
67 u8 * eap_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
68                         char *label, size_t len)
69 {
70         struct tls_keys keys;
71         u8 *rnd = NULL, *out;
72
73         out = malloc(len);
74         if (out == NULL)
75                 return NULL;
76
77         if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, out, len) ==
78             0)
79                 return out;
80
81         if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
82                 goto fail;
83
84         if (keys.client_random == NULL || keys.server_random == NULL ||
85             keys.master_key == NULL)
86                 goto fail;
87
88         rnd = malloc(keys.client_random_len + keys.server_random_len);
89         if (rnd == NULL)
90                 goto fail;
91         memcpy(rnd, keys.client_random, keys.client_random_len);
92         memcpy(rnd + keys.client_random_len, keys.server_random,
93                keys.server_random_len);
94
95         if (tls_prf(keys.master_key, keys.master_key_len,
96                     label, rnd, keys.client_random_len +
97                     keys.server_random_len, out, len))
98                 goto fail;
99
100         free(rnd);
101         return out;
102
103 fail:
104         free(out);
105         free(rnd);
106         return NULL;
107 }
108
109
110 int eap_tls_data_reassemble(struct eap_sm *sm, 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                         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 = realloc(data->tls_in, data->tls_in_len + *in_len);
132                 if (buf == NULL) {
133                         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                 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_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
166                            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                 /* No more data to send out - expect to receive more data from
172                  * the peer. */
173                 int res = eap_tls_data_reassemble(sm, data, &in_data, &in_len);
174                 if (res < 0 || res == 1) {
175                         wpa_printf(MSG_DEBUG, "SSL: data reassembly failed");
176                         return res;
177                 }
178                 /* Full TLS message reassembled - continue handshake processing
179                  */
180                 if (data->tls_out) {
181                         /* This should not happen.. */
182                         wpa_printf(MSG_INFO, "SSL: eap_tls_process_helper - "
183                                    "pending tls_out data even though "
184                                    "tls_out_len = 0");
185                         free(data->tls_out);
186                         WPA_ASSERT(data->tls_out == NULL);
187                 }
188                 data->tls_out = tls_connection_server_handshake(
189                         sm->ssl_ctx, data->conn, in_data, in_len,
190                         &data->tls_out_len);
191
192                 /* Clear reassembled input data (if the buffer was needed). */
193                 data->tls_in_left = data->tls_in_total = data->tls_in_len = 0;
194                 free(data->tls_in);
195                 data->tls_in = NULL;
196         }
197
198         if (data->tls_out == NULL) {
199                 wpa_printf(MSG_DEBUG, "SSL: failed to generate output data");
200                 data->tls_out_len = 0;
201                 return -1;
202         }
203         if (data->tls_out_len == 0) {
204                 /* TLS negotiation should now be complete since all other cases
205                  * needing more that should have been catched above based on
206                  * the TLS Message Length field. */
207                 wpa_printf(MSG_DEBUG, "SSL: No data to be sent out");
208                 free(data->tls_out);
209                 data->tls_out = NULL;
210
211                 if (tls_connection_get_read_alerts(sm->ssl_ctx, data->conn)) {
212                         wpa_printf(MSG_DEBUG, "SSL: Remote end sent a fatal "
213                                    "alert - abort handshake");
214                         return -1;
215                 }
216
217                 return 1;
218         }
219
220         wpa_printf(MSG_DEBUG, "SSL: %lu bytes left to be sent out (of total "
221                    "%lu bytes)",
222                    (unsigned long) data->tls_out_len - data->tls_out_pos,
223                    (unsigned long) data->tls_out_len);
224
225         return 0;
226 }
227
228
229 int eap_tls_buildReq_helper(struct eap_sm *sm, struct eap_ssl_data *data,
230                             int eap_type, int peap_version, u8 id,
231                             u8 **out_data, size_t *out_len)
232 {
233         size_t len;
234         u8 *pos, *flags;
235         struct eap_hdr *req;
236
237         *out_len = 0;
238
239         req = malloc(sizeof(struct eap_hdr) + 2 + 4 + data->tls_out_limit);
240         if (req == NULL) {
241                 *out_data = NULL;
242                 return -1;
243         }
244         req->code = EAP_CODE_REQUEST;
245         req->identifier = id;
246         pos = (u8 *) (req + 1);
247         *pos++ = eap_type;
248         flags = pos++;
249         *flags = peap_version;
250         if (data->tls_out_pos == 0 &&
251             data->tls_out_len > data->tls_out_limit) {
252                 *flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED;
253                 *pos++ = (data->tls_out_len >> 24) & 0xff;
254                 *pos++ = (data->tls_out_len >> 16) & 0xff;
255                 *pos++ = (data->tls_out_len >> 8) & 0xff;
256                 *pos++ = data->tls_out_len & 0xff;
257         }
258
259         len = data->tls_out_len - data->tls_out_pos;
260         if (len > data->tls_out_limit) {
261                 *flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS;
262                 len = data->tls_out_limit;
263                 wpa_printf(MSG_DEBUG, "SSL: sending %lu bytes, more fragments "
264                            "will follow", (unsigned long) len);
265         }
266         memcpy(pos, &data->tls_out[data->tls_out_pos], len);
267         data->tls_out_pos += len;
268         *out_len = (pos - (u8 *) req) + len;
269         req->length = htons(*out_len);
270         *out_data = (u8 *) req;
271
272         if (!(*flags & EAP_TLS_FLAGS_MORE_FRAGMENTS)) {
273                 data->tls_out_len = 0;
274                 data->tls_out_pos = 0;
275                 free(data->tls_out);
276                 data->tls_out = NULL;
277         }
278
279         return 0;
280 }
281
282
283 u8 * eap_tls_build_ack(size_t *reqDataLen, u8 id, int eap_type,
284                        int peap_version)
285 {
286         struct eap_hdr *req;
287         u8 *pos;
288
289         *reqDataLen = sizeof(struct eap_hdr) + 2;
290         req = malloc(*reqDataLen);
291         if (req == NULL)
292                 return NULL;
293         wpa_printf(MSG_DEBUG, "SSL: Building ACK");
294         req->code = EAP_CODE_REQUEST;
295         req->identifier = id;
296         req->length = htons(*reqDataLen);
297         pos = (u8 *) (req + 1);
298         *pos++ = eap_type; /* Type */
299         *pos = peap_version; /* Flags */
300         return (u8 *) req;
301 }