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.
This commit is contained in:
Tobias Brunner
2013-06-25 17:17:40 +02:00
36 changed files with 326 additions and 71 deletions
+6
View File
@@ -174,6 +174,9 @@ used certificates.
Maximum size (in bytes) of a sent fragment when using the proprietary IKEv1 Maximum size (in bytes) of a sent fragment when using the proprietary IKEv1
fragmentation extension. fragmentation extension.
.TP .TP
.BR charon.group
Name of the group the daemon changes to after startup
.TP
.BR charon.half_open_timeout " [30]" .BR charon.half_open_timeout " [30]"
Timeout in seconds for connecting IKE_SAs (also see IKE_SA_INIT DROPPING). Timeout in seconds for connecting IKE_SAs (also see IKE_SA_INIT DROPPING).
.TP .TP
@@ -317,6 +320,9 @@ Section to define syslog loggers, see LOGGER CONFIGURATION
.TP .TP
.BR charon.threads " [16]" .BR charon.threads " [16]"
Number of worker threads in charon Number of worker threads in charon
.TP
.BR charon.user
Name of the user the daemon changes to after startup
.SS charon.plugins subsection .SS charon.plugins subsection
.TP .TP
.BR charon.plugins.android_log.loglevel " [1]" .BR charon.plugins.android_log.loglevel " [1]"
+3 -3
View File
@@ -169,13 +169,13 @@ static int run()
static bool lookup_uid_gid() static bool lookup_uid_gid()
{ {
#ifdef IPSEC_USER #ifdef IPSEC_USER
if (!charon->caps->resolve_uid(charon->caps, IPSEC_USER)) if (!lib->caps->resolve_uid(lib->caps, IPSEC_USER))
{ {
return FALSE; return FALSE;
} }
#endif #endif
#ifdef IPSEC_GROUP #ifdef IPSEC_GROUP
if (!charon->caps->resolve_gid(charon->caps, IPSEC_GROUP)) if (!lib->caps->resolve_gid(lib->caps, IPSEC_GROUP))
{ {
return FALSE; return FALSE;
} }
@@ -360,7 +360,7 @@ int main(int argc, char *argv[])
{ {
exit(SS_RC_INITIALIZATION_FAILED); exit(SS_RC_INITIALIZATION_FAILED);
} }
if (!charon->caps->drop(charon->caps)) if (!lib->caps->drop(lib->caps))
{ {
exit(SS_RC_INITIALIZATION_FAILED); exit(SS_RC_INITIALIZATION_FAILED);
} }
+20 -7
View File
@@ -28,6 +28,17 @@
#include <nm/nm_backend.h> #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 * Hook in library for debugging messages
*/ */
@@ -121,18 +132,20 @@ static void segv_handler(int signal)
*/ */
static bool lookup_uid_gid() static bool lookup_uid_gid()
{ {
#ifdef IPSEC_USER char *name;
if (!charon->caps->resolve_uid(charon->caps, IPSEC_USER))
name = lib->settings->get_str(lib->settings, "charon-nm.user",
IPSEC_USER);
if (name && !lib->caps->resolve_uid(lib->caps, name))
{ {
return FALSE; return FALSE;
} }
#endif name = lib->settings->get_str(lib->settings, "charon-nm.group",
#ifdef IPSEC_GROUP IPSEC_GROUP);
if (!charon->caps->resolve_gid(charon->caps, IPSEC_GROUP)) if (name && !lib->caps->resolve_gid(lib->caps, name))
{ {
return FALSE; return FALSE;
} }
#endif
return TRUE; return TRUE;
} }
@@ -214,7 +227,7 @@ int main(int argc, char *argv[])
} }
lib->plugins->status(lib->plugins, LEVEL_CTRL); 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"); DBG1(DBG_DMN, "capability dropping failed - aborting charon-nm");
goto deinit; goto deinit;
+6 -1
View File
@@ -142,7 +142,12 @@ static bool nm_backend_init()
} }
/* bypass file permissions to read from users ssh-agent */ /* 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, lib->processor->queue_job(lib->processor,
(job_t*)callback_job_create_with_prio((callback_job_cb_t)run, this, (job_t*)callback_job_create_with_prio((callback_job_cb_t)run, this,
+5 -5
View File
@@ -151,13 +151,13 @@ static void segv_handler(int signal)
static bool lookup_uid_gid() static bool lookup_uid_gid()
{ {
#ifdef IPSEC_USER #ifdef IPSEC_USER
if (!charon->caps->resolve_uid(charon->caps, IPSEC_USER)) if (!lib->caps->resolve_uid(lib->caps, IPSEC_USER))
{ {
return FALSE; return FALSE;
} }
#endif #endif
#ifdef IPSEC_GROUP #ifdef IPSEC_GROUP
if (!charon->caps->resolve_gid(charon->caps, IPSEC_GROUP)) if (!lib->caps->resolve_gid(lib->caps, IPSEC_GROUP))
{ {
return FALSE; return FALSE;
} }
@@ -201,8 +201,8 @@ static bool check_pidfile()
if (pidfile) if (pidfile)
{ {
ignore_result(fchown(fileno(pidfile), ignore_result(fchown(fileno(pidfile),
charon->caps->get_uid(charon->caps), lib->caps->get_uid(lib->caps),
charon->caps->get_gid(charon->caps))); lib->caps->get_gid(lib->caps)));
fprintf(pidfile, "%d\n", getpid()); fprintf(pidfile, "%d\n", getpid());
fflush(pidfile); fflush(pidfile);
} }
@@ -327,7 +327,7 @@ int main(int argc, char *argv[])
goto deinit; goto deinit;
} }
if (!charon->caps->drop(charon->caps)) if (!lib->caps->drop(lib->caps))
{ {
DBG1(DBG_DMN, "capability dropping failed - aborting %s", dmn_name); DBG1(DBG_DMN, "capability dropping failed - aborting %s", dmn_name);
goto deinit; goto deinit;
+21 -10
View File
@@ -43,6 +43,17 @@
*/ */
#define PID_FILE IPSEC_PIDDIR "/charon.pid" #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) * Global reference to PID file (required to truncate, if undeletable)
*/ */
@@ -148,20 +159,20 @@ static void run()
*/ */
static bool lookup_uid_gid() static bool lookup_uid_gid()
{ {
#ifdef IPSEC_USER char *name;
if (!charon->caps->resolve_uid(charon->caps, IPSEC_USER))
name = lib->settings->get_str(lib->settings, "charon.user", IPSEC_USER);
if (name && !lib->caps->resolve_uid(lib->caps, name))
{ {
return FALSE; return FALSE;
} }
#endif name = lib->settings->get_str(lib->settings, "charon.group", IPSEC_GROUP);
#ifdef IPSEC_GROUP if (name && !lib->caps->resolve_gid(lib->caps, name))
if (!charon->caps->resolve_gid(charon->caps, IPSEC_GROUP))
{ {
return FALSE; return FALSE;
} }
#endif
#ifdef ANDROID #ifdef ANDROID
charon->caps->set_uid(charon->caps, AID_VPN); lib->caps->set_uid(lib->caps, AID_VPN);
#endif #endif
return TRUE; return TRUE;
} }
@@ -219,8 +230,8 @@ static bool check_pidfile()
if (pidfile) if (pidfile)
{ {
ignore_result(fchown(fileno(pidfile), ignore_result(fchown(fileno(pidfile),
charon->caps->get_uid(charon->caps), lib->caps->get_uid(lib->caps),
charon->caps->get_gid(charon->caps))); lib->caps->get_gid(lib->caps)));
fprintf(pidfile, "%d\n", getpid()); fprintf(pidfile, "%d\n", getpid());
fflush(pidfile); fflush(pidfile);
} }
@@ -406,7 +417,7 @@ int main(int argc, char *argv[])
goto deinit; goto deinit;
} }
if (!charon->caps->drop(charon->caps)) if (!lib->caps->drop(lib->caps))
{ {
DBG1(DBG_DMN, "capability dropping failed - aborting charon"); DBG1(DBG_DMN, "capability dropping failed - aborting charon");
goto deinit; goto deinit;
-8
View File
@@ -33,10 +33,6 @@
#include <processing/jobs/start_action_job.h> #include <processing/jobs/start_action_job.h>
#include <threading/mutex.h> #include <threading/mutex.h>
#ifndef CAP_NET_ADMIN
#define CAP_NET_ADMIN 12
#endif
#ifndef LOG_AUTHPRIV /* not defined on OpenSolaris */ #ifndef LOG_AUTHPRIV /* not defined on OpenSolaris */
#define LOG_AUTHPRIV LOG_AUTH #define LOG_AUTHPRIV LOG_AUTH
#endif #endif
@@ -471,7 +467,6 @@ static void destroy(private_daemon_t *this)
DESTROY_IF(this->public.xauth); DESTROY_IF(this->public.xauth);
DESTROY_IF(this->public.backends); DESTROY_IF(this->public.backends);
DESTROY_IF(this->public.socket); DESTROY_IF(this->public.socket);
DESTROY_IF(this->public.caps);
/* rehook library logging, shutdown logging */ /* rehook library logging, shutdown logging */
dbg = dbg_old; dbg = dbg_old;
@@ -581,7 +576,6 @@ private_daemon_t *daemon_create(const char *name)
.ref = 1, .ref = 1,
); );
charon = &this->public; charon = &this->public;
this->public.caps = capabilities_create();
this->public.controller = controller_create(); this->public.controller = controller_create();
this->public.eap = eap_manager_create(); this->public.eap = eap_manager_create();
this->public.xauth = xauth_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->public.shunts = shunt_manager_create();
this->kernel_handler = kernel_handler_create(); this->kernel_handler = kernel_handler_create();
this->public.caps->keep(this->public.caps, CAP_NET_ADMIN);
return this; return this;
} }
-6
View File
@@ -163,7 +163,6 @@ typedef struct daemon_t daemon_t;
#include <config/backend_manager.h> #include <config/backend_manager.h>
#include <sa/eap/eap_manager.h> #include <sa/eap/eap_manager.h>
#include <sa/xauth/xauth_manager.h> #include <sa/xauth/xauth_manager.h>
#include <utils/capabilities.h>
#ifdef ME #ifdef ME
#include <sa/ikev2/connect_manager.h> #include <sa/ikev2/connect_manager.h>
@@ -272,11 +271,6 @@ struct daemon_t {
mediation_manager_t *mediation_manager; mediation_manager_t *mediation_manager;
#endif /* ME */ #endif /* ME */
/**
* POSIX capability dropping
*/
capabilities_t *caps;
/** /**
* Name of the binary that uses the library (used for settings etc.) * Name of the binary that uses the library (used for settings etc.)
*/ */
+11
View File
@@ -107,6 +107,17 @@ plugin_t *dhcp_plugin_create()
{ {
private_dhcp_plugin_t *this; 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, INIT(this,
.public = { .public = {
.plugin = { .plugin = {
@@ -84,8 +84,8 @@ static bool open_socket(private_duplicheck_notify_t *this)
return FALSE; return FALSE;
} }
umask(old); umask(old);
if (chown(addr.sun_path, charon->caps->get_uid(charon->caps), if (chown(addr.sun_path, lib->caps->get_uid(lib->caps),
charon->caps->get_gid(charon->caps)) != 0) lib->caps->get_gid(lib->caps)) != 0)
{ {
DBG1(DBG_CFG, "changing duplicheck socket permissions failed: %s", DBG1(DBG_CFG, "changing duplicheck socket permissions failed: %s",
strerror(errno)); strerror(errno));
@@ -98,6 +98,12 @@ plugin_t *duplicheck_plugin_create()
return NULL; 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, INIT(this,
.public = { .public = {
.plugin = { .plugin = {
@@ -92,6 +92,12 @@ plugin_t *error_notify_plugin_create()
{ {
private_error_notify_plugin_t *this; 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, INIT(this,
.public = { .public = {
.plugin = { .plugin = {
@@ -84,8 +84,8 @@ static bool open_socket(private_error_notify_socket_t *this)
return FALSE; return FALSE;
} }
umask(old); umask(old);
if (chown(addr.sun_path, charon->caps->get_uid(charon->caps), if (chown(addr.sun_path, lib->caps->get_uid(lib->caps),
charon->caps->get_gid(charon->caps)) != 0) lib->caps->get_gid(lib->caps)) != 0)
{ {
DBG1(DBG_CFG, "changing notify socket permissions failed: %s", DBG1(DBG_CFG, "changing notify socket permissions failed: %s",
strerror(errno)); strerror(errno));
+6
View File
@@ -92,6 +92,12 @@ plugin_t *farp_plugin_create()
{ {
private_farp_plugin_t *this; 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, INIT(this,
.public = { .public = {
.plugin = { .plugin = {
+2 -2
View File
@@ -129,8 +129,8 @@ ha_ctl_t *ha_ctl_create(ha_segments_t *segments, ha_cache_t *cache)
} }
umask(old); umask(old);
} }
if (chown(HA_FIFO, charon->caps->get_uid(charon->caps), if (chown(HA_FIFO, lib->caps->get_uid(lib->caps),
charon->caps->get_gid(charon->caps)) != 0) lib->caps->get_gid(lib->caps)) != 0)
{ {
DBG1(DBG_CFG, "changing HA FIFO permissions failed: %s", DBG1(DBG_CFG, "changing HA FIFO permissions failed: %s",
strerror(errno)); strerror(errno));
+2 -2
View File
@@ -316,8 +316,8 @@ static void disable_all(private_ha_kernel_t *this)
{ {
while (enumerator->enumerate(enumerator, NULL, &file, NULL)) while (enumerator->enumerate(enumerator, NULL, &file, NULL))
{ {
if (chown(file, charon->caps->get_uid(charon->caps), if (chown(file, lib->caps->get_uid(lib->caps),
charon->caps->get_gid(charon->caps)) != 0) lib->caps->get_gid(lib->caps)) != 0)
{ {
DBG1(DBG_CFG, "changing ClusterIP permissions failed: %s", DBG1(DBG_CFG, "changing ClusterIP permissions failed: %s",
strerror(errno)); strerror(errno));
+6
View File
@@ -179,6 +179,12 @@ plugin_t *ha_plugin_create()
return NULL; 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, INIT(this,
.public = { .public = {
.plugin = { .plugin = {
@@ -102,6 +102,13 @@ plugin_t *kernel_libipsec_plugin_create()
{ {
private_kernel_libipsec_plugin_t *this; 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, INIT(this,
.public = { .public = {
.plugin = { .plugin = {
@@ -110,8 +110,8 @@ static bool open_socket(private_load_tester_control_t *this)
return FALSE; return FALSE;
} }
umask(old); umask(old);
if (chown(addr.sun_path, charon->caps->get_uid(charon->caps), if (chown(addr.sun_path, lib->caps->get_uid(lib->caps),
charon->caps->get_gid(charon->caps)) != 0) lib->caps->get_gid(lib->caps)) != 0)
{ {
DBG1(DBG_CFG, "changing load-tester socket permissions failed: %s", DBG1(DBG_CFG, "changing load-tester socket permissions failed: %s",
strerror(errno)); strerror(errno));
@@ -269,6 +269,12 @@ plugin_t *load_tester_plugin_create()
return NULL; 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, INIT(this,
.public = { .public = {
.plugin = { .plugin = {
@@ -92,6 +92,12 @@ plugin_t *lookip_plugin_create()
{ {
private_lookip_plugin_t *this; 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, INIT(this,
.public = { .public = {
.plugin = { .plugin = {
+2 -2
View File
@@ -94,8 +94,8 @@ static bool open_socket(private_lookip_socket_t *this)
return FALSE; return FALSE;
} }
umask(old); umask(old);
if (chown(addr.sun_path, charon->caps->get_uid(charon->caps), if (chown(addr.sun_path, lib->caps->get_uid(lib->caps),
charon->caps->get_gid(charon->caps)) != 0) lib->caps->get_gid(lib->caps)) != 0)
{ {
DBG1(DBG_CFG, "changing lookip socket permissions failed: %s", DBG1(DBG_CFG, "changing lookip socket permissions failed: %s",
strerror(errno)); strerror(errno));
+8 -2
View File
@@ -739,6 +739,12 @@ plugin_t *smp_plugin_create()
private_smp_t *this; private_smp_t *this;
mode_t old; 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, INIT(this,
.public = { .public = {
.plugin = { .plugin = {
@@ -768,8 +774,8 @@ plugin_t *smp_plugin_create()
return NULL; return NULL;
} }
umask(old); umask(old);
if (chown(unix_addr.sun_path, charon->caps->get_uid(charon->caps), if (chown(unix_addr.sun_path, lib->caps->get_uid(lib->caps),
charon->caps->get_gid(charon->caps)) != 0) lib->caps->get_gid(lib->caps)) != 0)
{ {
DBG1(DBG_CFG, "changing XML socket permissions failed: %s", strerror(errno)); DBG1(DBG_CFG, "changing XML socket permissions failed: %s", strerror(errno));
} }
@@ -692,6 +692,18 @@ socket_default_socket_t *socket_default_socket_create()
this->natt = 0; 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 /* 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 * ports also for IPv4. On OS X, we have to do it the other way round
* for the same effect. */ * for the same effect. */
@@ -91,6 +91,12 @@ plugin_t *stroke_plugin_create()
{ {
private_stroke_plugin_t *this; 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, INIT(this,
.public = { .public = {
.plugin = { .plugin = {
+2 -2
View File
@@ -847,8 +847,8 @@ static bool open_socket(private_stroke_socket_t *this)
return FALSE; return FALSE;
} }
umask(old); umask(old);
if (chown(socket_addr.sun_path, charon->caps->get_uid(charon->caps), if (chown(socket_addr.sun_path, lib->caps->get_uid(lib->caps),
charon->caps->get_gid(charon->caps)) != 0) lib->caps->get_gid(lib->caps)) != 0)
{ {
DBG1(DBG_CFG, "changing stroke socket permissions failed: %s", DBG1(DBG_CFG, "changing stroke socket permissions failed: %s",
strerror(errno)); strerror(errno));
@@ -77,8 +77,8 @@ static bool open_socket(private_whitelist_control_t *this)
return FALSE; return FALSE;
} }
umask(old); umask(old);
if (chown(addr.sun_path, charon->caps->get_uid(charon->caps), if (chown(addr.sun_path, lib->caps->get_uid(lib->caps),
charon->caps->get_gid(charon->caps)) != 0) lib->caps->get_gid(lib->caps)) != 0)
{ {
DBG1(DBG_CFG, "changing whitelist socket permissions failed: %s", DBG1(DBG_CFG, "changing whitelist socket permissions failed: %s",
strerror(errno)); strerror(errno));
@@ -92,6 +92,12 @@ plugin_t *whitelist_plugin_create()
{ {
private_whitelist_plugin_t *this; 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, INIT(this,
.public = { .public = {
.plugin = { .plugin = {
@@ -52,6 +52,13 @@ plugin_t *xauth_pam_plugin_create()
{ {
xauth_pam_plugin_t *this; 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, INIT(this,
.plugin = { .plugin = {
.get_name = _get_name, .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; return &this->plugin;
} }
-1
View File
@@ -97,4 +97,3 @@ bool libhydra_init(const char *daemon)
} }
return !this->integrity_failed; return !this->integrity_failed;
} }
@@ -65,6 +65,14 @@ plugin_t *kernel_netlink_plugin_create()
{ {
private_kernel_netlink_plugin_t *this; 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, INIT(this,
.public = { .public = {
.plugin = { .plugin = {
@@ -62,6 +62,12 @@ plugin_t *kernel_pfkey_plugin_create()
{ {
private_kernel_pfkey_plugin_t *this; 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, INIT(this,
.public = { .public = {
.plugin = { .plugin = {
+2
View File
@@ -89,6 +89,7 @@ void library_deinit()
this->public.creds->destroy(this->public.creds); this->public.creds->destroy(this->public.creds);
this->public.encoding->destroy(this->public.encoding); this->public.encoding->destroy(this->public.encoding);
this->public.crypto->destroy(this->public.crypto); this->public.crypto->destroy(this->public.crypto);
this->public.caps->destroy(this->public.caps);
this->public.proposal->destroy(this->public.proposal); this->public.proposal->destroy(this->public.proposal);
this->public.fetcher->destroy(this->public.fetcher); this->public.fetcher->destroy(this->public.fetcher);
this->public.resolver->destroy(this->public.resolver); this->public.resolver->destroy(this->public.resolver);
@@ -255,6 +256,7 @@ bool library_init(char *settings)
this->public.settings = settings_create(settings); this->public.settings = settings_create(settings);
this->public.hosts = host_resolver_create(); this->public.hosts = host_resolver_create();
this->public.proposal = proposal_keywords_create(); this->public.proposal = proposal_keywords_create();
this->public.caps = capabilities_create();
this->public.crypto = crypto_factory_create(); this->public.crypto = crypto_factory_create();
this->public.creds = credential_factory_create(); this->public.creds = credential_factory_create();
this->public.credmgr = credential_manager_create(); this->public.credmgr = credential_manager_create();
+6
View File
@@ -101,6 +101,7 @@
#include "credentials/credential_manager.h" #include "credentials/credential_manager.h"
#include "credentials/cred_encoding.h" #include "credentials/cred_encoding.h"
#include "utils/chunk.h" #include "utils/chunk.h"
#include "utils/capabilities.h"
#include "utils/integrity_checker.h" #include "utils/integrity_checker.h"
#include "utils/leak_detective.h" #include "utils/leak_detective.h"
#include "utils/settings.h" #include "utils/settings.h"
@@ -140,6 +141,11 @@ struct library_t {
*/ */
proposal_keywords_t *proposal; proposal_keywords_t *proposal;
/**
* POSIX capability dropping
*/
capabilities_t *caps;
/** /**
* crypto algorithm registry and factory * crypto algorithm registry and factory
*/ */
+113 -6
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2012 Tobias Brunner * Copyright (C) 2012-2013 Tobias Brunner
* Hochschule fuer Technik Rapperswil * Hochschule fuer Technik Rapperswil
* Copyright (C) 2012 Martin Willi * Copyright (C) 2012 Martin Willi
* Copyright (C) 2012 revosec AG * Copyright (C) 2012 revosec AG
@@ -76,9 +76,63 @@ struct private_capabilities_t {
#endif #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 #ifdef CAPABILITIES_LIBCAP
cap_set_flag(this->caps, CAP_EFFECTIVE, 1, &cap, CAP_SET); cap_set_flag(this->caps, CAP_EFFECTIVE, 1, &cap, CAP_SET);
cap_set_flag(this->caps, CAP_INHERITABLE, 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].permitted |= 1 << cap;
this->caps[i].inheritable |= 1 << cap; this->caps[i].inheritable |= 1 << cap;
#endif /* CAPABILITIES_NATIVE */ #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, METHOD(capabilities_t, get_uid, uid_t,
private_capabilities_t *this) private_capabilities_t *this)
{ {
return this->uid; return this->uid ?: geteuid();
} }
METHOD(capabilities_t, get_gid, gid_t, METHOD(capabilities_t, get_gid, gid_t,
private_capabilities_t *this) private_capabilities_t *this)
{ {
return this->gid; return this->gid ?: getegid();
} }
METHOD(capabilities_t, set_uid, void, METHOD(capabilities_t, set_uid, void,
@@ -271,7 +378,7 @@ METHOD(capabilities_t, drop, bool,
#endif /* CAPABILITIES_NATIVE */ #endif /* CAPABILITIES_NATIVE */
#ifdef CAPABILITIES #ifdef CAPABILITIES
DBG1(DBG_LIB, "dropped capabilities, running as uid %u, gid %u", DBG1(DBG_LIB, "dropped capabilities, running as uid %u, gid %u",
this->uid, this->gid); geteuid(), getegid());
#endif /* CAPABILITIES */ #endif /* CAPABILITIES */
return TRUE; return TRUE;
} }
+21 -3
View File
@@ -1,4 +1,6 @@
/* /*
* Copyright (C) 2013 Tobias Brunner
* Hochschule fuer Technik Rapperswil
* Copyright (C) 2012 Martin Willi * Copyright (C) 2012 Martin Willi
* Copyright (C) 2012 revosec AG * Copyright (C) 2012 revosec AG
* *
@@ -21,6 +23,8 @@
#ifndef CAPABILITIES_H_ #ifndef CAPABILITIES_H_
#define CAPABILITIES_H_ #define CAPABILITIES_H_
typedef struct capabilities_t capabilities_t;
#include <library.h> #include <library.h>
#ifdef HAVE_SYS_CAPABILITY_H #ifdef HAVE_SYS_CAPABILITY_H
# include <sys/capability.h> # include <sys/capability.h>
@@ -28,7 +32,18 @@
# include <linux/capability.h> # include <linux/capability.h>
#endif #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. * POSIX capability dropping abstraction layer.
@@ -36,11 +51,14 @@ typedef struct capabilities_t capabilities_t;
struct 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 * @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. * Get the user ID set through set_uid/resolve_uid.