Implement privilege dropping

Add two options, -R/--chroot and -U/--user=user, to chroot to the
config directory (where tinc.conf is located) and to perform
setuid to the user specified, after all the initialization is done.

What's left is handling of pid file since we can't remove it anymore.
This commit is contained in:
Michael Tokarev 2009-05-18 16:25:41 +04:00 committed by Guus Sliepen
parent 6698f7c390
commit ec316aa32e
3 changed files with 103 additions and 3 deletions

View file

@ -1511,6 +1511,23 @@ Write PID to @var{file} instead of @file{@value{localstatedir}/run/tinc.@var{net
Disables encryption and authentication. Disables encryption and authentication.
Only useful for debugging. Only useful for debugging.
@item -R, --chroot
Change process root directory to the directory where the config file is
located (@file{@value{sysconfdir}/tinc/@var{netname}/} as determined by
-n/--net option or as given by -c/--config option), for added security.
The chroot is performed after all the initialization is done, after
writing pid files and opening network sockets.
Note that this option alone does not do any good without -U/--user, below.
Note also that tinc can't run scripts anymore (such as tinc-down or host-up),
unless it's setup to be runnable inside chroot environment.
@item -U, --user=@var{user}
Switch to the given @var{user} after initialization, at the same time as
chroot is performed (see --chroot above). With this option tinc drops
privileges, for added security.
@item --help @item --help
Display a short reminder of these runtime options and terminate. Display a short reminder of these runtime options and terminate.

View file

@ -8,7 +8,7 @@
.Nd tinc VPN daemon .Nd tinc VPN daemon
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm .Nm
.Op Fl cdDkKnL .Op Fl cdDkKnLRU
.Op Fl -config Ns = Ns Ar DIR .Op Fl -config Ns = Ns Ar DIR
.Op Fl -no-detach .Op Fl -no-detach
.Op Fl -debug Ns Op = Ns Ar LEVEL .Op Fl -debug Ns Op = Ns Ar LEVEL
@ -19,6 +19,8 @@
.Op Fl -logfile Ns Op = Ns Ar FILE .Op Fl -logfile Ns Op = Ns Ar FILE
.Op Fl -pidfile Ns = Ns Ar FILE .Op Fl -pidfile Ns = Ns Ar FILE
.Op Fl -bypass-security .Op Fl -bypass-security
.Op Fl -chroot
.Op Fl -user Ns = Ns Ar USER
.Op Fl -help .Op Fl -help
.Op Fl -version .Op Fl -version
.Sh DESCRIPTION .Sh DESCRIPTION
@ -87,6 +89,14 @@ Under Windows this option will be ignored.
.It Fl -bypass-security .It Fl -bypass-security
Disables encryption and authentication of the meta protocol. Disables encryption and authentication of the meta protocol.
Only useful for debugging. Only useful for debugging.
.It Fl -chroot
With this option tinc chroots into the directory where network
config is located (@sysconfdir@/tinc/NETNAME if -n option is used,
or to the directory specified with -c option) after initialization.
.It Fl -user Ns = Ns Ar USER
setuid to the specified
.Ar USER
after initialization.
.It Fl -help .It Fl -help
Display short list of options. Display short list of options.
.It Fl -version .It Fl -version

View file

@ -39,6 +39,12 @@
#include LZO1X_H #include LZO1X_H
#ifndef HAVE_MINGW
#include <pwd.h>
#include <grp.h>
#include <time.h>
#endif
#include <getopt.h> #include <getopt.h>
#include "pidfile.h" #include "pidfile.h"
@ -73,6 +79,12 @@ bool bypass_security = false;
/* If nonzero, disable swapping for this process. */ /* If nonzero, disable swapping for this process. */
bool do_mlock = false; bool do_mlock = false;
/* If nonzero, chroot to netdir after startup. */
static bool do_chroot = false;
/* If !NULL, do setuid to given user after startup */
static const char *switchuser = NULL;
/* If nonzero, write log entries to a separate file. */ /* If nonzero, write log entries to a separate file. */
bool use_logfile = false; bool use_logfile = false;
@ -94,6 +106,8 @@ static struct option const long_options[] = {
{"debug", optional_argument, NULL, 'd'}, {"debug", optional_argument, NULL, 'd'},
{"bypass-security", no_argument, NULL, 3}, {"bypass-security", no_argument, NULL, 3},
{"mlock", no_argument, NULL, 'L'}, {"mlock", no_argument, NULL, 'L'},
{"chroot", no_argument, NULL, 'R'},
{"user", required_argument, NULL, 'U'},
{"logfile", optional_argument, NULL, 4}, {"logfile", optional_argument, NULL, 4},
{"pidfile", required_argument, NULL, 5}, {"pidfile", required_argument, NULL, 5},
{NULL, 0, NULL, 0} {NULL, 0, NULL, 0}
@ -119,6 +133,8 @@ static void usage(bool status)
" -L, --mlock Lock tinc into main memory.\n" " -L, --mlock Lock tinc into main memory.\n"
" --logfile[=FILENAME] Write log entries to a logfile.\n" " --logfile[=FILENAME] Write log entries to a logfile.\n"
" --pidfile=FILENAME Write PID to FILENAME.\n" " --pidfile=FILENAME Write PID to FILENAME.\n"
" -R, --chroot chroot to NET dir at startup.\n"
" -U, --user=USER setuid to given USER at startup.\n"
" --help Display this help and exit.\n" " --help Display this help and exit.\n"
" --version Output version information and exit.\n\n")); " --version Output version information and exit.\n\n"));
printf(_("Report bugs to tinc@tinc-vpn.org.\n")); printf(_("Report bugs to tinc@tinc-vpn.org.\n"));
@ -130,7 +146,7 @@ static bool parse_options(int argc, char **argv)
int r; int r;
int option_index = 0; int option_index = 0;
while((r = getopt_long(argc, argv, "c:DLd::k::n:K::", long_options, &option_index)) != EOF) { while((r = getopt_long(argc, argv, "c:DLd::k::n:K::RU:", long_options, &option_index)) != EOF) {
switch (r) { switch (r) {
case 0: /* long option */ case 0: /* long option */
break; break;
@ -210,6 +226,14 @@ static bool parse_options(int argc, char **argv)
generate_keys = 1024; generate_keys = 1024;
break; break;
case 'R': /* chroot to NETNAME dir */
do_chroot = true;
break;
case 'U': /* setuid to USER */
switchuser = optarg;
break;
case 1: /* show help */ case 1: /* show help */
show_help = true; show_help = true;
break; break;
@ -407,6 +431,51 @@ static void free_names() {
if (confbase) free(confbase); if (confbase) free(confbase);
} }
static bool drop_privs() {
#ifdef HAVE_MINGW
if (switchuser) {
logger(LOG_ERR, _("%s not supported on this platform"), "-U");
return false;
}
if (do_chroot) {
logger(LOG_ERR, _("%s not supported on this platform"), "-R");
return false;
}
#else
uid_t uid = 0;
if (switchuser) {
struct passwd *pw = getpwnam(switchuser);
if (!pw) {
logger(LOG_ERR, _("unknown user `%s'"), switchuser);
return false;
}
uid = pw->pw_uid;
if (initgroups(switchuser, pw->pw_gid) != 0 ||
setgid(pw->pw_gid) != 0) {
logger(LOG_ERR, _("%s failed"), "initgroups()");
return false;
}
endgrent();
endpwent();
}
if (do_chroot) {
tzset(); /* for proper timestamps in logs */
if (chroot(confbase) != 0 || chdir(".") != 0) {
logger(LOG_ERR, _("%s failed"), "chroot()");
return false;
}
free(confbase);
confbase = xstrdup("");
}
if (switchuser)
if (setuid(uid) != 0) {
logger(LOG_ERR, _("%s failed"), "setuid()");
return false;
}
#endif
return true;
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
program_name = argv[0]; program_name = argv[0];
@ -506,6 +575,10 @@ int main2(int argc, char **argv)
if(!setup_network()) if(!setup_network())
goto end; goto end;
/* drop privileges */
if (!drop_privs())
goto end;
/* Initiate all outgoing connections. */ /* Initiate all outgoing connections. */
try_outgoing_connections(); try_outgoing_connections();
@ -536,6 +609,6 @@ end:
exit_configuration(&config_tree); exit_configuration(&config_tree);
free_names(); free_names();
return status; return status;
} }