]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
mka: Fix lowest acceptable Packet Number (LPN) calculation and use
authorMike Siedzik <msiedzik@extremenetworks.com>
Tue, 20 Feb 2018 19:28:39 +0000 (14:28 -0500)
committerJouni Malinen <j@w1.fi>
Wed, 26 Dec 2018 14:42:25 +0000 (16:42 +0200)
The purpose of the Lowest Acceptable PN (lpn) parameters in the MACsec
SAK Use parameter set is to enforce delay protection. Per IEEE Std
802.1X-2010, Clause 9, "Each SecY uses MKA to communicate the lowest PN
used for transmission with the SAK within the last two seconds, allowing
receivers to bound transmission delays."

When encoding the SAK Use parameter set the KaY should set llpn and olpn
to the lowest PN transmitted by the latest SAK and oldest SAK (if
active) within the last two seconds. Because MKPDUs are transmitted
every 2 seconds (MKA_HELLO_TIME), the solution implemented here
calculates lpn based on the txsc->next_pn read during the previous MKPDU
transmit.

Upon receiving and decoding a SAK Use parameter set with delay
protection enabled, the KaY will update the SecY's lpn if the delay
protect lpn is greater than the SecY's current lpn (which is a product
of last PN received and replay protection and window size).

Signed-off-by: Michael Siedzik <msiedzik@extremenetworks.com>
src/drivers/driver.h
src/drivers/driver_macsec_linux.c
src/pae/ieee802_1x_kay.c
src/pae/ieee802_1x_kay.h
src/pae/ieee802_1x_secy_ops.c
src/pae/ieee802_1x_secy_ops.h
wpa_supplicant/driver_i.h
wpa_supplicant/wpas_kay.c

