Commit Diff


commit - 2030e314860f1ba4a1b0294b741164dca2391466
commit + ae08ec7da5bf349bea4621219df41f297b3116e9
blob - ad0aeba97f7232a1d19b9386ae486e802cb90884
blob + 4382cd017e3394902cda8acc1039180613f0b74f
--- gmid.1
+++ gmid.1
@@ -129,6 +129,22 @@ Add a mapping for the given
 to the given
 .Ar mime-type .
 Both argument are strings.
+.It Ic chroot Pa path
+.Xr chroot 2
+the process to the given
+.Pa path .
+The daemon has to be run with root privileges and thus the option
+.Ic user
+needs to be provided, so
+.Nm
+can drop the privileges.
+Note that they are dropped after loading the TLS keys, so it's
+recommended to put those outside the chroot.
+Future version of
+.Nm
+may require this.
+.It Ic user Ar string
+Run the daemon as the given user.
 .El
 .Ss Servers
 Every virtual host is defined by a
blob - f2db8c66f860bc88d146e1aa0dd60cada498403c
blob + 7b1238e37afbcfbfe0d8ed4fd64d3f936768f082
--- gmid.c
+++ gmid.c
@@ -18,6 +18,7 @@
 #include <fcntl.h>
 #include <limits.h>
 #include <netdb.h>
+#include <pwd.h>
 #include <signal.h>
 #include <stdarg.h>
 #include <string.h>
@@ -32,6 +33,8 @@ int exfd;
 
 struct conf conf;
 
+struct tls *ctx;
+
 void
 fatal(const char *fmt, ...)
 {
@@ -242,19 +245,11 @@ parse_conf(const char *path)
 }
 
 void
-load_vhosts(struct tls_config *tlsconf)
+load_vhosts(void)
 {
 	struct vhost *h;
 
-	/* we need to set something, then we can add how many key we want */
-	if (tls_config_set_keypair_file(tlsconf, hosts->cert, hosts->key))
-		fatal("tls_config_set_keypair_file failed");
-
 	for (h = hosts; h->domain != NULL; ++h) {
-		if (tls_config_add_keypair_file(tlsconf, h->cert, h->key) == -1)
-			fatal("failed to load the keypair (%s, %s)",
-			    h->cert, h->key);
-
 		if ((h->dirfd = open(h->dir, O_RDONLY | O_DIRECTORY)) == -1)
 			fatal("open %s for domain %s", h->dir, h->domain);
 	}
@@ -315,14 +310,11 @@ make_socket(int port, int family)
 	return sock;
 }
 
-int
-listener_main()
+void
+setup_tls(void)
 {
-	int sock4, sock6;
-	struct tls *ctx = NULL;
 	struct tls_config *tlsconf;
-
-	load_default_mime(&conf.mime);
+	struct vhost *h;
 
 	if ((tlsconf = tls_config_new()) == NULL)
 		fatal("tls_config_new");
@@ -337,11 +329,27 @@ listener_main()
 	if ((ctx = tls_server()) == NULL)
 		fatal("tls_server failure");
 
-	load_vhosts(tlsconf);
+	/* we need to set something, then we can add how many key we want */
+	if (tls_config_set_keypair_file(tlsconf, hosts->cert, hosts->key))
+		fatal("tls_config_set_keypair_file failed");
 
+	for (h = hosts; h->domain != NULL; ++h) {
+		if (tls_config_add_keypair_file(tlsconf, h->cert, h->key) == -1)
+			fatal("failed to load the keypair (%s, %s)",
+			    h->cert, h->key);
+	}
+
 	if (tls_configure(ctx, tlsconf) == -1)
 		fatal("tls_configure: %s", tls_error(ctx));
+}
 
+int
+listener_main(void)
+{
+	int sock4, sock6;
+
+	load_default_mime(&conf.mime);
+
 	if (!conf.foreground && daemon(0, 1) == -1)
 		exit(1);
 
@@ -350,6 +358,8 @@ listener_main()
 	if (conf.ipv6)
 		sock6 = make_socket(conf.port, AF_INET6);
 
+	load_vhosts();
+
 	sandbox();
 	loop(ctx, sock4, sock6);
 
@@ -371,9 +381,41 @@ init_config(void)
 	conf.protos = TLS_PROTOCOL_TLSv1_2 | TLS_PROTOCOL_TLSv1_3;
 
 	init_mime(&conf.mime);
+
+	conf.chroot = NULL;
+	conf.user = NULL;
 }
 
 void
