[05/15] mka: finish implementation of CP state machine "port_enabled" parameter

Message ID 20180302201103.16264-6-msiedzik@extremenetworks.com
State New
Headers show
Series
  • MKA bugfixes and enhancements
Related show

Commit Message

Michael Siedzik March 2, 2018, 8:10 p.m.
From: Mike Siedzik <msiedzik@extremenetworks.com>

KaY's controlled port (CP) state machine contains a 'port_enabled'
parameter, but the current implementation is incomplete.  There is no
way to set the value, and it is ignored by the CP state machine.

A new function, ieee802_1x_kay_notify_port_enabled(), has been added
that allows the system to notify KaY and CP of changes to the common
port's MAC_Operational state.  When port_enabled is set FALSE, the KaY
will stop transmitting MKPDUs and delete all secure associations as well
as all receive secure channels.  When port_enabled is set TRUE, the KaY
will resume MKA for all participants.

The KaY will also notify the CP whenever port_enabled changes state.
When port_enabled is FALSE the CP state machine will be forced back to
the INIT state, as perscribed by IEEE802.1X-2010 Figure 12-2 - CP state
machine.

Additionally fixed the setting of ieee802_1x_mka_participant.active,
which is used to calculate ieee802_1x_kay.active, which corresponds to
ieee8021XKayMkaActive in IEEE8021X-PAE-MIB.

Additionally fixed the behavior of ieee802_1x_kay_mka_participate(),
which now sets/clears ieee802_1x_mka_participant.participant.  I assume
this parameter is supposed to correspond to MKA.participate, which is
defined in IEEE802.1X-2010 Clause 12.2 Kay Interfaces.

 "- MKA.participate: Set by the Logon Process to request the actor's
    active participation in MKA.  Cleared by the Logon process to
    request the actor to cease participation."

Currently no process in hostap calls ieee802_1x_kay_mka_particiate(),
but if one ever does in the future the function should now work.

Signed-off-by: Michael Siedzik <msiedzik@extremenetworks.com>
---
 src/pae/ieee802_1x_cp.c    |  13 +++++-
 src/pae/ieee802_1x_cp.h    |   1 +
 src/pae/ieee802_1x_kay.c   | 103 ++++++++++++++++++++++++++++++++++++++++++---
 src/pae/ieee802_1x_kay.h   |   2 +
 src/pae/ieee802_1x_kay_i.h |   4 +-
 5 files changed, 114 insertions(+), 9 deletions(-)

--
2.11.1

Patch

diff --git a/src/pae/ieee802_1x_cp.c b/src/pae/ieee802_1x_cp.c
index 360fcd3f5..e6b2767e2 100644
--- a/src/pae/ieee802_1x_cp.c
+++ b/src/pae/ieee802_1x_cp.c
@@ -126,7 +126,6 @@  SM_STATE(CP, INIT)
        sm->otx = FALSE;
        sm->orx = FALSE;

-       sm->port_enabled = TRUE;
        sm->chgd_server = FALSE;
 }

@@ -448,6 +447,7 @@  struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init(struct ieee802_1x_kay *kay)
        sm->replay_window = kay->macsec_replay_window;

        sm->controlled_port_enabled = FALSE;
+       sm->port_enabled = TRUE;

        sm->lki = NULL;
        sm->lrx = FALSE;
@@ -693,6 +693,17 @@  void ieee802_1x_cp_set_usingtransmitas(void *cp_ctx, Boolean status)


 /**
+ * ieee802_1x_cp_set_portenabled -
+ * @status: indicates MAC_Operational status of the common port
+ */
+void ieee802_1x_cp_set_portenabled(void *cp_ctx, Boolean status)
+{
+       struct ieee802_1x_cp_sm *sm = cp_ctx;
+       sm->port_enabled = status;
+}
+
+
+/**
  * ieee802_1x_cp_sm_step - Advance EAPOL state machines
  * @sm: EAPOL state machine
  *
diff --git a/src/pae/ieee802_1x_cp.h b/src/pae/ieee802_1x_cp.h
index 695629e5c..0d5358666 100644
--- a/src/pae/ieee802_1x_cp.h
+++ b/src/pae/ieee802_1x_cp.h
@@ -36,5 +36,6 @@  void ieee802_1x_cp_set_usingreceivesas(void *cp_ctx, Boolean status);
 void ieee802_1x_cp_set_allreceiving(void *cp_ctx, Boolean status);
 void ieee802_1x_cp_set_servertransmitting(void *cp_ctx, Boolean status);
 void ieee802_1x_cp_set_usingtransmitas(void *cp_ctx, Boolean status);
+void ieee802_1x_cp_set_portenabled(void *cp_ctx, Boolean status);

 #endif /* IEEE802_1X_CP_H */
diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c
index fd329e610..17519ae69 100644
--- a/src/pae/ieee802_1x_kay.c
+++ b/src/pae/ieee802_1x_kay.c
@@ -772,6 +772,11 @@  ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg,
                return NULL;
        }

+       if (!participant->participant) {
+               wpa_printf(MSG_MSGDUMP, "KaY: MKA participation disabled");
+               return NULL;
+       }
+
        /* If the peer's MI is my MI, I will choose new MI */
        if (os_memcmp(body->actor_mi, participant->mi, MI_LEN) == 0) {
                if (!reset_participant_mi(participant))
@@ -1240,7 +1245,6 @@  ieee802_1x_mka_encode_sak_use_body(
        /* set CP's variable */
        if (body->ltx) {
                kay->tx_enable = TRUE;
-               kay->port_enable = TRUE;
        }
        if (body->lrx)
                kay->rx_enable = TRUE;
@@ -2329,6 +2333,28 @@  static void ieee802_1x_delete_transmit_sa(struct ieee802_1x_kay *kay,


 /**
+ * ieee802_1x_kay_recalc_mka_active -
+ */
+static void
+ieee802_1x_kay_recalc_mka_active(struct ieee802_1x_kay *kay)
+{
+       struct ieee802_1x_mka_participant *participant;
+       Boolean active = FALSE;
+
+       /* Recalculate KaY active (ieee8021XKayMkaActive) */
+       dl_list_for_each(participant, &kay->participant_list,
+                        struct ieee802_1x_mka_participant, list) {
+               if (participant->active) {
+                       active = TRUE;
+                       break;
+               }
+       }
+
+       kay->active = active;
+}
+
+
+/**
  * ieee802_1x_participant_timer -
  */
 static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx)
@@ -2338,6 +2364,7 @@  static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx)
        struct ieee802_1x_kay_peer *peer, *pre_peer;
        time_t now = time(NULL);
        Boolean lp_changed;
+       Boolean participate;
        struct receive_sc *rxsc, *pre_rxsc;
        struct transmit_sa *txsa, *pre_txsa;

@@ -2348,6 +2375,10 @@  static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx)
                        goto delete_mka;
        }

+       /* only participate in MKA (i.e., transmit MKPDUs) if port
+        * is enabled and participant has not been disabled */
+       participate = kay->port_enable && participant->participant;
+
        /* should delete MKA instance if there are not live peers
         * when the MKA life elapsed since its creating */
        if (participant->mka_life) {
@@ -2362,7 +2393,7 @@  static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx)
        lp_changed = FALSE;
        dl_list_for_each_safe(peer, pre_peer, &participant->live_peers,
                              struct ieee802_1x_kay_peer, list) {
-               if (now > peer->expire) {
+               if (!participate || (now > peer->expire)) {
                        wpa_printf(MSG_DEBUG, "KaY: Live peer removed");
                        wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi,
                                    sizeof(peer->mi));
@@ -2420,7 +2451,7 @@  static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx)

        dl_list_for_each_safe(peer, pre_peer, &participant->potential_peers,
                              struct ieee802_1x_kay_peer, list) {
-               if (now > peer->expire) {
+               if (!participate || (now > peer->expire)) {
                        wpa_printf(MSG_DEBUG, "KaY: Potential peer removed");
                        wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi,
                                    sizeof(peer->mi));
@@ -2430,6 +2461,12 @@  static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx)
                }
        }

+       if (!participate) {
+               participant->active = FALSE;
+               ieee802_1x_kay_recalc_mka_active(kay);
+               return;
+       }
+
        if (participant->new_sak) {
                if (!ieee802_1x_kay_generate_new_sak(participant))
                        participant->to_dist_sak = TRUE;
@@ -2836,7 +2873,7 @@  int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay)
        struct ieee802_1x_mka_participant *principal;

        principal = ieee802_1x_kay_get_principal_participant(kay);
-       if (!principal)
+       if (!principal || !principal->participant)
                return -1;

        if (principal->retry_count < MAX_RETRY_CNT || principal->mode == PSK) {
@@ -3321,7 +3358,7 @@  ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn,
        participant->cached = FALSE;

        participant->active = FALSE;
-       participant->participant = FALSE;
+       participant->participant = TRUE;
        participant->retain = FALSE;
        participant->activate = DEFAULT;

@@ -3465,6 +3502,8 @@  ieee802_1x_kay_delete_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn)
        os_memset(&participant->kek, 0, sizeof(participant->kek));
        os_memset(&participant->ick, 0, sizeof(participant->ick));
        os_free(participant);
