#include "syshead.h"
+#include "openvpn.h"
+#include "options.h"
+
#include "buffer.h"
#include "crypto.h"
#include "error.h"
#include <direct.h>
#endif
+#ifdef HAVE_LIBCAPNG
+#include <cap-ng.h>
+#include <sys/prctl.h>
+#endif
+
/* Redefine the top level directory of the filesystem
* to restrict access to files for security */
void
return ret;
}
-void
+static void
platform_user_set(const struct platform_state_user *state)
{
#if defined(HAVE_GETPWNAM) && defined(HAVE_SETUID)
return ret;
}
-void
+static void
platform_group_set(const struct platform_state_group *state)
{
#if defined(HAVE_GETGRNAM) && defined(HAVE_SETGID)
#endif
}
+/*
+ * Determine if we need to retain process capabilities. DCO and SITNL need it.
+ * Enforce it for DCO, but only try and soft-fail for SITNL to keep backwards compat.
+ *
+ * Returns the tri-state expected by platform_user_group_set.
+ * -1: try to keep caps, but continue if impossible
+ * 0: don't keep caps
+ * 1: keep caps, fail hard if impossible
+ */
+static int
+need_keep_caps(struct context *c)
+{
+ if (!c)
+ {
+ return -1;
+ }
+
+ if (dco_enabled(&c->options))
+ {
+#ifdef TARGET_LINUX
+ /* DCO on Linux does not work at all without CAP_NET_ADMIN */
+ return 1;
+#else
+ /* Windows/BSD/... has no equivalent capability mechanism */
+ return -1;
+#endif
+ }
+
+#ifdef ENABLE_SITNL
+ return -1;
+#else
+ return 0;
+#endif
+}
+
+/* Set user and group, retaining neccesary capabilities required by the platform.
+ *
+ * The keep_caps argument has 3 possible states:
+ * >0: Retain capabilities, and fail hard on failure to do so.
+ * ==0: Don't attempt to retain any capabilities, just sitch user/group.
+ * <0: Try to retain capabilities, but continue on failure.
+ */
+void platform_user_group_set(const struct platform_state_user *user_state,
+ const struct platform_state_group *group_state,
+ struct context *c)
+{
+ int keep_caps = need_keep_caps(c);
+ unsigned int err_flags = (keep_caps > 0) ? M_FATAL : M_NONFATAL;
+#ifdef HAVE_LIBCAPNG
+ int new_gid = -1, new_uid = -1;
+ int res;
+
+ if (keep_caps == 0)
+ {
+ goto fallback;
+ }
+
+ /*
+ * new_uid/new_gid defaults to -1, which will not make
+ * libcap-ng change the UID/GID unless configured
+ */
+ if (group_state->groupname && group_state->gr)
+ {
+ new_gid = group_state->gr->gr_gid;
+ }
+ if (user_state->username && user_state->pw)
+ {
+ new_uid = user_state->pw->pw_uid;
+ }
+
+ /* Prepare capabilities before dropping UID/GID */
+ capng_clear(CAPNG_SELECT_BOTH);
+ res = capng_update(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED, CAP_NET_ADMIN);
+ if (res < 0)
+ {
+ msg(err_flags, "capng_update(CAP_NET_ADMIN) failed: %d", res);
+ goto fallback;
+ }
+
+ /* Change to new UID/GID.
+ * capng_change_id() internally calls capng_apply() to apply prepared capabilities.
+ */
+ res = capng_change_id(new_uid, new_gid, CAPNG_DROP_SUPP_GRP | CAPNG_CLEAR_BOUNDING);
+ if (res == -4 || res == -6)
+ {
+ /* -4 and -6 mean failure of setuid/gid respectively.
+ There is no point for us to continue if those failed. */
+ msg(M_ERR, "capng_change_id('%s','%s') failed: %d",
+ user_state->username, group_state->groupname, res);
+ }
+ else if (res == -3)
+ {
+ msg(M_NONFATAL | M_ERRNO, "capng_change_id() failed applying capabilities");
+ msg(err_flags, "NOTE: previous error likely due to missing capability CAP_SETPCAP.");
+ goto fallback;
+ }
+ else if (res < 0)
+ {
+ msg(err_flags | M_ERRNO, "capng_change_id('%s','%s') failed retaining capabilities: %d",
+ user_state->username, group_state->groupname, res);
+ goto fallback;
+ }
+
+ if (new_uid >= 0)
+ {
+ msg(M_INFO, "UID set to %s", user_state->username);
+ }
+ if (new_gid >= 0)
+ {
+ msg(M_INFO, "GID set to %s", group_state->groupname);
+ }
+
+ msg(M_INFO, "Capabilities retained: CAP_NET_ADMIN");
+ return;
+
+fallback:
+ /* capng_change_id() can leave this flag clobbered on failure
+ * This is working around a bug in libcap-ng, which can leave the flag set
+ * on failure: https://github.com/stevegrubb/libcap-ng/issues/33 */
+ if (prctl(PR_GET_KEEPCAPS) && prctl(PR_SET_KEEPCAPS, 0) < 0)
+ {
+ msg(M_ERR, "Clearing KEEPCAPS flag failed");
+ }
+#endif /* HAVE_LIBCAPNG */
+
+ if (keep_caps)
+ {
+ msg(err_flags, "Unable to retain capabilities");
+ }
+
+ platform_group_set(group_state);
+ platform_user_set(user_state);
+}
+
/* Change process priority */
void
platform_nice(int niceval)