add chroot support

This allows people to chroot sslh into a path to further harden it.

We have to rework the user logic a bit because we need to look up
the user details *before* we chroot (as we need to read /etc/passwd
files), but do the actual priv dropping *after* we chroot (so we
have permission to make the actual chroot call).

Similarly, we need to open the syslog before we drop privs because
/dev/log won't be available inside the chroot.
This commit is contained in:
Mike Frysinger 2016-10-07 16:19:13 -04:00
parent d9541392f8
commit 0fb4c6b2ad
6 changed files with 58 additions and 31 deletions

View File

@ -9,6 +9,7 @@ transparent: false;
timeout: 2;
user: "nobody";
pidfile: "/var/run/sslh.pid";
chroot: "/var/empty";
# Change hostname with your external address name.

View File

@ -41,7 +41,7 @@ int foreground = 0;
int background = 0;
int transparent = 0;
int numeric = 0;
const char *user_name, *pid_file, *facility = "auth";
const char *user_name, *pid_file, *chroot_path, *facility = "auth";
struct addrinfo *addr_listen = NULL; /* what addresses do we listen to? */
@ -719,33 +719,47 @@ void set_capabilities(void) {
}
/* We don't want to run as root -- drop privileges if required */
void drop_privileges(const char* user_name)
void drop_privileges(const char* user_name, const char* chroot_path)
{
int res;
struct passwd *pw = getpwnam(user_name);
if (!pw) {
fprintf(stderr, "%s: not found\n", user_name);
exit(2);
struct passwd *pw = NULL;
if (user_name) {
pw = getpwnam(user_name);
if (!pw) {
fprintf(stderr, "%s: not found\n", user_name);
exit(2);
}
if (verbose)
fprintf(stderr, "turning into %s\n", user_name);
}
if (verbose)
fprintf(stderr, "turning into %s\n", user_name);
set_keepcaps(1);
if (chroot_path) {
if (verbose)
fprintf(stderr, "chrooting into %s\n", chroot_path);
/* remove extraneous groups in case we belong to several extra groups that
* may have unwanted rights. If non-root when calling setgroups(), it
* fails, which is fine because... we have no unwanted rights
* (see POS36-C for security context)
* */
setgroups(0, NULL);
res = chroot(chroot_path);
CHECK_RES_DIE(res, "chroot");
}
res = setgid(pw->pw_gid);
CHECK_RES_DIE(res, "setgid");
res = setuid(pw->pw_uid);
CHECK_RES_DIE(res, "setuid");
if (user_name) {
set_keepcaps(1);
set_capabilities();
set_keepcaps(0);
/* remove extraneous groups in case we belong to several extra groups
* that may have unwanted rights. If non-root when calling setgroups(),
* it fails, which is fine because... we have no unwanted rights
* (see POS36-C for security context)
* */
setgroups(0, NULL);
res = setgid(pw->pw_gid);
CHECK_RES_DIE(res, "setgid");
res = setuid(pw->pw_uid);
CHECK_RES_DIE(res, "setuid");
set_capabilities();
set_keepcaps(0);
}
}
/* Writes my PID */

View File

@ -102,7 +102,7 @@ void log_connection(struct connection *cnx);
int check_access_rights(int in_socket, const char* service);
void setup_signals(void);
void setup_syslog(const char* bin_name);
void drop_privileges(const char* user_name);
void drop_privileges(const char* user_name, const char* chroot_path);
void write_pid_file(const char* pidfile);
void log_message(int type, char* msg, ...);
void dump_connection(struct connection *cnx);
@ -118,7 +118,7 @@ extern int probing_timeout, verbose, inetd, foreground,
extern struct sockaddr_storage addr_ssl, addr_ssh, addr_openvpn;
extern struct addrinfo *addr_listen;
extern const char* USAGE_STRING;
extern const char* user_name, *pid_file, *facility;
extern const char* user_name, *pid_file, *chroot_path, *facility;
extern const char* server_type;
/* sslh-fork.c */

View File

@ -11,6 +11,7 @@ transparent: false;
timeout: 2;
user: "nobody";
pidfile: "/var/run/sslh.pid";
chroot: "/var/empty";
# Specify which syslog facility to use (names for your
# system are usually defined in /usr/include/*/sys/syslog.h

View File

@ -40,7 +40,7 @@ const char* USAGE_STRING =
"sslh " VERSION "\n" \
"usage:\n" \
"\tsslh [-v] [-i] [-V] [-f] [-n] [--transparent] [-F<file>]\n"
"\t[-t <timeout>] [-P <pidfile>] -u <username> -p <add> [-p <addr> ...] \n" \
"\t[-t <timeout>] [-P <pidfile>] [-u <username>] [-C <chroot>] -p <add> [-p <addr> ...] \n" \
"%s\n\n" /* Dynamically built list of builtin protocols */ \
"\t[--on-timeout <addr>]\n" \
"-v: verbose\n" \
@ -48,6 +48,7 @@ const char* USAGE_STRING =
"-f: foreground\n" \
"-n: numeric output\n" \
"-u: specify under which user to run\n" \
"-C: specify under which chroot path to run\n" \
"--transparent: behave as a transparent proxy\n" \
"-F: use configuration file (warning: no space between -F and file name!)\n" \
"--on-timeout: connect to specified address upon timeout (default: ssh address)\n" \
@ -71,6 +72,7 @@ static struct option const_options[] = {
{ "user", required_argument, 0, 'u' },
{ "config", optional_argument, 0, 'F' },
{ "pidfile", required_argument, 0, 'P' },
{ "chroot", required_argument, 0, 'C' },
{ "timeout", required_argument, 0, 't' },
{ "on-timeout", required_argument, 0, OPT_ONTIMEOUT },
{ "listen", required_argument, 0, 'p' },
@ -78,7 +80,7 @@ static struct option const_options[] = {
};
static struct option* all_options;
static struct proto* builtins;
static const char *optstr = "vt:T:p:VP:F::";
static const char *optstr = "vt:T:p:VP:C:F::";
@ -399,6 +401,7 @@ static int config_parse(char *filename, struct addrinfo **listen, struct proto *
config_lookup_string(&config, "user", &user_name);
config_lookup_string(&config, "pidfile", &pid_file);
config_lookup_string(&config, "chroot", &chroot_path);
config_lookup_string(&config, "syslog_facility", &facility);
@ -561,6 +564,10 @@ next_arg:
pid_file = optarg;
break;
case 'C':
chroot_path = optarg;
break;
case 'v':
verbose++;
break;
@ -605,6 +612,7 @@ int main(int argc, char *argv[])
/* Init defaults */
pid_file = NULL;
user_name = NULL;
chroot_path = NULL;
cmdline_config(argc, argv, &protocols);
parse_cmdline(argc, argv, protocols);
@ -643,13 +651,12 @@ int main(int argc, char *argv[])
if (pid_file)
write_pid_file(pid_file);
if (user_name)
drop_privileges(user_name);
/* Open syslog connection */
/* Open syslog connection before we drop privs/chroot */
setup_syslog(argv[0]);
if (user_name || chroot_path)
drop_privileges(user_name, chroot_path);
if (verbose)
printcaps();

View File

@ -6,7 +6,7 @@
=head1 SYNOPSIS
sslh [B<-F>I<config file>] [ B<-t> I<num> ] [B<--transparent>] [B<-p> I<listening address> [B<-p> I<listening address> ...] [B<--ssl> I<target address for SSL>] [B<--tls> I<target address for TLS>] [B<--ssh> I<target address for SSH>] [B<--openvpn> I<target address for OpenVPN>] [B<--http> I<target address for HTTP>] [B<--xmpp> I<target address for XMPP>] [B<--tinc> I<target address for TINC>] [B<--anyprot> I<default target address>] [B<--on-timeout> I<protocol name>] [B<-u> I<username>] [B<-P> I<pidfile>] [-v] [-i] [-V] [-f] [-n]
sslh [B<-F>I<config file>] [B<-t> I<num>] [B<--transparent>] [B<-p> I<listening address> [B<-p> I<listening address> ...] [B<--ssl> I<target address for SSL>] [B<--tls> I<target address for TLS>] [B<--ssh> I<target address for SSH>] [B<--openvpn> I<target address for OpenVPN>] [B<--http> I<target address for HTTP>] [B<--xmpp> I<target address for XMPP>] [B<--tinc> I<target address for TINC>] [B<--anyprot> I<default target address>] [B<--on-timeout> I<protocol name>] [B<-u> I<username>] [B<-C> I<chroot>] [B<-P> I<pidfile>] [-v] [-i] [-V] [-f] [-n]
=head1 DESCRIPTION
@ -184,6 +184,10 @@ Prints B<sslh> version.
Requires to run under the specified username.
=item B<-C> I<chroot>, B<--chroot> I<chroot>
Requires to run under the specified chroot.
=item B<-P> I<pidfile>, B<--pidfile> I<pidfile>
Specifies a file in which to write the PID of the main