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 */ 41static int 42spdyf_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 55void 56SPDYF_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 67void 68SPDYF_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 77int 78SPDYF_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 126void 127SPDYF_openssl_deinit(struct SPDY_Daemon *daemon) 128{ 129 SSL_CTX_free(daemon->io_context); 130} 131 132 133int 134SPDYF_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 169void 170SPDYF_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 183int 184SPDYF_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 218int 219SPDYF_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 254int 255SPDYF_openssl_is_pending(struct SPDY_Session *session) 256{ 257 /* From openssl docs: 258 * BUGS 259SSL_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 265int 266SPDYF_openssl_before_write(struct SPDY_Session *session) 267{ 268 (void)session; 269 270 return SPDY_YES; 271} 272 273 274int 275SPDYF_openssl_after_write(struct SPDY_Session *session, int was_written) 276{ 277 (void)session; 278 279 return was_written; 280} 281