reducing capabilities of the threads to a minimum
authorMartin Willi <martin@strongswan.org>
Thu, 3 May 2007 14:21:22 +0000 (14:21 -0000)
committerMartin Willi <martin@strongswan.org>
Thu, 3 May 2007 14:21:22 +0000 (14:21 -0000)
proper flush of pending packets on daemon shutdown
adding local address as gateway address in dynamic route

src/charon/control/interfaces/dbus_interface.c
src/charon/control/interfaces/stroke_interface.c
src/charon/control/interfaces/stroke_interface.h
src/charon/control/interfaces/xml_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

index 178f74f..5805d2b 100644 (file)
@@ -89,6 +89,79 @@ static void set_state(private_dbus_interface_t *this, NMVPNState state)
        this->state = state;
 }
 
+
+/**
+ * get the child_cfg with the same name as the peer cfg
+ */
+static child_cfg_t* get_child_from_peer(peer_cfg_t *peer_cfg, char *name)
+{
+       child_cfg_t *current, *found = NULL;
+       iterator_t *iterator;
+       
+       iterator = peer_cfg->create_child_cfg_iterator(peer_cfg);
+       while (iterator->iterate(iterator, (void**)&current))
+       {
+               if (streq(current->get_name(current), name))
+               {
+                       found = current;
+                       found->get_ref(found);
+                       break;
+               }
+       }
+       iterator->destroy(iterator);
+       return found;
+}
+
+/**
+ * get a peer configuration by its name, or a name of its children
+ */
+static peer_cfg_t *get_peer_cfg_by_name(char *name)
+{
+       iterator_t *i1, *i2;
+       peer_cfg_t *current, *found = NULL;
+       child_cfg_t *child;
+
+       i1 = charon->backends->create_iterator(charon->backends);
+       while (i1->iterate(i1, (void**)&current))
+       {
+               /* compare peer_cfgs name first */
+               if (streq(current->get_name(current), name))
+               {
+                       found = current;
+                       found->get_ref(found);
+                       break;
+               }
+               /* compare all child_cfg names otherwise */
+               i2 = current->create_child_cfg_iterator(current);
+               while (i2->iterate(i2, (void**)&child))
+               {
+                       if (streq(child->get_name(child), name))
+                       {
+                               found = current;
+                               found->get_ref(found);
+                               break;
+                       }
+               }
+               i2->destroy(i2);
+               if (found)
+               {
+                       break;
+               }
+       }
+       i1->destroy(i1);
+       return found;
+}
+
+/**
+ * logging dummy
+ */
+static bool dbus_log(void *param, signal_t signal, level_t level,
+                                        ike_sa_t *ike_sa, char *format, va_list args)
+{
+       return TRUE;
+}
+
+
 /**
  * process NetworkManagers startConnection method call
  */
@@ -101,6 +174,9 @@ static bool start_connection(private_dbus_interface_t *this, DBusMessage* msg)
        char *dev, *domain, *banner;
        const dbus_int32_t array[] = {};
        const dbus_int32_t *varray = array;
+       peer_cfg_t *peer_cfg;
+       child_cfg_t *child_cfg;
+       status_t status = FAILED;
 
        if (!dbus_message_get_args(msg, &this->err,
                         DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &user,
@@ -113,33 +189,48 @@ static bool start_connection(private_dbus_interface_t *this, DBusMessage* msg)
        }
        set_state(this, NM_VPN_STATE_STARTING);
        
-       reply = dbus_message_new_method_return(msg);
-       dbus_connection_send(this->conn, reply, NULL);
-       
-       signal = dbus_message_new_signal(NM_DBUS_PATH_STRONG,
-                                                                        NM_DBUS_INTERFACE_STRONG,
-                                                                        NM_DBUS_VPN_SIGNAL_IP4_CONFIG);
-       
-       me = other = p2p = mss = netmask = 0;
-       dev = domain = banner = "";
-       if (dbus_message_append_args(signal,
-                                       DBUS_TYPE_UINT32, &other,
-                                       DBUS_TYPE_STRING, &dev,
-                                       DBUS_TYPE_UINT32, &me,
-                                       DBUS_TYPE_UINT32, &p2p,
-                                       DBUS_TYPE_UINT32, &netmask,
-                                       DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &varray, 0,
-                                       DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &varray, 0,
-                                       DBUS_TYPE_UINT32, &mss,
-                                       DBUS_TYPE_STRING, &domain,
-                                       DBUS_TYPE_STRING, &banner))
+       peer_cfg = get_peer_cfg_by_name(name);
+       if (peer_cfg)
        {
-               dbus_connection_send(this->conn, signal, NULL);
-       }                                                
-       dbus_message_unref(signal);
+               child_cfg = get_child_from_peer(peer_cfg, name);
+               if (child_cfg)
+               {
+                       status = charon->interfaces->initiate(charon->interfaces, peer_cfg,
+                                                                                                 child_cfg, dbus_log, NULL);
+               }
+       }       
        
