Download raw body.
-portable gotd chroot and privdrop
Currently, gotd does not run in -portable because it ends up
trying to call chroot() as a non-root user. It looks like the
privdrop parts are either unfinished or have been removed at
some point, since only the repo-read process even tries to drop
privileges.
Below is the best I could come up with in order to get gotd running
on a Debian system. I assume that the user account which gotd runs as
has a home directory to chroot into. This might not be the case by
default on every OS but is probably a better choice than hard-coding
something like "/var/empty" which will not exist everywhere either.
The only process which keeps running as root is the parent. All the
others are run as the gotd user account (_gotd on Debian as created by
the package). Not every process can use chroot, but several can.
I would merge some of this into got.git, the -U option in particular.
One change I made in got.git in order to help -portable gotd turned
out to be counter-productive. So I'll probably revert that change once
this patch is approved and merged into -portable.
ok?
make gotd run properly on -portable with privdrop and chroot
M gotd/chroot-notobsd.c | 1+ 3-
M gotd/gotd.c | 39+ 28-
M gotd/gotd.h | 1+ 1-
3 files changed, 41 insertions(+), 32 deletions(-)
commit - e18a5f85e18c62a89a11d0066a46a5d1acb56ef15cfb08c7f64df771d2fb5c89
commit + 27480126341922d439ec3620855d1cad8323d88d109d11f6d76feeab9c6033d0
blob - 2d912f39049f4188fa607e318740f84a2f6baccf88cfa92a692996caef2a80b3
blob + f33feda01b5e1c724e3137fd7c201487706b17e188dcb1ed1276902030ef73b6
--- gotd/chroot-notobsd.c
+++ gotd/chroot-notobsd.c
@@ -22,7 +22,7 @@
#include "log.h"
-int
+void
enter_chroot(const char *path)
{
log_info("chroot into %s", path);
@@ -30,6 +30,4 @@ enter_chroot(const char *path)
fatal("chroot");
if (chdir("/") == -1)
fatal("chdir(\"/\")");
-
- return 1;
}
blob - c5a8eeb0ec0ca9641f64f796f57c671a7f3bb7a0e093b13580c1189acfafdbe0
blob + 301afe0a5e95da3d58f9ac3d29167bcf1285472c22cc0dd5e0273934374c0d48
--- gotd/gotd.c
+++ gotd/gotd.c
@@ -3044,7 +3044,7 @@ start_child(enum gotd_procid proc_id, const char *repo
char *argv0, const char *confpath, const char *secretspath,
int fd, int daemonize, int verbosity)
{
- const char *argv[13];
+ const char *argv[15];
int argc = 0;
pid_t pid;
@@ -3097,6 +3097,9 @@ start_child(enum gotd_procid proc_id, const char *repo
argv[argc++] = "-f";
argv[argc++] = confpath;
+ argv[argc++] = "-U";
+ argv[argc++] = gotd.user_name;
+
if (secretspath) {
argv[argc++] = "-s";
argv[argc++] = secretspath;
@@ -3489,7 +3492,7 @@ main(int argc, char **argv)
log_init(1, LOG_DAEMON); /* Log to stderr until daemonized. */
- while ((ch = getopt_long(argc, argv, "df:nP:s:T:vV", longopts, NULL)) != -1) {
+ while ((ch = getopt_long(argc, argv, "df:nP:s:T:U:vV", longopts, NULL)) != -1) {
switch (ch) {
case 'd':
daemonize = 0;
@@ -3540,6 +3543,11 @@ main(int argc, char **argv)
errx(1, "unknown proc type %s", optarg);
}
break;
+ case 'U':
+ if (strlcpy(gotd.user_name, optarg,
+ sizeof(gotd.user_name)) >= sizeof(gotd.user_name))
+ fatal("username too long: %s", optarg);
+ break;
case 'v':
if (verbosity < 3)
verbosity++;
@@ -3564,8 +3572,6 @@ main(int argc, char **argv)
}
if (geteuid() && proc_id == GOTD_PROC_GOTD)
fatalx("need root privileges");
- if (geteuid() == 0 && proc_id != GOTD_PROC_GOTD)
- fatalx("must not run as root");
if (proc_id == GOTD_PROC_GOTD) {
const char *p = secretspath ? secretspath : GOTD_SECRETS_PATH;
@@ -3750,6 +3756,15 @@ main(int argc, char **argv)
close(GOTD_FILENO_MSG_PIPE);
reload_reset();
+ } else {
+ pw = getpwnam(gotd.user_name);
+ if (pw == NULL) {
+ uid = strtonum(gotd.user_name, 0, UINT_MAX - 1, &errstr);
+ if (errstr == NULL)
+ pw = getpwuid(uid);
+ }
+ if (pw == NULL)
+ fatalx("user %s not found", gotd.user_name);
}
gotd.argv0 = argv0;
@@ -3804,18 +3819,10 @@ main(int argc, char **argv)
event_init();
- if (proc_id == GOTD_PROC_RELOAD)
+ switch (proc_id) {
+ case GOTD_PROC_RELOAD:
proc_id = GOTD_PROC_GOTD;
-
- if (proc_id == GOTD_PROC_GOTD) {
- /* Drop root privileges. */
- if (setgid(pw->pw_gid) == -1)
- fatal("setgid %d failed", pw->pw_gid);
- if (setuid(pw->pw_uid) == -1)
- fatal("setuid %d failed", pw->pw_uid);
- }
-
- switch (proc_id) {
+ /* FALLTHOUGH */
case GOTD_PROC_GOTD:
if (verbosity) {
log_info("socket: %s", gotd.unix_socket_path);
@@ -3843,7 +3850,8 @@ main(int argc, char **argv)
* sockets by revoking all filesystem access via unveil(2).
*/
apply_unveil_none();
-
+ enter_chroot(pw->pw_dir);
+ drop_privs(pw);
listen_main(title);
/* NOTREACHED */
break;
@@ -3859,7 +3867,7 @@ main(int argc, char **argv)
* files will still work since "getpw" bypasses unveil(2).
*/
apply_unveil_none();
-
+ drop_privs(pw);
auth_main(title);
/* NOTREACHED */
break;
@@ -3874,6 +3882,7 @@ main(int argc, char **argv)
err(1, "pledge");
#endif
apply_unveil_repo_readonly(repo_path, 1);
+ drop_privs(pw);
session_read_main(title, repo_path, pack_fds, temp_fds);
/* NOTREACHED */
break;
@@ -3891,6 +3900,7 @@ main(int argc, char **argv)
err(1, "pledge");
#endif
apply_unveil_repo_readwrite(repo_path);
+ drop_privs(pw);
session_write_main(title, repo_path, pack_fds, temp_fds,
tmp_fd);
/* NOTREACHED */
@@ -3902,17 +3912,12 @@ main(int argc, char **argv)
err(1, "pledge");
#endif
apply_unveil_repo_readonly(repo_path, 0);
-
- if (enter_chroot(repo_path)) {
- log_info("change repo path %s", repo_path);
- free(repo_path);
- repo_path = strdup("/");
- if (repo_path == NULL)
- fatal("strdup");
- log_info("repo path is now %s", repo_path);
- }
+ enter_chroot(repo_path);
+ free(repo_path);
+ repo_path = strdup("/");
+ if (repo_path == NULL)
+ fatal("strdup");
drop_privs(pw);
-
repo_read_main(title, repo_path, pack_fds, temp_fds);
/* NOTREACHED */
exit(0);
@@ -3942,6 +3947,12 @@ main(int argc, char **argv)
err(1, "pledge");
#endif
apply_unveil_repo_readonly(repo_path, 0);
+ enter_chroot(repo_path);
+ free(repo_path);
+ repo_path = strdup("/");
+ if (repo_path == NULL)
+ fatal("strdup");
+ drop_privs(pw);
repo_write_main(title, repo_path, pack_fds, temp_fds,
tmp_f1, tmp_f2, diff_f1, diff_f2, diff_fd1, diff_fd2);
/* NOTREACHED */
@@ -3955,7 +3966,7 @@ main(int argc, char **argv)
* Limit "exec" promise to notification helpers via unveil(2).
*/
unveil_notification_helpers();
-
+ drop_privs(pw);
notify_main(title);
/* NOTREACHED */
exit(0);
blob - 20a4a6e93efc2cb08d25c7004bd1f74166941d0c830aaa2242fbbb5ff2404b8d
blob + e825fa2b1827b5471fcf08b6629301af0f5f1ba96766e4d90ca827971433735a
--- gotd/gotd.h
+++ gotd/gotd.h
@@ -672,7 +672,7 @@ struct gotd_imsg_notify {
/* Followed by username_len data bytes. */
};
-int enter_chroot(const char *);
+void enter_chroot(const char *);
int gotd_parse_config(const char *, int, enum gotd_procid,
struct gotd_secrets *, struct gotd *);
struct gotd_repo *gotd_find_repo_by_name(const char *, struct gotd_repolist *);
-portable gotd chroot and privdrop