aboutsummaryrefslogtreecommitdiff
path: root/src/microspdy/io_openssl.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/microspdy/io_openssl.c')
-rw-r--r--src/microspdy/io_openssl.c280
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;
+}