From b9fcc619912859d48020ffb1b1e1096030df7f02 Mon Sep 17 00:00:00 2001 From: Micah Morton Date: Tue, 17 Apr 2018 13:29:03 -0700 Subject: [PATCH] Allow strongSwan to be spawned as non-root user This patch allows for giving strongSwan only the runtime capabilities it needs, rather than full root privileges. Adds preprocessor directives which allow strongSwan to be configured to 1) start up as a non-root user 2) avoid modprobe()'ing IPsec kernel modules into the kernel, which would normally require root or CAP_SYS_MODULE Additionally, some small mods to charon/libstrongswan ensure that charon fully supports starting as a non-root user. Tested with strongSwan 5.5.3. --- src/charon/charon.c | 12 +++++++++--- .../networking/streams/stream_service_unix.c | 9 ++++++--- src/libstrongswan/utils/capabilities.c | 5 ++++- src/libstrongswan/utils/capabilities.h | 3 +++ src/starter/starter.c | 4 ++++ 5 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/charon/charon.c b/src/charon/charon.c index 1d1e623c1..30f6ce2f6 100644 --- a/src/charon/charon.c +++ b/src/charon/charon.c @@ -231,9 +231,15 @@ static bool check_pidfile() DBG1(DBG_LIB, "setting FD_CLOEXEC for '"PID_FILE"' failed: %s", strerror(errno)); } - ignore_result(fchown(fd, - lib->caps->get_uid(lib->caps), - lib->caps->get_gid(lib->caps))); + /* Only fchown() the pidfile if we have CAP_CHOWN. Otherwise, + * directory permissions should allow pidfile to be accessed + * by the UID/GID under which the charon deamon will run. */ + if (lib->caps->check(lib->caps, CAP_CHOWN)) + { + ignore_result(fchown(fd, + lib->caps->get_uid(lib->caps), + lib->caps->get_gid(lib->caps))); + } fprintf(pidfile, "%d\n", getpid()); fflush(pidfile); return FALSE; diff --git a/src/libstrongswan/networking/streams/stream_service_unix.c b/src/libstrongswan/networking/streams/stream_service_unix.c index 1ed27c499..92640fc91 100644 --- a/src/libstrongswan/networking/streams/stream_service_unix.c +++ b/src/libstrongswan/networking/streams/stream_service_unix.c @@ -39,8 +39,9 @@ stream_service_t *stream_service_create_unix(char *uri, int backlog) } if (!lib->caps->check(lib->caps, CAP_CHOWN)) { /* required to chown(2) service socket */ - DBG1(DBG_NET, "socket '%s' requires CAP_CHOWN capability", uri); - return NULL; + DBG1(DBG_NET, "cannot change ownership of socket '%s' without " + "CAP_CHOWN capability. socket directory should be accessible to " + "UID/GID under which the deamon will run", uri); } fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd == -1) @@ -58,7 +59,9 @@ stream_service_t *stream_service_create_unix(char *uri, int backlog) return NULL; } umask(old); - if (chown(addr.sun_path, lib->caps->get_uid(lib->caps), + /* only attempt to chown() socket if we have CAP_CHOWN */ + if (lib->caps->check(lib->caps, CAP_CHOWN) && + chown(addr.sun_path, lib->caps->get_uid(lib->caps), lib->caps->get_gid(lib->caps)) != 0) { DBG1(DBG_NET, "changing socket permissions for '%s' failed: %s", diff --git a/src/libstrongswan/utils/capabilities.c b/src/libstrongswan/utils/capabilities.c index ce5f550b5..bd74e7ce7 100644 --- a/src/libstrongswan/utils/capabilities.c +++ b/src/libstrongswan/utils/capabilities.c @@ -422,7 +422,10 @@ METHOD(capabilities_t, drop, bool, { #ifndef WIN32 #ifdef HAVE_PRCTL - prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); + if (has_capability(this, CAP_SETPCAP, NULL)) + { + prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); + } #endif if (this->uid && !init_supplementary_groups(this)) diff --git a/src/libstrongswan/utils/capabilities.h b/src/libstrongswan/utils/capabilities.h index 20c18554b..385a337e5 100644 --- a/src/libstrongswan/utils/capabilities.h +++ b/src/libstrongswan/utils/capabilities.h @@ -47,6 +47,9 @@ typedef struct capabilities_t capabilities_t; #ifndef CAP_DAC_OVERRIDE # define CAP_DAC_OVERRIDE 1 #endif +#ifndef CAP_SETPCAP +# define CAP_SETPCAP 8 +#endif /** * POSIX capability dropping abstraction layer. diff --git a/src/starter/starter.c b/src/starter/starter.c index 6c0739340..8ca1af29c 100644 --- a/src/starter/starter.c +++ b/src/starter/starter.c @@ -477,6 +477,7 @@ int main (int argc, char **argv) } } +#ifndef STARTER_ALLOW_NON_ROOT /* verify that we can start */ if (getuid() != 0) { @@ -484,6 +485,7 @@ int main (int argc, char **argv) cleanup(); exit(LSB_RC_NOT_ALLOWED); } +#endif if (check_pid(pid_file)) { @@ -520,6 +522,7 @@ int main (int argc, char **argv) exit(LSB_RC_INVALID_ARGUMENT); } +#ifndef SKIP_KERNEL_IPSEC_MODPROBES /* determine if we have a native netkey IPsec stack */ if (!starter_netkey_init()) { @@ -530,6 +533,7 @@ int main (int argc, char **argv) DBG1(DBG_APP, "no known IPsec stack detected, ignoring!"); } } +#endif last_reload = time_monotonic(NULL);