-       set_state(this, NM_VPN_STATE_STARTED);
+       if (status == SUCCESS)
+       {
+               signal = dbus_message_new_signal(NM_DBUS_PATH_STRONG,
+                                                                                NM_DBUS_INTERFACE_STRONG,
+                                                                                NM_DBUS_VPN_SIGNAL_IP4_CONFIG);
+               me = other = p2p = mss = netmask = 0;
+               dev = domain = banner = "";
+               if (dbus_message_append_args(signal,
+                                               DBUS_TYPE_UINT32, &other,
+                                               DBUS_TYPE_STRING, &dev,
+                                               DBUS_TYPE_UINT32, &me,
+                                               DBUS_TYPE_UINT32, &p2p,
+                                               DBUS_TYPE_UINT32, &netmask,
+                                               DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &varray, 0,
+                                               DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &varray, 0,
+                                               DBUS_TYPE_UINT32, &mss,
+                                               DBUS_TYPE_STRING, &domain,
+                                               DBUS_TYPE_STRING, &banner, DBUS_TYPE_INVALID))
+               {
+                       dbus_connection_send(this->conn, signal, NULL);
+               }                                                
+               dbus_message_unref(signal);
+               set_state(this, NM_VPN_STATE_STARTED);
+       }
+       else
+       {
+               set_state(this, NM_VPN_STATE_STOPPED);
+       }
        
+       reply = dbus_message_new_method_return(msg);
+       dbus_connection_send(this->conn, reply, NULL);
        dbus_connection_flush(this->conn);
        dbus_message_unref(reply);
        return TRUE;
@@ -247,6 +338,9 @@ 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);
+
        while (dbus_connection_read_write_dispatch(this->conn, -1))
        {
                /* nothing */
@@ -273,7 +367,7 @@ interface_t *interface_create()
        DBusObjectPathVTable v = {NULL, (void*)&message_handler, NULL, NULL, NULL, NULL};
        private_dbus_interface_t *this = malloc_thing(private_dbus_interface_t);
   
-       this->public.interface.destroy = (void (*)(dbus_interface_t*))destroy;
+       this->public.interface.destroy = (void (*)(interface_t*))destroy;
        
        dbus_error_init(&this->err); 
        this->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &this->err);
@@ -320,5 +414,5 @@ interface_t *interface_create()
                charon->kill(charon, "unable to create stroke thread");
        }
 
-       return &this->public;
+       return &this->public.interface;
 }
index e48ed26..7ae34f8 100755 (executable)
@@ -1528,6 +1528,9 @@ static void stroke_receive(private_stroke_interface_t *this)
        int oldstate;
        int strokefd;
        
+       /* drop threads capabilities */
+       charon->drop_capabilities(charon, FALSE, FALSE);
+       
        /* ignore sigpipe. writing over the pipe back to the console
         * only fails if SIGPIPE is ignored. */
        signal(SIGPIPE, SIG_IGN);
@@ -1585,7 +1588,7 @@ interface_t *interface_create()
        this->socket = socket(AF_UNIX, SOCK_STREAM, 0);
        if (this->socket == -1)
        {
-               DBG1(DBG_CFG, "could not create whack socket");
+               DBG1(DBG_CFG, "could not create stroke socket");
                free(this);
                return NULL;
        }
@@ -1618,5 +1621,5 @@ interface_t *interface_create()
                }
        }
        
-       return (interface_t*)(&this->public);
+       return &this->public.interface;
 }
index 5eaa324..f1b6802 100644 (file)
@@ -50,7 +50,7 @@ struct stroke_interface_t {
 /**
  * @brief Create the stroke interface and listen on the socket.
  * 
- * @return                     stroke_t object
+ * @return                     interface_t for the stroke interface
  * 
  * @ingroup interfaces
  */
index ad92e80..e570f25 100644 (file)
@@ -59,5 +59,5 @@ interface_t *interface_create()
 
        this->public.interface.destroy = (void (*)(xml_interface_t*))destroy;
        
-       return &this->public;
+       return &this->public.interface;
 }
