TCP 認證選項的 Linux 實現 (RFC5925)

TCP 認證選項 (TCP-AO) 提供了一個 TCP 擴充套件,旨在驗證受信任對等方之間的報文段。它添加了一個新的 TCP 頭部選項,其中包含訊息認證碼 (MAC)。MAC 由 TCP 報文段的內容生成,使用一種與兩個對等方均知的密碼的雜湊函式。TCP-AO 的目的是棄用 TCP-MD5,提供更好的安全性、金鑰輪換和對各種雜湊演算法的支援。

1. 介紹

TCP-AO 和 TCP-MD5 的簡短有限比較

TCP-MD5

TCP-AO

支援的雜湊演算法

MD5(加密弱點)

必須支援 HMAC-SHA1(選擇字首攻擊)和 CMAC-AES-128(僅側通道攻擊)。可以支援任何雜湊演算法。

MAC 長度(位元組)

16

通常 12-16。允許適合 TCP 頭部的其他變體。

每個 TCP 連線的金鑰數量

1

許多

更改活動金鑰的可能性

不切實際(兩個對等方必須在 MSL 期間更改)

協議支援

抵禦 ICMP “硬錯誤” 的保護

是:在已建立的連線上預設忽略它們

抵禦流量交叉攻擊的保護

是:偽頭部包含 TCP 埠。

抵禦重放 TCP 報文段的保護

序列號擴充套件 (SNE) 和初始序列號 (ISN)

支援無連線重置

否。需要 ISN+SNE 才能正確簽署 RST。

標準

RFC 2385

RFC 5925, RFC 5926

1.1 常見問題 (FAQ) 及 RFC 5925 引用

問:對於相同的 4 元組(源地址、源埠、目的地址、目的埠),SendID 或 RecvID 是否可以是唯一的?

答:否 [3.1]

>> The IDs of MKTs MUST NOT overlap where their TCP connection
identifiers overlap.

問:活動連線的主金鑰元組 (MKT) 可以刪除嗎?

答:否,除非它被複制到傳輸控制塊 (TCB) [3.1]

It is presumed that an MKT affecting a particular connection cannot
be destroyed during an active connection -- or, equivalently, that
its parameters are copied to an area local to the connection (i.e.,
instantiated) and so changes would affect only new connections.

問:如果需要刪除一箇舊的 MKT,應該如何操作才能不刪除活動連線的 MKT?(因為它可能在任何時候被使用)

答:RFC 5925 未指定,這似乎是金鑰管理的問題,需確保在嘗試刪除之前沒有人使用此類 MKT。

問:舊的 MKT 可以永遠存在並被另一個對等方使用嗎?

答:可以,何時刪除舊金鑰是金鑰管理的任務 [6.1]

Deciding when to start using a key is a performance issue. Deciding
when to remove an MKT is a security issue. Invalid MKTs are expected
to be removed. TCP-AO provides no mechanism to coordinate their removal,
as we consider this a key management operation.

另請參見 [6.1]

The only way to avoid reuse of previously used MKTs is to remove the MKT
when it is no longer considered permitted.

Linux TCP-AO 將盡力阻止您刪除正在使用的金鑰,認為這是金鑰管理失敗。但由於保留過時的金鑰可能會成為安全問題,並且對等方可能會透過始終將其設定為 RNextKeyID 而無意中阻止舊金鑰的刪除——因此提供了強制金鑰刪除機制,使用者空間必須提供要使用的 KeyID 來替代正在刪除的 KeyID,核心將原子地刪除舊金鑰,即使對等方仍在請求它。強制刪除不作任何保證,因為對等方可能尚未擁有新金鑰——TCP 連線可能會因此中斷。或者,可以選擇關閉套接字。

問:當新連線收到沒有已知 MKT 的 RecvID 的資料包時會發生什麼?

答:RFC 5925 規定預設情況下會接受並記錄警告,但行為可由使用者配置 [7.5.1.a]

If the segment is a SYN, then this is the first segment of a new
connection. Find the matching MKT for this segment, using the segment's
socket pair and its TCP-AO KeyID, matched against the MKT's TCP connection
identifier and the MKT's RecvID.

   i. If there is no matching MKT, remove TCP-AO from the segment.
      Proceed with further TCP handling of the segment.
      NOTE: this presumes that connections that do not match any MKT
      should be silently accepted, as noted in Section 7.3.

[7.3]:

>> A TCP-AO implementation MUST allow for configuration of the behavior
of segments with TCP-AO but that do not match an MKT. The initial default
of this configuration SHOULD be to silently accept such connections.
If this is not the desired case, an MKT can be included to match such
connections, or the connection can indicate that TCP-AO is required.
Alternately, the configuration can be changed to discard segments with
the AO option not matching an MKT.

