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