Browse Source

Merge pull request #217 from MGralka/ssl_client

Use SSL client certificates
bel2125 9 years ago
parent
commit
b9b940577a
4 changed files with 92 additions and 5 deletions
  1. 5 1
      .gitignore
  2. 24 1
      docs/UserManual.md
  3. 60 2
      src/civetweb.c
  4. 3 1
      src/main.c

+ 5 - 1
.gitignore

@@ -67,6 +67,7 @@ local.properties
 # Build results
 
 [Dd]ebug/
+[Dd]ebug CONSOLE/
 [Rr]elease/
 x64/
 [Bb]in/
@@ -244,7 +245,8 @@ requests.db
 ##########################
 ## Files created by ctags
 ##########################
-tags
+?tags
+?tags?
 
 ##########################
 ## Files created by autotools
@@ -256,3 +258,5 @@ tags
 ## Travis Build Dir
 ##########################
 ci/lua
+
+

+ 24 - 1
docs/UserManual.md

@@ -386,6 +386,30 @@ See the [Wikipedia page on HTTP status codes](http://en.wikipedia.org/wiki/HTTP_
 URL encoded request strings are decoded in the server, unless it is disabled
 by setting this option to `no`.
 
+### ssl_verify_peer `no`
+Enable client's certificate verification by the server.
+
+### ssl_ca_path
+Name of a directory containing trusted CA certificates. Each file in the
+directory must contain only a single CA certificate. The files must be named
+by the subject name’s hash and an extension of “.0”. If there is more than one
+certificate with the same subject name they should have extensions ".0", ".1",
+".2" and so on respectively.
+
+### ssl_ca_file
+Path to a .pem file containing trusted certificates. The file may contain
+more than one certificate.
+
+### ssl_verify_depth `9`
+Sets maximum depth of certificate chain. If client's certificate chain is longer
+than the depth set here connection is refused.
+
+### ssl_default_verify_paths `yes`
+Loads default trusted certificates locations set at openssl compile time.
+
+### ssl_forward_secrecy `yes`
+Enable [forward secrecy](https://en.wikipedia.org/wiki/Forward_secrecy).
+
 # Lua Scripts and Lua Server Pages
 Pre-built Windows and Mac civetweb binaries have built-in Lua scripting
 support as well as support for Lua Server Pages.
@@ -549,4 +573,3 @@ An example is shown in
 - Embedding with OpenSSL on Windows might fail because of calling convention.
   To force Civetweb to use `__stdcall` convention, add `/Gz` compilation
   flag in Visual Studio compiler.
-

+ 60 - 2
src/civetweb.c

@@ -811,6 +811,7 @@ typedef int socklen_t;
 typedef struct ssl_st SSL;
 typedef struct ssl_method_st SSL_METHOD;
 typedef struct ssl_ctx_st SSL_CTX;
+typedef struct x5099_store_ctx_st X509_STORE_CTX;
 
 struct ssl_func {
 	const char *name;  /* SSL function name */
@@ -842,6 +843,11 @@ struct ssl_func {
 #define SSL_pending (*(int (*)(SSL *))ssl_sw[18].ptr)
 #define SSL_CTX_set_verify (*(void (*)(SSL_CTX *, int, int))ssl_sw[19].ptr)
 #define SSL_shutdown (*(int (*)(SSL *))ssl_sw[20].ptr)
+#define SSL_CTX_load_verify_locations 										   \
+(*(int (*)(SSL_CTX *, const char *, const char *))ssl_sw[21].ptr)
+#define SSL_CTX_set_default_verify_paths									   \
+(*(int (*)(SSL_CTX *))ssl_sw[22].ptr)
+#define SSL_CTX_set_verify_depth (*(void (*)(SSL_CTX *, int))ssl_sw[23].ptr)
 
 #define CRYPTO_num_locks (*(int (*)(void))crypto_sw[0].ptr)
 #define CRYPTO_set_locking_callback                                            \
@@ -876,6 +882,9 @@ static struct ssl_func ssl_sw[] = {{"SSL_free", NULL},
                                    {"SSL_pending", NULL},
                                    {"SSL_CTX_set_verify", NULL},
                                    {"SSL_shutdown", NULL},
+                                   {"SSL_CTX_load_verify_locations", NULL},
+                                   {"SSL_CTX_set_default_verify_paths", NULL},
+                                   {"SSL_CTX_set_verify_depth", NULL},
                                    {NULL, NULL}};
 
 /* Similar array as ssl_sw. These functions could be located in different
@@ -971,6 +980,12 @@ enum {
 	REWRITE,
 	HIDE_FILES,
 	REQUEST_TIMEOUT,
+	SSL_VERIFY_PEER,
+	SSL_CA_PATH,
+	SSL_CA_FILE,
+	SSL_VERIFY_DEPTH,
+	SSL_DEFAULT_VERIFY_PATHS,
+	SSL_FORWARD_SECRECY,
 #if defined(USE_WEBSOCKET)
 	WEBSOCKET_TIMEOUT,
 #endif
@@ -1030,6 +1045,12 @@ static struct mg_option config_options[] = {
     {"url_rewrite_patterns", CONFIG_TYPE_STRING, NULL},
     {"hide_files_patterns", CONFIG_TYPE_EXT_PATTERN, NULL},
     {"request_timeout_ms", CONFIG_TYPE_NUMBER, "30000"},
+	{"ssl_verify_peer", CONFIG_TYPE_BOOLEAN, "no"},
+	{"ssl_ca_path", CONFIG_TYPE_DIRECTORY, NULL},
+	{"ssl_ca_file", CONFIG_TYPE_FILE, NULL},
+	{"ssl_verify_depth", CONFIG_TYPE_NUMBER, "9"},
+	{"ssl_default_verify_paths", CONFIG_TYPE_BOOLEAN, "yes"},
+	{"ssl_forward_secrecy", CONFIG_TYPE_BOOLEAN, "yes"},
 #if defined(USE_WEBSOCKET)
     {"websocket_timeout_ms", CONFIG_TYPE_NUMBER, "30000"},
 #endif
@@ -9872,7 +9893,8 @@ sslize(struct mg_connection *conn, SSL_CTX *s, int (*func)(SSL *))
 		return 0;
 	}
 
-	return (conn->ssl = SSL_new(s)) != NULL
+	conn->ssl = SSL_new(s);
+	return (conn->ssl != NULL)
 	       && SSL_set_fd(conn->ssl, conn->client.sock) == 1
 	       && func(conn->ssl) == 1;
 }
@@ -10004,6 +10026,11 @@ set_ssl_option(struct mg_context *ctx)
 {
 	const char *pem;
 	int callback_ret;
+	int should_verify_peer;
+	const char* ca_path;
+	const char* ca_file;
+	int use_default_verify_paths;
+	int verify_depth;
 
 	/* If PEM file is not specified and the init_ssl callback
 	 * is not specified, skip SSL initialization. */
@@ -10068,6 +10095,36 @@ set_ssl_option(struct mg_context *ctx)
 		(void)SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, pem);
 	}
 
+	should_verify_peer = (ctx->config[SSL_VERIFY_PEER] != NULL)
+		&& (mg_strcasecmp(ctx->config[SSL_VERIFY_PEER], "yes") == 0);
+
+	use_default_verify_paths = (ctx->config[SSL_DEFAULT_VERIFY_PATHS] != NULL)
+			&& (mg_strcasecmp(ctx->config[SSL_DEFAULT_VERIFY_PATHS], "yes") == 0);
+
+	if (should_verify_peer) {
+		ca_path = ctx->config[SSL_CA_PATH];
+		ca_file = ctx->config[SSL_CA_FILE];
+		if (SSL_CTX_load_verify_locations(ctx->ssl_ctx, ca_file, ca_path) != 1) {
+			mg_cry(fc(ctx), "SSL_CTX_load_verify_locations error: %s "
+		    "ssl_verify_peer requires setting "
+			"either ssl_ca_path or ssl_ca_file. Is any of them present in "
+			"the .conf file?", ssl_error());
+			return 0;
+		}
+		SSL_CTX_set_verify(ctx->ssl_ctx, 3, 0);
+
+		if (use_default_verify_paths
+		&& SSL_CTX_set_default_verify_paths(ctx->ssl_ctx) != 1) {
+			mg_cry(fc(ctx), "SSL_CTX_set_default_verify_paths error: %s", ssl_error());
+			return 0;
+		}
+
+		if (ctx->config[SSL_VERIFY_DEPTH]){
+			verify_depth = atoi(ctx->config[SSL_VERIFY_DEPTH]);
+			SSL_CTX_set_verify_depth(ctx->ssl_ctx, verify_depth);
+		}
+	}
+
 	return 1;
 }
 
@@ -10368,7 +10425,8 @@ mg_connect_client(const char *host,
 		(void)pthread_mutex_init(&conn->mutex, &pthread_mutex_attr);
 #ifndef NO_SSL
 		if (use_ssl) {
-			/* SSL_CTX_set_verify call is needed to switch off server
+			/* TODO: Check ssl_verify_peer and ssl_ca_path here.
+			   SSL_CTX_set_verify call is needed to switch off server
 			 * certificate checking, which is off by default in OpenSSL and on
 			 * in yaSSL. */
 			SSL_CTX_set_verify(conn->client_ssl_ctx, 0, 0);

+ 3 - 1
src/main.c

@@ -1015,6 +1015,8 @@ start_civetweb(int argc, char *argv[])
 	verify_existence(options, "document_root", 1);
 	verify_existence(options, "cgi_interpreter", 0);
 	verify_existence(options, "ssl_certificate", 0);
+	verify_existence(options, "ssl_ca_path", 1);
+	verify_existence(options, "ssl_ca_file", 0);
 #ifdef USE_LUA
 	verify_existence(options, "lua_preload_file", 0);
 #endif
@@ -1689,7 +1691,7 @@ show_settings_dialog()
 #define WIDTH (460)
 #define LABEL_WIDTH (90)
 
-	unsigned char mem[4096], *p;
+	unsigned char mem[8192], *p;
 	const struct mg_option *options;
 	DWORD style;
 	DLGTEMPLATE *dia = (DLGTEMPLATE *)mem;