[10.2.b]

Connections not matching any MKT do not require TCP-AO. Further, incoming
segments with TCP-AO are not discarded solely because they include
the option, provided they do not match any MKT.

請注意,Linux TCP-AO 實現在這方面有所不同。目前,具有未知金鑰簽名的 TCP-AO 報文段會被丟棄並記錄警告。

問:RFC 是否以任何方式暗示集中式核心金鑰管理?(即所有連線上的金鑰必須同時輪換嗎?)

答:未指定。MKT 可以在使用者空間中管理,與金鑰更改相關的唯一部分是 [7.3]

>> All TCP segments MUST be checked against the set of MKTs for matching
TCP connection identifiers.

問:當對等方請求的 RNextKeyID 未知時會發生什麼?連線應該重置嗎?

答:不應該,無需執行任何操作 [7.5.2.e]

ii. If they differ, determine whether the RNextKeyID MKT is ready.

    1. If the MKT corresponding to the segment’s socket pair and RNextKeyID
    is not available, no action is required (RNextKeyID of a received
    segment needs to match the MKT’s SendID).

問:current_key 是如何設定的,何時更改?是使用者觸發的更改,還是遠端對等方的請求觸發的?是使用者顯式設定的,還是透過匹配規則設定的?

答:current_key 由 RNextKeyID 設定 [6.1]

Rnext_key is changed only by manual user intervention or MKT management
protocol operation. It is not manipulated by TCP-AO. Current_key is updated
by TCP-AO when processing received TCP segments as discussed in the segment
processing description in Section 7.5. Note that the algorithm allows
the current_key to change to a new MKT, then change back to a previously
used MKT (known as "backing up"). This can occur during an MKT change when
segments are received out of order, and is considered a feature of TCP-AO,
because reordering does not result in drops.

[7.5.2.e.ii]

2. If the matching MKT corresponding to the segment’s socket pair and
RNextKeyID is available:

   a. Set current_key to the RNextKeyID MKT.

問:如果兩個對等方都有多個 MKT 匹配連線的套接字對(具有不同的 KeyID),傳送方/接收方應如何選擇要使用的 KeyID?

答:某種機制應該選擇“所需”的 MKT [3.3]

Multiple MKTs may match a single outgoing segment, e.g., when MKTs
are being changed. Those MKTs cannot have conflicting IDs (as noted
elsewhere), and some mechanism must determine which MKT to use for each
given outgoing segment.

>> An outgoing TCP segment MUST match at most one desired MKT, indicated
by the segment’s socket pair. The segment MAY match multiple MKTs, provided
that exactly one MKT is indicated as desired. Other information in
the segment MAY be used to determine the desired MKT when multiple MKTs
match; such information MUST NOT include values in any TCP option fields.

問:TCP-MD5 連線可以遷移到 TCP-AO 嗎(反之亦然)?

答:否 [1]

TCP MD5-protected connections cannot be migrated to TCP-AO because TCP MD5
does not support any changes to a connection’s security algorithm
once established.

問:如果連線上所有 MKT 都被刪除,它還能成為非 TCP-AO 簽名的連線嗎?

答:[7.5.2] 沒有 [7.5.1.i] 中 SYN 資料包處理的相同選擇,該選擇允許接受沒有簽名的報文段(這將是不安全的)。雖然未直接禁止切換到非 TCP-AO 連線,但這似乎是 RFC 的意思。此外,TCP-AO 連線要求始終有一個 current_key [3.3]

TCP-AO requires that every protected TCP segment match exactly one MKT.

[3.3]:

>> An incoming TCP segment including TCP-AO MUST match exactly one MKT,
indicated solely by the segment’s socket pair and its TCP-AO KeyID.

[4.4]:

One or more MKTs. These are the MKTs that match this connection’s
socket pair.

問:非 TCP-AO 連線可以成為支援 TCP-AO 的連線嗎?

答:否:對於已經建立的非 TCP-AO 連線,不可能切換到使用 TCP-AO,因為流量金鑰生成需要初始序列號。換句話說,開始使用 TCP-AO 需要重新建立 TCP 連線。

2. 核心中 MKTs 資料庫與使用者空間資料庫的比較

Linux TCP-AO 支援使用 setsockopt()s 實現,類似於 TCP-MD5。這意味著想要使用 TCP-AO 的使用者空間應用程式在新增、刪除或輪換 MKT 時,應該在 TCP 套接字上執行 setsockopt()。這種方法將金鑰管理責任以及對邊緣情況的決策(例如,如果對等方不遵守 RNextKeyID,該怎麼辦)轉移到使用者空間;將更多程式碼移到使用者空間,特別是負責策略決策的程式碼。此外,它靈活且可擴充套件性好(比核心內資料庫所需的鎖定更少)。還需要記住,主要目標使用者是 BGP 程序,而不是任何隨機應用程式,這意味著與 IPsec 隧道相比,不需要真正的透明性,並且現代 BGP 守護程式已經支援 TCP-MD5 的 setsockopt()s