+drop_priv(void)
+{
+	struct passwd *pw = NULL;
+
+	if (conf.chroot != NULL && conf.user == NULL)
+		fatal("can't chroot without an user to switch to after.");
+
+	if (conf.user != NULL) {
+		if ((pw = getpwnam(conf.user)) == NULL)
+			fatal("can't find user %s", conf.user);
+	}
+
+	if (conf.chroot != NULL) {
+		if (chroot(conf.chroot) != 0 || chdir("/") != 0)
+			fatal("%s: %s", conf.chroot, strerror(errno));
+	}
+
+	if (pw != NULL) {
+		if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
+			fatal("setresuid(%d): %s", pw->pw_uid,
+			    strerror(errno));
+	}
+
+	if (getuid() == 0)
+		LOGW(NULL, "%s",
+		    "not a good idea to run a network daemon as root");
+}
+
+void
 usage(const char *me)
 {
 	fprintf(stderr,
@@ -461,6 +503,11 @@ main(int argc, char **argv)
 		return 0;
 	}
 
+	/* setup tls before dropping privileges: we don't want user
+	 * to put private certs inside the chroot. */
+	setup_tls();
+	drop_priv();
+
 	signal(SIGPIPE, SIG_IGN);
 	signal(SIGCHLD, SIG_IGN);
 
blob - 2364e7c7bc0cbda5e0eeb965b11ca858c6c21db1
blob + 968559bddf67af3f24a4879b61fd3bf1783d0594
--- gmid.h
+++ gmid.h
@@ -92,11 +92,13 @@ struct mime {
 };
 
 struct conf {
-	int		foreground;
-	int		port;
-	int		ipv6;
-	uint32_t	protos;
-	struct mime	mime;
+	int		 foreground;
+	int		 port;
+	int		 ipv6;
+	uint32_t	 protos;
+	struct mime	 mime;
+	char		*chroot;
+	char		*user;
 };
 
 extern struct conf conf;
@@ -169,10 +171,12 @@ char		*absolutify_path(const char*);
 void		 yyerror(const char*);
 int		 parse_portno(const char*);
 void		 parse_conf(const char*);
-void		 load_vhosts(struct tls_config*);
+void		 load_vhosts(void);
 int		 make_socket(int, int);
+void		 setup_tls(void);
 int		 listener_main(void);
 void		 init_config(void);
+void		 drop_priv(void);
 void		 usage(const char*);
 
 /* provided by lex/yacc */
blob - 240f7c466e7c1551d21d5664d4aaab6974aba9d3
blob + 099b3c4b19d822ba495bb7d4980f114d99cbd54f
--- lex.l
+++ lex.l
@@ -58,6 +58,8 @@ protocols	return TPROTOCOLS;
 mime		return TMIME;
 default		return TDEFAULT;
 type		return TTYPE;
+chroot		return TCHROOT;
+user		return TUSER;
 server		return TSERVER;
 
 location	return TLOCATION;
blob - 5e7cb216241e588d84ac594ed7b7a2ef4a4671cc
blob + 97934599fd9039217e0a83785418fb3fb5a25d6f
--- parse.y
+++ parse.y
@@ -45,7 +45,8 @@ extern void yyerror(const char*);
 	int		 num;
 }
 
-%token TDAEMON TIPV6 TPORT TPROTOCOLS TMIME TDEFAULT TTYPE TSERVER
+%token TDAEMON TIPV6 TPORT TPROTOCOLS TMIME TDEFAULT TTYPE
+%token TCHROOT TUSER TSERVER
 %token TLOCATION TCERT TKEY TROOT TCGI TLANG TINDEX TAUTO
 %token TERR
 
@@ -69,6 +70,8 @@ option		: TDAEMON TBOOL		{ conf.foreground = !$2; }
 				errx(1, "invalid protocols string \"%s\"", $2);
 		}
 		| TMIME TSTRING TSTRING	{ add_mime(&conf.mime, $2, $3); }
+		| TCHROOT TSTRING	{ conf.chroot = $2; }
+		| TUSER TSTRING		{ conf.user = $2; }
 		;
 
 vhosts		: /* empty */