/* 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 . */ /** * @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; }