所考慮方法的優缺點

setsockopt()

核心內資料庫 (DB)

可擴充套件性

setsockopt() 命令應是可擴充套件的系統呼叫

Netlink 訊息簡單且可擴充套件

所需的使用者空間更改

BGP 或任何需要 TCP-AO 的應用程式需要執行 setsockopt()s 並進行金鑰管理

可以像隧道一樣透明,提供類似 ip tcpao add key(刪除/顯示/輪換)的功能

MKTs 刪除或新增

使用者空間更難

核心更難

可轉儲性

getsockopt()

Netlink .dump() 回撥

核心資源/記憶體限制

相等

可伸縮性

TCP_LISTEN 套接字上的爭用

整個資料庫上的爭用

監控和警告

TCP_DIAG

相同的 Netlink 套接字

MKTs 匹配

一半問題:只有監聽套接字

3. 使用者應用程式程式設計介面 (uAPI)

Linux 提供了一組 setsockopt()sgetsockopt()s,允許使用者空間按套接字管理 TCP-AO。為了新增/刪除 MKT,必須使用 TCP_AO_ADD_KEYTCP_AO_DEL_KEY TCP 套接字選項。不允許在已建立的非 TCP-AO 連線上新增金鑰,也不允許從 TCP-AO 連線中刪除最後一個金鑰。

setsockopt(TCP_AO_DEL_KEY) 命令可以指定 tcp_ao_del::current_key + tcp_ao_del::set_current 和/或 tcp_ao_del::rnext + tcp_ao_del::set_rnext,這將使刪除變為“強制”:它為使用者空間提供了一種刪除正在使用的金鑰並原子地設定另一個金鑰的方法。這並非用於正常使用,僅當對等方忽略 RNextKeyID 並持續請求/使用舊金鑰時才應使用。它提供了一種強制刪除不受信任金鑰的方式,但這可能會中斷 TCP-AO 連線。

常規/正常金鑰輪換可以使用 setsockopt(TCP_AO_INFO) 執行。它還提供了一個 uAPI 來更改每個套接字的 TCP-AO 設定,例如忽略 ICMP,以及清除每個套接字的 TCP-AO 資料包計數器。相應的 getsockopt(TCP_AO_INFO) 可用於獲取這些每個套接字的 TCP-AO 設定。

另一個有用的命令是 getsockopt(TCP_AO_GET_KEYS)。可以使用它列出 TCP 套接字上的所有 MKT,或者使用過濾器獲取特定對等方和/或 sndid/rcvid、VRF L3 介面的金鑰,或獲取 current_key/rnext_key。

要修復 TCP-AO 連線,可以使用 setsockopt(TCP_AO_REPAIR),前提是使用者之前使用 getsockopt(TCP_AO_REPAIR) 檢查點/轉儲了套接字。

對於數千個 TCP-AO 金鑰的擴充套件 TCP_LISTEN 套接字,這裡有一個提示:在 getsockopt(TCP_AO_GET_KEYS) 中使用過濾器,並使用 setsockopt(TCP_AO_DEL_KEY) 進行非同步刪除。

Linux TCP-AO 還提供了一堆報文段計數器,有助於故障排除/除錯問題。每個 MKT 都有好/壞計數器,反映有多少資料包透過/未透過驗證。每個 TCP-AO 套接字都有以下計數器: - 用於良好報文段(正確簽名) - 用於不良報文段(TCP-AO 驗證失敗) - 用於未知金鑰的報文段 - 用於期望 AO 簽名但未找到簽名的報文段 - 用於被忽略的 ICMP 數量

TCP-AO 的每個套接字計數器也與每個網路名稱空間的計數器重複,透過 SNMP 公開。這些是 TCPAOGoodTCPAOBadTCPAOKeyNotFoundTCPAORequiredTCPAODroppedIcmps

出於監控目的,有以下 TCP-AO 跟蹤事件:tcp_hash_bad_headertcp_hash_ao_requiredtcp_ao_handshake_failuretcp_ao_wrong_maclentcp_ao_wrong_maclentcp_ao_key_not_foundtcp_ao_rnext_requesttcp_ao_synack_no_keytcp_ao_snd_sne_updatetcp_ao_rcv_sne_update。可以單獨啟用其中任何一個,並且可以按網路名稱空間、4 元組、家族、L3 索引和 TCP 頭部標誌進行過濾。如果報文段有 TCP-AO 頭部,過濾器還可以包含 keyid、rnext 和 maclen。SNE 更新包括翻轉後的號碼。

