• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2     This file is part of libmicrospdy
3     Copyright 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 io_openssl.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 "io_openssl.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
spdyf_next_protos_advertised_cb(SSL * ssl,const unsigned char ** out,unsigned int * outlen,void * arg)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
SPDYF_openssl_global_init()56 SPDYF_openssl_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
SPDYF_openssl_global_deinit()68 SPDYF_openssl_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
SPDYF_openssl_init(struct SPDY_Daemon * daemon)78 SPDYF_openssl_init(struct SPDY_Daemon *daemon)
79 {
80     int options;
81     //create ssl context. TLSv1 used
82     if(NULL == (daemon->io_context = SSL_CTX_new(TLSv1_server_method())))
83     {
84 		SPDYF_DEBUG("Couldn't create ssl context");
85 		return SPDY_NO;
86         }
87 	//set options for tls
88 	//TODO DH is not enabled for easier debugging
89     //SSL_CTX_set_options(daemon->io_context, SSL_OP_SINGLE_DH_USE);
90 
91     //TODO here session tickets are disabled for easier debuging with
92     //wireshark when using Chrome
93     // SSL_OP_NO_COMPRESSION disables TLS compression to avoid CRIME attack
94     options = SSL_OP_NO_TICKET;
95 #ifdef SSL_OP_NO_COMPRESSION
96     options |= SSL_OP_NO_COMPRESSION;
97 #elif OPENSSL_VERSION_NUMBER >= 0x00908000L /* workaround for OpenSSL 0.9.8 */
98     sk_SSL_COMP_zero(SSL_COMP_get_compression_methods());
99 #endif
100 
101     SSL_CTX_set_options(daemon->io_context, options);
102     if(1 != SSL_CTX_use_certificate_file(daemon->io_context, daemon->certfile , SSL_FILETYPE_PEM))
103     {
104 		SPDYF_DEBUG("Couldn't load the cert file");
105 		SSL_CTX_free(daemon->io_context);
106 		return SPDY_NO;
107 	}
108     if(1 != SSL_CTX_use_PrivateKey_file(daemon->io_context, daemon->keyfile, SSL_FILETYPE_PEM))
109     {
110 		SPDYF_DEBUG("Couldn't load the name file");
111 		SSL_CTX_free(daemon->io_context);
112 		return SPDY_NO;
113 	}
114     SSL_CTX_set_next_protos_advertised_cb(daemon->io_context, &spdyf_next_protos_advertised_cb, NULL);
115     if (1 != SSL_CTX_set_cipher_list(daemon->io_context, "HIGH"))
116     {
117 		SPDYF_DEBUG("Couldn't set the desired cipher list");
118 		SSL_CTX_free(daemon->io_context);
119 		return SPDY_NO;
120 	}
121 
122 	return SPDY_YES;
123 }
124 
125 
126 void
SPDYF_openssl_deinit(struct SPDY_Daemon * daemon)127 SPDYF_openssl_deinit(struct SPDY_Daemon *daemon)
128 {
129     SSL_CTX_free(daemon->io_context);
130 }
131 
132 
133 int
SPDYF_openssl_new_session(struct SPDY_Session * session)134 SPDYF_openssl_new_session(struct SPDY_Session *session)
135 {
136 	int ret;
137 
138 	if(NULL == (session->io_context = SSL_new(session->daemon->io_context)))
139     {
140 		SPDYF_DEBUG("Couldn't create ssl structure");
141 		return SPDY_NO;
142 	}
143 	if(1 != (ret = SSL_set_fd(session->io_context, session->socket_fd)))
144     {
145 		SPDYF_DEBUG("SSL_set_fd %i",ret);
146 		SSL_free(session->io_context);
147 		session->io_context = NULL;
148 		return SPDY_NO;
149 	}
150 
151 	//for non-blocking I/O SSL_accept may return -1
152 	//and this function won't work
153 	if(1 != (ret = SSL_accept(session->io_context)))
154     {
155 		SPDYF_DEBUG("SSL_accept %i",ret);
156 		SSL_free(session->io_context);
157 		session->io_context = NULL;
158 		return SPDY_NO;
159 	}
160 	/* alternatively
161 	SSL_set_accept_state(session->io_context);
162 	* may be called and then the negotiation will be done on reading
163 	*/
164 
165 	return SPDY_YES;
166 }
167 
168 
169 void
SPDYF_openssl_close_session(struct SPDY_Session * session)170 SPDYF_openssl_close_session(struct SPDY_Session *session)
171 {
172 	//SSL_shutdown sends TLS "close notify" as in TLS standard.
173 	//The function may fail as it waits for the other party to also close
174 	//the TLS session. The lib just sends it and will close the socket
175 	//after that because the browsers don't seem to care much about
176 	//"close notify"
177 	SSL_shutdown(session->io_context);
178 
179 	SSL_free(session->io_context);
180 }
181 
182 
183 int
SPDYF_openssl_recv(struct SPDY_Session * session,void * buffer,size_t size)184 SPDYF_openssl_recv(struct SPDY_Session *session,
185 				void * buffer,
186 				size_t size)
187 {
188 	int ret;
189 	int n = SSL_read(session->io_context,
190 					buffer,
191 					size);
192 	//if(n > 0) SPDYF_DEBUG("recvd: %i",n);
193 	if (n <= 0)
194 	{
195 		ret = SSL_get_error(session->io_context, n);
196 		switch(ret)
197 		{
198 			case SSL_ERROR_ZERO_RETURN:
199 				return 0;
200 
201 			case SSL_ERROR_WANT_READ:
202 			case SSL_ERROR_WANT_WRITE:
203 				return SPDY_IO_ERROR_AGAIN;
204 
205 			case SSL_ERROR_SYSCALL:
206 				if(EINTR == errno)
207 					return SPDY_IO_ERROR_AGAIN;
208 				return SPDY_IO_ERROR_ERROR;
209 			default:
210 				return SPDY_IO_ERROR_ERROR;
211 		}
212 	}
213 
214 	return n;
215 }
216 
217 
218 int
SPDYF_openssl_send(struct SPDY_Session * session,const void * buffer,size_t size)219 SPDYF_openssl_send(struct SPDY_Session *session,
220 				const void * buffer,
221 				size_t size)
222 {
223 	int ret;
224 
225 	int n = SSL_write(session->io_context,
226 					buffer,
227 					size);
228 	//if(n > 0) SPDYF_DEBUG("sent: %i",n);
229 	if (n <= 0)
230 	{
231 		ret = SSL_get_error(session->io_context, n);
232 		switch(ret)
233 		{
234 			case SSL_ERROR_ZERO_RETURN:
235 				return 0;
236 
237 			case SSL_ERROR_WANT_READ:
238 			case SSL_ERROR_WANT_WRITE:
239 				return SPDY_IO_ERROR_AGAIN;
240 
241 			case SSL_ERROR_SYSCALL:
242 				if(EINTR == errno)
243 					return SPDY_IO_ERROR_AGAIN;
244 				return SPDY_IO_ERROR_ERROR;
245 			default:
246 				return SPDY_IO_ERROR_ERROR;
247 		}
248 	}
249 
250 	return n;
251 }
252 
253 
254 int
SPDYF_openssl_is_pending(struct SPDY_Session * session)255 SPDYF_openssl_is_pending(struct SPDY_Session *session)
256 {
257 	/* From openssl docs:
258 	 * BUGS
259 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().
260 	 */
261 	return SSL_pending(session->io_context) > 0 ? SPDY_YES : SPDY_NO;
262 }
263 
264 
265 int
SPDYF_openssl_before_write(struct SPDY_Session * session)266 SPDYF_openssl_before_write(struct SPDY_Session *session)
267 {
268   (void)session;
269 
270   return SPDY_YES;
271 }
272 
273 
274 int
SPDYF_openssl_after_write(struct SPDY_Session * session,int was_written)275 SPDYF_openssl_after_write(struct SPDY_Session *session, int was_written)
276 {
277   (void)session;
278 
279   return was_written;
280 }
281