mirror of
https://github.com/strongswan/strongswan.git
synced 2025-10-04 00:00:14 -04:00
kernel-netlink: Add support for optional security label on SAs and policies
This commit is contained in:
parent
c4e2b7617f
commit
bf0542c4e1
@ -38,6 +38,7 @@ typedef struct kernel_ipsec_query_policy_t kernel_ipsec_query_policy_t;
|
||||
#include <networking/host.h>
|
||||
#include <ipsec/ipsec_types.h>
|
||||
#include <selectors/traffic_selector.h>
|
||||
#include <selectors/sec_label.h>
|
||||
#include <plugins/plugin.h>
|
||||
#include <kernel/kernel_interface.h>
|
||||
|
||||
@ -97,6 +98,8 @@ struct kernel_ipsec_add_sa_t {
|
||||
hw_offload_t hw_offload;
|
||||
/** Mark the SA should apply to packets after processing */
|
||||
mark_t mark;
|
||||
/** Security label to match or apply */
|
||||
sec_label_t *label;
|
||||
/** TRUE to use Extended Sequence Numbers */
|
||||
bool esn;
|
||||
/** TRUE to copy the DF bit to the outer IPv4 header in tunnel mode */
|
||||
@ -160,6 +163,8 @@ struct kernel_ipsec_policy_id_t {
|
||||
uint32_t if_id;
|
||||
/** Network interface restricting policy */
|
||||
char *interface;
|
||||
/** Security label restricting policy */
|
||||
sec_label_t *label;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -577,6 +577,9 @@ struct policy_entry_t {
|
||||
/** Optional interface ID */
|
||||
uint32_t if_id;
|
||||
|
||||
/** Optional security label */
|
||||
sec_label_t *label;
|
||||
|
||||
/** Associated route installed for this policy */
|
||||
route_entry_t *route;
|
||||
|
||||
@ -609,6 +612,7 @@ static void policy_entry_destroy(private_kernel_netlink_ipsec_t *this,
|
||||
policy->direction, this);
|
||||
policy->used_by->destroy(policy->used_by);
|
||||
}
|
||||
DESTROY_IF(policy->label);
|
||||
free(policy);
|
||||
}
|
||||
|
||||
@ -618,8 +622,15 @@ static void policy_entry_destroy(private_kernel_netlink_ipsec_t *this,
|
||||
static u_int policy_hash(policy_entry_t *key)
|
||||
{
|
||||
chunk_t chunk = chunk_from_thing(key->sel);
|
||||
return chunk_hash_inc(chunk, chunk_hash_inc(chunk_from_thing(key->mark),
|
||||
u_int hash;
|
||||
|
||||
hash = chunk_hash_inc(chunk, chunk_hash_inc(chunk_from_thing(key->mark),
|
||||
chunk_hash(chunk_from_thing(key->if_id))));
|
||||
if (key->label)
|
||||
{
|
||||
hash = key->label->hash(key->label, hash);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -630,7 +641,8 @@ static bool policy_equals(policy_entry_t *key, policy_entry_t *other_key)
|
||||
return memeq(&key->sel, &other_key->sel, sizeof(struct xfrm_selector)) &&
|
||||
key->mark == other_key->mark &&
|
||||
key->if_id == other_key->if_id &&
|
||||
key->direction == other_key->direction;
|
||||
key->direction == other_key->direction &&
|
||||
sec_labels_equal(key->label, other_key->label);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1286,6 +1298,47 @@ static bool add_mark(struct nlmsghdr *hdr, int buflen, mark_t mark)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the security label for debug messages
|
||||
*/
|
||||
static void format_label(char *buf, int buflen, sec_label_t *label)
|
||||
{
|
||||
if (label)
|
||||
{
|
||||
snprintf(buf, buflen, " (ctx %s)", label->get_string(label));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a security label to message if required
|
||||
*/
|
||||
static bool add_label(struct nlmsghdr *hdr, int buflen, sec_label_t *label)
|
||||
{
|
||||
if (label)
|
||||
{
|
||||
#ifdef USE_SELINUX
|
||||
struct xfrm_user_sec_ctx *ctx;
|
||||
chunk_t enc = label->get_encoding(label);
|
||||
int len = sizeof(*ctx) + enc.len;
|
||||
|
||||
ctx = netlink_reserve(hdr, buflen, XFRMA_SEC_CTX, len);
|
||||
if (!ctx)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
/* this attribute for some reason duplicates the generic header */
|
||||
ctx->exttype = XFRMA_SEC_CTX;
|
||||
ctx->len = len;
|
||||
|
||||
ctx->ctx_doi = XFRM_SC_DOI_LSM;
|
||||
ctx->ctx_alg = XFRM_SC_ALG_SELINUX;
|
||||
ctx->ctx_len = enc.len;
|
||||
memcpy((void*)(ctx + 1), enc.ptr, enc.len);
|
||||
#endif
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a uint32 attribute to message
|
||||
*/
|
||||
@ -1874,6 +1927,11 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (!add_label(hdr, sizeof(request), data->label))
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (ipcomp == IPCOMP_NONE && (data->mark.value | data->mark.mask))
|
||||
{
|
||||
if (!add_uint32(hdr, sizeof(request), XFRMA_SET_MARK,
|
||||
@ -2747,7 +2805,9 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this,
|
||||
}
|
||||
tmpl->reqid = ipsec->cfg.reqid;
|
||||
tmpl->id.proto = protos[i].proto;
|
||||
if (policy->direction == POLICY_OUT)
|
||||
/* in order to match SAs with all matching labels, we can't have the
|
||||
* SPI in the template */
|
||||
if (policy->direction == POLICY_OUT && !policy->label)
|
||||
{
|
||||
tmpl->id.spi = protos[i].spi;
|
||||
}
|
||||
@ -2781,6 +2841,11 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this,
|
||||
policy_change_done(this, policy);
|
||||
return FAILED;
|
||||
}
|
||||
if (!add_label(hdr, sizeof(request), policy->label))
|
||||
{
|
||||
policy_change_done(this, policy);
|
||||
return FAILED;
|
||||
}
|
||||
this->mutex->unlock(this->mutex);
|
||||
|
||||
status = this->socket_xfrm->send_ack(this->socket_xfrm, hdr);
|
||||
@ -2826,7 +2891,7 @@ METHOD(kernel_ipsec_t, add_policy, status_t,
|
||||
policy_sa_t *assigned_sa, *current_sa;
|
||||
enumerator_t *enumerator;
|
||||
bool found = FALSE, update = TRUE;
|
||||
char markstr[32] = "";
|
||||
char markstr[32] = "", labelstr[128] = "";
|
||||
uint32_t cur_priority = 0;
|
||||
int use_count;
|
||||
|
||||
@ -2835,10 +2900,12 @@ METHOD(kernel_ipsec_t, add_policy, status_t,
|
||||
.sel = ts2selector(id->src_ts, id->dst_ts, id->interface),
|
||||
.mark = id->mark.value & id->mark.mask,
|
||||
.if_id = id->if_id,
|
||||
.label = id->label ? id->label->clone(id->label) : NULL,
|
||||
.direction = id->dir,
|
||||
.reqid = data->sa->reqid,
|
||||
);
|
||||
format_mark(markstr, sizeof(markstr), id->mark);
|
||||
format_label(labelstr, sizeof(labelstr), id->label);
|
||||
|
||||
/* find the policy, which matches EXACTLY */
|
||||
this->mutex->lock(this->mutex);
|
||||
@ -2848,18 +2915,18 @@ METHOD(kernel_ipsec_t, add_policy, status_t,
|
||||
if (current->reqid && data->sa->reqid &&
|
||||
current->reqid != data->sa->reqid)
|
||||
{
|
||||
DBG1(DBG_CFG, "unable to install policy %R === %R %N%s for reqid "
|
||||
DBG1(DBG_CFG, "unable to install policy %R === %R %N%s%s for reqid "
|
||||
"%u, the same policy for reqid %u exists",
|
||||
id->src_ts, id->dst_ts, policy_dir_names, id->dir, markstr,
|
||||
data->sa->reqid, current->reqid);
|
||||
labelstr, data->sa->reqid, current->reqid);
|
||||
policy_entry_destroy(this, policy);
|
||||
this->mutex->unlock(this->mutex);
|
||||
return INVALID_STATE;
|
||||
}
|
||||
/* use existing policy */
|
||||
DBG2(DBG_KNL, "policy %R === %R %N%s already exists, increasing "
|
||||
DBG2(DBG_KNL, "policy %R === %R %N%s%s already exists, increasing "
|
||||
"refcount", id->src_ts, id->dst_ts, policy_dir_names, id->dir,
|
||||
markstr);
|
||||
markstr, labelstr);
|
||||
policy_entry_destroy(this, policy);
|
||||
policy = current;
|
||||
found = TRUE;
|
||||
@ -2923,9 +2990,9 @@ METHOD(kernel_ipsec_t, add_policy, status_t,
|
||||
{ /* we don't update the policy if the priority is lower than that of
|
||||
* the currently installed one */
|
||||
policy_change_done(this, policy);
|
||||
DBG2(DBG_KNL, "not updating policy %R === %R %N%s [priority %u, "
|
||||
DBG2(DBG_KNL, "not updating policy %R === %R %N%s%s [priority %u, "
|
||||
"refcount %d]", id->src_ts, id->dst_ts, policy_dir_names,
|
||||
id->dir, markstr, cur_priority, use_count);
|
||||
id->dir, markstr, labelstr, cur_priority, use_count);
|
||||
return SUCCESS;
|
||||
}
|
||||
policy->reqid = assigned_sa->sa->cfg.reqid;
|
||||
@ -2935,15 +3002,16 @@ METHOD(kernel_ipsec_t, add_policy, status_t,
|
||||
found = TRUE;
|
||||
}
|
||||
|
||||
DBG2(DBG_KNL, "%s policy %R === %R %N%s [priority %u, refcount %d]",
|
||||
DBG2(DBG_KNL, "%s policy %R === %R %N%s%s [priority %u, refcount %d]",
|
||||
found ? "updating" : "adding", id->src_ts, id->dst_ts,
|
||||
policy_dir_names, id->dir, markstr, assigned_sa->priority, use_count);
|
||||
policy_dir_names, id->dir, markstr, labelstr, assigned_sa->priority,
|
||||
use_count);
|
||||
|
||||
if (add_policy_internal(this, policy, assigned_sa, found) != SUCCESS)
|
||||
{
|
||||
DBG1(DBG_KNL, "unable to %s policy %R === %R %N%s",
|
||||
DBG1(DBG_KNL, "unable to %s policy %R === %R %N%s%s",
|
||||
found ? "update" : "add", id->src_ts, id->dst_ts,
|
||||
policy_dir_names, id->dir, markstr);
|
||||
policy_dir_names, id->dir, markstr, labelstr);
|
||||
return FAILED;
|
||||
}
|
||||
return SUCCESS;
|
||||
@ -2958,13 +3026,14 @@ METHOD(kernel_ipsec_t, query_policy, status_t,
|
||||
struct xfrm_userpolicy_id *policy_id;
|
||||
struct xfrm_userpolicy_info *policy = NULL;
|
||||
size_t len;
|
||||
char markstr[32] = "";
|
||||
char markstr[32] = "", labelstr[128] = "";
|
||||
|
||||
memset(&request, 0, sizeof(request));
|
||||
format_mark(markstr, sizeof(markstr), id->mark);
|
||||
format_label(labelstr, sizeof(labelstr), id->label);
|
||||
|
||||
DBG2(DBG_KNL, "querying policy %R === %R %N%s", id->src_ts, id->dst_ts,
|
||||
policy_dir_names, id->dir, markstr);
|
||||
DBG2(DBG_KNL, "querying policy %R === %R %N%s%s", id->src_ts, id->dst_ts,
|
||||
policy_dir_names, id->dir, markstr, labelstr);
|
||||
|
||||
hdr = &request.hdr;
|
||||
hdr->nlmsg_flags = NLM_F_REQUEST;
|
||||
@ -2983,6 +3052,10 @@ METHOD(kernel_ipsec_t, query_policy, status_t,
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
if (!add_label(hdr, sizeof(request), id->label))
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
if (this->socket_xfrm->send(this->socket_xfrm, hdr, &out, &len) == SUCCESS)
|
||||
{
|
||||
@ -3054,20 +3127,22 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
|
||||
.if_id = id->if_id,
|
||||
.cfg = *data->sa,
|
||||
};
|
||||
char markstr[32] = "";
|
||||
char markstr[32] = "", labelstr[128] = "";
|
||||
int use_count;
|
||||
status_t status = SUCCESS;
|
||||
|
||||
format_mark(markstr, sizeof(markstr), id->mark);
|
||||
format_label(labelstr, sizeof(labelstr), id->label);
|
||||
|
||||
DBG2(DBG_KNL, "deleting policy %R === %R %N%s", id->src_ts, id->dst_ts,
|
||||
policy_dir_names, id->dir, markstr);
|
||||
DBG2(DBG_KNL, "deleting policy %R === %R %N%s%s", id->src_ts, id->dst_ts,
|
||||
policy_dir_names, id->dir, markstr, labelstr);
|
||||
|
||||
/* create a policy */
|
||||
memset(&policy, 0, sizeof(policy_entry_t));
|
||||
policy.sel = ts2selector(id->src_ts, id->dst_ts, id->interface);
|
||||
policy.mark = id->mark.value & id->mark.mask;
|
||||
policy.if_id = id->if_id;
|
||||
policy.label = id->label;
|
||||
policy.direction = id->dir;
|
||||
|
||||
/* find the policy */
|
||||
@ -3075,8 +3150,9 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
|
||||
current = this->policies->get(this->policies, &policy);
|
||||
if (!current)
|
||||
{
|
||||
DBG1(DBG_KNL, "deleting policy %R === %R %N%s failed, not found",
|
||||
id->src_ts, id->dst_ts, policy_dir_names, id->dir, markstr);
|
||||
DBG1(DBG_KNL, "deleting policy %R === %R %N%s%s failed, not found",
|
||||
id->src_ts, id->dst_ts, policy_dir_names, id->dir, markstr,
|
||||
labelstr);
|
||||
this->mutex->unlock(this->mutex);
|
||||
return NOT_FOUND;
|
||||
}
|
||||
@ -3089,7 +3165,7 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
|
||||
current->waiting--;
|
||||
|
||||
/* remove mapping to SA by reqid and priority */
|
||||
auto_priority = get_priority(current, data->prio,id->interface);
|
||||
auto_priority = get_priority(current, data->prio, id->interface);
|
||||
priority = this->get_priority ? this->get_priority(id, data)
|
||||
: data->manual_prio;
|
||||
priority = priority ?: auto_priority;
|
||||
@ -3121,22 +3197,23 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
|
||||
if (!is_installed)
|
||||
{ /* no need to update as the policy was not installed for this SA */
|
||||
policy_change_done(this, current);
|
||||
DBG2(DBG_KNL, "not updating policy %R === %R %N%s [priority %u, "
|
||||
DBG2(DBG_KNL, "not updating policy %R === %R %N%s%s [priority %u, "
|
||||
"refcount %d]", id->src_ts, id->dst_ts, policy_dir_names,
|
||||
id->dir, markstr, cur_priority, use_count);
|
||||
id->dir, markstr, labelstr, cur_priority, use_count);
|
||||
return SUCCESS;
|
||||
}
|
||||
current->used_by->get_first(current->used_by, (void**)&mapping);
|
||||
current->reqid = mapping->sa->cfg.reqid;
|
||||
|
||||
DBG2(DBG_KNL, "updating policy %R === %R %N%s [priority %u, "
|
||||
DBG2(DBG_KNL, "updating policy %R === %R %N%s%s [priority %u, "
|
||||
"refcount %d]", id->src_ts, id->dst_ts, policy_dir_names, id->dir,
|
||||
markstr, mapping->priority, use_count);
|
||||
markstr, labelstr, mapping->priority, use_count);
|
||||
|
||||
if (add_policy_internal(this, current, mapping, TRUE) != SUCCESS)
|
||||
{
|
||||
DBG1(DBG_KNL, "unable to update policy %R === %R %N%s",
|
||||
id->src_ts, id->dst_ts, policy_dir_names, id->dir, markstr);
|
||||
DBG1(DBG_KNL, "unable to update policy %R === %R %N%s%s",
|
||||
id->src_ts, id->dst_ts, policy_dir_names, id->dir, markstr,
|
||||
labelstr);
|
||||
return FAILED;
|
||||
}
|
||||
return SUCCESS;
|
||||
@ -3163,6 +3240,11 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
|
||||
policy_change_done(this, current);
|
||||
return FAILED;
|
||||
}
|
||||
if (!add_label(hdr, sizeof(request), id->label))
|
||||
{
|
||||
policy_change_done(this, current);
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
if (current->route)
|
||||
{
|
||||
@ -3173,16 +3255,16 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
|
||||
route->pass) != SUCCESS)
|
||||
{
|
||||
DBG1(DBG_KNL, "error uninstalling route installed with policy "
|
||||
"%R === %R %N%s", id->src_ts, id->dst_ts, policy_dir_names,
|
||||
id->dir, markstr);
|
||||
"%R === %R %N%s%s", id->src_ts, id->dst_ts, policy_dir_names,
|
||||
id->dir, markstr, labelstr);
|
||||
}
|
||||
}
|
||||
this->mutex->unlock(this->mutex);
|
||||
|
||||
if (this->socket_xfrm->send_ack(this->socket_xfrm, hdr) != SUCCESS)
|
||||
{
|
||||
DBG1(DBG_KNL, "unable to delete policy %R === %R %N%s", id->src_ts,
|
||||
id->dst_ts, policy_dir_names, id->dir, markstr);
|
||||
DBG1(DBG_KNL, "unable to delete policy %R === %R %N%s%s", id->src_ts,
|
||||
id->dst_ts, policy_dir_names, id->dir, markstr, labelstr);
|
||||
status = FAILED;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user