aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTobias Brunner <tobias@strongswan.org>2013-06-25 17:17:40 +0200
committerTobias Brunner <tobias@strongswan.org>2013-06-25 17:17:40 +0200
commit9da5a2ed1e72d1b68feefd5811a089d0881dd87d (patch)
tree2eeb8375e6ffb24321e1d7e3a4ba06183bf9eaec
parenta65024264379bacc2733282f2b59b8c14e3897c0 (diff)
parentac2ffde4ae8ceaddeab8b6f740804536033153e6 (diff)
downloadstrongswan-9da5a2ed1e72d1b68feefd5811a089d0881dd87d.tar.bz2
strongswan-9da5a2ed1e72d1b68feefd5811a089d0881dd87d.tar.xz
Merge branch 'check-caps'
Plugins may now ensure the process has all the required capabilities. Some minor changes to UID/GID handling are also included.
-rw-r--r--man/strongswan.conf.5.in6
-rw-r--r--src/charon-cmd/charon-cmd.c6
-rw-r--r--src/charon-nm/charon-nm.c27
-rw-r--r--src/charon-nm/nm/nm_backend.c7
-rw-r--r--src/charon-tkm/src/charon-tkm.c10
-rw-r--r--src/charon/charon.c31
-rw-r--r--src/libcharon/daemon.c8
-rw-r--r--src/libcharon/daemon.h6
-rw-r--r--src/libcharon/plugins/dhcp/dhcp_plugin.c11
-rw-r--r--src/libcharon/plugins/duplicheck/duplicheck_notify.c4
-rw-r--r--src/libcharon/plugins/duplicheck/duplicheck_plugin.c6
-rw-r--r--src/libcharon/plugins/error_notify/error_notify_plugin.c6
-rw-r--r--src/libcharon/plugins/error_notify/error_notify_socket.c4
-rw-r--r--src/libcharon/plugins/farp/farp_plugin.c6
-rw-r--r--src/libcharon/plugins/ha/ha_ctl.c4
-rw-r--r--src/libcharon/plugins/ha/ha_kernel.c4
-rw-r--r--src/libcharon/plugins/ha/ha_plugin.c6
-rw-r--r--src/libcharon/plugins/kernel_libipsec/kernel_libipsec_plugin.c7
-rw-r--r--src/libcharon/plugins/load_tester/load_tester_control.c4
-rw-r--r--src/libcharon/plugins/load_tester/load_tester_plugin.c6
-rw-r--r--src/libcharon/plugins/lookip/lookip_plugin.c6
-rw-r--r--src/libcharon/plugins/lookip/lookip_socket.c4
-rw-r--r--src/libcharon/plugins/smp/smp.c10
-rw-r--r--src/libcharon/plugins/socket_default/socket_default_socket.c12
-rw-r--r--src/libcharon/plugins/stroke/stroke_plugin.c6
-rw-r--r--src/libcharon/plugins/stroke/stroke_socket.c4
-rw-r--r--src/libcharon/plugins/whitelist/whitelist_control.c4
-rw-r--r--src/libcharon/plugins/whitelist/whitelist_plugin.c6
-rw-r--r--src/libcharon/plugins/xauth_pam/xauth_pam_plugin.c10
-rw-r--r--src/libhydra/hydra.c1
-rw-r--r--src/libhydra/plugins/kernel_netlink/kernel_netlink_plugin.c8
-rw-r--r--src/libhydra/plugins/kernel_pfkey/kernel_pfkey_plugin.c6
-rw-r--r--src/libstrongswan/library.c2
-rw-r--r--src/libstrongswan/library.h6
-rw-r--r--src/libstrongswan/utils/capabilities.c119
-rw-r--r--src/libstrongswan/utils/capabilities.h24
36 files changed, 326 insertions, 71 deletions
diff --git a/man/strongswan.conf.5.in b/man/strongswan.conf.5.in
index 065bb1c4d..fd8e2f216 100644
--- a/man/strongswan.conf.5.in
+++ b/man/strongswan.conf.5.in
@@ -174,6 +174,9 @@ used certificates.
Maximum size (in bytes) of a sent fragment when using the proprietary IKEv1
fragmentation extension.
.TP
+.BR charon.group
+Name of the group the daemon changes to after startup
+.TP
.BR charon.half_open_timeout " [30]"
Timeout in seconds for connecting IKE_SAs (also see IKE_SA_INIT DROPPING).
.TP
@@ -317,6 +320,9 @@ Section to define syslog loggers, see LOGGER CONFIGURATION
.TP
.BR charon.threads " [16]"
Number of worker threads in charon
+.TP
+.BR charon.user
+Name of the user the daemon changes to after startup
.SS charon.plugins subsection
.TP
.BR charon.plugins.android_log.loglevel " [1]"
diff --git a/src/charon-cmd/charon-cmd.c b/src/charon-cmd/charon-cmd.c
index f3059bea5..494e4f84e 100644
--- a/src/charon-cmd/charon-cmd.c
+++ b/src/charon-cmd/charon-cmd.c
@@ -169,13 +169,13 @@ static int run()
static bool lookup_uid_gid()
{
#ifdef IPSEC_USER
- if (!charon->caps->resolve_uid(charon->caps, IPSEC_USER))
+ if (!lib->caps->resolve_uid(lib->caps, IPSEC_USER))
{
return FALSE;
}
#endif
#ifdef IPSEC_GROUP
- if (!charon->caps->resolve_gid(charon->caps, IPSEC_GROUP))
+ if (!lib->caps->resolve_gid(lib->caps, IPSEC_GROUP))
{
return FALSE;
}
@@ -360,7 +360,7 @@ int main(int argc, char *argv[])
{
exit(SS_RC_INITIALIZATION_FAILED);
}
- if (!charon->caps->drop(charon->caps))
+ if (!lib->caps->drop(lib->caps))
{
exit(SS_RC_INITIALIZATION_FAILED);
}
diff --git a/src/charon-nm/charon-nm.c b/src/charon-nm/charon-nm.c
index d61ddee85..9ce6dbaeb 100644
--- a/src/charon-nm/charon-nm.c
+++ b/src/charon-nm/charon-nm.c
@@ -29,6 +29,17 @@
#include <nm/nm_backend.h>
/**
+ * Default user and group
+ */
+#ifndef IPSEC_USER
+#define IPSEC_USER NULL
+#endif
+
+#ifndef IPSEC_GROUP
+#define IPSEC_GROUP NULL
+#endif
+
+/**
* Hook in library for debugging messages
*/
extern void (*dbg) (debug_t group, level_t level, char *fmt, ...);
@@ -121,18 +132,20 @@ static void segv_handler(int signal)
*/
static bool lookup_uid_gid()
{
-#ifdef IPSEC_USER
- if (!charon->caps->resolve_uid(charon->caps, IPSEC_USER))
+ char *name;
+
+ name = lib->settings->get_str(lib->settings, "charon-nm.user",
+ IPSEC_USER);
+ if (name && !lib->caps->resolve_uid(lib->caps, name))
{
return FALSE;
}
-#endif
-#ifdef IPSEC_GROUP
- if (!charon->caps->resolve_gid(charon->caps, IPSEC_GROUP))
+ name = lib->settings->get_str(lib->settings, "charon-nm.group",
+ IPSEC_GROUP);
+ if (name && !lib->caps->resolve_gid(lib->caps, name))
{
return FALSE;
}
-#endif
return TRUE;
}
@@ -214,7 +227,7 @@ int main(int argc, char *argv[])
}
lib->plugins->status(lib->plugins, LEVEL_CTRL);
- if (!charon->caps->drop(charon->caps))
+ if (!lib->caps->drop(lib->caps))
{
DBG1(DBG_DMN, "capability dropping failed - aborting charon-nm");
goto deinit;
diff --git a/src/charon-nm/nm/nm_backend.c b/src/charon-nm/nm/nm_backend.c
index c18bf992a..c83978291 100644
--- a/src/charon-nm/nm/nm_backend.c
+++ b/src/charon-nm/nm/nm_backend.c
@@ -142,7 +142,12 @@ static bool nm_backend_init()
}
/* bypass file permissions to read from users ssh-agent */
- charon->caps->keep(charon->caps, CAP_DAC_OVERRIDE);
+ if (!lib->caps->keep(lib->caps, CAP_DAC_OVERRIDE))
+ {
+ DBG1(DBG_CFG, "NM backend requires CAP_DAC_OVERRIDE capability");
+ nm_backend_deinit();
+ return FALSE;
+ }
lib->processor->queue_job(lib->processor,
(job_t*)callback_job_create_with_prio((callback_job_cb_t)run, this,
diff --git a/src/charon-tkm/src/charon-tkm.c b/src/charon-tkm/src/charon-tkm.c
index 4e364e7be..14a735590 100644
--- a/src/charon-tkm/src/charon-tkm.c
+++ b/src/charon-tkm/src/charon-tkm.c
@@ -151,13 +151,13 @@ static void segv_handler(int signal)
static bool lookup_uid_gid()
{
#ifdef IPSEC_USER
- if (!charon->caps->resolve_uid(charon->caps, IPSEC_USER))
+ if (!lib->caps->resolve_uid(lib->caps, IPSEC_USER))
{
return FALSE;
}
#endif
#ifdef IPSEC_GROUP
- if (!charon->caps->resolve_gid(charon->caps, IPSEC_GROUP))
+ if (!lib->caps->resolve_gid(lib->caps, IPSEC_GROUP))
{
return FALSE;
}
@@ -201,8 +201,8 @@ static bool check_pidfile()
if (pidfile)
{
ignore_result(fchown(fileno(pidfile),
- charon->caps->get_uid(charon->caps),
- charon->caps->get_gid(charon->caps)));
+ lib->caps->get_uid(lib->caps),
+ lib->caps->get_gid(lib->caps)));
fprintf(pidfile, "%d\n", getpid());
fflush(pidfile);
}
@@ -327,7 +327,7 @@ int main(int argc, char *argv[])
goto deinit;
}
- if (!charon->caps->drop(charon->caps))
+ if (!lib->caps->drop(lib->caps))
{
DBG1(DBG_DMN, "capability dropping failed - aborting %s", dmn_name);
goto deinit;
diff --git a/src/charon/charon.c b/src/charon/charon.c
index eb7dd58e3..340f852cd 100644
--- a/src/charon/charon.c
+++ b/src/charon/charon.c
@@ -44,6 +44,17 @@
#define PID_FILE IPSEC_PIDDIR "/charon.pid"
/**
+ * Default user and group
+ */
+#ifndef IPSEC_USER
+#define IPSEC_USER NULL
+#endif
+
+#ifndef IPSEC_GROUP
+#define IPSEC_GROUP NULL
+#endif
+
+/**
* Global reference to PID file (required to truncate, if undeletable)
*/
static FILE *pidfile = NULL;
@@ -148,20 +159,20 @@ static void run()
*/
static bool lookup_uid_gid()
{
-#ifdef IPSEC_USER
- if (!charon->caps->resolve_uid(charon->caps, IPSEC_USER))
+ char *name;
+
+ name = lib->settings->get_str(lib->settings, "charon.user", IPSEC_USER);
+ if (name && !lib->caps->resolve_uid(lib->caps, name))
{
return FALSE;
}
-#endif
-#ifdef IPSEC_GROUP
- if (!charon->caps->resolve_gid(charon->caps, IPSEC_GROUP))
+ name = lib->settings->get_str(lib->settings, "charon.group", IPSEC_GROUP);
+ if (name && !lib->caps->resolve_gid(lib->caps, name))
{
return FALSE;
}
-#endif
#ifdef ANDROID
- charon->caps->set_uid(charon->caps, AID_VPN);
+ lib->caps->set_uid(lib->caps, AID_VPN);
#endif
return TRUE;
}
@@ -219,8 +230,8 @@ static bool check_pidfile()
if (pidfile)
{
ignore_result(fchown(fileno(pidfile),
- charon->caps->get_uid(charon->caps),
- charon->caps->get_gid(charon->caps)));
+ lib->caps->get_uid(lib->caps),
+ lib->caps->get_gid(lib->caps)));
fprintf(pidfile, "%d\n", getpid());
fflush(pidfile);
}
@@ -406,7 +417,7 @@ int main(int argc, char *argv[])
goto deinit;
}
- if (!charon->caps->drop(charon->caps))
+ if (!lib->caps->drop(lib->caps))
{
DBG1(DBG_DMN, "capability dropping failed - aborting charon");
goto deinit;
diff --git a/src/libcharon/daemon.c b/src/libcharon/daemon.c
index 70262b736..1ad80693a 100644
--- a/src/libcharon/daemon.c
+++ b/src/libcharon/daemon.c
@@ -33,10 +33,6 @@
#include <processing/jobs/start_action_job.h>
#include <threading/mutex.h>
-#ifndef CAP_NET_ADMIN
-#define CAP_NET_ADMIN 12
-#endif
-
#ifndef LOG_AUTHPRIV /* not defined on OpenSolaris */
#define LOG_AUTHPRIV LOG_AUTH
#endif
@@ -471,7 +467,6 @@ static void destroy(private_daemon_t *this)
DESTROY_IF(this->public.xauth);
DESTROY_IF(this->public.backends);
DESTROY_IF(this->public.socket);
- DESTROY_IF(this->public.caps);
/* rehook library logging, shutdown logging */
dbg = dbg_old;
@@ -581,7 +576,6 @@ private_daemon_t *daemon_create(const char *name)
.ref = 1,
);
charon = &this->public;
- this->public.caps = capabilities_create();
this->public.controller = controller_create();
this->public.eap = eap_manager_create();
this->public.xauth = xauth_manager_create();
@@ -591,8 +585,6 @@ private_daemon_t *daemon_create(const char *name)
this->public.shunts = shunt_manager_create();
this->kernel_handler = kernel_handler_create();
- this->public.caps->keep(this->public.caps, CAP_NET_ADMIN);
-
return this;
}
diff --git a/src/libcharon/daemon.h b/src/libcharon/daemon.h
index 2926d945b..24e623c44 100644
--- a/src/libcharon/daemon.h
+++ b/src/libcharon/daemon.h
@@ -163,7 +163,6 @@ typedef struct daemon_t daemon_t;
#include <config/backend_manager.h>
#include <sa/eap/eap_manager.h>
#include <sa/xauth/xauth_manager.h>
-#include <utils/capabilities.h>
#ifdef ME
#include <sa/ikev2/connect_manager.h>
@@ -273,11 +272,6 @@ struct daemon_t {
#endif /* ME */
/**
- * POSIX capability dropping
- */
- capabilities_t *caps;
-
- /**
* Name of the binary that uses the library (used for settings etc.)
*/
const char *name;
diff --git a/src/libcharon/plugins/dhcp/dhcp_plugin.c b/src/libcharon/plugins/dhcp/dhcp_plugin.c
index a31f12689..31195e25b 100644
--- a/src/libcharon/plugins/dhcp/dhcp_plugin.c
+++ b/src/libcharon/plugins/dhcp/dhcp_plugin.c
@@ -107,6 +107,17 @@ plugin_t *dhcp_plugin_create()
{
private_dhcp_plugin_t *this;
+ if (!lib->caps->keep(lib->caps, CAP_NET_BIND_SERVICE))
+ { /* required to bind DHCP socket (port 68) */
+ DBG1(DBG_NET, "dhcp plugin requires CAP_NET_BIND_SERVICE capability");
+ return NULL;
+ }
+ else if (!lib->caps->keep(lib->caps, CAP_NET_RAW))
+ { /* required to open DHCP receive socket (AF_PACKET) */
+ DBG1(DBG_NET, "dhcp plugin requires CAP_NET_RAW capability");
+ return NULL;
+ }
+
INIT(this,
.public = {
.plugin = {
diff --git a/src/libcharon/plugins/duplicheck/duplicheck_notify.c b/src/libcharon/plugins/duplicheck/duplicheck_notify.c
index cd5d4970b..1091258da 100644
--- a/src/libcharon/plugins/duplicheck/duplicheck_notify.c
+++ b/src/libcharon/plugins/duplicheck/duplicheck_notify.c
@@ -84,8 +84,8 @@ static bool open_socket(private_duplicheck_notify_t *this)
return FALSE;
}
umask(old);
- if (chown(addr.sun_path, charon->caps->get_uid(charon->caps),
- charon->caps->get_gid(charon->caps)) != 0)
+ if (chown(addr.sun_path, lib->caps->get_uid(lib->caps),
+ lib->caps->get_gid(lib->caps)) != 0)
{
DBG1(DBG_CFG, "changing duplicheck socket permissions failed: %s",
strerror(errno));
diff --git a/src/libcharon/plugins/duplicheck/duplicheck_plugin.c b/src/libcharon/plugins/duplicheck/duplicheck_plugin.c
index 4d018dbef..d6e652d59 100644
--- a/src/libcharon/plugins/duplicheck/duplicheck_plugin.c
+++ b/src/libcharon/plugins/duplicheck/duplicheck_plugin.c
@@ -98,6 +98,12 @@ plugin_t *duplicheck_plugin_create()
return NULL;
}
+ if (!lib->caps->keep(lib->caps, CAP_CHOWN))
+ { /* required to chown(2) notify socket */
+ DBG1(DBG_CFG, "duplicheck plugin requires CAP_CHOWN capability");
+ return NULL;
+ }
+
INIT(this,
.public = {
.plugin = {
diff --git a/src/libcharon/plugins/error_notify/error_notify_plugin.c b/src/libcharon/plugins/error_notify/error_notify_plugin.c
index a1e4351eb..48b3d94db 100644
--- a/src/libcharon/plugins/error_notify/error_notify_plugin.c
+++ b/src/libcharon/plugins/error_notify/error_notify_plugin.c
@@ -92,6 +92,12 @@ plugin_t *error_notify_plugin_create()
{
private_error_notify_plugin_t *this;
+ if (!lib->caps->keep(lib->caps, CAP_CHOWN))
+ { /* required to chown(2) notify socket */
+ DBG1(DBG_CFG, "error-notify plugin requires CAP_CHOWN capability");
+ return NULL;
+ }
+
INIT(this,
.public = {
.plugin = {
diff --git a/src/libcharon/plugins/error_notify/error_notify_socket.c b/src/libcharon/plugins/error_notify/error_notify_socket.c
index 3ea657ba5..2fc74202b 100644
--- a/src/libcharon/plugins/error_notify/error_notify_socket.c
+++ b/src/libcharon/plugins/error_notify/error_notify_socket.c
@@ -84,8 +84,8 @@ static bool open_socket(private_error_notify_socket_t *this)
return FALSE;
}
umask(old);
- if (chown(addr.sun_path, charon->caps->get_uid(charon->caps),
- charon->caps->get_gid(charon->caps)) != 0)
+ if (chown(addr.sun_path, lib->caps->get_uid(lib->caps),
+ lib->caps->get_gid(lib->caps)) != 0)
{
DBG1(DBG_CFG, "changing notify socket permissions failed: %s",
strerror(errno));
diff --git a/src/libcharon/plugins/farp/farp_plugin.c b/src/libcharon/plugins/farp/farp_plugin.c
index cbc0bcf82..d31defca4 100644
--- a/src/libcharon/plugins/farp/farp_plugin.c
+++ b/src/libcharon/plugins/farp/farp_plugin.c
@@ -92,6 +92,12 @@ plugin_t *farp_plugin_create()
{
private_farp_plugin_t *this;
+ if (!lib->caps->keep(lib->caps, CAP_NET_RAW))
+ { /* required to open ARP socket (AF_PACKET) */
+ DBG1(DBG_NET, "farp plugin requires CAP_NET_RAW capability");
+ return NULL;
+ }
+
INIT(this,
.public = {
.plugin = {
diff --git a/src/libcharon/plugins/ha/ha_ctl.c b/src/libcharon/plugins/ha/ha_ctl.c
index cb9af3aed..178a0349b 100644
--- a/src/libcharon/plugins/ha/ha_ctl.c
+++ b/src/libcharon/plugins/ha/ha_ctl.c
@@ -129,8 +129,8 @@ ha_ctl_t *ha_ctl_create(ha_segments_t *segments, ha_cache_t *cache)
}
umask(old);
}
- if (chown(HA_FIFO, charon->caps->get_uid(charon->caps),
- charon->caps->get_gid(charon->caps)) != 0)
+ if (chown(HA_FIFO, lib->caps->get_uid(lib->caps),
+ lib->caps->get_gid(lib->caps)) != 0)
{
DBG1(DBG_CFG, "changing HA FIFO permissions failed: %s",
strerror(errno));
diff --git a/src/libcharon/plugins/ha/ha_kernel.c b/src/libcharon/plugins/ha/ha_kernel.c
index c45339690..eed89e0bf 100644
--- a/src/libcharon/plugins/ha/ha_kernel.c
+++ b/src/libcharon/plugins/ha/ha_kernel.c
@@ -316,8 +316,8 @@ static void disable_all(private_ha_kernel_t *this)
{
while (enumerator->enumerate(enumerator, NULL, &file, NULL))
{
- if (chown(file, charon->caps->get_uid(charon->caps),
- charon->caps->get_gid(charon->caps)) != 0)
+ if (chown(file, lib->caps->get_uid(lib->caps),
+ lib->caps->get_gid(lib->caps)) != 0)
{
DBG1(DBG_CFG, "changing ClusterIP permissions failed: %s",
strerror(errno));
diff --git a/src/libcharon/plugins/ha/ha_plugin.c b/src/libcharon/plugins/ha/ha_plugin.c
index 677985c57..e7697dc4f 100644
--- a/src/libcharon/plugins/ha/ha_plugin.c
+++ b/src/libcharon/plugins/ha/ha_plugin.c
@@ -179,6 +179,12 @@ plugin_t *ha_plugin_create()
return NULL;
}
+ if (!lib->caps->keep(lib->caps, CAP_CHOWN))
+ { /* required to chown(2) control socket */
+ DBG1(DBG_CFG, "ha plugin requires CAP_CHOWN capability");
+ return NULL;
+ }
+
INIT(this,
.public = {
.plugin = {
diff --git a/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_plugin.c b/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_plugin.c
index d5f3bc248..bac3c1c45 100644
--- a/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_plugin.c
+++ b/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_plugin.c
@@ -102,6 +102,13 @@ plugin_t *kernel_libipsec_plugin_create()
{
private_kernel_libipsec_plugin_t *this;
+ if (!lib->caps->keep(lib->caps, CAP_NET_ADMIN))
+ { /* required to create TUN devices */
+ DBG1(DBG_KNL, "kernel-libipsec plugin requires CAP_NET_ADMIN "
+ "capability");
+ return NULL;
+ }
+
INIT(this,
.public = {
.plugin = {
diff --git a/src/libcharon/plugins/load_tester/load_tester_control.c b/src/libcharon/plugins/load_tester/load_tester_control.c
index 0c21c23ca..3c82b5c30 100644
--- a/src/libcharon/plugins/load_tester/load_tester_control.c
+++ b/src/libcharon/plugins/load_tester/load_tester_control.c
@@ -110,8 +110,8 @@ static bool open_socket(private_load_tester_control_t *this)
return FALSE;
}
umask(old);
- if (chown(addr.sun_path, charon->caps->get_uid(charon->caps),
- charon->caps->get_gid(charon->caps)) != 0)
+ if (chown(addr.sun_path, lib->caps->get_uid(lib->caps),
+ lib->caps->get_gid(lib->caps)) != 0)
{
DBG1(DBG_CFG, "changing load-tester socket permissions failed: %s",
strerror(errno));
diff --git a/src/libcharon/plugins/load_tester/load_tester_plugin.c b/src/libcharon/plugins/load_tester/load_tester_plugin.c
index 6fee2bf3b..a32a2a435 100644
--- a/src/libcharon/plugins/load_tester/load_tester_plugin.c
+++ b/src/libcharon/plugins/load_tester/load_tester_plugin.c
@@ -269,6 +269,12 @@ plugin_t *load_tester_plugin_create()
return NULL;
}
+ if (!lib->caps->keep(lib->caps, CAP_CHOWN))
+ { /* required to chown(2) control socket */
+ DBG1(DBG_CFG, "load-tester plugin requires CAP_CHOWN capability");
+ return NULL;
+ }
+
INIT(this,
.public = {
.plugin = {
diff --git a/src/libcharon/plugins/lookip/lookip_plugin.c b/src/libcharon/plugins/lookip/lookip_plugin.c
index 319d72753..89bdad92e 100644
--- a/src/libcharon/plugins/lookip/lookip_plugin.c
+++ b/src/libcharon/plugins/lookip/lookip_plugin.c
@@ -92,6 +92,12 @@ plugin_t *lookip_plugin_create()
{
private_lookip_plugin_t *this;
+ if (!lib->caps->keep(lib->caps, CAP_CHOWN))
+ { /* required to chown(2) control socket */
+ DBG1(DBG_CFG, "lookip plugin requires CAP_CHOWN capability");
+ return NULL;
+ }
+
INIT(this,
.public = {
.plugin = {
diff --git a/src/libcharon/plugins/lookip/lookip_socket.c b/src/libcharon/plugins/lookip/lookip_socket.c
index f2a469e92..b1a46f46a 100644
--- a/src/libcharon/plugins/lookip/lookip_socket.c
+++ b/src/libcharon/plugins/lookip/lookip_socket.c
@@ -94,8 +94,8 @@ static bool open_socket(private_lookip_socket_t *this)
return FALSE;
}
umask(old);
- if (chown(addr.sun_path, charon->caps->get_uid(charon->caps),
- charon->caps->get_gid(charon->caps)) != 0)
+ if (chown(addr.sun_path, lib->caps->get_uid(lib->caps),
+ lib->caps->get_gid(lib->caps)) != 0)
{
DBG1(DBG_CFG, "changing lookip socket permissions failed: %s",
strerror(errno));
diff --git a/src/libcharon/plugins/smp/smp.c b/src/libcharon/plugins/smp/smp.c
index ad5029d1c..d13b82216 100644
--- a/src/libcharon/plugins/smp/smp.c
+++ b/src/libcharon/plugins/smp/smp.c
@@ -739,6 +739,12 @@ plugin_t *smp_plugin_create()
private_smp_t *this;
mode_t old;
+ if (!lib->caps->keep(lib->caps, CAP_CHOWN))
+ { /* required to chown(2) control socket */
+ DBG1(DBG_CFG, "smp plugin requires CAP_CHOWN capability");
+ return NULL;
+ }
+
INIT(this,
.public = {
.plugin = {
@@ -768,8 +774,8 @@ plugin_t *smp_plugin_create()
return NULL;
}
umask(old);
- if (chown(unix_addr.sun_path, charon->caps->get_uid(charon->caps),
- charon->caps->get_gid(charon->caps)) != 0)
+ if (chown(unix_addr.sun_path, lib->caps->get_uid(lib->caps),
+ lib->caps->get_gid(lib->caps)) != 0)
{
DBG1(DBG_CFG, "changing XML socket permissions failed: %s", strerror(errno));
}
diff --git a/src/libcharon/plugins/socket_default/socket_default_socket.c b/src/libcharon/plugins/socket_default/socket_default_socket.c
index aa3d795fc..494bf57b1 100644
--- a/src/libcharon/plugins/socket_default/socket_default_socket.c
+++ b/src/libcharon/plugins/socket_default/socket_default_socket.c
@@ -692,6 +692,18 @@ socket_default_socket_t *socket_default_socket_create()
this->natt = 0;
}
+ if ((this->port && this->port < 1024) || (this->natt && this->natt < 1024))
+ {
+ if (!lib->caps->keep(lib->caps, CAP_NET_BIND_SERVICE))
+ {
+ /* required to bind ports < 1024 */
+ DBG1(DBG_NET, "socket-default plugin requires CAP_NET_BIND_SERVICE "
+ "capability");
+ destroy(this);
+ return NULL;
+ }
+ }
+
/* we allocate IPv6 sockets first as that will reserve randomly allocated
* ports also for IPv4. On OS X, we have to do it the other way round
* for the same effect. */
diff --git a/src/libcharon/plugins/stroke/stroke_plugin.c b/src/libcharon/plugins/stroke/stroke_plugin.c
index 4e47a120d..6c4687f4a 100644
--- a/src/libcharon/plugins/stroke/stroke_plugin.c
+++ b/src/libcharon/plugins/stroke/stroke_plugin.c
@@ -91,6 +91,12 @@ plugin_t *stroke_plugin_create()
{
private_stroke_plugin_t *this;
+ if (!lib->caps->keep(lib->caps, CAP_CHOWN))
+ { /* required to chown(2) stroke socket */
+ DBG1(DBG_CFG, "stroke plugin requires CAP_CHOWN capability");
+ return NULL;
+ }
+
INIT(this,
.public = {
.plugin = {
diff --git a/src/libcharon/plugins/stroke/stroke_socket.c b/src/libcharon/plugins/stroke/stroke_socket.c
index d152ecd70..931dba1f4 100644
--- a/src/libcharon/plugins/stroke/stroke_socket.c
+++ b/src/libcharon/plugins/stroke/stroke_socket.c
@@ -847,8 +847,8 @@ static bool open_socket(private_stroke_socket_t *this)
return FALSE;
}
umask(old);
- if (chown(socket_addr.sun_path, charon->caps->get_uid(charon->caps),
- charon->caps->get_gid(charon->caps)) != 0)
+ if (chown(socket_addr.sun_path, lib->caps->get_uid(lib->caps),
+ lib->caps->get_gid(lib->caps)) != 0)
{
DBG1(DBG_CFG, "changing stroke socket permissions failed: %s",
strerror(errno));
diff --git a/src/libcharon/plugins/whitelist/whitelist_control.c b/src/libcharon/plugins/whitelist/whitelist_control.c
index a75ea9aee..b90b62ac1 100644
--- a/src/libcharon/plugins/whitelist/whitelist_control.c
+++ b/src/libcharon/plugins/whitelist/whitelist_control.c
@@ -77,8 +77,8 @@ static bool open_socket(private_whitelist_control_t *this)
return FALSE;
}
umask(old);
- if (chown(addr.sun_path, charon->caps->get_uid(charon->caps),
- charon->caps->get_gid(charon->caps)) != 0)
+ if (chown(addr.sun_path, lib->caps->get_uid(lib->caps),
+ lib->caps->get_gid(lib->caps)) != 0)
{
DBG1(DBG_CFG, "changing whitelist socket permissions failed: %s",
strerror(errno));
diff --git a/src/libcharon/plugins/whitelist/whitelist_plugin.c b/src/libcharon/plugins/whitelist/whitelist_plugin.c
index 5ba3e1449..4f397d76e 100644
--- a/src/libcharon/plugins/whitelist/whitelist_plugin.c
+++ b/src/libcharon/plugins/whitelist/whitelist_plugin.c
@@ -92,6 +92,12 @@ plugin_t *whitelist_plugin_create()
{
private_whitelist_plugin_t *this;
+ if (!lib->caps->keep(lib->caps, CAP_CHOWN))
+ { /* required to chown(2) control socket */
+ DBG1(DBG_CFG, "whitelist plugin requires CAP_CHOWN capability");
+ return NULL;
+ }
+
INIT(this,
.public = {
.plugin = {
diff --git a/src/libcharon/plugins/xauth_pam/xauth_pam_plugin.c b/src/libcharon/plugins/xauth_pam/xauth_pam_plugin.c
index b9ba0b5ac..2ef9a6c8f 100644
--- a/src/libcharon/plugins/xauth_pam/xauth_pam_plugin.c
+++ b/src/libcharon/plugins/xauth_pam/xauth_pam_plugin.c
@@ -52,6 +52,13 @@ plugin_t *xauth_pam_plugin_create()
{
xauth_pam_plugin_t *this;
+ /* required for PAM authentication */
+ if (!lib->caps->keep(lib->caps, CAP_AUDIT_WRITE))
+ {
+ DBG1(DBG_DMN, "xauth-pam plugin requires CAP_AUDIT_WRITE capability");
+ return NULL;
+ }
+
INIT(this,
.plugin = {
.get_name = _get_name,
@@ -60,8 +67,5 @@ plugin_t *xauth_pam_plugin_create()
},
);
- /* required for PAM authentication */
- charon->caps->keep(charon->caps, CAP_AUDIT_WRITE);
-
return &this->plugin;
}
diff --git a/src/libhydra/hydra.c b/src/libhydra/hydra.c
index b199b2ffb..f531bd5f4 100644
--- a/src/libhydra/hydra.c
+++ b/src/libhydra/hydra.c
@@ -97,4 +97,3 @@ bool libhydra_init(const char *daemon)
}
return !this->integrity_failed;
}
-
diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_plugin.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_plugin.c
index 0eb00dadf..8d5a0d5e8 100644
--- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_plugin.c
+++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_plugin.c
@@ -65,6 +65,14 @@ plugin_t *kernel_netlink_plugin_create()
{
private_kernel_netlink_plugin_t *this;
+ if (!lib->caps->keep(lib->caps, CAP_NET_ADMIN))
+ { /* required to bind/use XFRM sockets / create/modify routing tables, but
+ * not if only the read-only parts of kernel-netlink-net are used, so
+ * we don't fail here */
+ DBG1(DBG_KNL, "kernel-netlink plugin might require CAP_NET_ADMIN "
+ "capability");
+ }
+
INIT(this,
.public = {
.plugin = {
diff --git a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_plugin.c b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_plugin.c
index 894175402..d2c00b0f2 100644
--- a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_plugin.c
+++ b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_plugin.c
@@ -62,6 +62,12 @@ plugin_t *kernel_pfkey_plugin_create()
{
private_kernel_pfkey_plugin_t *this;
+ if (!lib->caps->keep(lib->caps, CAP_NET_ADMIN))
+ { /* required to open PF_KEY sockets */
+ DBG1(DBG_KNL, "kernel-pfkey plugin requires CAP_NET_ADMIN capability");
+ return NULL;
+ }
+
INIT(this,
.public = {
.plugin = {
diff --git a/src/libstrongswan/library.c b/src/libstrongswan/library.c
index 174a4cbe9..05d984b18 100644
--- a/src/libstrongswan/library.c
+++ b/src/libstrongswan/library.c
@@ -89,6 +89,7 @@ void library_deinit()
this->public.creds->destroy(this->public.creds);
this->public.encoding->destroy(this->public.encoding);
this->public.crypto->destroy(this->public.crypto);
+ this->public.caps->destroy(this->public.caps);
this->public.proposal->destroy(this->public.proposal);
this->public.fetcher->destroy(this->public.fetcher);
this->public.resolver->destroy(this->public.resolver);
@@ -255,6 +256,7 @@ bool library_init(char *settings)
this->public.settings = settings_create(settings);
this->public.hosts = host_resolver_create();
this->public.proposal = proposal_keywords_create();
+ this->public.caps = capabilities_create();
this->public.crypto = crypto_factory_create();
this->public.creds = credential_factory_create();
this->public.credmgr = credential_manager_create();
diff --git a/src/libstrongswan/library.h b/src/libstrongswan/library.h
index 3b6d02002..1168da8fd 100644
--- a/src/libstrongswan/library.h
+++ b/src/libstrongswan/library.h
@@ -101,6 +101,7 @@
#include "credentials/credential_manager.h"
#include "credentials/cred_encoding.h"
#include "utils/chunk.h"
+#include "utils/capabilities.h"
#include "utils/integrity_checker.h"
#include "utils/leak_detective.h"
#include "utils/settings.h"
@@ -141,6 +142,11 @@ struct library_t {
proposal_keywords_t *proposal;
/**
+ * POSIX capability dropping
+ */
+ capabilities_t *caps;
+
+ /**
* crypto algorithm registry and factory
*/
crypto_factory_t *crypto;
diff --git a/src/libstrongswan/utils/capabilities.c b/src/libstrongswan/utils/capabilities.c
index 89f47820e..8bc75b633 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,9 +76,63 @@ struct private_capabilities_t {
#endif
};
-METHOD(capabilities_t, keep, void,
- private_capabilities_t *this, u_int cap)
+/**
+ * Verify that the current process has the given capability
+ */
+static bool has_capability(u_int cap)
+{
+#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)
{
+ if (!has_capability(cap))
+ {
+ return FALSE;
+ }
#ifdef CAPABILITIES_LIBCAP
cap_set_flag(this->caps, CAP_EFFECTIVE, 1, &cap, CAP_SET);
cap_set_flag(this->caps, CAP_INHERITABLE, 1, &cap, CAP_SET);
@@ -96,18 +150,71 @@ METHOD(capabilities_t, keep, void,
this->caps[i].permitted |= 1 << cap;
this->caps[i].inheritable |= 1 << cap;
#endif /* CAPABILITIES_NATIVE */
+ return TRUE;
+}
+
+/**
+ * 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);
+ groups = calloc(ngroups, sizeof(gid_t));
+ ngroups = getgroups(ngroups, groups);
+ if (ngroups == -1)
+ {
+ DBG1(DBG_LIB, "getting groups for current process failed: %s",
+ strerror(errno));
+ return FALSE;
+ }
+ for (i = 0; i < ngroups; i++)
+ {
+ if (group == groups[i])
+ {
+ found = TRUE;
+ break;
+ }
+ }
+ free(groups);
+ return found;
+}
+
+METHOD(capabilities_t, keep, bool,
+ private_capabilities_t *this, u_int cap)
+{
+ 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 */
+ return TRUE;
+ }
+ }
+ }
+ return keep_capability(this, cap);
}
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,
@@ -271,7 +378,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;
}
diff --git a/src/libstrongswan/utils/capabilities.h b/src/libstrongswan/utils/capabilities.h
index cd23cbf10..4128909b6 100644
--- a/src/libstrongswan/utils/capabilities.h
+++ b/src/libstrongswan/utils/capabilities.h
@@ -1,4 +1,6 @@
/*
+ * Copyright (C) 2013 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
* Copyright (C) 2012 Martin Willi
* Copyright (C) 2012 revosec AG
*
@@ -21,6 +23,8 @@
#ifndef CAPABILITIES_H_
#define CAPABILITIES_H_
+typedef struct capabilities_t capabilities_t;
+
#include <library.h>
#ifdef HAVE_SYS_CAPABILITY_H
# include <sys/capability.h>
@@ -28,7 +32,18 @@
# include <linux/capability.h>
#endif
-typedef struct capabilities_t capabilities_t;
+#ifndef CAP_CHOWN
+# define CAP_CHOWN 0
+#endif
+#ifndef CAP_NET_BIND_SERVICE
+# define CAP_NET_BIND_SERVICE 10
+#endif
+#ifndef CAP_NET_ADMIN
+# define CAP_NET_ADMIN 12
+#endif
+#ifndef CAP_NET_RAW
+# define CAP_NET_RAW 13
+#endif
/**
* POSIX capability dropping abstraction layer.
@@ -36,11 +51,14 @@ typedef struct capabilities_t capabilities_t;
struct capabilities_t {
/**
- * Register a capability to keep while calling drop().
+ * Register a capability to keep while calling drop(). Verifies that the
+ * capability is currently held.
*
* @param cap capability to keep
+ * @return FALSE if the capability is currently not held
*/
- void (*keep)(capabilities_t *this, u_int cap);
+ bool (*keep)(capabilities_t *this,
+ u_int cap) __attribute__((warn_unused_result));
/**
* Get the user ID set through set_uid/resolve_uid.