index ac16eb2..4ef878f 100644 (file)
@@ -23,6 +23,8 @@
  */
 
 #include <stdio.h>
+#include <linux/types.h>
+#include <linux/capability.h>
 #include <signal.h>
 #include <pthread.h>
 #include <sys/stat.h>
@@ -178,7 +180,6 @@ static void destroy(private_daemon_t *this)
        DESTROY_IF(this->public.event_queue);
        DESTROY_IF(this->public.credentials);
        DESTROY_IF(this->public.backends);
-       sched_yield();
        /* we hope the sender could send the outstanding deletes, but 
         * we shut down here at any cost */
        DESTROY_IF(this->public.sender);
@@ -192,6 +193,7 @@ static void destroy(private_daemon_t *this)
        free(this);
 }
 
+
 /**
  * Enforce daemon shutdown, with a given reason to do so.
  */
@@ -216,6 +218,39 @@ 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)
+{
+       struct __user_cap_header_struct hdr;
+       struct __user_cap_data_struct data;
+       u_int32_t keep = 0;
+       
+       if (netlink)
+       {
+               /* CAP_NET_ADMIN is needed to use netlink */
+               keep |= (1<<CAP_NET_ADMIN);
+       }
+       if (bind)
+       {
+               /* CAP_NET_BIND_SERVICE to bind services below port 1024, 
+                * CAP_NET_RAW to create RAW sockets. */
+               keep |= (1<<CAP_NET_BIND_SERVICE);
+               keep |= (1<<CAP_NET_RAW);
+       }
+       
+       hdr.version = _LINUX_CAPABILITY_VERSION;
+       hdr.pid = 0;
+       data.effective = data.permitted = keep;
+       data.inheritable = 0;
+       
+       if (capset(&hdr, &data))
+       {
+               kill_daemon(this, "unable to drop threads capabilities");
+       }
+}
+
+/**
  * Initialize the daemon, optional with a strict crl policy
  */
 static void initialize(private_daemon_t *this, bool syslog, level_t levels[])
