changing UID/GID after startup of pluto/charon
authorMartin Willi <martin@strongswan.org>
Mon, 7 May 2007 12:38:46 +0000 (12:38 -0000)
committerMartin Willi <martin@strongswan.org>
Mon, 7 May 2007 12:38:46 +0000 (12:38 -0000)
added --with-uid/--with-gid configure option

16 files changed:
configure.in
src/charon/config/credentials/local_credential_store.c
src/charon/control/interfaces/dbus_interface.c
src/charon/control/interfaces/stroke_interface.c
src/charon/daemon.c
src/charon/daemon.h
src/charon/kernel/kernel_interface.c
src/charon/network/receiver.c
src/charon/network/sender.c
src/charon/processing/scheduler.c
src/charon/processing/thread_pool.c
src/charon/sa/authenticators/eap/eap_method.c
src/pluto/Makefile.am
src/pluto/plutomain.c
src/starter/invokecharon.c
src/starter/invokepluto.c

index 8b54b42..eade13e 100644 (file)
@@ -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 <linux/capset.h>],
+    [
+       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
index 82ea787..a920150 100644 (file)
@@ -24,6 +24,7 @@
 #include <dirent.h>
 #include <string.h>
 #include <pthread.h>
+#include <errno.h>
 
 #include <library.h>
 #include <utils/lexparser.h>
@@ -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));
        }
 }
 
index 5805d2b..8f048ba 100644 (file)
@@ -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))
        {
index 7ae34f8..d841992 100755 (executable)
@@ -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. */
index 4ef878f..e266c96 100644 (file)
@@ -23,8 +23,8 @@
  */
 
 #include <stdio.h>
-#include <linux/types.h>
 #include <linux/capability.h>
+#include <sys/prctl.h>
 #include <signal.h>
 #include <pthread.h>
 #include <sys/stat.h>
 #include <config/backends/local_backend.h>
 #include <sa/authenticators/eap/eap_method.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 */
 
 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<<CAP_NET_BIND_SERVICE);
                keep |= (1<<CAP_NET_RAW);
+               keep |= (1<<CAP_DAC_READ_SEARCH);
        }
        
        hdr.version = _LINUX_CAPABILITY_VERSION;
@@ -244,6 +251,16 @@ static void drop_capabilities(private_daemon_t *this, bool netlink, bool bind)
        data.effective = data.permitted = keep;
        data.inheritable = 0;
        
+       if (change_uid)
+       {
+#              if IPSEC_GID
+                       setgid(IPSEC_GID);
+#              endif
+#              if IPSEC_UID
+                       setuid(IPSEC_UID);
+#              endif
+       }
+       
        if (capset(&hdr, &data))
        {
                kill_daemon(this, "unable to drop threads capabilities");
@@ -355,7 +372,7 @@ private_daemon_t *daemon_create(void)
                
        /* assign methods */
        this->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;
 }
+
index b7edad8..f8add30 100644 (file)
@@ -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);
 };
index d0560b1..c68c504 100644 (file)
@@ -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) 
        {
index 55464db..abb7105 100644 (file)
@@ -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;
                }
                
index 90f03b7..37e60b6 100644 (file)
@@ -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)
        {
index d4accb6..2fb4e16 100644 (file)
@@ -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)
        {
index e78e378..09e1707 100644 (file)
@@ -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)
        {
index a4d8abb..9665c86 100644 (file)
@@ -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);
index 93fb75a..7dd5f42 100644 (file)
@@ -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
 
index e235ff7..d9b2167 100644 (file)
@@ -29,6 +29,8 @@
 #include <resolv.h>
 #include <arpa/nameser.h>      /* missing from <resolv.h> on old systems */
 #include <sys/queue.h>
+#include <linux/capability.h>
+#include <sys/prctl.h>
 
 #include <freeswan.h>
 
 #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<<CAP_NET_ADMIN | 1<<CAP_NET_BIND_SERVICE;
+    data.inheritable = 0;
+
+    prctl(PR_SET_KEEPCAPS, 1);
+
+#   if IPSEC_GID
+       setgid(IPSEC_GID);
+#   endif
+#   if IPSEC_UID
+       setuid(IPSEC_UID);
+#   endif
+    if (capset(&hdr, &data))
+    {
+       plog("unable to drop root privileges");
+       abort();
+    }
+
     /* loading X.509 CA certificates */
     load_authcerts("CA cert", CA_CERT_PATH, AUTH_CA);
     /* loading X.509 AA certificates */
index 422e478..48cb415 100644 (file)
@@ -181,7 +181,11 @@ starter_start_charon (starter_config_t *cfg, bool debug)
            FILE *f;
 
            plog("no %s file, generating RSA key", SECRETS_FILE);
+           seteuid(IPSEC_UID);
+           setegid(IPSEC_GID);
            system("ipsec scepclient --out pkcs1 --out cert-self --quiet");
+           seteuid(0);
+           setegid(0);
 
            /* ipsec.secrets is root readable only */
            oldmask = umask(0066);
@@ -194,6 +198,7 @@ starter_start_charon (starter_config_t *cfg, bool debug)
                fprintf(f, ": RSA myKey.der\n");
                fclose(f);
            }
+           chown(SECRETS_FILE, IPSEC_UID, IPSEC_GID);
            umask(oldmask);
        }
 
index 1b11b4a..240d983 100644 (file)
@@ -216,7 +216,11 @@ starter_start_pluto (starter_config_t *cfg, bool debug)
            FILE *f;
 
            plog("no %s file, generating RSA key", SECRETS_FILE);
+           seteuid(IPSEC_UID);
+           setegid(IPSEC_GID);
            system("ipsec scepclient --out pkcs1 --out cert-self --quiet");
+           seteuid(0);
+           setegid(0);
 
            /* ipsec.secrets is root readable only */
            oldmask = umask(0066);
@@ -229,6 +233,7 @@ starter_start_pluto (starter_config_t *cfg, bool debug)
                fprintf(f, ": RSA myKey.der\n");
                fclose(f);
            }
+           chown(SECRETS_FILE, IPSEC_UID, IPSEC_GID);
            umask(oldmask);
        }