From 0619ddfaa4c374025844257648c61f71e1fdfcb5 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 3 Jul 2012 13:07:24 +0200 Subject: [PATCH] Refactored heavily #ifdefd capability code to its own libstrongswan class --- src/charon-nm/charon-nm.c | 58 +---- src/charon-nm/nm/nm_backend.c | 4 +- src/charon/charon.c | 64 +---- src/libcharon/Makefile.am | 4 - src/libcharon/daemon.c | 88 +------ src/libcharon/daemon.h | 30 +-- .../plugins/duplicheck/duplicheck_notify.c | 3 +- .../plugins/eap_gtc/eap_gtc_plugin.c | 2 +- src/libcharon/plugins/ha/ha_ctl.c | 3 +- src/libcharon/plugins/ha/ha_kernel.c | 3 +- src/libcharon/plugins/smp/smp.c | 3 +- src/libcharon/plugins/stroke/stroke_socket.c | 3 +- .../plugins/whitelist/whitelist_control.c | 3 +- src/libstrongswan/Makefile.am | 5 + src/libstrongswan/utils/capabilities.c | 246 ++++++++++++++++++ src/libstrongswan/utils/capabilities.h | 107 ++++++++ 16 files changed, 393 insertions(+), 233 deletions(-) create mode 100644 src/libstrongswan/utils/capabilities.c create mode 100644 src/libstrongswan/utils/capabilities.h diff --git a/src/charon-nm/charon-nm.c b/src/charon-nm/charon-nm.c index d847d1e83a..c1101a43c7 100644 --- a/src/charon-nm/charon-nm.c +++ b/src/charon-nm/charon-nm.c @@ -18,11 +18,6 @@ #include #include #include -#include -#include -#ifdef HAVE_PRCTL -#include -#endif #include #include @@ -149,60 +144,17 @@ static void initialize_logger() static bool lookup_uid_gid() { #ifdef IPSEC_USER + if (!charon->caps->resolve_uid(charon->caps, IPSEC_USER)) { - char buf[1024]; - struct passwd passwd, *pwp; - - if (getpwnam_r(IPSEC_USER, &passwd, buf, sizeof(buf), &pwp) != 0 || - pwp == NULL) - { - DBG1(DBG_DMN, "resolving user '"IPSEC_USER"' failed"); - return FALSE; - } - charon->uid = pwp->pw_uid; + return FALSE; } #endif #ifdef IPSEC_GROUP + if (!charon->caps->resolve_gid(charon->caps, IPSEC_GROUP)) { - char buf[1024]; - struct group group, *grp; - - if (getgrnam_r(IPSEC_GROUP, &group, buf, sizeof(buf), &grp) != 0 || - grp == NULL) - { - DBG1(DBG_DMN, "resolving group '"IPSEC_GROUP"' failed"); - return FALSE; - } - charon->gid = grp->gr_gid; - } -#endif - return TRUE; -} - -/** - * Drop process capabilities - */ -static bool drop_capabilities() -{ -#ifdef HAVE_PRCTL - prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); -#endif - - if (setgid(charon->gid) != 0) - { - DBG1(DBG_DMN, "change to unprivileged group failed"); - return FALSE; - } - if (setuid(charon->uid) != 0) - { - DBG1(DBG_DMN, "change to unprivileged user failed"); - return FALSE; - } - if (!charon->drop_capabilities(charon)) - { - DBG1(DBG_DMN, "unable to drop daemon capabilities"); return FALSE; } +#endif return TRUE; } @@ -275,7 +227,7 @@ int main(int argc, char *argv[]) goto deinit; } - if (!drop_capabilities()) + if (!charon->caps->drop(charon->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 de9bf27b7b..a9ad9bdc96 100644 --- a/src/charon-nm/nm/nm_backend.c +++ b/src/charon-nm/nm/nm_backend.c @@ -137,7 +137,7 @@ static bool nm_backend_init() } /* bypass file permissions to read from users ssh-agent */ - charon->keep_cap(charon, CAP_DAC_OVERRIDE); + charon->caps->keep(charon->caps, CAP_DAC_OVERRIDE); lib->processor->queue_job(lib->processor, (job_t*)callback_job_create_with_prio((callback_job_cb_t)run, this, @@ -171,4 +171,4 @@ void nm_backend_register() }; lib->plugins->add_static_features(lib->plugins, "nm-backend", features, countof(features), TRUE); -} \ No newline at end of file +} diff --git a/src/charon/charon.c b/src/charon/charon.c index 516abb8e7e..be4a9548ee 100644 --- a/src/charon/charon.c +++ b/src/charon/charon.c @@ -17,9 +17,6 @@ */ #include -#ifdef HAVE_PRCTL -#include -#endif #define _POSIX_PTHREAD_SEMANTICS /* for two param sigwait on OpenSolaris */ #include #undef _POSIX_PTHREAD_SEMANTICS @@ -31,8 +28,6 @@ #include #include #include -#include -#include #include #include @@ -143,68 +138,25 @@ static void run() } } -/** - * drop daemon capabilities - */ -static bool drop_capabilities() -{ -#ifdef HAVE_PRCTL - prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); -#endif - - if (setgid(charon->gid) != 0) - { - DBG1(DBG_DMN, "change to unprivileged group failed"); - return FALSE; - } - if (setuid(charon->uid) != 0) - { - DBG1(DBG_DMN, "change to unprivileged user failed"); - return FALSE; - } - if (!charon->drop_capabilities(charon)) - { - DBG1(DBG_DMN, "unable to drop daemon capabilities"); - return FALSE; - } - return TRUE; -} - /** * lookup UID and GID */ static bool lookup_uid_gid() { #ifdef IPSEC_USER + if (!charon->caps->resolve_uid(charon->caps, IPSEC_USER)) { - char buf[1024]; - struct passwd passwd, *pwp; - - if (getpwnam_r(IPSEC_USER, &passwd, buf, sizeof(buf), &pwp) != 0 || - pwp == NULL) - { - DBG1(DBG_DMN, "resolving user '"IPSEC_USER"' failed"); - return FALSE; - } - charon->uid = pwp->pw_uid; + return FALSE; } #endif #ifdef IPSEC_GROUP + if (!charon->caps->resolve_gid(charon->caps, IPSEC_GROUP)) { - char buf[1024]; - struct group group, *grp; - - if (getgrnam_r(IPSEC_GROUP, &group, buf, sizeof(buf), &grp) != 0 || - grp == NULL) - { - DBG1(DBG_DMN, "resolving group '"IPSEC_GROUP"' failed"); - return FALSE; - } - charon->gid = grp->gr_gid; + return FALSE; } #endif #ifdef ANDROID - charon->uid = AID_VPN; + charon->caps->set_uid(charon->caps, AID_VPN); #endif return TRUE; } @@ -260,7 +212,9 @@ static bool check_pidfile() pidfile = fopen(PID_FILE, "w"); if (pidfile) { - ignore_result(fchown(fileno(pidfile), charon->uid, charon->gid)); + ignore_result(fchown(fileno(pidfile), + charon->caps->get_uid(charon->caps), + charon->caps->get_gid(charon->caps))); fprintf(pidfile, "%d\n", getpid()); fflush(pidfile); } @@ -582,7 +536,7 @@ int main(int argc, char *argv[]) goto deinit; } - if (!drop_capabilities()) + if (!charon->caps->drop(charon->caps)) { DBG1(DBG_DMN, "capability dropping failed - aborting charon"); goto deinit; diff --git a/src/libcharon/Makefile.am b/src/libcharon/Makefile.am index c45b443b83..0d5695885c 100644 --- a/src/libcharon/Makefile.am +++ b/src/libcharon/Makefile.am @@ -153,10 +153,6 @@ if USE_ME sa/ikev2/tasks/ike_me.c sa/ikev2/tasks/ike_me.h endif -if USE_LIBCAP - libcharon_la_LIBADD += -lcap -endif - # build optional plugins ######################## diff --git a/src/libcharon/daemon.c b/src/libcharon/daemon.c index f64c70b27e..ece5afff9e 100644 --- a/src/libcharon/daemon.c +++ b/src/libcharon/daemon.c @@ -21,14 +21,6 @@ #include #include -#ifdef CAPABILITIES -# ifdef HAVE_SYS_CAPABILITY_H -# include -# elif defined(CAPABILITIES_NATIVE) -# include -# endif /* CAPABILITIES_NATIVE */ -#endif /* CAPABILITIES */ - #include "daemon.h" #include @@ -52,17 +44,6 @@ struct private_daemon_t { * Handler for kernel events */ kernel_handler_t *kernel_handler; - - /** - * capabilities to keep - */ -#ifdef CAPABILITIES_LIBCAP - cap_t caps; -#endif /* CAPABILITIES_LIBCAP */ -#ifdef CAPABILITIES_NATIVE - struct __user_cap_data_struct caps[2]; -#endif /* CAPABILITIES_NATIVE */ - }; /** @@ -125,9 +106,6 @@ static void destroy(private_daemon_t *this) /* make sure the cache is clear before unloading plugins */ lib->credmgr->flush_cache(lib->credmgr, CERT_ANY); lib->plugins->unload(lib->plugins); -#ifdef CAPABILITIES_LIBCAP - cap_free(this->caps); -#endif /* CAPABILITIES_LIBCAP */ DESTROY_IF(this->kernel_handler); DESTROY_IF(this->public.traps); DESTROY_IF(this->public.shunts); @@ -138,6 +116,7 @@ static void destroy(private_daemon_t *this) DESTROY_IF(this->public.backends); DESTROY_IF(this->public.sender); DESTROY_IF(this->public.socket); + DESTROY_IF(this->public.caps); /* rehook library logging, shutdown logging */ dbg = dbg_old; @@ -150,57 +129,6 @@ static void destroy(private_daemon_t *this) free(this); } -METHOD(daemon_t, keep_cap, void, - private_daemon_t *this, u_int cap) -{ -#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); - cap_set_flag(this->caps, CAP_PERMITTED, 1, &cap, CAP_SET); -#endif /* CAPABILITIES_LIBCAP */ -#ifdef CAPABILITIES_NATIVE - int i = 0; - - if (cap >= 32) - { - i++; - cap -= 32; - } - this->caps[i].effective |= 1 << cap; - this->caps[i].permitted |= 1 << cap; - this->caps[i].inheritable |= 1 << cap; -#endif /* CAPABILITIES_NATIVE */ -} - -METHOD(daemon_t, drop_capabilities, bool, - private_daemon_t *this) -{ -#ifdef CAPABILITIES_LIBCAP - if (cap_set_proc(this->caps) != 0) - { - return FALSE; - } -#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 - }; - if (capset(&header, this->caps) != 0) - { - return FALSE; - } -#endif /* CAPABILITIES_NATIVE */ - return TRUE; -} - METHOD(daemon_t, start, void, private_daemon_t *this) { @@ -269,8 +197,6 @@ private_daemon_t *daemon_create(const char *name) INIT(this, .public = { - .keep_cap = _keep_cap, - .drop_capabilities = _drop_capabilities, .initialize = _initialize, .start = _start, .bus = bus_create(), @@ -280,6 +206,7 @@ private_daemon_t *daemon_create(const char *name) }, ); 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(); @@ -289,16 +216,7 @@ private_daemon_t *daemon_create(const char *name) this->public.shunts = shunt_manager_create(); this->kernel_handler = kernel_handler_create(); -#ifdef CAPABILITIES -#ifdef CAPABILITIES_LIBCAP - this->caps = cap_init(); -#endif /* CAPABILITIES_LIBCAP */ - keep_cap(this, CAP_NET_ADMIN); - if (lib->leak_detective) - { - keep_cap(this, CAP_SYS_NICE); - } -#endif /* CAPABILITIES */ + 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 c679ccb1c0..f42a9f0782 100644 --- a/src/libcharon/daemon.h +++ b/src/libcharon/daemon.h @@ -165,6 +165,7 @@ typedef struct daemon_t daemon_t; #include #include #include +#include #ifdef ME #include @@ -269,40 +270,15 @@ struct daemon_t { #endif /* ME */ /** - * User ID the daemon will user after initialization + * POSIX capability dropping */ - uid_t uid; - - /** - * Group ID the daemon will use after initialization - */ - gid_t gid; + capabilities_t *caps; /** * Name of the binary that uses the library (used for settings etc.) */ const char *name; - /** - * Do not drop a given capability after initialization. - * - * Some plugins might need additional capabilites. They tell the daemon - * during plugin initialization which one they need, the daemon won't - * drop these. - */ - void (*keep_cap)(daemon_t *this, u_int cap); - - /** - * Drop all capabilities of the current process. - * - * Drops all capabalities, excect those exlcuded using keep_cap(). - * This should be called after the initialization of the daemon because - * some plugins require the process to keep additional capabilities. - * - * @return TRUE, if successful - */ - bool (*drop_capabilities)(daemon_t *this); - /** * Initialize the daemon. * diff --git a/src/libcharon/plugins/duplicheck/duplicheck_notify.c b/src/libcharon/plugins/duplicheck/duplicheck_notify.c index 3da640efee..06a88ed7d3 100644 --- a/src/libcharon/plugins/duplicheck/duplicheck_notify.c +++ b/src/libcharon/plugins/duplicheck/duplicheck_notify.c @@ -84,7 +84,8 @@ static bool open_socket(private_duplicheck_notify_t *this) return FALSE; } umask(old); - if (chown(addr.sun_path, charon->uid, charon->gid) != 0) + if (chown(addr.sun_path, charon->caps->get_uid(charon->caps), + charon->caps->get_gid(charon->caps)) != 0) { DBG1(DBG_CFG, "changing duplicheck socket permissions failed: %s", strerror(errno)); diff --git a/src/libcharon/plugins/eap_gtc/eap_gtc_plugin.c b/src/libcharon/plugins/eap_gtc/eap_gtc_plugin.c index bd70b757a7..c7fd3b05d7 100644 --- a/src/libcharon/plugins/eap_gtc/eap_gtc_plugin.c +++ b/src/libcharon/plugins/eap_gtc/eap_gtc_plugin.c @@ -63,7 +63,7 @@ plugin_t *eap_gtc_plugin_create() ); /* required for PAM authentication */ - charon->keep_cap(charon, CAP_AUDIT_WRITE); + charon->caps->keep(charon->caps, CAP_AUDIT_WRITE); charon->eap->add_method(charon->eap, EAP_GTC, 0, EAP_SERVER, (eap_constructor_t)eap_gtc_create_server); diff --git a/src/libcharon/plugins/ha/ha_ctl.c b/src/libcharon/plugins/ha/ha_ctl.c index 32f7d04d92..cb9af3aedd 100644 --- a/src/libcharon/plugins/ha/ha_ctl.c +++ b/src/libcharon/plugins/ha/ha_ctl.c @@ -129,7 +129,8 @@ ha_ctl_t *ha_ctl_create(ha_segments_t *segments, ha_cache_t *cache) } umask(old); } - if (chown(HA_FIFO, charon->uid, charon->gid) != 0) + if (chown(HA_FIFO, charon->caps->get_uid(charon->caps), + charon->caps->get_gid(charon->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 2377a26301..c453396902 100644 --- a/src/libcharon/plugins/ha/ha_kernel.c +++ b/src/libcharon/plugins/ha/ha_kernel.c @@ -316,7 +316,8 @@ static void disable_all(private_ha_kernel_t *this) { while (enumerator->enumerate(enumerator, NULL, &file, NULL)) { - if (chown(file, charon->uid, charon->gid) != 0) + if (chown(file, charon->caps->get_uid(charon->caps), + charon->caps->get_gid(charon->caps)) != 0) { DBG1(DBG_CFG, "changing ClusterIP permissions failed: %s", strerror(errno)); diff --git a/src/libcharon/plugins/smp/smp.c b/src/libcharon/plugins/smp/smp.c index 870f0a0222..32fc0c0e13 100644 --- a/src/libcharon/plugins/smp/smp.c +++ b/src/libcharon/plugins/smp/smp.c @@ -757,7 +757,8 @@ plugin_t *smp_plugin_create() return NULL; } umask(old); - if (chown(unix_addr.sun_path, charon->uid, charon->gid) != 0) + if (chown(unix_addr.sun_path, charon->caps->get_uid(charon->caps), + charon->caps->get_gid(charon->caps)) != 0) { DBG1(DBG_CFG, "changing XML socket permissions failed: %s", strerror(errno)); } diff --git a/src/libcharon/plugins/stroke/stroke_socket.c b/src/libcharon/plugins/stroke/stroke_socket.c index e2865a640e..698c45ed87 100644 --- a/src/libcharon/plugins/stroke/stroke_socket.c +++ b/src/libcharon/plugins/stroke/stroke_socket.c @@ -758,7 +758,8 @@ static bool open_socket(private_stroke_socket_t *this) return FALSE; } umask(old); - if (chown(socket_addr.sun_path, charon->uid, charon->gid) != 0) + if (chown(socket_addr.sun_path, charon->caps->get_uid(charon->caps), + charon->caps->get_gid(charon->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 0c20bd1aa9..a75ea9aeef 100644 --- a/src/libcharon/plugins/whitelist/whitelist_control.c +++ b/src/libcharon/plugins/whitelist/whitelist_control.c @@ -77,7 +77,8 @@ static bool open_socket(private_whitelist_control_t *this) return FALSE; } umask(old); - if (chown(addr.sun_path, charon->uid, charon->gid) != 0) + if (chown(addr.sun_path, charon->caps->get_uid(charon->caps), + charon->caps->get_gid(charon->caps)) != 0) { DBG1(DBG_CFG, "changing whitelist socket permissions failed: %s", strerror(errno)); diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index bbc169c9d5..55525b6f18 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -77,6 +77,7 @@ utils/linked_list.c utils/linked_list.h \ utils/hashtable.c utils/hashtable.h \ utils/enumerator.c utils/enumerator.h \ utils/optionsfrom.c utils/optionsfrom.h \ +utils/capabilities.c utils/capabilities.h \ utils/backtrace.c utils/backtrace.h @@ -111,6 +112,10 @@ if USE_VSTR libstrongswan_la_LIBADD += -lvstr endif +if USE_LIBCAP + libstrongswan_la_LIBADD += -lcap +endif + EXTRA_DIST = \ asn1/oid.txt asn1/oid.pl \ crypto/proposal/proposal_keywords.txt \ diff --git a/src/libstrongswan/utils/capabilities.c b/src/libstrongswan/utils/capabilities.c new file mode 100644 index 0000000000..b396c6a921 --- /dev/null +++ b/src/libstrongswan/utils/capabilities.c @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "capabilities.h" + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_PRCTL +# include +#endif /* HAVE_PRCTL */ + +#include + +typedef struct private_capabilities_t private_capabilities_t; + +/** + * Private data of an capabilities_t object. + */ +struct private_capabilities_t { + + /** + * Public capabilities_t interface. + */ + capabilities_t public; + + /** + * user ID to switch during rights dropping + */ + uid_t uid; + + /** + * group ID to switch during rights dropping + */ + gid_t gid; + + /** + * capabilities to keep + */ +#ifdef CAPABILITIES_LIBCAP + cap_t caps; +#endif /* CAPABILITIES_LIBCAP */ +#ifdef CAPABILITIES_NATIVE + struct __user_cap_data_struct caps[2]; +#endif /* CAPABILITIES_NATIVE */ +}; + +METHOD(capabilities_t, keep, void, + private_capabilities_t *this, u_int cap) +{ +#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); + cap_set_flag(this->caps, CAP_PERMITTED, 1, &cap, CAP_SET); +#endif /* CAPABILITIES_LIBCAP */ +#ifdef CAPABILITIES_NATIVE + int i = 0; + + if (cap >= 32) + { + i++; + cap -= 32; + } + this->caps[i].effective |= 1 << cap; + this->caps[i].permitted |= 1 << cap; + this->caps[i].inheritable |= 1 << cap; +#endif /* CAPABILITIES_NATIVE */ +} + +METHOD(capabilities_t, get_uid, uid_t, + private_capabilities_t *this) +{ + return this->uid; +} + +METHOD(capabilities_t, get_gid, gid_t, + private_capabilities_t *this) +{ + return this->gid; +} + +METHOD(capabilities_t, set_uid, void, + private_capabilities_t *this, uid_t uid) +{ + this->uid = uid; +} + +METHOD(capabilities_t, set_gid, void, + private_capabilities_t *this, gid_t gid) +{ + this->gid = gid; +} + +METHOD(capabilities_t, resolve_uid, bool, + private_capabilities_t *this, char *username) +{ + const char *errstr = "user not found"; + struct passwd passwd, *pwp; + char buf[1024]; + int err; + + err = getpwnam_r(username, &passwd, buf, sizeof(buf), &pwp); + if (pwp == NULL) + { + if (err) + { + errstr = strerror(err); + } + DBG1(DBG_LIB, "resolving user '%s' failed: %s", username, errstr); + return FALSE; + } + this->uid = pwp->pw_uid; + return TRUE; +} + +METHOD(capabilities_t, resolve_gid, bool, + private_capabilities_t *this, char *groupname) +{ + const char *errstr = "group not found"; + struct group group, *grp; + char buf[1024]; + int err; + + err = getgrnam_r(groupname, &group, buf, sizeof(buf), &grp); + if (grp == NULL) + { + if (err) + { + errstr = strerror(err); + } + DBG1(DBG_LIB, "resolving user '%s' failed: %s", groupname, errstr); + return FALSE; + } + this->gid = grp->gr_gid; + return TRUE; +} + +METHOD(capabilities_t, drop, bool, + private_capabilities_t *this) +{ +#ifdef HAVE_PRCTL + prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); +#endif + + if (this->gid && setgid(this->gid) != 0) + { + DBG1(DBG_LIB, "change to unprivileged group %u failed: %s", + this->gid, strerror(errno)); + return FALSE; + } + if (this->uid && setuid(this->uid) != 0) + { + DBG1(DBG_LIB, "change to unprivileged user %u failed: %s", + this->uid, strerror(errno)); + return FALSE; + } + +#ifdef CAPABILITIES_LIBCAP + if (cap_set_proc(this->caps) != 0) + { + DBG1(DBG_LIB, "dropping capabilities failed: %s", strerror(errno)); + return FALSE; + } +#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 + }; + if (capset(&header, this->caps) != 0) + { + DBG1(DBG_LIB, "dropping capabilities failed: %s", strerror(errno)); + return FALSE; + } +#endif /* CAPABILITIES_NATIVE */ +#ifdef CAPABILITIES + DBG1(DBG_LIB, "dropped capabilities, running as uid %u, gid %u", + this->uid, this->gid); +#endif /* CAPABILITIES */ + return TRUE; +} + +METHOD(capabilities_t, destroy, void, + private_capabilities_t *this) +{ +#ifdef CAPABILITIES_LIBCAP + cap_free(this->caps); +#endif /* CAPABILITIES_LIBCAP */ + free(this); +} + +/** + * See header + */ +capabilities_t *capabilities_create() +{ + private_capabilities_t *this; + + INIT(this, + .public = { + .keep = _keep, + .get_uid = _get_uid, + .get_gid = _get_gid, + .set_uid = _set_uid, + .set_gid = _set_gid, + .resolve_uid = _resolve_uid, + .resolve_gid = _resolve_gid, + .drop = _drop, + .destroy = _destroy, + }, + ); + +#ifdef CAPABILITIES +#ifdef CAPABILITIES_LIBCAP + this->caps = cap_init(); +#endif /* CAPABILITIES_LIBCAP */ + if (lib->leak_detective) + { + keep(this, CAP_SYS_NICE); + } +#endif /* CAPABILITIES */ + + return &this->public; +} diff --git a/src/libstrongswan/utils/capabilities.h b/src/libstrongswan/utils/capabilities.h new file mode 100644 index 0000000000..df29cd3a43 --- /dev/null +++ b/src/libstrongswan/utils/capabilities.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup capabilities capabilities + * @{ @ingroup utils + */ + +#ifndef CAPABILITIES_H_ +#define CAPABILITIES_H_ + +#include +#ifdef HAVE_SYS_CAPABILITY_H +# include +#else +# include +#endif + +typedef struct capabilities_t capabilities_t; + +/** + * POSIX capability dropping abstraction layer. + */ +struct capabilities_t { + + /** + * Register a capability to keep while calling drop(). + * + * @param cap capability to keep + */ + void (*keep)(capabilities_t *this, u_int cap); + + /** + * Get the user ID set through set_uid/resolve_uid. + * + * @return currently set user ID + */ + uid_t (*get_uid)(capabilities_t *this); + + /** + * Get the group ID set through set_gid/resolve_gid. + * + * @return currently set group ID + */ + gid_t (*get_gid)(capabilities_t *this); + + /** + * Set the numerical user ID to use during rights dropping. + * + * @param uid user ID to use + */ + void (*set_uid)(capabilities_t *this, uid_t uid); + + /** + * Set the numerical group ID to use during rights dropping. + * + * @param gid group ID to use + */ + void (*set_gid)(capabilities_t *this, gid_t gid); + + /** + * Resolve a username and set the user ID accordingly. + * + * @param username username get the uid for + * @return TRUE if username resolved and uid set + */ + bool (*resolve_uid)(capabilities_t *this, char *username); + + /** + * Resolve a groupname and set the group ID accordingly. + * + * @param groupname groupname to get the gid for + * @return TRUE if groupname resolved and gid set + */ + bool (*resolve_gid)(capabilities_t *this, char *groupname); + + /** + * Drop all capabilities not previously passed to keep(), switch to UID/GID. + * + * @return TRUE if capability drop successful + */ + bool (*drop)(capabilities_t *this); + + /** + * Destroy a capabilities_t. + */ + void (*destroy)(capabilities_t *this); +}; + +/** + * Create a capabilities instance. + */ +capabilities_t *capabilities_create(); + +#endif /** CAPABILITIES_H_ @}*/ -- 2.47.2