add-wss-ssl-openssl-support.patch

Signed-off-by: Andy Green <andy@warmcat.com>
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;