+
+       ieee802_1x_kay_recalc_mka_active(kay);
 }


@@ -3484,7 +3523,57 @@  void ieee802_1x_kay_mka_participate(struct ieee802_1x_kay *kay,
        if (!participant)
                return;

-       participant->active = status;
+       if (participant->participant == status)
+               return;
+
+       participant->participant = status;
+
+       if (status) {
+               /* Restart timer immediately if both port and participant are enabled */
+               if (kay->port_enable) {
+                       eloop_register_timeout(0, 0, ieee802_1x_participant_timer,
+                                              participant, NULL);
+               }
+       } else {
+               participant->active = FALSE;
+               ieee802_1x_kay_recalc_mka_active(kay);
+       }
+}
+
+
+/**
+ * ieee802_1x_kay_notify_port_enabled -
+ */
+void ieee802_1x_kay_notify_port_enabled(struct ieee802_1x_kay *kay,
+                                       Boolean enabled)
+{
+       struct ieee802_1x_mka_participant *participant;
+       unsigned int usecs;
+
+       if (kay->port_enable == enabled)
+               return;
+
+       wpa_printf(MSG_DEBUG, "KaY: External notification - "
+                  "portEnabled=%d", enabled);
+
+       kay->port_enable = enabled;
+
+       ieee802_1x_cp_set_portenabled(kay->cp, enabled);
+
+       if (!enabled) {
+               /* Existing participants will be cleaned up next time their timers expire */
+               kay->active = FALSE;
+       } else {
+               /* Staggered restart of participant timers */
+               dl_list_for_each(participant, &kay->participant_list,
+                                struct ieee802_1x_mka_participant, list) {
+                       if (participant->participant) {
+                               usecs = os_random() % (MKA_HELLO_TIME * 1000);
+                               eloop_register_timeout(0, usecs, ieee802_1x_participant_timer,
+                                                      participant, NULL);
+                       }
+               }
+       }
 }


@@ -3575,6 +3664,7 @@  int ieee802_1x_kay_get_status(struct ieee802_1x_kay *kay, char *buf,
                return 0;

        len = os_snprintf(buf, buflen,
+                         "Port status=%s\n"
                          "PAE KaY status=%s\n"
                          "Authenticated=%s\n"
                          "Secured=%s\n"
@@ -3584,6 +3674,7 @@  int ieee802_1x_kay_get_status(struct ieee802_1x_kay *kay, char *buf,
                          "Is Key Server=%s\n"
                          "Number of Keys Distributed=%u\n"
                          "Number of Keys Received=%u\n",
+                         kay->port_enable ? "Enabled" : "Disabled",
                          kay->active ? "Active" : "Not-Active",
                          kay->authenticated ? "Yes" : "No",
                          kay->secured ? "Yes" : "No",
diff --git a/src/pae/ieee802_1x_kay.h b/src/pae/ieee802_1x_kay.h
index 8f394fd96..8c1a3b2fa 100644
--- a/src/pae/ieee802_1x_kay.h
+++ b/src/pae/ieee802_1x_kay.h
@@ -249,6 +249,8 @@  void ieee802_1x_kay_delete_mka(struct ieee802_1x_kay *kay,
 void ieee802_1x_kay_mka_participate(struct ieee802_1x_kay *kay,
                                    struct mka_key_name *ckn,
                                    Boolean status);
+void ieee802_1x_kay_notify_port_enabled(struct ieee802_1x_kay *kay,
+                                       Boolean enabled);
 int ieee802_1x_kay_new_sak(struct ieee802_1x_kay *kay);
 int ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay,
                                       unsigned int cs_index);
diff --git a/src/pae/ieee802_1x_kay_i.h b/src/pae/ieee802_1x_kay_i.h
index bc522d898..c10851d2d 100644
--- a/src/pae/ieee802_1x_kay_i.h
+++ b/src/pae/ieee802_1x_kay_i.h
@@ -90,8 +90,8 @@  struct ieee802_1x_mka_participant {
        Boolean cached;

        /* used by management to monitor and control activation */
-       Boolean active;
-       Boolean participant;
+       Boolean active;      /* used to calculate ieee8021XKayMkaActive (IEEE8021X-PAE-MIB) */
+       Boolean participant; /* MKA.participate, from IEEE802.1X-2010, Clause 12.2 */
        Boolean retain;
        enum mka_created_mode mode;