diff options
Diffstat (limited to 'src/microspdy/io_openssl.c')
-rw-r--r-- | src/microspdy/io_openssl.c | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/src/microspdy/io_openssl.c b/src/microspdy/io_openssl.c new file mode 100644 index 00000000..f71a9230 --- /dev/null +++ b/src/microspdy/io_openssl.c @@ -0,0 +1,280 @@ +/* + This file is part of libmicrospdy + Copyright 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); + if (1 != SSL_CTX_set_cipher_list(daemon->io_context, "HIGH")) + { + 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; + return SPDY_IO_ERROR_ERROR; + 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; + return SPDY_IO_ERROR_ERROR; + 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; +} |