RFC 5925 非常寬鬆地規定了 MKTs 如何進行 TCP 埠匹配。

TCP connection identifier. A TCP socket pair, i.e., a local IP
address, a remote IP address, a TCP local port, and a TCP remote port.
Values can be partially specified using ranges (e.g., 2-30), masks
(e.g., 0xF0), wildcards (e.g., "*"), or any other suitable indication.

目前 Linux TCP-AO 實現不提供任何 TCP 埠匹配。可能,埠範圍對於 uAPI 來說最靈活,但到目前為止尚未實現。

4. setsockopt()accept() 競態

與只有一個金鑰的已建立 TCP-MD5 連線不同,TCP-AO 連線可以有多個金鑰,這意味著監聽套接字上接受的連線也可能有任意數量的金鑰。由於在第一個正確簽名的 SYN 上覆制所有這些金鑰會使請求套接字變大,這是不可取的。目前,實現不會將金鑰複製到請求套接字,而是從“父”監聽套接字中查詢它們。

結果是,當用戶空間刪除 TCP-AO 金鑰時,這可能會破壞請求套接字上尚未建立的連線,並且不會從已建立但尚未 accept() 的套接字中刪除金鑰,使其掛在接受佇列中。

反之亦然:如果使用者空間在監聽套接字上為對等方新增新金鑰,則接受佇列中已建立的套接字將不會擁有新金鑰。

目前,解決這兩個競態問題:setsockopt(TCP_AO_ADD_KEY)accept() 以及 setsockopt(TCP_AO_DEL_KEY)accept() 的方法是委託給使用者空間。這意味著使用者空間應檢查由 accept() 返回的套接字上的 MKT,以驗證監聽套接字上發生的任何金鑰輪換是否反映在新建立的連線上。

這類似於核心方面對 TCP-MD5 的“不作為”方法,並且可能稍後透過引入新標誌到 tcp_ao_addtcp_ao_del 來改變。

請注意,這種競態很少發生,因為它需要在新 TCP 連線的三次握手期間發生 TCP-AO 金鑰輪換。

5. 與 TCP-MD5 的互動

TCP 連線不能在 TCP-AO 和 TCP-MD5 選項之間遷移。已建立的、具有 AO 或 MD5 金鑰的套接字被限制新增其他選項的金鑰。

對於監聽套接字,情況有所不同:BGP 伺服器可能希望同時接收 TCP-AO 和(已棄用)TCP-MD5 客戶端。因此,兩種型別的金鑰都可以新增到 TCP_CLOSED 或 TCP_LISTEN 套接字。不允許為同一對等方新增不同型別的金鑰。

6. SNE Linux 實現

RFC 5925 [6.2] 描述瞭如何使用 SNE 擴充套件 TCP 序列號的演算法。簡而言之:TCP 必須跟蹤以前的序列號,並在當前 SEQ 號翻轉時設定 sne_flag。噹噹前和以前的 SEQ 號都超過 0x7fff(即 32KB)時,該標誌被清除。

在 sne_flag 設定期間,演算法會將每個資料包的 SEQ 號與 0x7fff 進行比較,如果高於 32KB,則假定該資料包在遞增之前應使用 SNE 進行驗證。結果,存在一個 [0; 32KB] 視窗,當資料包帶有 (SNE - 1) 時可以接受。

Linux 實現對此做了一些簡化:由於網路堆疊已經跟蹤了所需 ACK 的第一個 SEQ 位元組 (snd_una) 和所需下一個 SEQ 位元組 (rcv_nxt)——這些資訊足以粗略估計傳送方和接收方在 4GB SEQ 號空間中的位置。當它們翻轉為零時,相應的 SNE 會遞增。

每個 TCP-AO 段都會呼叫 tcp_ao_compute_sne()。它將段中的 SEQ 號與 snd_una 或 rcv_nxt 進行比較,並將結果擬合到它們周圍的 2GB 視窗中,檢測 SEQ 號翻轉。這大大簡化了程式碼,並且只需要在每個 TCP-AO 套接字上儲存 SNE 號碼。

2GB 視窗乍一看似乎比 RFC 5926 允許的範圍寬鬆得多。但這僅用於在翻轉之前/之後選擇正確的 SNE。它允許更多的 TCP 段重放,但所有常規 TCP 檢查在 tcp_sequence() 中仍然應用於已驗證的段。因此,它在演算法的簡單性和對大 TCP 視窗來說似乎更好的行為之間進行權衡,允許稍微更寬鬆地接受重放/重傳的段。