diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 740ec4c3d..5a21af296 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -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 diff --git a/src/drivers/driver_macsec_linux.c b/src/drivers/driver_macsec_linux.c index 4605ee64a..9d981bb70 100644 --- a/src/drivers/driver_macsec_linux.c +++ b/src/drivers/driver_macsec_linux.c @@ -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, diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c index 827e42d43..a96340c50 100644 --- a/src/pae/ieee802_1x_kay.c +++ b/src/pae/ieee802_1x_kay.c @@ -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); + 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; - lpn = lpn > rxsa->lowest_pn ? - lpn : rxsa->lowest_pn; - break; - } + /* 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; diff --git a/src/pae/ieee802_1x_kay.h b/src/pae/ieee802_1x_kay.h index 425732c25..5891f7400 100644 --- a/src/pae/ieee802_1x_kay.h +++ b/src/pae/ieee802_1x_kay.h @@ -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); diff --git a/src/pae/ieee802_1x_secy_ops.c b/src/pae/ieee802_1x_secy_ops.c index ab5339bb2..4e5379ff7 100644 --- a/src/pae/ieee802_1x_secy_ops.c +++ b/src/pae/ieee802_1x_secy_ops.c @@ -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; diff --git a/src/pae/ieee802_1x_secy_ops.h b/src/pae/ieee802_1x_secy_ops.h index 9fb29c3dd..2d112ba7c 100644 --- a/src/pae/ieee802_1x_secy_ops.h +++ b/src/pae/ieee802_1x_secy_ops.h @@ -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); diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h index 86c891aa4..4a9f472e8 100644 --- a/wpa_supplicant/driver_i.h +++ b/wpa_supplicant/driver_i.h @@ -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) diff --git a/wpa_supplicant/wpas_kay.c b/wpa_supplicant/wpas_kay.c index c662bbbeb..99599daf3 100644 --- a/wpa_supplicant/wpas_kay.c +++ b/wpa_supplicant/wpas_kay.c @@ -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;