Home | History | Annotate | Download | only in microspdy
      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
     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_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
     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
     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
    127 SPDYF_openssl_deinit(struct SPDY_Daemon *daemon)
    128 {
    129     SSL_CTX_free(daemon->io_context);
    130 }
    131 
    132 
    133 int
    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
    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
    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
    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
    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
    266 SPDYF_openssl_before_write(struct SPDY_Session *session)
    267 {
    268   (void)session;
    269 
    270   return SPDY_YES;
    271 }
    272 
    273 
    274 int
    275 SPDYF_openssl_after_write(struct SPDY_Session *session, int was_written)
    276 {
    277   (void)session;
    278 
    279   return was_written;
    280 }
    281