blob: 41fee03470f6d1fe69bdcd7a782a72d14df128b8 [file] [log] [blame]
From 4dc6419db2d263a38df6daf9ad6dcc58d4558e8b Mon Sep 17 00:00:00 2001
From: Micah Morton <mortonm@chromium.org>
Date: Fri, 4 May 2018 12:44:52 -0700
Subject: [PATCH] Allow strongSwan to be spawned as non-root user.
NOTE: This patch has been upstreamed as of
https://github.com/strongswan/strongswan/pull/105 , so shouldn't need to
be carried forward.
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
supports starting as a non-root user.
Tested with strongSwan 5.5.3.
---
src/charon/charon.c | 21 ++++++++++++---
.../networking/streams/stream_service_unix.c | 31 ++++++++++++++++++----
src/libstrongswan/utils/capabilities.c | 5 +++-
src/libstrongswan/utils/capabilities.h | 4 +++
src/starter/starter.c | 7 ++---
5 files changed, 56 insertions(+), 12 deletions(-)
diff --git a/src/charon/charon.c b/src/charon/charon.c
index 520cb3c..5170cbd 100644
--- a/src/charon/charon.c
+++ b/src/charon/charon.c
@@ -224,9 +224,24 @@ static bool check_pidfile()
DBG1(DBG_LIB, "setting FD_CLOEXEC for '"PID_FILE"' failed: %s",
strerror(errno));
}
- ignore_result(fchown(fileno(pidfile),
- lib->caps->get_uid(lib->caps),
- lib->caps->get_gid(lib->caps)));
+ /* Only change owner of the pidfile if we have CAP_CHOWN. Otherwise,
+ * attempt to change group of pidfile to group under which charon
+ * runs after dropping caps. This requires the user that charon
+ * starts as to:
+ * a) Have write access to the socket dir.
+ * b) Belong to the group that charon will run under after dropping
+ * caps.
+ */
+ if (lib->caps->check(lib->caps, CAP_CHOWN))
+ {
+ ignore_result(fchown(fileno(pidfile),
+ lib->caps->get_uid(lib->caps),
+ lib->caps->get_gid(lib->caps)));
+ } else {
+ ignore_result(fchown(fileno(pidfile),
+ -1,
+ lib->caps->get_gid(lib->caps)));
+ }
fprintf(pidfile, "%d\n", getpid());
fflush(pidfile);
}
diff --git a/src/libstrongswan/networking/streams/stream_service_unix.c b/src/libstrongswan/networking/streams/stream_service_unix.c
index 1ed27c4..f4bd30a 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 deamon will run", uri);
}
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd == -1)
@@ -58,12 +59,32 @@ 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 change owner of socket if we have CAP_CHOWN. Otherwise,
+ * attempt to change group of socket to group under which charon runs after
+ * dropping caps. This requires the user that charon starts as to:
+ * a) Have write access to the socket dir.
+ * b) Belong to the group that charon will run under after dropping caps.
+ *
+ */
+ if (lib->caps->check(lib->caps, CAP_CHOWN))
+ {
+ if (chown(addr.sun_path, lib->caps->get_uid(lib->caps),
lib->caps->get_gid(lib->caps)) != 0)
+ {
+ DBG1(DBG_NET, "changing socket owner/group for '%s' failed: %s",
+ uri, strerror(errno));
+ }
+ } else
{
- DBG1(DBG_NET, "changing socket permissions for '%s' failed: %s",
- uri, strerror(errno));
+ if (chown(addr.sun_path, -1, lib->caps->get_gid(lib->caps)) != 0)
+ {
+ DBG1(DBG_NET, "changing socket group for '%s' failed: %s",
+ uri, strerror(errno));
+ }
+
}
+
if (listen(fd, backlog) < 0)
{
DBG1(DBG_NET, "listen on socket '%s' failed: %s", uri, strerror(errno));
diff --git a/src/libstrongswan/utils/capabilities.c b/src/libstrongswan/utils/capabilities.c
index ce5f550..bd74e7c 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 20c1855..6b17119 100644
--- a/src/libstrongswan/utils/capabilities.h
+++ b/src/libstrongswan/utils/capabilities.h
@@ -47,6 +47,10 @@ 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 51a42a5..22f0fc5 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,7 +485,7 @@ int main (int argc, char **argv)
cleanup();
exit(LSB_RC_NOT_ALLOWED);
}
-
+#endif
if (check_pid(pid_file))
{
DBG1(DBG_APP, "%s is already running (%s exists) -- skipping daemon start",
@@ -519,7 +520,7 @@ int main (int argc, char **argv)
cleanup();
exit(LSB_RC_INVALID_ARGUMENT);
}
-
+#ifndef SKIP_KERNEL_IPSEC_MODPROBES
/* determine if we have a native netkey IPsec stack */
if (!starter_netkey_init())
{
@@ -530,7 +531,7 @@ int main (int argc, char **argv)
DBG1(DBG_APP, "no known IPsec stack detected, ignoring!");
}
}
-
+#endif
last_reload = time_monotonic(NULL);
if (check_pid(starter_pid_file))
--
2.13.5