[PATCH] uhttpd: implement TLS client certificate authentication

Luka Logar luka.logar at cifra.si
Fri Feb 19 15:01:51 EST 2021


Enable client certificate authentication by specifying a -b path_to_ca_cert_file on the command line.
When this parameter is set, TLS server requests a client certificate (signed by the specified CA). If
client certificate authentication is successful, client cert data is stored in HTTPS_CLIENT_CERT,
HTTPS_CLIENT_CERT_SHA256 and HTTPS_CLIENT_CERT_SN environment variables. Currently not supported by
mbedtls backend.

Signed-off-by: Luka Logar <luka.logar at cifra.si>
---
 main.c | 10 ++++++++--
 proc.c |  9 +++++++++
 tls.c  |  7 ++++++-
 tls.h  |  2 +-
 4 files changed, 24 insertions(+), 4 deletions(-)

diff --git a/main.c b/main.c
index 73e3d42..fb6a84e 100644
--- a/main.c
+++ b/main.c
@@ -141,6 +141,7 @@ static int usage(const char *name)
 		"	-K file         ASN.1 server private key file\n"
 		"	-P ciphers      Colon separated list of allowed TLS ciphers\n"
 		"	-q              Redirect all HTTP requests to HTTPS\n"
+		"	-b client_ca    Enable TLS client auth using client_ca for certificate selection\n"
 #endif
 		"	-h directory    Specify the document root, default is '.'\n"
 		"	-E string       Use given virtual URL as 404 error handler\n"
@@ -252,6 +253,7 @@ int main(int argc, char **argv)
 #ifdef HAVE_TLS
 	int n_tls = 0;
 	const char *tls_key = NULL, *tls_crt = NULL, *tls_ciphers = NULL;
+	const char *tls_client_ca = NULL;
 #endif
 #ifdef HAVE_LUA
 	const char *lua_prefix = NULL, *lua_handler = NULL;
@@ -263,7 +265,7 @@ int main(int argc, char **argv)
 	init_defaults_pre();
 	signal(SIGPIPE, SIG_IGN);
 
-	while ((ch = getopt(argc, argv, "A:aC:c:Dd:E:e:fh:H:I:i:K:k:L:l:m:N:n:P:p:qRr:Ss:T:t:U:u:Xx:y:")) != -1) {
+	while ((ch = getopt(argc, argv, "A:ab:C:c:Dd:E:e:fh:H:I:i:K:k:L:l:m:N:n:P:p:qRr:Ss:T:t:U:u:Xx:y:")) != -1) {
 		switch(ch) {
 #ifdef HAVE_TLS
 		case 'C':
@@ -282,6 +284,10 @@ int main(int argc, char **argv)
 			conf.tls_redirect = 1;
 			break;
 
+		case 'b':
+			tls_client_ca = optarg;
+			break;
+
 		case 's':
 			n_tls++;
 			/* fall through */
@@ -535,7 +541,7 @@ int main(int argc, char **argv)
 			return 1;
 		}
 
-		if (uh_tls_init(tls_key, tls_crt, tls_ciphers))
+		if (uh_tls_init(tls_key, tls_crt, tls_ciphers, tls_client_ca))
 		    return 1;
 	}
 #endif
diff --git a/proc.c b/proc.c
index 2b69703..798f385 100644
--- a/proc.c
+++ b/proc.c
@@ -90,6 +90,9 @@ enum extra_vars {
 	VAR_PATH_INFO,
 	VAR_USER,
 	VAR_HTTPS,
+	VAR_HTTPS_CLIENT_CERT,
+	VAR_HTTPS_CLIENT_CERT_SHA256,
+	VAR_HTTPS_CLIENT_CERT_SN,
 	VAR_REDIRECT,
 	VAR_SERVER_NAME,
 	VAR_SERVER_ADDR,
@@ -118,6 +121,9 @@ static struct env_var extra_vars[] = {
 	[VAR_PATH_INFO] = { "PATH_INFO" },
 	[VAR_USER] = { "REMOTE_USER" },
 	[VAR_HTTPS] = { "HTTPS" },
+	[VAR_HTTPS_CLIENT_CERT] = { "HTTPS_CLIENT_CERT" },
+	[VAR_HTTPS_CLIENT_CERT_SHA256] = { "HTTPS_CLIENT_CERT_SHA256" },
+	[VAR_HTTPS_CLIENT_CERT_SN] = { "HTTPS_CLIENT_CERT_SN" },
 	[VAR_REDIRECT] = { "REDIRECT_STATUS", redirect_status },
 	[VAR_SERVER_NAME] = { "SERVER_NAME", local_addr },
 	[VAR_SERVER_ADDR] = { "SERVER_ADDR", local_addr },
@@ -154,6 +160,9 @@ struct env_var *uh_get_process_vars(struct client *cl, struct path_info *pi)
 	extra_vars[VAR_PATH_INFO].value = pi->info;
 	extra_vars[VAR_USER].value = req->realm ? req->realm->user : NULL;
 	extra_vars[VAR_HTTPS].value = cl->tls ? "on" : NULL;
+	extra_vars[VAR_HTTPS_CLIENT_CERT].value = cl->ssl.peer_cert;
+	extra_vars[VAR_HTTPS_CLIENT_CERT_SHA256].value = cl->ssl.peer_cert ? cl->ssl.peer_cert_sha256 : NULL;
+	extra_vars[VAR_HTTPS_CLIENT_CERT_SN].value = cl->ssl.peer_cert_sn;
 
 	snprintf(redirect_status, sizeof(redirect_status),
 		 "%d", req->redirect_status);
diff --git a/tls.c b/tls.c
index 1da0881..bb616eb 100644
--- a/tls.c
+++ b/tls.c
@@ -31,7 +31,7 @@ static struct ustream_ssl_ops *ops;
 static void *dlh;
 static void *ctx;
 
-int uh_tls_init(const char *key, const char *crt, const char *ciphers)
+int uh_tls_init(const char *key, const char *crt, const char *ciphers, const char *client_ca)
 {
 	static bool _init = false;
 
@@ -68,6 +68,11 @@ int uh_tls_init(const char *key, const char *crt, const char *ciphers)
 		return -EINVAL;
 	}
 
+	if (client_ca && (ops->context_set_require_validation(ctx, 1) || ops->context_add_ca_crt_file(ctx, client_ca))) {
+		fprintf(stderr, "Can not enable TLS client authentication\n");
+		return -EINVAL;
+	}
+
 	return 0;
 }
 
diff --git a/tls.h b/tls.h
index f457cb7..2b0b26a 100644
--- a/tls.h
+++ b/tls.h
@@ -22,7 +22,7 @@
 
 #ifdef HAVE_TLS
 
-int uh_tls_init(const char *key, const char *crt, const char *ciphers);
+int uh_tls_init(const char *key, const char *crt, const char *ciphers, const char *client_ca);
 void uh_tls_client_attach(struct client *cl);
 void uh_tls_client_detach(struct client *cl);
 
-- 
2.25.1





More information about the openwrt-devel mailing list