1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
|
/*
This file is part of libmicrospdy
Copyright (C) 2012 Andrey Uzunov
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file io_openssl.c
* @brief TLS handling using libssl. The current code assumes that
* blocking I/O is in use.
* @author Andrey Uzunov
*/
#include "platform.h"
#include "internal.h"
#include "session.h"
#include "io_openssl.h"
/**
* Callback to advertise spdy ver. 3 in Next Protocol Negotiation
*
* @param ssl openssl context for a connection
* @param out must be set to the raw data that is advertised in NPN
* @param outlen must be set to size of out
* @param arg
* @return SSL_TLSEXT_ERR_OK to do advertising
*/
static int
spdyf_next_protos_advertised_cb (SSL *ssl, const unsigned char **out, unsigned int *outlen, void *arg)
{
(void)ssl;
(void)arg;
static unsigned char npn_spdy3[] = {0x06, // length of "spdy/3"
0x73,0x70,0x64,0x79,0x2f,0x33};// spdy/3
*out = npn_spdy3;
*outlen = 7; // total length of npn_spdy3
return SSL_TLSEXT_ERR_OK;
}
void
SPDYF_openssl_global_init()
{
//error strings are now not used by the lib
//SSL_load_error_strings();
//init libssl
SSL_library_init(); //always returns 1
//the table for looking up algos is not used now by the lib
//OpenSSL_add_all_algorithms();
}
void
SPDYF_openssl_global_deinit()
{
//if SSL_load_error_strings was called
//ERR_free_strings();
//if OpenSSL_add_all_algorithms was called
//EVP_cleanup();
}
int
SPDYF_openssl_init(struct SPDY_Daemon *daemon)
{
int options;
//create ssl context. TLSv1 used
if(NULL == (daemon->io_context = SSL_CTX_new(TLSv1_server_method())))
{
SPDYF_DEBUG("Couldn't create ssl context");
return SPDY_NO;
}
//set options for tls
//TODO DH is not enabled for easier debugging
//SSL_CTX_set_options(daemon->io_context, SSL_OP_SINGLE_DH_USE);
//TODO here session tickets are disabled for easier debuging with
//wireshark when using Chrome
// SSL_OP_NO_COMPRESSION disables TLS compression to avoid CRIME attack
options = SSL_OP_NO_TICKET;
#ifdef SSL_OP_NO_COMPRESSION
options |= SSL_OP_NO_COMPRESSION;
#elif OPENSSL_VERSION_NUMBER >= 0x00908000L /* workaround for OpenSSL 0.9.8 */
sk_SSL_COMP_zero(SSL_COMP_get_compression_methods());
#endif
SSL_CTX_set_options(daemon->io_context, options);
if(1 != SSL_CTX_use_certificate_file(daemon->io_context, daemon->certfile , SSL_FILETYPE_PEM))
{
SPDYF_DEBUG("Couldn't load the cert file");
SSL_CTX_free(daemon->io_context);
return SPDY_NO;
}
if(1 != SSL_CTX_use_PrivateKey_file(daemon->io_context, daemon->keyfile, SSL_FILETYPE_PEM))
{
SPDYF_DEBUG("Couldn't load the name file");
SSL_CTX_free(daemon->io_context);
return SPDY_NO;
}
SSL_CTX_set_next_protos_advertised_cb(daemon->io_context, &spdyf_next_protos_advertised_cb, NULL);
//TODO only RC4-SHA is used to make it easy to debug with wireshark
if (1 != SSL_CTX_set_cipher_list(daemon->io_context, "RC4-SHA"))
{
SPDYF_DEBUG("Couldn't set the desired cipher list");
SSL_CTX_free(daemon->io_context);
return SPDY_NO;
}
return SPDY_YES;
}
void
SPDYF_openssl_deinit(struct SPDY_Daemon *daemon)
{
SSL_CTX_free(daemon->io_context);
}
int
SPDYF_openssl_new_session(struct SPDY_Session *session)
{
int ret;
if(NULL == (session->io_context = SSL_new(session->daemon->io_context)))
{
SPDYF_DEBUG("Couldn't create ssl structure");
return SPDY_NO;
}
if(1 != (ret = SSL_set_fd(session->io_context, session->socket_fd)))
{
SPDYF_DEBUG("SSL_set_fd %i",ret);
SSL_free(session->io_context);
session->io_context = NULL;
return SPDY_NO;
}
//for non-blocking I/O SSL_accept may return -1
//and this function won't work
if(1 != (ret = SSL_accept(session->io_context)))
{
SPDYF_DEBUG("SSL_accept %i",ret);
SSL_free(session->io_context);
session->io_context = NULL;
return SPDY_NO;
}
/* alternatively
SSL_set_accept_state(session->io_context);
* may be called and then the negotiation will be done on reading
*/
return SPDY_YES;
}
void
SPDYF_openssl_close_session(struct SPDY_Session *session)
{
//SSL_shutdown sends TLS "close notify" as in TLS standard.
//The function may fail as it waits for the other party to also close
//the TLS session. The lib just sends it and will close the socket
//after that because the browsers don't seem to care much about
//"close notify"
SSL_shutdown(session->io_context);
SSL_free(session->io_context);
}
int
SPDYF_openssl_recv(struct SPDY_Session *session,
void * buffer,
size_t size)
{
int ret;
int n = SSL_read(session->io_context,
buffer,
size);
//if(n > 0) SPDYF_DEBUG("recvd: %i",n);
if (n <= 0)
{
ret = SSL_get_error(session->io_context, n);
switch(ret)
{
case SSL_ERROR_ZERO_RETURN:
return 0;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
return SPDY_IO_ERROR_AGAIN;
case SSL_ERROR_SYSCALL:
if(EINTR == errno)
return SPDY_IO_ERROR_AGAIN;
default:
return SPDY_IO_ERROR_ERROR;
}
}
return n;
}
int
SPDYF_openssl_send(struct SPDY_Session *session,
const void * buffer,
size_t size)
{
int ret;
int n = SSL_write(session->io_context,
buffer,
size);
//if(n > 0) SPDYF_DEBUG("sent: %i",n);
if (n <= 0)
{
ret = SSL_get_error(session->io_context, n);
switch(ret)
{
case SSL_ERROR_ZERO_RETURN:
return 0;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
return SPDY_IO_ERROR_AGAIN;
case SSL_ERROR_SYSCALL:
if(EINTR == errno)
return SPDY_IO_ERROR_AGAIN;
default:
return SPDY_IO_ERROR_ERROR;
}
}
return n;
}
int
SPDYF_openssl_is_pending(struct SPDY_Session *session)
{
/* From openssl docs:
* BUGS
SSL_pending() takes into account only bytes from the TLS/SSL record that is currently being processed (if any). If the SSL object's read_ahead flag is set, additional protocol bytes may have been read containing more TLS/SSL records; these are ignored by SSL_pending().
*/
return SSL_pending(session->io_context) > 0 ? SPDY_YES : SPDY_NO;
}
int
SPDYF_openssl_before_write(struct SPDY_Session *session)
{
(void)session;
return SPDY_YES;
}
int
SPDYF_openssl_after_write(struct SPDY_Session *session, int was_written)
{
(void)session;
return was_written;
}
|