reducing capabilities of the threads to a minimum
proper flush of pending packets on daemon shutdown adding local address as gateway address in dynamic route
This commit is contained in:
@@ -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**)¤t))
|
||||
{
|
||||
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**)¤t))
|
||||
{
|
||||
/* 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);
|
||||
|
||||
peer_cfg = get_peer_cfg_by_name(name);
|
||||
if (peer_cfg)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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_connection_send(this->conn, signal, NULL);
|
||||
}
|
||||
dbus_message_unref(signal);
|
||||
|
||||
set_state(this, NM_VPN_STATE_STARTED);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -59,5 +59,5 @@ interface_t *interface_create()
|
||||
|
||||
this->public.interface.destroy = (void (*)(xml_interface_t*))destroy;
|
||||
|
||||
return &this->public;
|
||||
return &this->public.interface;
|
||||
}
|
||||
|
||||
+46
-7
@@ -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.
|
||||
*/
|
||||
@@ -215,6 +217,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
|
||||
*/
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
@@ -418,6 +420,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.
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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...");
|
||||
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user