add-wss-ssl-openssl-support.patch
Signed-off-by: Andy Green <andy@warmcat.com>
diff --git a/Makefile b/Makefile
index 3aa2237..dda1115 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,5 @@
-export CFLAGS= -Wall -Werror -rdynamic -fPIC -c
+export CFLAGS= -Wall -Werror -rdynamic -fPIC -c -DLWS_OPENSSL_SUPPORT
+export LFLAGS= -lssl
all:
make -C lib
make -C test-server
@@ -14,4 +15,6 @@
make -C lib install
make -C test-server install
+gencert:
+ make -C test-server gencert
diff --git a/lib/Makefile b/lib/Makefile
index be381c3..a84373a 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -3,7 +3,7 @@
all:
gcc $(CFLAGS) libwebsockets.c
gcc $(CFLAGS) md5.c
- gcc libwebsockets.o md5.o --shared -o libwebsockets.so
+ gcc $(LFLAGS) libwebsockets.o md5.o --shared -o libwebsockets.so
clean:
rm -f *.o *.so
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index 065cb01..9c798e4 100644
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -21,6 +21,17 @@
#include <poll.h>
#include <sys/mman.h>
+#ifdef LWS_OPENSSL_SUPPORT
+#include <openssl/ssl.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+
+SSL_CTX *ssl_ctx;
+int use_ssl;
+#endif
+
+//#define DEBUG
+
#include "libwebsockets.h"
#ifdef DEBUG
@@ -41,6 +52,7 @@
#define LWS_ADDITIONAL_HDR_ALLOC 64
+
enum lws_connection_states {
WSI_STATE_HTTP,
WSI_STATE_HTTP_HEADERS,
@@ -108,6 +120,26 @@
enum lws_rx_parse_state lws_rx_parse_state;
size_t rx_packet_length;
+#ifdef LWS_OPENSSL_SUPPORT
+ char m_fOccupied;
+ struct sockaddr_in m_addr;
+ int m_addrlen;
+
+ SSL *ssl;
+
+ // these are valid if it is a POST
+
+ char m_fOngoingPost;
+ int m_nSessionID;
+
+ time_t m_timeStarted;
+ long long m_llTransferred;
+ long long m_llSizeIfKnown;
+
+ char m_szTitle[PATH_MAX];
+ char m_szStatus[PATH_MAX];
+#endif
+
/* last */
char user_space[0];
};
@@ -142,8 +174,19 @@
// fprintf(stderr, "closing fd=%d\n", wsi->sock);
- shutdown(wsi->sock, SHUT_RDWR);
- close(wsi->sock);
+#ifdef LWS_OPENSSL_SUPPORT
+ if (use_ssl) {
+ n = SSL_get_fd(wsi->ssl);
+ SSL_shutdown(wsi->ssl);
+ close(n);
+ SSL_free(wsi->ssl);
+ } else {
+#endif
+ shutdown(wsi->sock, SHUT_RDWR);
+ close(wsi->sock);
+#ifdef LWS_OPENSSL_SUPPORT
+ }
+#endif
free(wsi);
}
@@ -156,6 +199,13 @@
* which will be used by the user application to store
* per-session data. A pointer to this space is given
* when the user callback is called.
+ * @ssl_cert_filepath: If libwebsockets was compiled to use ssl, and you want
+ * to listen using SSL, set to the filepath to fetch the
+ * server cert from, otherwise NULL for unencrypted
+ * @ssl_private_key_filepath: filepath to private key if wanting SSL mode,
+ * else ignored
+ * @gid: group id to change to after setting listen socket, or -1.
+ * @uid: user id to change to after setting listen socket, or -1.
*
* This function forks to create the listening socket and takes care
* of all initialization in one step.
@@ -177,7 +227,10 @@
int (*callback)(struct libwebsocket *,
enum libwebsocket_callback_reasons,
void *, void *, size_t),
- int protocol, size_t user_area_size)
+ int protocol, size_t user_area_size,
+ const char * ssl_cert_filepath,
+ const char * ssl_private_key_filepath,
+ int gid, int uid)
{
int n;
int client;
@@ -188,9 +241,74 @@
struct libwebsocket *wsi[MAX_CLIENTS + 1];
struct pollfd fds[MAX_CLIENTS + 1];
int fds_count = 0;
- unsigned char buf[256];
+ unsigned char buf[1024];
int opt = 1;
+#ifdef LWS_OPENSSL_SUPPORT
+ const SSL_METHOD *method;
+ char ssl_err_buf[512];
+
+ use_ssl = ssl_cert_filepath != NULL && ssl_private_key_filepath != NULL;
+ if (use_ssl)
+ fprintf(stderr, " Compiled with SSL support, using it\n");
+ else
+ fprintf(stderr, " Compiled with SSL support, but not using it\n");
+
+#else
+ if (ssl_cert_filepath != NULL && ssl_private_key_filepath != NULL) {
+ fprintf(stderr, " Not compiled for OpenSSl support!\n");
+ return -1;
+ }
+ fprintf(stderr, " Compiled without SSL support, listening unencrypted\n");
+#endif
+
+#ifdef LWS_OPENSSL_SUPPORT
+ if (use_ssl) {
+ SSL_library_init();
+
+ OpenSSL_add_all_algorithms();
+ SSL_load_error_strings();
+
+ // Firefox insists on SSLv23 not SSLv3
+ // Konq disables SSLv2 by default now, SSLv23 works
+
+ method = SSLv23_server_method(); // create server instance
+ if (!method) {
+ fprintf(stderr, "problem creating ssl method: %s\n",
+ ERR_error_string(ERR_get_error(), ssl_err_buf));
+ return -1;
+ }
+ ssl_ctx = SSL_CTX_new(method); /* create context */
+ if (!ssl_ctx) {
+ printf("problem creating ssl context: %s\n",
+ ERR_error_string(ERR_get_error(), ssl_err_buf));
+ return -1;
+ }
+ /* set the local certificate from CertFile */
+ n = SSL_CTX_use_certificate_file(ssl_ctx,
+ ssl_cert_filepath, SSL_FILETYPE_PEM);
+ if (n != 1) {
+ fprintf(stderr, "problem getting cert '%s': %s\n",
+ ssl_cert_filepath,
+ ERR_error_string(ERR_get_error(), ssl_err_buf));
+ return -1;
+ }
+ /* set the private key from KeyFile */
+ if (SSL_CTX_use_PrivateKey_file(ssl_ctx, ssl_private_key_filepath,
+ SSL_FILETYPE_PEM) != 1) {
+ fprintf(stderr, "ssl problem getting key '%s': %s\n", ssl_private_key_filepath, ERR_error_string(ERR_get_error(), ssl_err_buf));
+ return (-1);
+ }
+ /* verify private key */
+ if (!SSL_CTX_check_private_key(ssl_ctx)) {
+ fprintf(stderr, "Private SSL key does not match cert\n");
+ return (-1);
+ }
+
+ /* SSL is happy and has a cert it's content with */
+ }
+#endif
+
/* sanity check */
switch (protocol) {
@@ -245,6 +363,15 @@
if (n)
return sockfd;
+ // drop any root privs for this thread
+
+ if (gid != -1)
+ if (setgid(gid))
+ fprintf(stderr, "setgid: %s\n", strerror(errno));
+ if (uid != -1)
+ if (setuid(uid))
+ fprintf(stderr, "setuid: %s\n", strerror(errno));
+
/* we are running in a forked subprocess now */
listen(sockfd, 5);
@@ -266,32 +393,64 @@
if (fds[0].revents & POLLIN) {
- /* listen socket got a new connection... */
-
+ /* listen socket got an unencrypted connection... */
+
clilen = sizeof(cli_addr);
- fd = accept(sockfd, (struct sockaddr *)&cli_addr,
- &clilen);
+ fd = accept(sockfd,
+ (struct sockaddr *)&cli_addr,
+ &clilen);
if (fd < 0) {
fprintf(stderr, "ERROR on accept");
continue;
}
-
+
if (fds_count >= MAX_CLIENTS) {
fprintf(stderr, "too busy");
close(fd);
continue;
}
-
-// fprintf(stderr, "accepted new conn port %u on fd=%d\n",
-// ntohs(cli_addr.sin_port), fd);
-
- /* intialize the instance struct */
-
+
wsi[fds_count] = malloc(sizeof(struct libwebsocket) +
user_area_size);
if (!wsi[fds_count])
return -1;
-
+
+
+#ifdef LWS_OPENSSL_SUPPORT
+ if (use_ssl) {
+
+ wsi[fds_count]->ssl = SSL_new(ssl_ctx); // get new SSL state with context
+ if (wsi[fds_count]->ssl == NULL) {
+ fprintf(stderr, "SSL_new failed: %s\n",
+ ERR_error_string(SSL_get_error(wsi[fds_count]->ssl, 0), NULL));
+ free(wsi[fds_count]);
+ continue;
+ }
+
+ SSL_set_fd(wsi[fds_count]->ssl, fd); // set SSL socket
+
+ n = SSL_accept(wsi[fds_count]->ssl);
+ if (n != 1) {
+ /* browsers seem to probe with various ssl params which fail then retry */
+ debug("SSL_accept failed for socket %u: %s\n",
+ fd,
+ ERR_error_string(SSL_get_error(wsi[fds_count]->ssl, n),
+ NULL));
+ SSL_free(wsi[fds_count]->ssl);
+ free(wsi[fds_count]);
+ continue;
+ }
+ debug("accepted new SSL conn port %u on fd=%d SSL ver %s\n",
+ ntohs(cli_addr.sin_port), fd, SSL_get_version(wsi[fds_count]->ssl));
+
+ } else {
+// fprintf(stderr, "accepted new conn port %u on fd=%d\n",
+// ntohs(cli_addr.sin_port), fd);
+ }
+#endif
+
+ /* intialize the instance struct */
+
wsi[fds_count]->sock = fd;
wsi[fds_count]->state = WSI_STATE_HTTP;
wsi[fds_count]->name_buffer_pos = 0;
@@ -328,8 +487,16 @@
continue;
// fprintf(stderr, "POLLIN\n");
-
- n = recv(fds[client].fd, buf, sizeof(buf), 0);
+
+#ifdef LWS_OPENSSL_SUPPORT
+ if (use_ssl)
+ n = SSL_read(wsi[client]->ssl, buf, sizeof buf);
+ else
+#endif
+ n = recv(fds[client].fd, buf, sizeof(buf), 0);
+
+// fprintf(stderr, "read returned %d\n", n);
+
if (n < 0) {
fprintf(stderr, "Socket read returned %d\n", n);
continue;
@@ -372,10 +539,14 @@
}
fatal:
+ /* listening socket */
close(fds[0].fd);
for (client = 1; client < fds_count; client++)
libwebsocket_close_and_free_session(wsi[client]);
+#ifdef LWS_OPENSSL_SUPPORT
+ SSL_CTX_free(ssl_ctx);
+#endif
kill(0, SIGTERM);
return 0;
@@ -598,7 +769,7 @@
"a v76 close, sending ack\n");
buf[0] = 0xff;
buf[1] = 0;
- n = write(wsi->sock, buf, 2);
+ n = libwebsocket_write(wsi, buf, 2, LWS_WRITE_HTTP);
if (n < 0) {
fprintf(stderr, "ERROR writing to socket");
return -1;
@@ -724,8 +895,17 @@
"Sec-WebSocket-Origin: ");
strcpy(p, wsi->utf8_token[WSI_TOKEN_ORIGIN].token);
p += wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len;
- strcpy(p, "\x0d\x0aSec-WebSocket-Location: ws://");
- p += strlen("\x0d\x0aSec-WebSocket-Location: ws://");
+#ifdef LWS_OPENSSL_SUPPORT
+ if (use_ssl) {
+ strcpy(p, "\x0d\x0aSec-WebSocket-Location: wss://");
+ p += strlen("\x0d\x0aSec-WebSocket-Location: wss://");
+ } else {
+#endif
+ strcpy(p, "\x0d\x0aSec-WebSocket-Location: ws://");
+ p += strlen("\x0d\x0aSec-WebSocket-Location: ws://");
+#ifdef LWS_OPENSSL_SUPPORT
+ }
+#endif
strcpy(p, wsi->utf8_token[WSI_TOKEN_HOST].token);
p += wsi->utf8_token[WSI_TOKEN_HOST].token_len;
strcpy(p, wsi->utf8_token[WSI_TOKEN_GET_URI].token);
@@ -778,7 +958,8 @@
#ifdef DEBUG
fwrite(response, 1, p - response, stderr);
#endif
- n = write(wsi->sock, response, p - response);
+ n = libwebsocket_write(wsi, (unsigned char *)response, p - response,
+ LWS_WRITE_HTTP);
if (n < 0) {
fprintf(stderr, "ERROR writing to socket");
goto bail;
@@ -955,13 +1136,23 @@
#endif
send_raw:
-
- n = send(wsi->sock, buf - pre, len + pre + post, 0);
- if (n < 0) {
- fprintf(stderr, "ERROR writing to socket");
- return -1;
+#ifdef LWS_OPENSSL_SUPPORT
+ if (use_ssl) {
+ n = SSL_write(wsi->ssl, buf - pre, len + pre + post);
+ if (n < 0) {
+ fprintf(stderr, "ERROR writing to socket");
+ return -1;
+ }
+ } else {
+#endif
+ n = send(wsi->sock, buf - pre, len + pre + post, 0);
+ if (n < 0) {
+ fprintf(stderr, "ERROR writing to socket");
+ return -1;
+ }
+#ifdef LWS_OPENSSL_SUPPORT
}
-
+#endif
// fprintf(stderr, "written %d bytes to client\n", (int)len);
return 0;
diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index df351ac..a4a0111 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -22,7 +22,10 @@
int (*callback)(struct libwebsocket *wsi,
enum libwebsocket_callback_reasons reason,
void *user, void *in, size_t len),
- int protocol, size_t user_space);
+ int protocol, size_t user_space,
+ const char * ssl_cert_filepath,
+ const char * ssl_private_key_filepath,
+ int gid, int uid);
/*
* IMPORTANT NOTICE!
diff --git a/libwebsockets-api-doc.html b/libwebsockets-api-doc.html
index a75ddb2..0c2d8eb 100644
--- a/libwebsockets-api-doc.html
+++ b/libwebsockets-api-doc.html
@@ -4,7 +4,11 @@
(<i>int</i> <b>port</b>,
<i>int (*</i><b>callback</b>) <i>(struct libwebsocket *, enum libwebsocket_callback_reasons, void *, void *, size_t)</i>,
<i>int</i> <b>protocol</b>,
-<i>size_t</i> <b>user_area_size</b>)
+<i>size_t</i> <b>user_area_size</b>,
+<i>const char *</i> <b>ssl_cert_filepath</b>,
+<i>const char *</i> <b>ssl_private_key_filepath</b>,
+<i>int</i> <b>gid</b>,
+<i>int</i> <b>uid</b>)
<h3>Arguments</h3>
<dl>
<dt><b>port</b>
@@ -18,6 +22,17 @@
which will be used by the user application to store
per-session data. A pointer to this space is given
when the user callback is called.
+<dt><b>ssl_cert_filepath</b>
+<dd>If libwebsockets was compiled to use ssl, and you want
+to listen using SSL, set to the filepath to fetch the
+server cert from, otherwise NULL for unencrypted
+<dt><b>ssl_private_key_filepath</b>
+<dd>filepath to private key if wanting SSL mode,
+else ignored
+<dt><b>gid</b>
+<dd>group id to change to after setting listen socket, or -1.
+<dt><b>uid</b>
+<dd>user id to change to after setting listen socket, or -1.
</dl>
<h3>Description</h3>
<blockquote>
diff --git a/test-server/Makefile b/test-server/Makefile
index 18dc56d..493f7b2 100644
--- a/test-server/Makefile
+++ b/test-server/Makefile
@@ -6,11 +6,20 @@
-o libwebsockets-test-server
clean:
- rm -f *.o libwebsockets-test-server
+ rm -f *.o libwebsockets-test-server *.pem
-install:
+install: ./libwebsockets-test-server.pem
cp -f libwebsockets-test-server $(DESTDIR)/usr/bin
mkdir -p $(DESTDIR)/usr/share/libwebsockets-test-server
- cp -f favicon.ico test.html $(DESTDIR)/usr/share/libwebsockets-test-server
+ cp -a favicon.ico test.html libwebsockets-test-server.key.pem libwebsockets-test-server.pem $(DESTDIR)/usr/share/libwebsockets-test-server
+# notice! You would want the key chmod 600 not 644 for real install!
+# this is just for test so you can run it without root easily
+
+./libwebsockets-test-server.pem:
+ printf "GB\nErewhon\nAll around\nlibwebsockets-test\n\nlocalhost\nnone@invalid.org\n" | \
+ openssl req -new -newkey rsa:1024 -days 10000 -nodes -x509 -keyout \
+ ./libwebsockets-test-server.key.pem -out ./libwebsockets-test-server.pem >/dev/null 2>&1 && \
+ chmod 644 ./libwebsockets-test-server.key.pem \
+ ./libwebsockets-test-server.pem
diff --git a/test-server/test-server.c b/test-server/test-server.c
index ae8deed..dfef268 100644
--- a/test-server/test-server.c
+++ b/test-server/test-server.c
@@ -17,6 +17,7 @@
#define LOCAL_RESOURCE_PATH "/usr/share/libwebsockets-test-server"
static int port = 7681;
static int ws_protocol = 76;
+static int use_ssl = 0;
struct per_session_data {
int number;
@@ -90,7 +91,7 @@
*/
case LWS_CALLBACK_SEND:
n = sprintf(p, "%d", pss->number++);
- n = libwebsocket_write(wsi, (unsigned char *)p, n, 0);
+ n = libwebsocket_write(wsi, (unsigned char *)p, n, LWS_WRITE_TEXT);
if (n < 0) {
fprintf(stderr, "ERROR writing to socket");
exit(1);
@@ -116,6 +117,9 @@
case LWS_CALLBACK_HTTP:
uri = libwebsocket_get_uri(wsi);
+
+ fprintf(stderr, "serving HTTP URI %s\n", uri);
+
if (uri && strcmp(uri, "/favicon.ico") == 0) {
if (libwebsockets_serve_http_file(wsi,
LOCAL_RESOURCE_PATH"/favicon.ico", "image/x-icon"))
@@ -139,12 +143,15 @@
{ "help", no_argument, NULL, 'h' },
{ "port", required_argument, NULL, 'p' },
{ "protocol", required_argument, NULL, 'r' },
+ { "ssl", no_argument, NULL, 's' },
{ NULL, 0, 0, 0 }
};
int main(int argc, char **argv)
{
int n = 0;
+ const char * cert_path = LOCAL_RESOURCE_PATH"/libwebsockets-test-server.pem";
+ const char * key_path = LOCAL_RESOURCE_PATH"/libwebsockets-test-server.key.pem";
fprintf(stderr, "libwebsockets test server\n"
"Copyright 2010 Andy Green <andy@warmcat.com> "
@@ -155,6 +162,9 @@
if (n < 0)
continue;
switch (n) {
+ case 's':
+ use_ssl = 1;
+ break;
case 'p':
port = atoi(optarg);
break;
@@ -167,9 +177,13 @@
exit(1);
}
}
+
+ if (!use_ssl)
+ cert_path = key_path = NULL;
if (libwebsocket_create_server(port, websocket_callback, ws_protocol,
- sizeof(struct per_session_data)) < 0) {
+ sizeof(struct per_session_data),
+ cert_path, key_path, -1, -1) < 0) {
fprintf(stderr, "libwebsocket init failed\n");
return -1;
}
diff --git a/test-server/test.html b/test-server/test.html
index 905a0c5..57e335e 100644
--- a/test-server/test.html
+++ b/test-server/test.html
@@ -8,8 +8,19 @@
<body>
<script>
var pos = 0;
+ var websocket_ads;
- var socket = new WebSocket("ws://127.0.0.1:7681");
+ /*
+ * We open the websocket encrypted if this page came on an
+ * https:// url itself, otherwise unencrypted
+ */
+
+ if (document.URL.substring(0, 5) == "https")
+ websocket_ads = "wss://127.0.0.1:7681";
+ else
+ websocket_ads = "ws://127.0.0.1:7681"
+
+ var socket = new WebSocket(websocket_ads);
try {
// alert('<p class="event">Socket Status: '+socket.readyState);