child-create: Add support to handle security labels
With SELinux and without a specific label from an acquire, we abort establishing the CHILD_SA (for the first one we prefer a childless IKE_SA, but since that's a separate extension, we fall back to letting the initial CHILD_SA fail as we won't propose a label). If trap policies are not installed already (e.g. because it's impossible to do so like as responder for roadwarriors), this will require installing them dynamically once the IKE_SA is established.
This commit is contained in:
@@ -84,15 +84,25 @@ struct private_child_create_t {
|
||||
proposal_t *proposal;
|
||||
|
||||
/**
|
||||
* traffic selectors for initiators side
|
||||
* traffic selectors for initiator side
|
||||
*/
|
||||
linked_list_t *tsi;
|
||||
|
||||
/**
|
||||
* traffic selectors for responders side
|
||||
* traffic selectors for responder side
|
||||
*/
|
||||
linked_list_t *tsr;
|
||||
|
||||
/**
|
||||
* labels for initiator side
|
||||
*/
|
||||
linked_list_t *labels_i;
|
||||
|
||||
/**
|
||||
* labels for responder side
|
||||
*/
|
||||
linked_list_t *labels_r;
|
||||
|
||||
/**
|
||||
* source of triggering packet
|
||||
*/
|
||||
@@ -210,6 +220,7 @@ static void schedule_delayed_retry(private_child_create_t *this)
|
||||
task->use_reqid(task, this->child.reqid);
|
||||
task->use_marks(task, this->child.mark_in, this->child.mark_out);
|
||||
task->use_if_ids(task, this->child.if_id_in, this->child.if_id_out);
|
||||
task->use_label(task, this->child.label);
|
||||
|
||||
DBG1(DBG_IKE, "creating CHILD_SA failed, trying again in %d seconds",
|
||||
retry);
|
||||
@@ -891,9 +902,11 @@ static bool build_payloads(private_child_create_t *this, message_t *message)
|
||||
}
|
||||
|
||||
/* add TSi/TSr payloads */
|
||||
ts_payload = ts_payload_create_from_traffic_selectors(TRUE, this->tsi, NULL);
|
||||
ts_payload = ts_payload_create_from_traffic_selectors(TRUE, this->tsi,
|
||||
this->child.label);
|
||||
message->add_payload(message, (payload_t*)ts_payload);
|
||||
ts_payload = ts_payload_create_from_traffic_selectors(FALSE, this->tsr, NULL);
|
||||
ts_payload = ts_payload_create_from_traffic_selectors(FALSE, this->tsr,
|
||||
this->child.label);
|
||||
message->add_payload(message, (payload_t*)ts_payload);
|
||||
|
||||
/* add a notify if we are not in tunnel mode */
|
||||
@@ -1039,10 +1052,12 @@ static void process_payloads(private_child_create_t *this, message_t *message)
|
||||
case PLV2_TS_INITIATOR:
|
||||
ts_payload = (ts_payload_t*)payload;
|
||||
this->tsi = ts_payload->get_traffic_selectors(ts_payload);
|
||||
this->labels_i = ts_payload->get_sec_labels(ts_payload);
|
||||
break;
|
||||
case PLV2_TS_RESPONDER:
|
||||
ts_payload = (ts_payload_t*)payload;
|
||||
this->tsr = ts_payload->get_traffic_selectors(ts_payload);
|
||||
this->labels_r = ts_payload->get_sec_labels(ts_payload);
|
||||
break;
|
||||
case PLV2_NOTIFY:
|
||||
handle_notify(this, (notify_payload_t*)payload);
|
||||
@@ -1054,6 +1069,16 @@ static void process_payloads(private_child_create_t *this, message_t *message)
|
||||
enumerator->destroy(enumerator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we have only the generic label available when using SELinux and not
|
||||
* a specific one from an acquire.
|
||||
*/
|
||||
static bool generic_label_only(private_child_create_t *this)
|
||||
{
|
||||
return this->config->get_label(this->config) && !this->child.label &&
|
||||
this->config->get_label_mode(this->config) == SEC_LABEL_MODE_SELINUX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we should defer the creation of this CHILD_SA until after the
|
||||
* IKE_SA has been established childless.
|
||||
@@ -1061,17 +1086,23 @@ static void process_payloads(private_child_create_t *this, message_t *message)
|
||||
static status_t defer_child_sa(private_child_create_t *this)
|
||||
{
|
||||
ike_cfg_t *ike_cfg;
|
||||
childless_t policy;
|
||||
|
||||
ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
|
||||
policy = ike_cfg->childless(ike_cfg);
|
||||
|
||||
if (this->ike_sa->supports_extension(this->ike_sa, EXT_IKE_CHILDLESS))
|
||||
{
|
||||
if (ike_cfg->childless(ike_cfg) == CHILDLESS_FORCE)
|
||||
/* with SELinux, we prefer not to create a CHILD_SA when we only have
|
||||
* the generic label available. if the peer does not support it,
|
||||
* creating the SA will most likely fail */
|
||||
if (policy == CHILDLESS_FORCE ||
|
||||
generic_label_only(this))
|
||||
{
|
||||
return NEED_MORE;
|
||||
}
|
||||
}
|
||||
else if (ike_cfg->childless(ike_cfg) == CHILDLESS_FORCE)
|
||||
else if (policy == CHILDLESS_FORCE)
|
||||
{
|
||||
DBG1(DBG_IKE, "peer does not support childless IKE_SA initiation");
|
||||
return DESTROY_ME;
|
||||
@@ -1099,7 +1130,7 @@ static bool child_sa_equals(child_sa_t *a, child_sa_t *b)
|
||||
|
||||
/**
|
||||
* Check if there is a duplicate CHILD_SA already established and we can abort
|
||||
* initiating this one
|
||||
* initiating this one.
|
||||
*/
|
||||
static bool check_for_duplicate(private_child_create_t *this)
|
||||
{
|
||||
@@ -1141,6 +1172,26 @@ static bool check_for_duplicate(private_child_create_t *this)
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this is an attempt to create an SA with generic label and should
|
||||
* be aborted.
|
||||
*/
|
||||
static bool check_for_generic_label(private_child_create_t *this)
|
||||
{
|
||||
if (generic_label_only(this))
|
||||
{
|
||||
sec_label_t *label;
|
||||
|
||||
label = this->config->get_label(this->config);
|
||||
DBG1(DBG_IKE, "not establishing CHILD_SA %s{%d} with generic "
|
||||
"label '%s'", this->child_sa->get_name(this->child_sa),
|
||||
this->child_sa->get_unique_id(this->child_sa),
|
||||
label->get_string(label));
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
METHOD(task_t, build_i, status_t,
|
||||
private_child_create_t *this, message_t *message)
|
||||
{
|
||||
@@ -1231,6 +1282,22 @@ METHOD(task_t, build_i, status_t,
|
||||
this->tsr->insert_first(this->tsr,
|
||||
this->packet_tsr->clone(this->packet_tsr));
|
||||
}
|
||||
|
||||
if (!generic_label_only(this) && !this->child.label)
|
||||
{ /* in the simple label mode we propose the configured label as we
|
||||
* won't have labels from acquires */
|
||||
this->child.label = this->config->get_label(this->config);
|
||||
if (this->child.label)
|
||||
{
|
||||
this->child.label = this->child.label->clone(this->child.label);
|
||||
}
|
||||
}
|
||||
if (this->child.label)
|
||||
{
|
||||
DBG2(DBG_CFG, "proposing security label '%s'",
|
||||
this->child.label->get_string(this->child.label));
|
||||
}
|
||||
|
||||
this->proposals = this->config->get_proposals(this->config,
|
||||
this->dh_group == MODP_NONE);
|
||||
this->mode = this->config->get_mode(this->config);
|
||||
@@ -1246,7 +1313,7 @@ METHOD(task_t, build_i, status_t,
|
||||
* by controller and trap manager */
|
||||
if (!this->rekey &&
|
||||
message->get_exchange_type(message) == CREATE_CHILD_SA &&
|
||||
check_for_duplicate(this))
|
||||
(check_for_generic_label(this) || check_for_duplicate(this)))
|
||||
{
|
||||
message->set_exchange_type(message, EXCHANGE_TYPE_UNDEFINED);
|
||||
return SUCCESS;
|
||||
@@ -1414,8 +1481,8 @@ static child_cfg_t* select_child_cfg(private_child_create_t *this)
|
||||
listr = get_dynamic_hosts(this->ike_sa, TRUE);
|
||||
listi = get_dynamic_hosts(this->ike_sa, FALSE);
|
||||
child_cfg = peer_cfg->select_child_cfg(peer_cfg,
|
||||
tsr ?: this->tsr, tsi ?: this->tsi,
|
||||
listr, listi, NULL, NULL);
|
||||
tsr ?: this->tsr, tsi ?: this->tsi,
|
||||
listr, listi, this->labels_r, this->labels_i);
|
||||
if ((tsi || tsr) && child_cfg &&
|
||||
child_cfg->get_mode(child_cfg) != MODE_TRANSPORT)
|
||||
{
|
||||
@@ -1427,8 +1494,8 @@ static child_cfg_t* select_child_cfg(private_child_create_t *this)
|
||||
{
|
||||
/* no match for the substituted NAT selectors, try it without */
|
||||
child_cfg = peer_cfg->select_child_cfg(peer_cfg,
|
||||
this->tsr, this->tsi,
|
||||
listr, listi, NULL, NULL);
|
||||
this->tsr, this->tsi,
|
||||
listr, listi, this->labels_r, this->labels_i);
|
||||
}
|
||||
listr->destroy(listr);
|
||||
listi->destroy(listi);
|
||||
@@ -1470,6 +1537,49 @@ static status_t handle_childless(private_child_create_t *this)
|
||||
return NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a security label.
|
||||
*
|
||||
* We already know that the proposed labels match the selected config, just make
|
||||
* sure that the proposed/returned labels are the same.
|
||||
*/
|
||||
static bool select_label(private_child_create_t *this)
|
||||
{
|
||||
sec_label_t *li, *lr;
|
||||
|
||||
if (!this->config->select_label(this->config, this->labels_i, FALSE, &li, NULL) ||
|
||||
!this->config->select_label(this->config, this->labels_r, FALSE, &lr, NULL))
|
||||
{ /* sanity check */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (li)
|
||||
{
|
||||
if (!li->equals(li, lr))
|
||||
{
|
||||
DBG1(DBG_CHD, "security labels in TSi and TSr don't match");
|
||||
return FALSE;
|
||||
}
|
||||
else if (!this->child.label)
|
||||
{
|
||||
this->child.label = li->clone(li);
|
||||
}
|
||||
else if (!this->child.label->equals(this->child.label, li))
|
||||
{
|
||||
DBG1(DBG_CHD, "returned security label '%s' doesn't match proposed "
|
||||
"'%s'", li->get_string(li),
|
||||
this->child.label->get_string(this->child.label));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
if (this->child.label)
|
||||
{
|
||||
DBG1(DBG_CFG, "selected security label: %s",
|
||||
this->child.label->get_string(this->child.label));
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(task_t, build_r, status_t,
|
||||
private_child_create_t *this, message_t *message)
|
||||
{
|
||||
@@ -1577,6 +1687,13 @@ METHOD(task_t, build_r, status_t,
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
|
||||
if (!select_label(this))
|
||||
{
|
||||
message->add_notify(message, FALSE, TS_UNACCEPTABLE, chunk_empty);
|
||||
handle_child_sa_failure(this, message);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
this->child.if_id_in_def = this->ike_sa->get_if_id(this->ike_sa, TRUE);
|
||||
this->child.if_id_out_def = this->ike_sa->get_if_id(this->ike_sa, FALSE);
|
||||
this->child.encap = this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY);
|
||||
@@ -1834,6 +1951,12 @@ METHOD(task_t, process_i, status_t,
|
||||
return delete_failed_sa(this);
|
||||
}
|
||||
|
||||
if (!select_label(this))
|
||||
{
|
||||
handle_child_sa_failure(this, message);
|
||||
return delete_failed_sa(this);
|
||||
}
|
||||
|
||||
if (select_and_install(this, no_dh, ike_auth) == SUCCESS)
|
||||
{
|
||||
if (!this->rekey)
|
||||
@@ -1869,6 +1992,13 @@ METHOD(child_create_t, use_if_ids, void,
|
||||
this->child.if_id_out = out;
|
||||
}
|
||||
|
||||
METHOD(child_create_t, use_label, void,
|
||||
private_child_create_t *this, sec_label_t *label)
|
||||
{
|
||||
DESTROY_IF(this->child.label);
|
||||
this->child.label = label ? label->clone(label) : NULL;
|
||||
}
|
||||
|
||||
METHOD(child_create_t, use_dh_group, void,
|
||||
private_child_create_t *this, diffie_hellman_group_t dh_group)
|
||||
{
|
||||
@@ -1921,6 +2051,14 @@ METHOD(task_t, migrate, void,
|
||||
{
|
||||
this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
|
||||
}
|
||||
if (this->labels_i)
|
||||
{
|
||||
this->labels_i->destroy_offset(this->labels_i, offsetof(sec_label_t, destroy));
|
||||
}
|
||||
if (this->labels_r)
|
||||
{
|
||||
this->labels_r->destroy_offset(this->labels_r, offsetof(sec_label_t, destroy));
|
||||
}
|
||||
DESTROY_IF(this->child_sa);
|
||||
DESTROY_IF(this->proposal);
|
||||
DESTROY_IF(this->nonceg);
|
||||
@@ -1963,6 +2101,14 @@ METHOD(task_t, destroy, void,
|
||||
{
|
||||
this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
|
||||
}
|
||||
if (this->labels_i)
|
||||
{
|
||||
this->labels_i->destroy_offset(this->labels_i, offsetof(sec_label_t, destroy));
|
||||
}
|
||||
if (this->labels_r)
|
||||
{
|
||||
this->labels_r->destroy_offset(this->labels_r, offsetof(sec_label_t, destroy));
|
||||
}
|
||||
if (!this->established)
|
||||
{
|
||||
DESTROY_IF(this->child_sa);
|
||||
@@ -1977,6 +2123,7 @@ METHOD(task_t, destroy, void,
|
||||
}
|
||||
DESTROY_IF(this->config);
|
||||
DESTROY_IF(this->nonceg);
|
||||
DESTROY_IF(this->child.label);
|
||||
free(this);
|
||||
}
|
||||
|
||||
@@ -1997,6 +2144,7 @@ child_create_t *child_create_create(ike_sa_t *ike_sa,
|
||||
.use_reqid = _use_reqid,
|
||||
.use_marks = _use_marks,
|
||||
.use_if_ids = _use_if_ids,
|
||||
.use_label = _use_label,
|
||||
.use_dh_group = _use_dh_group,
|
||||
.task = {
|
||||
.get_type = _get_type,
|
||||
|
||||
@@ -68,6 +68,13 @@ struct child_create_t {
|
||||
*/
|
||||
void (*use_if_ids)(child_create_t *this, uint32_t in, uint32_t out);
|
||||
|
||||
/**
|
||||
* Use specific security label, overriding configuration.
|
||||
*
|
||||
* @param label security label
|
||||
*/
|
||||
void (*use_label)(child_create_t *this, sec_label_t *label);
|
||||
|
||||
/**
|
||||
* Initially propose a specific DH group to override configuration.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user