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

31
README
View File

@ -157,16 +157,21 @@ in inetd mode.
sslh options: -i for inetd mode, --http to forward http
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
server's capabilities to the minimum it needs (see
capabilities(8). For sslh, this is CAP_NET_ADMIN (to
perform transparent proxy-ing) and CAP_NET_BIND_SERVICE (to
bind to port 443 without being root).
On Linux (only?), you can compile sslh with USELIBCAP=1 to
make use of POSIX capabilities; this will save the required
capabilities needed for transparent proxying for unprivileged
processes.
The simplest way to use capabilities is to give them to the
executable as root:
Alternatively, you may use filesystem capabilities instead
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
@ -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
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.
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 ====
@ -192,8 +193,8 @@ simplifies IP-based access control (or makes it possible at
all).
sslh needs extended rights to perform this: you'll need to
give it cap_net_admin capabilities (see appropriate chapter)
or run it as root (but don't do that).
give it cap_net_admin capabilities manually or enable libcap
support (see appropriate chapter).
The firewalling tables also need to be adjusted as follow
(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;
#endif
#ifdef LIBCAP
#include <sys/prctl.h>
#include <sys/capability.h>
#endif
/* check result and die, printing the offending address and error */
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);
}
/* 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 */
void drop_privileges(const char* user_name)
{
@ -521,10 +575,15 @@ void drop_privileges(const char* user_name)
if (verbose)
fprintf(stderr, "turning into %s\n", user_name);
set_keepcaps(1);
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 */