diff --git a/configure.in b/configure.in index 8b54b42c9..eade13ea3 100644 --- a/configure.in +++ b/configure.in @@ -107,6 +107,20 @@ AC_ARG_WITH( ) AC_SUBST(LINUX_HEADERS) +AC_ARG_WITH( + [uid], + AS_HELP_STRING([--with-uid=uid],[change user of the daemons to UID after startup (default is 0).]), + [AC_DEFINE_UNQUOTED(IPSEC_UID, $withval) AC_SUBST(ipsecuid, "$withval")], + [AC_DEFINE_UNQUOTED(IPSEC_UID, 0) AC_SUBST(ipsecuid, "0")] +) + +AC_ARG_WITH( + [gid], + AS_HELP_STRING([--with-gid=gid],[change group of the daemons to GID after startup (default is 0).]), + [AC_DEFINE_UNQUOTED(IPSEC_GID, $withval) AC_SUBST(ipsecgid, "$withval")], + [AC_DEFINE_UNQUOTED(IPSEC_GID, 0) AC_SUBST(ipsecgid, "0")] +) + AC_ARG_ENABLE( [http], AS_HELP_STRING([--enable-http],[enable OCSP and fetching of Certificates and CRLs over HTTP (default is NO). Requires libcurl.]), @@ -260,6 +274,14 @@ AC_TRY_COMPILE( ], [AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no]); AC_MSG_ERROR([No usable gmp.h found!])] ) +AC_MSG_CHECKING([capset() definition]) +AC_TRY_COMPILE( + [#include ], + [ + void *test = capset; + ], + [AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no]); AC_DEFINE_UNQUOTED(NO_CAPSET_DEFINED, 1)] +) if test "$ldap" = "true"; then AC_CHECK_HEADER([ldap.h],,[AC_MSG_ERROR([LDAP enabled, but ldap.h not found!])]) fi diff --git a/src/charon/config/credentials/local_credential_store.c b/src/charon/config/credentials/local_credential_store.c index 82ea78768..a920150ac 100644 --- a/src/charon/config/credentials/local_credential_store.c +++ b/src/charon/config/credentials/local_credential_store.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -1382,7 +1383,8 @@ error: } else { - DBG1(DBG_CFG, "could not open file '%s'", SECRETS_FILE); + DBG1(DBG_CFG, "could not open file '%s': %s", SECRETS_FILE, + strerror(errno)); } } diff --git a/src/charon/control/interfaces/dbus_interface.c b/src/charon/control/interfaces/dbus_interface.c index 5805d2b46..8f048ba9e 100644 --- a/src/charon/control/interfaces/dbus_interface.c +++ b/src/charon/control/interfaces/dbus_interface.c @@ -339,7 +339,7 @@ static DBusHandlerResult signal_handler(DBusConnection *con, DBusMessage *msg, static void dispatch(private_dbus_interface_t *this) { /* drop threads capabilities */ - charon->drop_capabilities(charon, FALSE, FALSE); + charon->drop_capabilities(charon, TRUE, FALSE, FALSE); while (dbus_connection_read_write_dispatch(this->conn, -1)) { diff --git a/src/charon/control/interfaces/stroke_interface.c b/src/charon/control/interfaces/stroke_interface.c index 7ae34f86c..d84199220 100755 --- a/src/charon/control/interfaces/stroke_interface.c +++ b/src/charon/control/interfaces/stroke_interface.c @@ -1529,7 +1529,7 @@ static void stroke_receive(private_stroke_interface_t *this) int strokefd; /* drop threads capabilities */ - charon->drop_capabilities(charon, FALSE, FALSE); + charon->drop_capabilities(charon, TRUE, FALSE, FALSE); /* ignore sigpipe. writing over the pipe back to the console * only fails if SIGPIPE is ignored. */ diff --git a/src/charon/daemon.c b/src/charon/daemon.c index 4ef878f0c..e266c967d 100644 --- a/src/charon/daemon.c +++ b/src/charon/daemon.c @@ -23,8 +23,8 @@ */ #include -#include #include +#include #include #include #include @@ -47,6 +47,10 @@ #include #include +/* on some distros, a capset definition is missing */ +#ifdef NO_CAPSET_DEFINED +extern int capset(cap_user_header_t hdrp, const cap_user_data_t datap); +#endif /* NO_CAPSET_DEFINED */ typedef struct private_daemon_t private_daemon_t; @@ -220,7 +224,8 @@ static void kill_daemon(private_daemon_t *this, char *reason) /** * drop daemon capabilities */ -static void drop_capabilities(private_daemon_t *this, bool netlink, bool bind) +static void drop_capabilities(private_daemon_t *this, bool change_uid, + bool netlink, bool bind) { struct __user_cap_header_struct hdr; struct __user_cap_data_struct data; @@ -234,9 +239,11 @@ static void drop_capabilities(private_daemon_t *this, bool netlink, bool bind) if (bind) { /* CAP_NET_BIND_SERVICE to bind services below port 1024, - * CAP_NET_RAW to create RAW sockets. */ + * CAP_NET_RAW to create RAW sockets. + * CAP_DAC_READ_SEARCH is needed to read ipsec.secrets */ keep |= (1<public.kill = (void (*) (daemon_t*,char*))kill_daemon; - this->public.drop_capabilities = (void(*)(daemon_t*,bool,bool))drop_capabilities; + this->public.drop_capabilities = (void(*)(daemon_t*,bool,bool,bool))drop_capabilities; /* NULL members for clean destruction */ this->public.socket = NULL; @@ -439,8 +456,10 @@ int main(int argc, char *argv[]) level_t levels[DBG_MAX]; int signal; - /* keep bind() and netlink capabilities */ - drop_capabilities(NULL, TRUE, TRUE); + prctl(PR_SET_KEEPCAPS, 1); + + /* keep bind() and netlink capabilities, stay as root until all files loaded */ + drop_capabilities(NULL, FALSE, TRUE, TRUE); /* use CTRL loglevel for default */ for (signal = 0; signal < DBG_MAX; signal++) @@ -517,7 +536,7 @@ int main(int argc, char *argv[]) initialize(private_charon, use_syslog, levels); /* drop bind() capability, netlink is needed for cleanup */ - drop_capabilities(private_charon, TRUE, FALSE); + drop_capabilities(private_charon, FALSE, TRUE, FALSE); /* load pluggable EAP modules */ eap_method_load(eapdir); @@ -549,6 +568,9 @@ int main(int argc, char *argv[]) } list->destroy(list); + /* change UID */ + drop_capabilities(private_charon, TRUE, FALSE, FALSE); + /* run daemon */ run(private_charon); @@ -560,3 +582,4 @@ int main(int argc, char *argv[]) return 0; } + diff --git a/src/charon/daemon.h b/src/charon/daemon.h index b7edad862..f8add303e 100644 --- a/src/charon/daemon.h +++ b/src/charon/daemon.h @@ -332,8 +332,6 @@ typedef struct daemon_t daemon_t; */ #define SECRETS_FILE CONFIG_DIR "/ipsec.secrets" -#define IPSEC_USER "nobody" - /** * @brief Main class of daemon, contains some globals. * @@ -423,17 +421,19 @@ struct daemon_t { /** * @brief Let the calling thread drop its capabilities. * - * @param this calling daemon - * @param netlink TRUE to keep CAP_NET_ADMIN (using netlink) - * @param bind TRUE to keep CAP_NET_BIND_SERVICE and CAP_NET_RAW + * @param this calling daemon + * @param change_uid TRUE to change UID/GID to IPSEC_UID/IPSEC_GID + * @param netlink TRUE to keep CAP_NET_ADMIN (using netlink) + * @param bind TRUE to keep CAP_NET_BIND_SERVICE and CAP_NET_RAW */ - void (*drop_capabilities) (daemon_t *this, bool netlink, bool bind); + void (*drop_capabilities) (daemon_t *this, bool change_uid, + bool netlink, bool bind); /** * @brief Shut down the daemon. * - * @param this the daemon to kill - * @param reason describtion why it will be killed + * @param this the daemon to kill + * @param reason describtion why it will be killed */ void (*kill) (daemon_t *this, char *reason); }; diff --git a/src/charon/kernel/kernel_interface.c b/src/charon/kernel/kernel_interface.c index d0560b112..c68c5041e 100644 --- a/src/charon/kernel/kernel_interface.c +++ b/src/charon/kernel/kernel_interface.c @@ -447,7 +447,7 @@ static void add_attribute(struct nlmsghdr *hdr, int rta_type, chunk_t data, static void receive_events(private_kernel_interface_t *this) { /* keep netlink capabilities only */ - charon->drop_capabilities(charon, TRUE, FALSE); + charon->drop_capabilities(charon, TRUE, TRUE, FALSE); while(TRUE) { diff --git a/src/charon/network/receiver.c b/src/charon/network/receiver.c index 55464db53..abb7105fd 100644 --- a/src/charon/network/receiver.c +++ b/src/charon/network/receiver.c @@ -255,14 +255,16 @@ static void receive_packets(private_receiver_t *this) (int)pthread_self()); /* drop threads capabilities */ - charon->drop_capabilities(charon, FALSE, FALSE); + charon->drop_capabilities(charon, TRUE, FALSE, FALSE); while (TRUE) { /* read in a packet */ if (charon->socket->receive(charon->socket, &packet) != SUCCESS) { - DBG1(DBG_NET, "receiving from socket failed!"); + DBG2(DBG_NET, "receiving from socket failed!"); + /* try again after a delay */ + sleep(1); continue; } diff --git a/src/charon/network/sender.c b/src/charon/network/sender.c index 90f03b7e8..37e60b61a 100644 --- a/src/charon/network/sender.c +++ b/src/charon/network/sender.c @@ -89,7 +89,7 @@ static void send_packets(private_sender_t * this) DBG1(DBG_NET, "sender thread running, thread_ID: %06u", (int)pthread_self()); /* drop threads capabilities */ - charon->drop_capabilities(charon, FALSE, FALSE); + charon->drop_capabilities(charon, TRUE, FALSE, FALSE); while (TRUE) { diff --git a/src/charon/processing/scheduler.c b/src/charon/processing/scheduler.c index d4accb630..2fb4e16e7 100644 --- a/src/charon/processing/scheduler.c +++ b/src/charon/processing/scheduler.c @@ -61,7 +61,7 @@ static void get_events(private_scheduler_t * this) (int)pthread_self()); /* drop threads capabilities */ - charon->drop_capabilities(charon, FALSE, FALSE); + charon->drop_capabilities(charon, TRUE, FALSE, FALSE); while (TRUE) { diff --git a/src/charon/processing/thread_pool.c b/src/charon/processing/thread_pool.c index e78e378ac..09e1707e5 100644 --- a/src/charon/processing/thread_pool.c +++ b/src/charon/processing/thread_pool.c @@ -74,7 +74,7 @@ static void process_jobs(private_thread_pool_t *this) (int)pthread_self()); /* drop threads capabilities, except CAP_NET_ADMIN */ - charon->drop_capabilities(charon, TRUE, FALSE); + charon->drop_capabilities(charon, TRUE, TRUE, FALSE); while (TRUE) { diff --git a/src/charon/sa/authenticators/eap/eap_method.c b/src/charon/sa/authenticators/eap/eap_method.c index a4d8abb58..9665c86b6 100644 --- a/src/charon/sa/authenticators/eap/eap_method.c +++ b/src/charon/sa/authenticators/eap/eap_method.c @@ -100,27 +100,10 @@ void eap_method_unload() void eap_method_load(char *directory) { struct dirent* entry; - struct stat stb; DIR* dir; eap_method_unload(); modules = linked_list_create(); - - if (stat(directory, &stb) == -1 || !(stb.st_mode & S_IFDIR)) - { - DBG1(DBG_CFG, "error opening EAP modules directory %s", directory); - return; - } - if (stb.st_uid != 0) - { - DBG1(DBG_CFG, "EAP modules directory %s not owned by root, skipped", directory); - return; - } - if (stb.st_mode & S_IWOTH || stb.st_mode & S_IWGRP) - { - DBG1(DBG_CFG, "EAP modules directory %s writable by others, skipped", directory); - return; - } dir = opendir(directory); if (dir == NULL) @@ -141,12 +124,6 @@ void eap_method_load(char *directory) snprintf(file, sizeof(file), "%s/%s", directory, entry->d_name); - if (stat(file, &stb) == -1 || !(stb.st_mode & S_IFREG)) - { - DBG2(DBG_CFG, " skipping %s, doesn't look like a file", - entry->d_name); - continue; - } ending = entry->d_name + strlen(entry->d_name) - 3; if (ending <= entry->d_name || !streq(ending, ".so")) { @@ -155,16 +132,6 @@ void eap_method_load(char *directory) entry->d_name); continue; } - if (stb.st_uid != 0) - { - DBG1(DBG_CFG, " skipping %s, file is not owned by root", entry->d_name); - return; - } - if (stb.st_mode & S_IWOTH || stb.st_mode & S_IWGRP) - { - DBG1(DBG_CFG, " skipping %s, file is writeable by others", entry->d_name); - continue; - } /* try to load the library */ module.handle = dlopen(file, RTLD_LAZY); diff --git a/src/pluto/Makefile.am b/src/pluto/Makefile.am index 93fb75a4a..7dd5f422b 100644 --- a/src/pluto/Makefile.am +++ b/src/pluto/Makefile.am @@ -137,4 +137,5 @@ install-exec-local : mkdir -p -m 755 $(confdir)/ipsec.d/crls mkdir -p -m 755 $(confdir)/ipsec.d/reqs mkdir -p -m 700 $(confdir)/ipsec.d/private + chown -R $(ipsecuid):$(ipsecgid) $(confdir)/ipsec.d diff --git a/src/pluto/plutomain.c b/src/pluto/plutomain.c index e235ff765..d9b2167c8 100644 --- a/src/pluto/plutomain.c +++ b/src/pluto/plutomain.c @@ -29,6 +29,8 @@ #include #include /* missing from on old systems */ #include +#include +#include #include @@ -64,6 +66,11 @@ #include "nat_traversal.h" #include "virtual.h" +/* on some distros, a capset() definition is missing */ +#ifdef NO_CAPSET_DEFINED +extern int capset(cap_user_header_t hdrp, const cap_user_data_t datap); +#endif /* NO_CAPSET_DEFINED */ + static void usage(const char *mess) { @@ -221,6 +228,8 @@ main(int argc, char **argv) bool force_keepalive = FALSE; char *virtual_private = NULL; int lockfd; + struct __user_cap_header_struct hdr; + struct __user_cap_data_struct data; /* handle arguments */ for (;;) @@ -596,6 +605,26 @@ main(int argc, char **argv) init_id(); init_fetch(); + /* drop unneeded capabilities and change UID/GID */ + hdr.version = _LINUX_CAPABILITY_VERSION; + hdr.pid = 0; + data.effective = data.permitted = 1<