index 740ec4c3d45105acb21e3ed860f60f8a6472f37f..5a21af296dd44f876bca5e9fcd85502c781806be 100644 (file)
@@ -3829,6 +3829,14 @@ struct wpa_driver_ops {
         */
        int (*set_transmit_next_pn)(void *priv, struct transmit_sa *sa);
 
+       /**
+        * set_receive_lowest_pn - Set receive lowest PN
+        * @priv: Private driver interface data
+        * @sa: secure association
+        * Returns: 0 on success, -1 on failure (or if not supported)
+        */
+       int (*set_receive_lowest_pn)(void *priv, struct receive_sa *sa);
+
        /**
         * create_receive_sc - create secure channel for receiving
         * @priv: Private driver interface data
index 4605ee64a1d635f104eaaf8d4aa9e16c7c60760e..9d981bb70f78c6fd2fcbf73e0527ec1c7bd73349 100644 (file)
@@ -689,6 +689,50 @@ static int macsec_drv_get_receive_lowest_pn(void *priv, struct receive_sa *sa)
 }
 
 
+/**
+ * macsec_drv_set_receive_lowest_pn - Set receive lowest PN
+ * @priv: Private driver interface data
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_set_receive_lowest_pn(void *priv, struct receive_sa *sa)
+{
+       struct macsec_drv_data *drv = priv;
+       struct macsec_genl_ctx *ctx = &drv->ctx;
+       struct nl_msg *msg;
+       struct nlattr *nest;
+       int ret = -1;
+
+       wpa_printf(MSG_DEBUG,
+                  DRV_PREFIX "%s: set_receive_lowest_pn -> %d: %d",
+                  drv->ifname, sa->an, sa->next_pn);
+
+       msg = msg_prepare(MACSEC_CMD_UPD_RXSA, ctx, drv->ifi);
+       if (!msg)
+               return ret;
+
+       nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+       if (!nest)
+               goto nla_put_failure;
+
+       NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
+       NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn);
+
+       nla_nest_end(msg, nest);
+
+       ret = nl_send_recv(ctx->sk, msg);
+       if (ret < 0) {
+               wpa_printf(MSG_ERROR,
+                          DRV_PREFIX "failed to communicate: %d (%s)",
+                          ret, nl_geterror(-ret));
+       }
+
+nla_put_failure:
+       nlmsg_free(msg);
+       return ret;
+}
+
+
 /**
  * macsec_drv_get_transmit_next_pn - Get transmit next PN
  * @priv: Private driver interface data
@@ -1373,6 +1417,7 @@ const struct wpa_driver_ops wpa_driver_macsec_linux_ops = {
        .set_current_cipher_suite = macsec_drv_set_current_cipher_suite,
        .enable_controlled_port = macsec_drv_enable_controlled_port,
        .get_receive_lowest_pn = macsec_drv_get_receive_lowest_pn,
+       .set_receive_lowest_pn = macsec_drv_set_receive_lowest_pn,
        .get_transmit_next_pn = macsec_drv_get_transmit_next_pn,
        .set_transmit_next_pn = macsec_drv_set_transmit_next_pn,
        .create_receive_sc = macsec_drv_create_receive_sc,
index 827e42d43f065bedf39cd6b4f17930b337ee7e08..a96340c50daad0cf7ca7e3b838daccc15aa2c102 100644 (file)
@@ -1155,27 +1155,38 @@ ieee802_1x_mka_get_sak_use_length(
 
 
 /**
- *
+ * ieee802_1x_mka_get_lpn
  */
 static u32
 ieee802_1x_mka_get_lpn(struct ieee802_1x_mka_participant *principal,
                       struct ieee802_1x_mka_ki *ki)
 {
-       struct receive_sa *rxsa;
-       struct receive_sc *rxsc;
+       struct transmit_sa *txsa;
        u32 lpn = 0;
 
-       dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
-               dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, list)
-               {
-                       if (is_ki_equal(&rxsa->pkey->key_identifier, ki)) {
-                               secy_get_receive_lowest_pn(principal->kay,
-                                                          rxsa);
-
-                               lpn = lpn > rxsa->lowest_pn ?
-                                       lpn : rxsa->lowest_pn;
-                               break;
-                       }
+       dl_list_for_each(txsa, &principal->txsc->sa_list,
+                        struct transmit_sa, list) {
+               if (is_ki_equal(&txsa->pkey->key_identifier, ki)) {
+                       /* Per IEEE Std 802.1X-2010, Clause 9, "Each SecY uses
+                        * MKA to communicate the lowest PN used for
+                        * transmission with the SAK within the last two
+                        * seconds".  Achieve this 2 second delay by setting the
+                        * lpn using the transmit next PN (i.e., txsa->next_pn)
+                        * that was read last time here (i.e., mka_hello_time
+                        * 2 seconds ago).
+                        *
+                        * The lowest acceptable PN is the same as the last
+                        * transmitted PN, which is one less than the next
+                        * transmit PN.
+                        *
+                        * NOTE: This method only works if mka_hello_time is 2s.
+                        */
+                       lpn = (txsa->next_pn > 0) ? (txsa->next_pn - 1) : 0;
+
+                       /* Now read the current transmit next PN for use next
+                        * time through. */
+                       secy_get_transmit_next_pn(principal->kay, txsa);
+                       break;
                }
        }
 
@@ -1277,7 +1288,8 @@ ieee802_1x_mka_decode_sak_use_body(
        struct ieee802_1x_mka_hdr *hdr;
        struct ieee802_1x_mka_sak_use_body *body;
        struct ieee802_1x_kay_peer *peer;
-       struct transmit_sa *txsa;
+       struct receive_sc *rxsc;
+       struct receive_sa *rxsa;
        struct data_key *sa_key = NULL;
        size_t body_len;
        struct ieee802_1x_mka_ki ki;
@@ -1396,25 +1408,38 @@ ieee802_1x_mka_decode_sak_use_body(
        }
 
        found = FALSE;
-       dl_list_for_each(txsa, &participant->txsc->sa_list,
-                        struct transmit_sa, list) {
-               if (sa_key != NULL && txsa->pkey == sa_key) {
-                       found = TRUE;
-                       break;
+       dl_list_for_each(rxsc, &participant->rxsc_list, struct receive_sc,
+                        list) {
+               dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa,
+                                list) {
+                       if (sa_key && rxsa->pkey == sa_key) {
+                               found = TRUE;
+                               break;
+                       }
                }
+               if (found)
+                       break;
        }
        if (!found) {
-               wpa_printf(MSG_WARNING, "KaY: Can't find txsa");
+               wpa_printf(MSG_WARNING, "KaY: Can't find rxsa");
                return -1;
        }
 
-       /* FIXME: Secy creates txsa with default npn. If MKA detected Latest Key
-        * npn is larger than txsa's npn, set it to txsa.
-        */
-       secy_get_transmit_next_pn(kay, txsa);
-       if (lpn > txsa->next_pn) {
-               secy_set_transmit_next_pn(kay, txsa);
-               wpa_printf(MSG_INFO, "KaY: update lpn =0x%x", lpn);
+       if (body->delay_protect) {
+               secy_get_receive_lowest_pn(participant->kay, rxsa);
+               if (lpn > rxsa->lowest_pn) {
+                       /* Delay protect window (communicated via MKA) is
+                        * tighter than SecY's current replay protect window,
+                        * so tell SecY the new (and higher) lpn. */
+                       rxsa->lowest_pn = lpn;
+                       secy_set_receive_lowest_pn(participant->kay, rxsa);
+                       wpa_printf(MSG_DEBUG, "KaY: update lpn =0x%x", lpn);
+               }
+               /* FIX: Delay protection for olpn not implemented.
+                * Note that Old Key is only active for MKA_SAK_RETIRE_TIME
+                * (3 seconds) and delay protection does allow PN's within
+                * a 2 seconds window, so olpn would be a lot of work for
+                * just 1 second's worth of protection. */
        }
 
        return 0;
index 425732c25ec9ade77148317c44dec403877f8614..5891f7400b9c9c529ca722dd86fbe1083ea57301 100644 (file)
@@ -150,6 +150,7 @@ struct ieee802_1x_kay_ctx {
        int (*get_receive_lowest_pn)(void *ctx, struct receive_sa *sa);
        int (*get_transmit_next_pn)(void *ctx, struct transmit_sa *sa);
        int (*set_transmit_next_pn)(void *ctx, struct transmit_sa *sa);
+       int (*set_receive_lowest_pn)(void *ctx, struct receive_sa *sa);
        int (*create_receive_sc)(void *ctx, struct receive_sc *sc,
                                 enum validate_frames vf,
                                 enum confidentiality_offset co);
index ab5339bb20462db65134295716d30847443608fc..4e5379ff72a30dd7b4b011891e76a065513d6936 100644 (file)
@@ -216,6 +216,27 @@ int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay,
 }
 
 
+int secy_set_receive_lowest_pn(struct ieee802_1x_kay *kay,
+                              struct receive_sa *rxsa)
+{
+       struct ieee802_1x_kay_ctx *ops;
+
+       if (!kay || !rxsa) {
+               wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+               return -1;
+       }
+
+       ops = kay->ctx;
+       if (!ops || !ops->set_receive_lowest_pn) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: secy set_receive_lowest_pn operation not supported");
+               return -1;
+       }
+
+       return ops->set_receive_lowest_pn(ops->ctx, rxsa);
+}
+
+
 int secy_create_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc)
 {
        struct ieee802_1x_kay_ctx *ops;
index 9fb29c3ddfa016f4128786471aaf2ea346cb01a6..2d112ba7c5d584f00aef2f704882b6ba21aa94b0 100644 (file)
@@ -36,6 +36,8 @@ int secy_get_transmit_next_pn(struct ieee802_1x_kay *kay,
                              struct transmit_sa *txsa);
 int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay,
                              struct transmit_sa *txsa);
+int secy_set_receive_lowest_pn(struct ieee802_1x_kay *kay,
+                              struct receive_sa *txsa);
 int secy_create_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc);
 int secy_delete_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc);
 int secy_create_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa);
