Implement libcap support

Use libcap for saving CAP_NET_ADMIN (if --transparent is given) over a
setuid(). We don’t need CAP_NET_BIND_SERVICE as the listening sockets
are established before dropping root.
This commit is contained in:
Sebastian Schmidt 2013-10-20 20:59:00 +02:00
parent 3f386b6541
commit 009faa64b7
3 changed files with 81 additions and 15 deletions

View File

@ -3,6 +3,7 @@
VERSION=$(shell ./genver.sh -r) VERSION=$(shell ./genver.sh -r)
USELIBCONFIG=1 # Use libconfig? (necessary to use configuration files) USELIBCONFIG=1 # Use libconfig? (necessary to use configuration files)
USELIBWRAP= # Use libwrap? USELIBWRAP= # Use libwrap?
USELIBCAP= # Use libcap?
COV_TEST= # Perform test coverage? COV_TEST= # Perform test coverage?
PREFIX=/usr/local PREFIX=/usr/local
@ -31,6 +32,11 @@ ifneq ($(strip $(USELIBCONFIG)),)
CPPFLAGS+=-DLIBCONFIG CPPFLAGS+=-DLIBCONFIG
endif endif
ifneq ($(strip $(USELIBCAP)),)
LIBS:=$(LIBS) -lcap
CPPFLAGS+=-DLIBCAP
endif
all: sslh $(MAN) echosrv all: sslh $(MAN) echosrv
.c.o: *.h .c.o: *.h

31
README
View File

@ -157,16 +157,21 @@ in inetd mode.
sslh options: -i for inetd mode, --http to forward http sslh options: -i for inetd mode, --http to forward http
connexions to port 80, and SSH connexions to port 22. connexions to port 80, and SSH connexions to port 22.
==== capapbilities support ==== ==== capabilities support ====
On Linux (only?), you can use POSIX capabilities to reduce a On Linux (only?), you can compile sslh with USELIBCAP=1 to
server's capabilities to the minimum it needs (see make use of POSIX capabilities; this will save the required
capabilities(8). For sslh, this is CAP_NET_ADMIN (to capabilities needed for transparent proxying for unprivileged
perform transparent proxy-ing) and CAP_NET_BIND_SERVICE (to processes.
bind to port 443 without being root).
The simplest way to use capabilities is to give them to the Alternatively, you may use filesystem capabilities instead
executable as root: of starting sslh as root and asking it to drop privileges.
You will need CAP_NET_BIND_SERVICE for listening on port 443
and CAP_NET_ADMIN for transparent proxying (see
capabilities(7)).
You can use the setcap(8) utility to give these capabilities
to the executable:
# setcap cap_net_bind_service,cap_net_admin+pe sslh-select # setcap cap_net_bind_service,cap_net_admin+pe sslh-select
@ -174,13 +179,9 @@ Then you can run sslh-select as an unpriviledged user, e.g.:
$ sslh-select -p myname:443 --ssh localhost:22 --ssl localhost:443 $ sslh-select -p myname:443 --ssh localhost:22 --ssl localhost:443
This has 2 advantages over starting as root with -u:
- You no longer start as root (duh)
- This enables transparent proxying.
Caveat: CAP_NET_ADMIN does give sslh too many rights, e.g. Caveat: CAP_NET_ADMIN does give sslh too many rights, e.g.
configuring the interface. If you're not going to use configuring the interface. If you're not going to use
transparent proxying, just don't use it. transparent proxying, just don't use it (or use the libcap method).
==== Transparent proxy support ==== ==== Transparent proxy support ====
@ -192,8 +193,8 @@ simplifies IP-based access control (or makes it possible at
all). all).
sslh needs extended rights to perform this: you'll need to sslh needs extended rights to perform this: you'll need to
give it cap_net_admin capabilities (see appropriate chapter) give it cap_net_admin capabilities manually or enable libcap
or run it as root (but don't do that). support (see appropriate chapter).
The firewalling tables also need to be adjusted as follow The firewalling tables also need to be adjusted as follow
(example to connect to https on 4443 -- adapt to your needs (example to connect to https on 4443 -- adapt to your needs

View File

@ -37,6 +37,10 @@ struct addrinfo *addr_listen = NULL; /* what addresses do we listen to? */
int allow_severity =0, deny_severity = 0; int allow_severity =0, deny_severity = 0;
#endif #endif
#ifdef LIBCAP
#include <sys/prctl.h>
#include <sys/capability.h>
#endif
/* check result and die, printing the offending address and error */ /* check result and die, printing the offending address and error */
void check_res_dumpdie(int res, struct addrinfo *addr, char* syscall) void check_res_dumpdie(int res, struct addrinfo *addr, char* syscall)
@ -509,6 +513,56 @@ void setup_syslog(const char* bin_name) {
log_message(LOG_INFO, "%s %s started\n", server_type, VERSION); log_message(LOG_INFO, "%s %s started\n", server_type, VERSION);
} }
/* Ask OS to keep capabilities over a setuid(nonzero) */
void set_keepcaps(int val) {
#ifdef LIBCAP
int res;
res = prctl(PR_SET_KEEPCAPS, val, 0, 0, 0);
if (res) {
perror("prctl");
exit(1);
}
#endif
}
/* set needed capabilities for effective and permitted, clear rest */
void set_capabilities(void) {
#ifdef LIBCAP
int res;
cap_t caps;
cap_value_t cap_list[10];
int ncap = 0;
if (transparent)
cap_list[ncap++] = CAP_NET_ADMIN;
caps = cap_init();
#define _cap_set_flag(flag) do { \
res = cap_clear_flag(caps, flag); \
CHECK_RES_DIE(res, "cap_clear_flag(" #flag ")"); \
if (ncap > 0) { \
res = cap_set_flag(caps, flag, ncap, cap_list, CAP_SET); \
CHECK_RES_DIE(res, "cap_set_flag(" #flag ")"); \
} \
} while(0)
_cap_set_flag(CAP_EFFECTIVE);
_cap_set_flag(CAP_PERMITTED);
#undef _cap_set_flag
res = cap_set_proc(caps);
CHECK_RES_DIE(res, "cap_set_proc");
res = cap_free(caps);
if (res) {
perror("cap_free");
exit(1);
}
#endif
}
/* We don't want to run as root -- drop privileges if required */ /* 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)
{ {
@ -521,10 +575,15 @@ void drop_privileges(const char* user_name)
if (verbose) if (verbose)
fprintf(stderr, "turning into %s\n", user_name); fprintf(stderr, "turning into %s\n", user_name);
set_keepcaps(1);
res = setgid(pw->pw_gid); res = setgid(pw->pw_gid);
CHECK_RES_DIE(res, "setgid"); CHECK_RES_DIE(res, "setgid");
res = setuid(pw->pw_uid); res = setuid(pw->pw_uid);
CHECK_RES_DIE(res, "setuid"); CHECK_RES_DIE(res, "setuid");
set_capabilities();
set_keepcaps(0);
} }
/* Writes my PID */ /* Writes my PID */