diff options
Diffstat (limited to 'src/microspdy/tls.c')
-rw-r--r-- | src/microspdy/tls.c | 255 |
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 | */ | ||
41 | static int | ||
42 | spdyf_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 | |||
55 | void | ||
56 | SPDYF_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 | |||
67 | void | ||
68 | SPDYF_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 | |||
77 | int | ||
78 | SPDYF_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 | |||
119 | void | ||
120 | SPDYF_tls_deinit(struct SPDY_Daemon *daemon) | ||
121 | { | ||
122 | SSL_CTX_free(daemon->tls_context); | ||
123 | } | ||
124 | |||
125 | |||
126 | int | ||
127 | SPDYF_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 | |||
162 | void | ||
163 | SPDYF_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 | |||
176 | int | ||
177 | SPDYF_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 | |||
211 | int | ||
212 | SPDYF_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 | |||
247 | int | ||
248 | SPDYF_tls_is_pending(struct SPDY_Session *session) | ||
249 | { | ||
250 | /* From openssl docs: | ||
251 | * BUGS | ||
252 | 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(). | ||
253 | */ | ||
254 | return SSL_pending(session->tls_context) > 0 ? SPDY_YES : SPDY_NO; | ||
255 | } | ||