index 86c891aa4bf29429d2f78081b2ce2b324f23bb5a..4a9f472e84eed1c7a14fa728bb30aa4ade75d4e0 100644 (file)
@@ -804,6 +804,14 @@ static inline int wpa_drv_set_transmit_next_pn(struct wpa_supplicant *wpa_s,
        return wpa_s->driver->set_transmit_next_pn(wpa_s->drv_priv, sa);
 }
 
+static inline int wpa_drv_set_receive_lowest_pn(struct wpa_supplicant *wpa_s,
+                                               struct receive_sa *sa)
+{
+       if (!wpa_s->driver->set_receive_lowest_pn)
+               return -1;
+       return wpa_s->driver->set_receive_lowest_pn(wpa_s->drv_priv, sa);
+}
+
 static inline int
 wpa_drv_create_receive_sc(struct wpa_supplicant *wpa_s, struct receive_sc *sc,
                          unsigned int conf_offset, int validation)
index c662bbbebd7665b136701eaa25b8c8f7f8877eb5..99599daf33a4635b99628031e2f3f6a537b49cf8 100644 (file)
@@ -92,6 +92,12 @@ static int wpas_set_transmit_next_pn(void *wpa_s, struct transmit_sa *sa)
 }
 
 
+static int wpas_set_receive_lowest_pn(void *wpa_s, struct receive_sa *sa)
+{
+       return wpa_drv_set_receive_lowest_pn(wpa_s, sa);
+}
+
+
 static unsigned int conf_offset_val(enum confidentiality_offset co)
 {
        switch (co) {
@@ -219,6 +225,7 @@ int ieee802_1x_alloc_kay_sm(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
        kay_ctx->get_receive_lowest_pn = wpas_get_receive_lowest_pn;
        kay_ctx->get_transmit_next_pn = wpas_get_transmit_next_pn;
        kay_ctx->set_transmit_next_pn = wpas_set_transmit_next_pn;
+       kay_ctx->set_receive_lowest_pn = wpas_set_receive_lowest_pn;
        kay_ctx->create_receive_sc = wpas_create_receive_sc;
        kay_ctx->delete_receive_sc = wpas_delete_receive_sc;
        kay_ctx->create_receive_sa = wpas_create_receive_sa;