From ae08ec7da5bf349bea4621219df41f297b3116e9 Mon Sep 17 00:00:00 2001 From: Omar Polo Date: Mon, 25 Jan 2021 10:30:07 +0000 Subject: [PATCH] chroot & drop privileges --- gmid.1 | 16 ++++++++++++ gmid.c | 79 +++++++++++++++++++++++++++++++++++++++++++++------------ gmid.h | 16 +++++++----- lex.l | 2 ++ parse.y | 5 +++- 5 files changed, 95 insertions(+), 23 deletions(-) diff --git a/gmid.1 b/gmid.1 index ad0aeba..4382cd0 100644 --- a/gmid.1 +++ b/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 diff --git a/gmid.c b/gmid.c index f2db8c6..7b1238e 100644 --- a/gmid.c +++ b/gmid.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -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,10 +329,26 @@ 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,6 +381,38 @@ 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 @@ -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); diff --git a/gmid.h b/gmid.h index 2364e7c..968559b 100644 --- a/gmid.h +++ b/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 */ diff --git a/lex.l b/lex.l index 240f7c4..099b3c4 100644 --- a/lex.l +++ b/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; diff --git a/parse.y b/parse.y index 5e7cb21..9793459 100644 --- a/parse.y +++ b/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 */