From patchwork Tue Aug 27 03:55:38 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Winter X-Patchwork-Id: 1153667 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=hostap-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=alliedtelesis.co.nz Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="BJxhL/U2"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=alliedtelesis.co.nz header.i=@alliedtelesis.co.nz header.b="1blY/U9q"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 46Hjg346yDz9s00 for ; Tue, 27 Aug 2019 19:06:55 +1000 (AEST) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=SSxi16c5f+aBW9L8gipzS5mY3uZImZ8eTq6S+hDq94U=; b=BJxhL/U2zReIje Hdj2UG6NkpnVxpKYV77PD5TiUtYdDRlR7OcqoJiAYctlVoT0RbcDk8uPd2L6D6//rRVadOtLZ38im 74SJfSWltGeJ/IIXo1xjadeTKY7DPpbNmlCMEyloh8J474b5TRccDcweRLVlIq0sd7zlxZgqylLnf Q+wbwgHlwY1LTH+R3bu6PmkpdwG3NYH2eOno2mBq1S5iuvZnkwBu14rSFJgKalLziqHqV+rZXMs1n 9l2e/qVD0/6n9mqDZaQfj1ziPO78VwkpwOXcqBmuS+lLZDYpab0O0bLjA5TNzX2AV95sB5It5eoNV Bc9sfJCqyk0KRXIa0XvA==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92 #3 (Red Hat Linux)) id 1i2XRE-0003JL-Gj; Tue, 27 Aug 2019 09:06:52 +0000 Received: from gate2.alliedtelesis.co.nz ([2001:df5:b000:5::4]) by bombadil.infradead.org with esmtps (Exim 4.92 #3 (Red Hat Linux)) id 1i2Saz-0007jS-QH for hostap@lists.infradead.org; Tue, 27 Aug 2019 03:56:40 +0000 Received: from mmarshal3.atlnz.lc (mmarshal3.atlnz.lc [10.32.18.43]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by gate2.alliedtelesis.co.nz (Postfix) with ESMTPS id E70C6891AA for ; Tue, 27 Aug 2019 15:56:24 +1200 (NZST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=alliedtelesis.co.nz; s=mail181024; t=1566878184; bh=WFfUdluM80xZoWt3Im7yxSJCiAwKMzSXOKsSWFhXlvQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=1blY/U9q4cf5u1xzMH+c4/cU25qrfWYOXhTwSef+GiAmst7kXFINT98RIMksVSUkd wbTG21dk8WUQV5sR9F7pnOCudqaJvJ3YAPf57hIESt3ZkdBJPPC6xE+QPvNIvOqX8w f57H0PEteq9yQK/167c3S66hgRnHykQdqDnX+9ykHlHFhjHdwoVg6Pxm91PzrR9EKD iXh0HB78HZ3UtJ6tF2jn1Q0DlMHz4ARpW4AZXyFRhXNaTD0q+XOavMe/hVxDPUJHMo fiej3dTVfyQLS6mkSKlMe+lsLAEnYmQnFC1g7qoX1/h4b7s4wxxq+R69168w+j/Pje TTw9Upyysqifw== Received: from smtp (Not Verified[10.32.16.33]) by mmarshal3.atlnz.lc with Trustwave SEG (v7, 5, 8, 10121) id ; Tue, 27 Aug 2019 15:56:22 +1200 Received: from thomaswi-dl.ws.atlnz.lc (thomaswi-dl.ws.atlnz.lc [10.33.12.31]) by smtp (Postfix) with ESMTP id 58E7713EED5; Tue, 27 Aug 2019 15:56:27 +1200 (NZST) Received: by thomaswi-dl.ws.atlnz.lc (Postfix, from userid 1719) id 5CDB44404D3; Tue, 27 Aug 2019 15:56:24 +1200 (NZST) From: Thomas Winter To: hostap@lists.infradead.org Subject: [PATCH 5/5] mka: Check OLPN for exhaustion on SAKuse decode Date: Tue, 27 Aug 2019 15:55:38 +1200 Message-Id: <47a5194cdcfa3c0838e6b04b1506f2360926d5e4.1566876816.git.Thomas.Winter@alliedtelesis.co.nz> X-Mailer: git-send-email 2.23.0 In-Reply-To: References: MIME-Version: 1.0 x-atlnz-ls: pat X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20190826_205638_485437_25F20F71 X-CRM114-Status: GOOD ( 23.35 ) X-Spam-Score: -0.2 (/) X-Spam-Report: SpamAssassin version 3.4.2 on bombadil.infradead.org summary: Content analysis details: (-0.2 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 SPF_PASS SPF: sender matches SPF record -0.0 SPF_HELO_PASS SPF: HELO matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain X-Mailman-Approved-At: Tue, 27 Aug 2019 02:06:29 -0700 X-BeenThere: hostap@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Thomas Winter Sender: "Hostap" Errors-To: hostap-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Most of the time is spent in the RETIRE state where LKI is not set and OKI is the currently used SAK so OLPN needs to be checked for PN exhaustion. Hostap implemented an interpretation of the standard as described in a proposed amendment titled "MKA pending PN exhastion" which was deemed to be wrong. This amendment was included in IEEE 802.1Xck-2018. Signed-off-by: Thomas Winter --- src/pae/ieee802_1x_kay.c | 193 +++++++++++++++++++++------------------ 1 file changed, 106 insertions(+), 87 deletions(-) diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c index db292df24..e06ec337b 100644 --- a/src/pae/ieee802_1x_kay.c +++ b/src/pae/ieee802_1x_kay.c @@ -1381,7 +1381,7 @@ ieee802_1x_mka_decode_sak_use_body( struct ieee802_1x_mka_sak_use_body *body; struct ieee802_1x_kay_peer *peer; struct receive_sc *rxsc; - struct receive_sa *rxsa; + struct receive_sa *rxsa = NULL; struct data_key *sa_key = NULL; size_t body_len; struct ieee802_1x_mka_ki ki; @@ -1389,6 +1389,9 @@ ieee802_1x_mka_decode_sak_use_body( Boolean all_receiving; Boolean found; struct ieee802_1x_kay *kay = participant->kay; + struct ieee802_1x_kay_peer *peer_iter; + u32 olpn; + u32 llpn; if (!participant->principal) { wpa_printf(MSG_WARNING, "KaY: Participant is not principal"); @@ -1429,46 +1432,6 @@ ieee802_1x_mka_decode_sak_use_body( if (body->ptx) wpa_printf(MSG_WARNING, "KaY: peer's plain tx are TRUE"); - - /* check latest key is valid */ - if (body->ltx || body->lrx) { - found = FALSE; - os_memcpy(ki.mi, body->lsrv_mi, sizeof(ki.mi)); - ki.kn = be_to_host32(body->lkn); - dl_list_for_each(sa_key, &participant->sak_list, - struct data_key, list) { - if (is_ki_equal(&sa_key->key_identifier, &ki)) { - found = TRUE; - break; - } - } - if (!found) { - wpa_printf(MSG_INFO, "KaY: Latest key is invalid"); - return -1; - } - if (os_memcmp(participant->lki.mi, body->lsrv_mi, - sizeof(participant->lki.mi)) == 0 && - be_to_host32(body->lkn) == participant->lki.kn && - body->lan == participant->lan) { - peer->sak_used = TRUE; - } - if (body->ltx && peer->is_key_server) { - ieee802_1x_cp_set_servertransmitting(kay->cp, TRUE); - ieee802_1x_cp_sm_step(kay->cp); - } - } - - /* check old key is valid (but only if we remember our old key) */ - if (participant->oki.kn != 0 && (body->otx || body->orx)) { - if (os_memcmp(participant->oki.mi, body->osrv_mi, - sizeof(participant->oki.mi)) != 0 || - be_to_host32(body->okn) != participant->oki.kn || - body->oan != participant->oan) { - wpa_printf(MSG_WARNING, "KaY: Old key is invalid"); - return -1; - } - } - /* TODO: how to set the MACsec hardware when delay_protect is true */ if (body->delay_protect && (!be_to_host32(body->llpn) || !be_to_host32(body->olpn))) { @@ -1477,65 +1440,121 @@ ieee802_1x_mka_decode_sak_use_body( return -1; } - /* check all live peer have used the sak for receiving sa */ - all_receiving = TRUE; - dl_list_for_each(peer, &participant->live_peers, - struct ieee802_1x_kay_peer, list) { - if (!peer->sak_used) { - all_receiving = FALSE; - break; - } + olpn = be_to_host32(body->olpn); + llpn = be_to_host32(body->llpn); + + /* Our most recent distributed key should be the first in the list. + * If it doesn't exist then we can't really do anything. + * Be lenient and don't return error here as there are legitimate cases + * where this can happen such as when a new participant joins the CA and + * the first frame it receives can have a SAKuse but not distSAK. + */ + sa_key = dl_list_first (&participant->sak_list, struct data_key, list); + if (!sa_key) { + wpa_printf(MSG_INFO, "KaY: We don't have a latest distributed key - ignore SAK use"); + return 0; } - if (all_receiving) { - participant->to_dist_sak = FALSE; - ieee802_1x_cp_set_allreceiving(kay->cp, TRUE); - ieee802_1x_cp_sm_step(kay->cp); + + /* The peer's most recent key will be the "latest key" if it is present + * otherwise it will be the "old key" if in the RETIRE state. + */ + if (body->lrx) { + os_memcpy(ki.mi, body->lsrv_mi, sizeof(ki.mi)); + ki.kn = be_to_host32(body->lkn); + lpn = llpn; + } else { + os_memcpy(ki.mi, body->osrv_mi, sizeof(ki.mi)); + ki.kn = be_to_host32(body->okn); + lpn = olpn; } - /* if I'm key server, and detects peer member pn exhaustion, rekey. */ - lpn = be_to_host32(body->llpn); - if (lpn > kay->pn_exhaustion) { - if (participant->is_key_server) { - participant->new_sak = TRUE; - wpa_printf(MSG_WARNING, "KaY: Peer LPN exhaustion"); - } + /* If the most recent distributed keys don't agree then someone is out of sync. + * Perhaps non key server hasn't processed the most recent distSAK yet + * and the key server is processing an old packet after it has done + * distSAK. Be lenient and don't return error in this particular case + * otherwise the key server will reset it's MI and cause a traffic disruption + * which is really undesired for a simple timing issue. + */ + if (!is_ki_equal(&sa_key->key_identifier, &ki)) { + wpa_printf(MSG_INFO, "KaY: Distributed keys don't match - ignore SAK use"); + return 0; } + sa_key->next_pn = lpn; - if (sa_key) - sa_key->next_pn = lpn; - found = FALSE; - 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; + /* The key server must check that all peers are using the most recent distributed key. + * Non key servers must check if the key server is transmitting. + */ + if (participant->is_key_server) { + /* Distributed keys are equal from above comparison. */ + peer->sak_used = TRUE; + + all_receiving = TRUE; + dl_list_for_each(peer_iter, &participant->live_peers, + struct ieee802_1x_kay_peer, list) { + if (!peer_iter->sak_used) { + all_receiving = FALSE; break; } } - if (found) - break; + if (all_receiving) { + participant->to_dist_sak = FALSE; + ieee802_1x_cp_set_allreceiving(kay->cp, TRUE); + ieee802_1x_cp_sm_step(kay->cp); + } + } else if (peer->is_key_server) { + if (body->ltx) { + ieee802_1x_cp_set_servertransmitting(kay->cp, TRUE); + ieee802_1x_cp_sm_step(kay->cp); + } } - if (!found) { - wpa_printf(MSG_WARNING, "KaY: Can't find rxsa"); - return -1; + + /* if I'm key server, and detects peer member pn exhaustion, rekey. + * We only need to check the PN of the most recent distributed key. This could + * be the peer's "latest" or "old" key depending on its current state. + * If both "old" and "latest" keys are present then the "old" key has + * already been exhausted. + */ + if (participant->is_key_server && lpn > kay->pn_exhaustion) { + participant->new_sak = TRUE; + wpa_printf(MSG_WARNING, "KaY: Peer LPN exhaustion"); } + /* Get the associated RX SAs of the keys for delay protection + * since both can be in use. + * Delay protect window (communicated via MKA) is + * tighter than SecY's current replay protect window, + * so tell SecY the new (and higher) 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); + found = FALSE; + 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; } - /* 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. */ + if (rxsa) { + secy_get_receive_lowest_pn(participant->kay, rxsa); + if (lpn > rxsa->lowest_pn) { + rxsa->lowest_pn = lpn; + secy_set_receive_lowest_pn(participant->kay, rxsa); + wpa_printf(MSG_DEBUG, "KaY: update dist LPN=0x%x", lpn); + } + } + + /* FIX: Delay protection for the SA being replaced is not implemented. + * Note that this key will be active for at least MKA_SAK_RETIRE_TIME + * (3 seconds) but could be longer depending on how long it takes to + * get from RECEIVE to TRANSMITTING or if going via ABANDON. + * Delay protection does allow PN's within a 2 seconds window, so getting + * PN would be a lot of work for just 1 second's worth of protection. + */ } return 0;