]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
child-create: Add support to handle security labels
authorTobias Brunner <tobias@strongswan.org>
Wed, 2 Feb 2022 09:40:16 +0000 (10:40 +0100)
committerTobias Brunner <tobias@strongswan.org>
Thu, 14 Apr 2022 16:42:01 +0000 (18:42 +0200)
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.

src/libcharon/sa/ikev2/tasks/child_create.c
src/libcharon/sa/ikev2/tasks/child_create.h

index 389ffb2d75833db40c74873ec3e5d9958d488aae..ddfe8cf05e09d478e4432789c55afdfbd7a2a05f 100644 (file)
@@ -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,
index eae1f3532f548edcc56a693229812a752ab8f5e9..40e25d89a874ca6b957047546f1471a065caa4ac 100644 (file)
@@ -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.
         *