From 0970e080d98acba655fc7beb5857742e6b70d7fc Mon Sep 17 00:00:00 2001
From: Sergey Lyubka <valenok@gmail.com>
Date: Sat, 21 Dec 2013 15:50:11 +0000
Subject: [PATCH] Added OpenSSL support

---
 mongoose.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 71 insertions(+), 8 deletions(-)

diff --git a/mongoose.c b/mongoose.c
index 92a82005e..b57df570f 100644
--- a/mongoose.c
+++ b/mongoose.c
@@ -109,6 +109,12 @@ typedef struct stat file_stat_t;
 #define O_BINARY 0
 #endif
 
+#ifdef USE_SSL
+// Following define gets rid of openssl deprecation messages
+#define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_6
+#include <openssl/ssl.h>
+#endif
+
 #include "mongoose.h"
 
 struct linked_list_link { struct linked_list_link *prev, *next; };
@@ -182,8 +188,8 @@ enum {
   ACCESS_CONTROL_LIST, ACCESS_LOG_FILE, AUTH_DOMAIN, CGI_INTERPRETER,
   CGI_PATTERN, DOCUMENT_ROOT, ENABLE_DIRECTORY_LISTING, ERROR_LOG_FILE,
   EXTRA_MIME_TYPES, GLOBAL_AUTH_FILE, HIDE_FILES_PATTERN, IDLE_TIMEOUT_MS,
-  INDEX_FILES, LISTENING_PORT, DAV_AUTH_FILE, RUN_AS_USER,
-  SSL_CERTIFICATE, URL_REWRITES, NUM_OPTIONS
+  INDEX_FILES, LISTENING_PORT, DAV_AUTH_FILE,
+  RUN_AS_USER, SSL_CERTIFICATE, URL_REWRITES, NUM_OPTIONS
 };
 
 struct mg_server {
@@ -193,7 +199,8 @@ struct mg_server {
   struct linked_list_link uri_handlers;
   char *config_options[NUM_OPTIONS];
   void *server_data;
-  sock_t ctl[2];  // Control socketpair. Used to wake up from select() call
+  void *ssl_ctx;    // SSL context
+  sock_t ctl[2];    // Control socketpair. Used to wake up from select() call
 };
 
 // Expandable IO buffer
@@ -212,7 +219,9 @@ union endpoint {
 };
 
 enum endpoint_type { EP_NONE, EP_FILE, EP_CGI, EP_USER, EP_PUT };
-enum connection_flags { CONN_CLOSE = 1, CONN_SPOOL_DONE = 2, CONN_SSL = 4 };
+enum connection_flags {
+  CONN_CLOSE = 1, CONN_SPOOL_DONE = 2, CONN_SSL_HANDS_SHAKEN = 4
+};
 
 struct connection {
   struct mg_connection mg_conn;   // XXX: Must be first
@@ -232,6 +241,7 @@ struct connection {
   int request_len;  // Request length, including last \r\n after last header
   int flags;        // CONN_* flags: CONN_CLOSE, CONN_SPOOL_DONE, etc
   mutex_t mutex;    // Guards concurrent mg_write() calls
+  void *ssl;        // SSL descriptor
 };
 
 static void close_local_endpoint(struct connection *conn);
@@ -726,7 +736,7 @@ static void prepare_cgi_environment(struct connection *conn,
 
   addenv(blk, "SCRIPT_FILENAME=%s", prog);
   addenv(blk, "PATH_TRANSLATED=%s", prog);
-  addenv(blk, "HTTPS=%s", conn->flags & CONN_SSL ? "on" : "off");
+  addenv(blk, "HTTPS=%s", conn->ssl != NULL ? "on" : "off");
 
   if ((s = mg_get_header(ri, "Content-Type")) != NULL)
     addenv(blk, "CONTENT_TYPE=%s", s);
@@ -892,6 +902,15 @@ static struct connection *accept_new_connection(struct mg_server *server) {
     closesocket(sock);
   } else if ((conn = (struct connection *) calloc(1, sizeof(*conn))) == NULL) {
     closesocket(sock);
+#ifdef USE_SSL
+  } else if (server->ssl_ctx != NULL &&
+             ((conn->ssl = SSL_new(server->ssl_ctx)) == NULL ||
+              SSL_set_fd(conn->ssl, sock) != 1)) {
+    DBG(("SSL error"));
+    closesocket(sock);
+    free(conn);
+    conn = NULL;
+#endif
   } else {
     set_close_on_exec(sock);
     set_non_blocking_mode(sock);
@@ -913,6 +932,9 @@ static void close_conn(struct connection *conn) {
   free(conn->path_info);
   free(conn->remote_iobuf.buf);
   free(conn->local_iobuf.buf);
+#ifdef USE_SSL
+  if (conn->ssl != NULL) SSL_free(conn->ssl);
+#endif
   mutex_destroy(&conn->mutex);
   free(conn);
 }
@@ -1494,7 +1516,12 @@ static int is_error(int n) {
 
 static void write_to_client(struct connection *conn) {
   struct iobuf *io = &conn->remote_iobuf;
-  int n = send(conn->client_sock, io->buf, io->len, 0);
+  int n = conn->ssl == NULL ? send(conn->client_sock, io->buf, io->len, 0) :
+#ifdef USE_SSL
+    SSL_write(conn->ssl, io->buf, io->len);
+#else
+  0;
+#endif
 
   DBG(("Written %d of %d(%d): [%.*s ...]", n, io->len, io->size, 40, io->buf));
 
@@ -2914,7 +2941,21 @@ static void process_request(struct connection *conn) {
 
 static void read_from_client(struct connection *conn) {
   char buf[IOBUF_SIZE];
-  int n = recv(conn->client_sock, buf, sizeof(buf), 0);
+  int n = 0;
+  if (conn->ssl != NULL) {
+#ifdef USE_SSL
+    if (conn->flags & CONN_SSL_HANDS_SHAKEN) {
+      n = SSL_read(conn->ssl, buf, sizeof(buf));
+    } else {
+      if (SSL_accept(conn->ssl) == 1) {
+        conn->flags |= CONN_SSL_HANDS_SHAKEN;
+      }
+      return;
+    }
+#endif
+  } else {
+    n = recv(conn->client_sock, buf, sizeof(buf), 0);
+  }
 
   if (is_error(n)) {
     conn->flags |= CONN_CLOSE;
@@ -3167,6 +3208,9 @@ void mg_destroy_server(struct mg_server **server) {
     LINKED_LIST_FOREACH(&(*server)->uri_handlers, lp, tmp) {
       free(LINKED_LIST_ENTRY(lp, struct uri_handler, link));
     }
+#ifdef USE_SSL
+    if ((*server)->ssl_ctx != NULL) SSL_CTX_free((*server)->ssl_ctx);
+#endif
     free(*server);
     *server = NULL;
   }
@@ -3356,6 +3400,19 @@ const char *mg_set_option(struct mg_server *server, const char *name,
       } else if (setuid(pw->pw_uid) != 0) {
         error_msg = "setuid() failed";
       }
+#endif
+    } else if (ind == SSL_CERTIFICATE) {
+#ifdef USE_SSL
+      SSL_library_init();
+      //SSL_load_error_strings();
+      if ((server->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
+        error_msg = "SSL_CTX_new() failed";
+      } else if (SSL_CTX_use_certificate_file(server->ssl_ctx, value, 1) == 0 ||
+                 SSL_CTX_use_PrivateKey_file(server->ssl_ctx, value, 1) == 0) {
+        error_msg = "Cannot load PEM file";
+      } else {
+        SSL_CTX_use_certificate_chain_file(server->ssl_ctx, value);
+      }
 #endif
     }
   }
@@ -3383,7 +3440,13 @@ struct mg_server *mg_create_server(void *server_data) {
 
   LINKED_LIST_INIT(&server->active_connections);
   LINKED_LIST_INIT(&server->uri_handlers);
-  do { mg_socketpair(server->ctl); } while (server->ctl[0] < 0);
+
+  // Create control socket pair. Do it in a loop to protect from
+  // interrupted syscalls in mg_socketpair().
+  do {
+    mg_socketpair(server->ctl);
+  } while (server->ctl[0] < 0);
+
   server->server_data = server_data;
   server->listening_sock = INVALID_SOCKET;
   set_default_option_values(server->config_options);
-- 
GitLab