From 5b34c975aee5a15e382bdfbd3d5110e4101a0879 Mon Sep 17 00:00:00 2001 From: James Devine Date: Sat, 14 Dec 2013 11:41:29 +0800 Subject: [PATCH] Optionally allow non-SSL connections on same port as SSL If enabled one listening socket will accept both SSL and plain HTTP connections. Do not enable if you regard SSL handshake as some kind of security, eg, use client-side certs to restrict access. AG: changed flag names, added extra comments, changelog, add -a in test server Signed-off-by: James Devine Signed-off-by: Andy Green --- changelog | 6 ++++++ lib/libwebsockets.c | 6 ++++++ lib/libwebsockets.h | 1 + lib/private-libwebsockets.h | 1 + lib/server.c | 39 +++++++++++++++++++++++++++++++++++---- test-server/test-server.c | 6 +++++- 6 files changed, 54 insertions(+), 5 deletions(-) diff --git a/changelog b/changelog index 99758be..602a9bb 100644 --- a/changelog +++ b/changelog @@ -20,6 +20,12 @@ processing is protected by a 5s timeout. The chunks are stored in a malloc'd buffer of size protocols[0].rx_buffer_size. +New server option you can enable from user code +LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT allows non-SSL connections to +also be accepted on an SSL listening port. It's disabled unless you enable +it explicitly. + + User api changes ---------------- diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index 7342e9b..8b2fec5 100644 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -1892,6 +1892,7 @@ libwebsocket_create_context(struct lws_context_creation_info *info) #ifdef LWS_OPENSSL_SUPPORT context->use_ssl = 0; + context->allow_non_ssl_on_ssl_port = 0; context->ssl_ctx = NULL; context->ssl_client_ctx = NULL; openssl_websocket_private_data_index = 0; @@ -2135,6 +2136,11 @@ libwebsocket_create_context(struct lws_context_creation_info *info) context->ssl_ctx, NULL, 0); } + if(info->options & LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT) { + /* Normally SSL listener rejects non-ssl, optionally allow */ + context->allow_non_ssl_on_ssl_port = 1; + } + if (context->use_ssl) { /* openssl init for server sockets */ diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index cfd1f8c..b2ba359 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -138,6 +138,7 @@ LWS_VISIBLE LWS_EXTERN void lwsl_hexdump(void *buf, size_t len); enum libwebsocket_context_options { LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT = 2, LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME = 4, + LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT = 8 }; enum libwebsocket_callback_reasons { diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index 12b8d5a..ea68f4b 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -289,6 +289,7 @@ struct libwebsocket_context { #ifdef LWS_OPENSSL_SUPPORT int use_ssl; + int allow_non_ssl_on_ssl_port; SSL_CTX *ssl_ctx; SSL_CTX *ssl_client_ctx; #endif diff --git a/lib/server.c b/lib/server.c index 6c35c98..700a6e2 100644 --- a/lib/server.c +++ b/lib/server.c @@ -354,6 +354,37 @@ int lws_server_socket_service(struct libwebsocket_context *context, wsi->user_space, (void *)(long)wsi->sock, POLLOUT); lws_latency_pre(context, wsi); + + n = recv(wsi->sock, context->service_buffer, + sizeof(context->service_buffer), MSG_PEEK); + + /* + * optionally allow non-SSL connect on SSL listening socket + * This is disabled by default, if enabled it goes around any + * SSL-level access control (eg, client-side certs) so leave + * it disabled unless you know it's not a problem for you + */ + + if (context->allow_non_ssl_on_ssl_port && n >= 1 && + context->service_buffer[0] >= ' ') { + /* + * TLS content-type for Handshake is 0x16 + * TLS content-type for ChangeCipherSpec Record is 0x14 + * + * A non-ssl session will start with the HTTP method in + * ASCII. If we see it's not a legit SSL handshake + * kill the SSL for this connection and try to handle + * as a HTTP connection upgrade directly. + */ + wsi->use_ssl = 0; + SSL_shutdown(wsi->ssl); + SSL_free(wsi->ssl); + wsi->ssl = NULL; + goto accepted; + } + + /* normal SSL connection processing path */ + n = SSL_accept(wsi->ssl); lws_latency(context, wsi, "SSL_accept LWS_CONNMODE_SSL_ACK_PENDING\n", n, n == 1); @@ -378,7 +409,6 @@ int lws_server_socket_service(struct libwebsocket_context *context, if (m == SSL_ERROR_WANT_WRITE) { context->fds[ wsi->position_in_fds_table].events |= POLLOUT; - /* external POLL support via protocol 0 */ context->protocols[0].callback(context, wsi, LWS_CALLBACK_SET_MODE_POLL_FD, @@ -387,13 +417,14 @@ int lws_server_socket_service(struct libwebsocket_context *context, break; } lwsl_debug("SSL_accept failed skt %u: %s\n", - pollfd->fd, - ERR_error_string(m, NULL)); + pollfd->fd, + ERR_error_string(m, NULL)); libwebsocket_close_and_free_session(context, wsi, - LWS_CLOSE_STATUS_NOSTATUS); + LWS_CLOSE_STATUS_NOSTATUS); break; } +accepted: /* OK, we are accepted */ libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); diff --git a/test-server/test-server.c b/test-server/test-server.c index 0a32ba0..f82cd0c 100644 --- a/test-server/test-server.c +++ b/test-server/test-server.c @@ -715,6 +715,7 @@ static struct option options[] = { { "debug", required_argument, NULL, 'd' }, { "port", required_argument, NULL, 'p' }, { "ssl", no_argument, NULL, 's' }, + { "allow-non-ssl", no_argument, NULL, 'a' }, { "interface", required_argument, NULL, 'i' }, { "closetest", no_argument, NULL, 'c' }, #ifndef LWS_NO_DAEMONIZE @@ -749,7 +750,7 @@ int main(int argc, char **argv) info.port = 7681; while (n >= 0) { - n = getopt_long(argc, argv, "ci:hsp:d:Dr:", options, NULL); + n = getopt_long(argc, argv, "ci:hsap:d:Dr:", options, NULL); if (n < 0) continue; switch (n) { @@ -767,6 +768,9 @@ int main(int argc, char **argv) case 's': use_ssl = 1; break; + case 'a': + opts |= LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT; + break; case 'p': info.port = atoi(optarg); break; -- 2.7.4