@@ -241,12 +276,9 @@ static void initialize(private_daemon_t *this, bool syslog, level_t levels[])
        /* apply loglevels */
        for (signal = 0; signal < DBG_MAX; signal++)
        {
-               if (syslog)
-               {
-                       this->public.syslog->set_level(this->public.syslog,
-                                                                                  signal, levels[signal]);
-               }
-               else
+               this->public.syslog->set_level(this->public.syslog,
+                                                                          signal, levels[signal]);
+               if (!syslog)
                {
                        this->public.outlog->set_level(this->public.outlog,
                                                                                   signal, levels[signal]);
@@ -323,6 +355,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;
        
        /* NULL members for clean destruction */
        this->public.socket = NULL;
@@ -406,6 +439,9 @@ int main(int argc, char *argv[])
        level_t levels[DBG_MAX];
        int signal;
        
+       /* keep bind() and netlink capabilities */
+       drop_capabilities(NULL, TRUE, TRUE);
+       
        /* use CTRL loglevel for default */
        for (signal = 0; signal < DBG_MAX; signal++)
        {
@@ -479,6 +515,9 @@ int main(int argc, char *argv[])
        
        /* initialize daemon */
        initialize(private_charon, use_syslog, levels);
+       
+       /* drop bind() capability, netlink is needed for cleanup */
+       drop_capabilities(private_charon, TRUE, FALSE);
 
        /* load pluggable EAP modules */
        eap_method_load(eapdir);
index c442094..b7edad8 100644 (file)
@@ -332,6 +332,8 @@ 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.
  *
@@ -419,6 +421,15 @@ struct daemon_t {
        interface_manager_t *interfaces;
        
        /**
+        * @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
+        */
+       void (*drop_capabilities) (daemon_t *this, bool netlink, bool bind);
+       
+       /**
         * @brief Shut down the daemon.
         * 
         * @param this          the daemon to kill
index ffe7fea..d0560b1 100644 (file)
@@ -171,6 +171,9 @@ struct route_entry_t {
 
        /** Source ip of the route */
        host_t *src_ip;
+       
+       /** gateway for this route */
+       host_t *gateway;
 
        /** Destination net */
        chunk_t dst_net;
@@ -185,6 +188,7 @@ struct route_entry_t {
 static void route_entry_destroy(route_entry_t *this)
 {
        this->src_ip->destroy(this->src_ip);
+       this->gateway->destroy(this->gateway);
        chunk_free(&this->dst_net);
        free(this);
 }
@@ -442,6 +446,9 @@ 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);
+
        while(TRUE) 
        {
                unsigned char response[512];
@@ -1023,6 +1030,8 @@ static status_t manage_srcroute(private_kernel_interface_t *this, int nlmsg_type
        add_attribute(hdr, RTA_DST, route->dst_net, sizeof(request));
        chunk = route->src_ip->get_address(route->src_ip);
        add_attribute(hdr, RTA_PREFSRC, chunk, sizeof(request));
+       chunk = route->gateway->get_address(route->gateway);
+       add_attribute(hdr, RTA_GATEWAY, chunk, sizeof(request));
        chunk.ptr = (char*)&route->if_index;
        chunk.len = sizeof(route->if_index);
        add_attribute(hdr, RTA_OIF, chunk, sizeof(request));
@@ -1689,6 +1698,8 @@ static status_t add_policy(private_kernel_interface_t *this,
                policy->route = malloc_thing(route_entry_t);
                if (get_address_by_ts(this, dst_ts, &policy->route->src_ip) == SUCCESS)
                {
+                       policy->route->gateway = (direction == POLICY_IN) ? 
+                                                                               dst->clone(dst) : src->clone(src);
                        policy->route->if_index = get_interface_index(this, dst);
                        policy->route->dst_net = chunk_alloc(policy->sel.family == AF_INET ? 4 : 16);
                        memcpy(policy->route->dst_net.ptr, &policy->sel.saddr, policy->route->dst_net.len);
index 640339c..55464db 100644 (file)
@@ -254,6 +254,9 @@ static void receive_packets(private_receiver_t *this)
        DBG1(DBG_NET, "receiver thread running, thread_ID: %06u", 
                 (int)pthread_self());
        
+       /* drop threads capabilities */
+       charon->drop_capabilities(charon, FALSE, FALSE);
+       
        while (TRUE)
        {
                /* read in a packet */
index c1cd0a6..90f03b7 100644 (file)
@@ -84,10 +84,12 @@ static void send_(private_sender_t *this, packet_t *packet)
  */
 static void send_packets(private_sender_t * this)
 {
-       
        /* cancellation disabled by default */
        pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
        DBG1(DBG_NET, "sender thread running, thread_ID: %06u", (int)pthread_self());
+       
+       /* drop threads capabilities */
+       charon->drop_capabilities(charon, FALSE, FALSE);
 
        while (TRUE)
        {
@@ -119,9 +121,14 @@ static void send_packets(private_sender_t * this)
  */
 static void destroy(private_sender_t *this)
 {
+       /* send all packets in the queue */
+       while (this->list->get_count(this->list))
+       {
+               sched_yield();
+       }
        pthread_cancel(this->assigned_thread);
        pthread_join(this->assigned_thread, NULL);
-       this->list->destroy_offset(this->list, offsetof(packet_t, destroy));
+       this->list->destroy(this->list);
        free(this);
 }
 
index 156c1e2..d4accb6 100644 (file)
@@ -60,6 +60,9 @@ static void get_events(private_scheduler_t * this)
        DBG1(DBG_JOB, "scheduler thread running, thread_ID: %06u", 
                 (int)pthread_self());
 
+       /* drop threads capabilities */
+       charon->drop_capabilities(charon, FALSE, FALSE);
+
        while (TRUE)
        {
                DBG2(DBG_JOB, "waiting for next event...");
index effa30a..e78e378 100644 (file)
@@ -57,7 +57,7 @@ struct private_thread_pool_t {
         * Array of thread ids.
         */
        pthread_t *threads;
-} ;
+};
 
 /**
  * Implementation of private_thread_pool_t.process_jobs.
@@ -73,6 +73,9 @@ static void process_jobs(private_thread_pool_t *this)
        DBG1(DBG_JOB, "worker thread running, thread_ID: %06u",
                 (int)pthread_self());
        
+       /* drop threads capabilities, except CAP_NET_ADMIN */
+       charon->drop_capabilities(charon, TRUE, FALSE);
+       
        while (TRUE)
        {
                /* TODO: should be atomic, but is not mission critical */