summaryrefslogtreecommitdiff
path: root/src/libstrongswan/utils/capabilities.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstrongswan/utils/capabilities.c')
-rw-r--r--src/libstrongswan/utils/capabilities.c152
1 files changed, 139 insertions, 13 deletions
diff --git a/src/libstrongswan/utils/capabilities.c b/src/libstrongswan/utils/capabilities.c
index 44a14496c..c5e90b6c3 100644
--- a/src/libstrongswan/utils/capabilities.c
+++ b/src/libstrongswan/utils/capabilities.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012-2013 Tobias Brunner
* Hochschule fuer Technik Rapperswil
* Copyright (C) 2012 Martin Willi
* Copyright (C) 2012 revosec AG
@@ -76,8 +76,116 @@ struct private_capabilities_t {
#endif
};
-METHOD(capabilities_t, keep, void,
- private_capabilities_t *this, u_int cap)
+/**
+ * Returns TRUE if the current process/user is member of the given group
+ */
+static bool has_group(gid_t group)
+{
+ gid_t *groups;
+ long ngroups, i;
+ bool found = FALSE;
+
+ if (group == getegid())
+ { /* it's unspecified if this is part of the list below or not */
+ return TRUE;
+ }
+ ngroups = sysconf(_SC_NGROUPS_MAX);
+ if (ngroups == -1)
+ {
+ DBG1(DBG_LIB, "getting groups for current process failed: %s",
+ strerror(errno));
+ return FALSE;
+ }
+ groups = calloc(ngroups + 1, sizeof(gid_t));
+ ngroups = getgroups(ngroups, groups);
+ if (ngroups == -1)
+ {
+ DBG1(DBG_LIB, "getting groups for current process failed: %s",
+ strerror(errno));
+ free(groups);
+ return FALSE;
+ }
+ for (i = 0; i < ngroups; i++)
+ {
+ if (group == groups[i])
+ {
+ found = TRUE;
+ break;
+ }
+ }
+ free(groups);
+ return found;
+}
+
+/**
+ * Verify that the current process has the given capability
+ */
+static bool has_capability(private_capabilities_t *this, u_int cap,
+ bool *ignore)
+{
+ if (cap == CAP_CHOWN)
+ { /* if new files/UNIX sockets are created they should be owned by the
+ * configured user and group. This requires a call to chown(2). But
+ * CAP_CHOWN is not always required. */
+ if (!this->uid || geteuid() == this->uid)
+ { /* if the owner does not change CAP_CHOWN is not needed */
+ if (!this->gid || has_group(this->gid))
+ { /* the same applies if the owner is a member of the group */
+ if (ignore)
+ { /* we don't have to keep this, if requested */
+ *ignore = TRUE;
+ }
+ return TRUE;
+ }
+ }
+ }
+#ifndef CAPABILITIES
+ /* if we can't check the actual capabilities assume only root has it */
+ return geteuid() == 0;
+#endif /* !CAPABILITIES */
+#ifdef CAPABILITIES_LIBCAP
+ cap_flag_value_t val;
+ cap_t caps;
+ bool ok;
+
+ caps = cap_get_proc();
+ if (!caps)
+ {
+ return FALSE;
+ }
+ ok = cap_get_flag(caps, cap, CAP_PERMITTED, &val) == 0 && val == CAP_SET;
+ cap_free(caps);
+ return ok;
+#endif /* CAPABILITIES_LIBCAP */
+#ifdef CAPABILITIES_NATIVE
+ struct __user_cap_header_struct header = {
+#if defined(_LINUX_CAPABILITY_VERSION_3)
+ .version = _LINUX_CAPABILITY_VERSION_3,
+#elif defined(_LINUX_CAPABILITY_VERSION_2)
+ .version = _LINUX_CAPABILITY_VERSION_2,
+#elif defined(_LINUX_CAPABILITY_VERSION_1)
+ .version = _LINUX_CAPABILITY_VERSION_1,
+#else
+ .version = _LINUX_CAPABILITY_VERSION,
+#endif
+ };
+ struct __user_cap_data_struct caps[2];
+ int i = 0;
+
+ if (cap >= 32)
+ {
+ i++;
+ cap -= 32;
+ }
+ return capget(&header, caps) == 0 && caps[i].permitted & (1 << cap);
+#endif /* CAPABILITIES_NATIVE */
+}
+
+/**
+ * Keep the given capability if it is held by the current process. Returns
+ * FALSE, if this is not the case.
+ */
+static bool keep_capability(private_capabilities_t *this, u_int cap)
{
#ifdef CAPABILITIES_LIBCAP
cap_set_flag(this->caps, CAP_EFFECTIVE, 1, &cap, CAP_SET);
@@ -96,18 +204,41 @@ METHOD(capabilities_t, keep, void,
this->caps[i].permitted |= 1 << cap;
this->caps[i].inheritable |= 1 << cap;
#endif /* CAPABILITIES_NATIVE */
+ return TRUE;
+}
+
+METHOD(capabilities_t, keep, bool,
+ private_capabilities_t *this, u_int cap)
+{
+ bool ignore = FALSE;
+
+ if (!has_capability(this, cap, &ignore))
+ {
+ return FALSE;
+ }
+ else if (ignore)
+ { /* don't keep capabilities that are not required */
+ return TRUE;
+ }
+ return keep_capability(this, cap);
+}
+
+METHOD(capabilities_t, check, bool,
+ private_capabilities_t *this, u_int cap)
+{
+ return has_capability(this, cap, NULL);
}
METHOD(capabilities_t, get_uid, uid_t,
private_capabilities_t *this)
{
- return this->uid;
+ return this->uid ?: geteuid();
}
METHOD(capabilities_t, get_gid, gid_t,
private_capabilities_t *this)
{
- return this->gid;
+ return this->gid ?: getegid();
}
METHOD(capabilities_t, set_uid, void,
@@ -225,7 +356,7 @@ METHOD(capabilities_t, drop, bool,
prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
#endif
- if (!init_supplementary_groups(this))
+ if (this->uid && !init_supplementary_groups(this))
{
DBG1(DBG_LIB, "initializing supplementary groups for %u failed",
this->uid);
@@ -271,7 +402,7 @@ METHOD(capabilities_t, drop, bool,
#endif /* CAPABILITIES_NATIVE */
#ifdef CAPABILITIES
DBG1(DBG_LIB, "dropped capabilities, running as uid %u, gid %u",
- this->uid, this->gid);
+ geteuid(), getegid());
#endif /* CAPABILITIES */
return TRUE;
}
@@ -298,6 +429,7 @@ capabilities_t *capabilities_create()
INIT(this,
.public = {
.keep = _keep,
+ .check = _check,
.get_uid = _get_uid,
.get_gid = _get_gid,
.set_uid = _set_uid,
@@ -309,15 +441,9 @@ capabilities_t *capabilities_create()
},
);
-#ifdef CAPABILITIES
#ifdef CAPABILITIES_LIBCAP
this->caps = cap_init();
#endif /* CAPABILITIES_LIBCAP */
- if (lib->leak_detective)
- {
- keep(this, CAP_SYS_NICE);
- }
-#endif /* CAPABILITIES */
#ifdef EMULATE_R_FUNCS
this->mutex = mutex_create(MUTEX_TYPE_DEFAULT);