From 009faa64b75801961f88ec5a7a09fd991168d4e9 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Sun, 20 Oct 2013 20:59:00 +0200 Subject: [PATCH] Implement libcap support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- Makefile | 6 ++++++ README | 31 +++++++++++++++-------------- common.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 15 deletions(-) diff --git a/Makefile b/Makefile index e06db53..800af29 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/README b/README index 15d6172..cb2f588 100644 --- a/README +++ b/README @@ -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 diff --git a/common.c b/common.c index 2214d7e..c0fa8eb 100644 --- a/common.c +++ b/common.c @@ -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 +#include +#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 */