mirror of
https://github.com/acidanthera/audk.git
synced 2025-04-08 17:05:09 +02:00
REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4541 REF: https://www.rfc-editor.org/rfc/rfc1948.txt REF: https://www.rfc-editor.org/rfc/rfc6528.txt REF: https://www.rfc-editor.org/rfc/rfc9293.txt Bug Overview: PixieFail Bug #8 CVE-2023-45236 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:N CWE-200 Exposure of Sensitive Information to an Unauthorized Actor Updates TCP ISN generation to use a cryptographic hash of the connection's identifying parameters and a secret key. This prevents an attacker from guessing the ISN used for some other connection. This is follows the guidance in RFC 1948, RFC 6528, and RFC 9293. RFC: 9293 Section 3.4.1. Initial Sequence Number Selection A TCP implementation MUST use the above type of "clock" for clock- driven selection of initial sequence numbers (MUST-8), and SHOULD generate its initial sequence numbers with the expression: ISN = M + F(localip, localport, remoteip, remoteport, secretkey) where M is the 4 microsecond timer, and F() is a pseudorandom function (PRF) of the connection's identifying parameters ("localip, localport, remoteip, remoteport") and a secret key ("secretkey") (SHLD-1). F() MUST NOT be computable from the outside (MUST-9), or an attacker could still guess at sequence numbers from the ISN used for some other connection. The PRF could be implemented as a cryptographic hash of the concatenation of the TCP connection parameters and some secret data. For discussion of the selection of a specific hash algorithm and management of the secret key data, please see Section 3 of [42]. For each connection there is a send sequence number and a receive sequence number. The initial send sequence number (ISS) is chosen by the data sending TCP peer, and the initial receive sequence number (IRS) is learned during the connection-establishing procedure. For a connection to be established or initialized, the two TCP peers must synchronize on each other's initial sequence numbers. This is done in an exchange of connection-establishing segments carrying a control bit called "SYN" (for synchronize) and the initial sequence numbers. As a shorthand, segments carrying the SYN bit are also called "SYNs". Hence, the solution requires a suitable mechanism for picking an initial sequence number and a slightly involved handshake to exchange the ISNs. Cc: Saloni Kasbekar <saloni.kasbekar@intel.com> Cc: Zachary Clark-williams <zachary.clark-williams@intel.com> Signed-off-by: Doug Flick [MSFT] <doug.edk2@gmail.com> Reviewed-by: Saloni Kasbekar <saloni.kasbekar@intel.com>
570 lines
12 KiB
C
570 lines
12 KiB
C
/** @file
|
|
TCP timer related functions.
|
|
|
|
Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
|
|
Copyright (c) Microsoft Corporation
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
|
|
#include "TcpMain.h"
|
|
|
|
UINT32 mTcpTick = 1000;
|
|
|
|
/**
|
|
Connect timeout handler.
|
|
|
|
@param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
|
|
|
|
**/
|
|
VOID
|
|
TcpConnectTimeout (
|
|
IN OUT TCP_CB *Tcb
|
|
);
|
|
|
|
/**
|
|
Timeout handler for TCP retransmission timer.
|
|
|
|
@param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
|
|
|
|
**/
|
|
VOID
|
|
TcpRexmitTimeout (
|
|
IN OUT TCP_CB *Tcb
|
|
);
|
|
|
|
/**
|
|
Timeout handler for window probe timer.
|
|
|
|
@param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
|
|
|
|
**/
|
|
VOID
|
|
TcpProbeTimeout (
|
|
IN OUT TCP_CB *Tcb
|
|
);
|
|
|
|
/**
|
|
Timeout handler for keepalive timer.
|
|
|
|
@param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
|
|
|
|
**/
|
|
VOID
|
|
TcpKeepaliveTimeout (
|
|
IN OUT TCP_CB *Tcb
|
|
);
|
|
|
|
/**
|
|
Timeout handler for FIN_WAIT_2 timer.
|
|
|
|
@param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
|
|
|
|
**/
|
|
VOID
|
|
TcpFinwait2Timeout (
|
|
IN OUT TCP_CB *Tcb
|
|
);
|
|
|
|
/**
|
|
Timeout handler for 2MSL timer.
|
|
|
|
@param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
|
|
|
|
**/
|
|
VOID
|
|
Tcp2MSLTimeout (
|
|
IN OUT TCP_CB *Tcb
|
|
);
|
|
|
|
TCP_TIMER_HANDLER mTcpTimerHandler[TCP_TIMER_NUMBER] = {
|
|
TcpConnectTimeout,
|
|
TcpRexmitTimeout,
|
|
TcpProbeTimeout,
|
|
TcpKeepaliveTimeout,
|
|
TcpFinwait2Timeout,
|
|
Tcp2MSLTimeout,
|
|
};
|
|
|
|
/**
|
|
Close the TCP connection.
|
|
|
|
@param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
|
|
|
|
**/
|
|
VOID
|
|
TcpClose (
|
|
IN OUT TCP_CB *Tcb
|
|
)
|
|
{
|
|
NetbufFreeList (&Tcb->SndQue);
|
|
NetbufFreeList (&Tcb->RcvQue);
|
|
|
|
TcpSetState (Tcb, TCP_CLOSED);
|
|
}
|
|
|
|
/**
|
|
Backoff the RTO.
|
|
|
|
@param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
|
|
|
|
**/
|
|
VOID
|
|
TcpBackoffRto (
|
|
IN OUT TCP_CB *Tcb
|
|
)
|
|
{
|
|
//
|
|
// Fold the RTT estimate if too many times, the estimate
|
|
// may be wrong, fold it. So the next time a valid
|
|
// measurement is sampled, we can start fresh.
|
|
//
|
|
if ((Tcb->LossTimes >= TCP_FOLD_RTT) && (Tcb->SRtt != 0)) {
|
|
Tcb->RttVar += Tcb->SRtt >> 2;
|
|
Tcb->SRtt = 0;
|
|
}
|
|
|
|
Tcb->Rto <<= 1;
|
|
|
|
if (Tcb->Rto < TCP_RTO_MIN) {
|
|
Tcb->Rto = TCP_RTO_MIN;
|
|
} else if (Tcb->Rto > TCP_RTO_MAX) {
|
|
Tcb->Rto = TCP_RTO_MAX;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Connect timeout handler.
|
|
|
|
@param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
|
|
|
|
**/
|
|
VOID
|
|
TcpConnectTimeout (
|
|
IN OUT TCP_CB *Tcb
|
|
)
|
|
{
|
|
if (!TCP_CONNECTED (Tcb->State)) {
|
|
DEBUG (
|
|
(DEBUG_ERROR,
|
|
"TcpConnectTimeout: connection closed because connection timer timeout for TCB %p\n",
|
|
Tcb)
|
|
);
|
|
|
|
if (EFI_ABORTED == Tcb->Sk->SockError) {
|
|
SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);
|
|
}
|
|
|
|
if (TCP_SYN_RCVD == Tcb->State) {
|
|
DEBUG (
|
|
(DEBUG_WARN,
|
|
"TcpConnectTimeout: send reset because connection timer timeout for TCB %p\n",
|
|
Tcb)
|
|
);
|
|
|
|
TcpResetConnection (Tcb);
|
|
}
|
|
|
|
TcpClose (Tcb);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Timeout handler for TCP retransmission timer.
|
|
|
|
@param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
|
|
|
|
**/
|
|
VOID
|
|
TcpRexmitTimeout (
|
|
IN OUT TCP_CB *Tcb
|
|
)
|
|
{
|
|
UINT32 FlightSize;
|
|
|
|
DEBUG (
|
|
(DEBUG_WARN,
|
|
"TcpRexmitTimeout: transmission timeout for TCB %p\n",
|
|
Tcb)
|
|
);
|
|
|
|
//
|
|
// Set the congestion window. FlightSize is the
|
|
// amount of data that has been sent but not
|
|
// yet ACKed.
|
|
//
|
|
FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna);
|
|
Tcb->Ssthresh = MAX ((UINT32)(2 * Tcb->SndMss), FlightSize / 2);
|
|
|
|
Tcb->CWnd = Tcb->SndMss;
|
|
Tcb->LossRecover = Tcb->SndNxt;
|
|
|
|
Tcb->LossTimes++;
|
|
if ((Tcb->LossTimes > Tcb->MaxRexmit) && !TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_CONNECT)) {
|
|
DEBUG (
|
|
(DEBUG_ERROR,
|
|
"TcpRexmitTimeout: connection closed because too many timeouts for TCB %p\n",
|
|
Tcb)
|
|
);
|
|
|
|
if (EFI_ABORTED == Tcb->Sk->SockError) {
|
|
SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);
|
|
}
|
|
|
|
TcpClose (Tcb);
|
|
return;
|
|
}
|
|
|
|
TcpBackoffRto (Tcb);
|
|
TcpRetransmit (Tcb, Tcb->SndUna);
|
|
TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto);
|
|
|
|
Tcb->CongestState = TCP_CONGEST_LOSS;
|
|
|
|
TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);
|
|
}
|
|
|
|
/**
|
|
Timeout handler for window probe timer.
|
|
|
|
@param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
|
|
|
|
**/
|
|
VOID
|
|
TcpProbeTimeout (
|
|
IN OUT TCP_CB *Tcb
|
|
)
|
|
{
|
|
//
|
|
// This is the timer for sender's SWSA. RFC1122 requires
|
|
// a timer set for sender's SWSA, and suggest combine it
|
|
// with window probe timer. If data is sent, don't set
|
|
// the probe timer, since retransmit timer is on.
|
|
//
|
|
if ((TcpDataToSend (Tcb, 1) != 0) && (TcpToSendData (Tcb, 1) > 0)) {
|
|
ASSERT (TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT) != 0);
|
|
Tcb->ProbeTimerOn = FALSE;
|
|
return;
|
|
}
|
|
|
|
TcpSendZeroProbe (Tcb);
|
|
TcpSetProbeTimer (Tcb);
|
|
}
|
|
|
|
/**
|
|
Timeout handler for keepalive timer.
|
|
|
|
@param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
|
|
|
|
**/
|
|
VOID
|
|
TcpKeepaliveTimeout (
|
|
IN OUT TCP_CB *Tcb
|
|
)
|
|
{
|
|
Tcb->KeepAliveProbes++;
|
|
|
|
//
|
|
// Too many Keep-alive probes, drop the connection
|
|
//
|
|
if (Tcb->KeepAliveProbes > Tcb->MaxKeepAlive) {
|
|
if (EFI_ABORTED == Tcb->Sk->SockError) {
|
|
SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);
|
|
}
|
|
|
|
TcpClose (Tcb);
|
|
return;
|
|
}
|
|
|
|
TcpSendZeroProbe (Tcb);
|
|
TcpSetKeepaliveTimer (Tcb);
|
|
}
|
|
|
|
/**
|
|
Timeout handler for FIN_WAIT_2 timer.
|
|
|
|
@param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
|
|
|
|
**/
|
|
VOID
|
|
TcpFinwait2Timeout (
|
|
IN OUT TCP_CB *Tcb
|
|
)
|
|
{
|
|
DEBUG (
|
|
(DEBUG_WARN,
|
|
"TcpFinwait2Timeout: connection closed because FIN_WAIT2 timer timeouts for TCB %p\n",
|
|
Tcb)
|
|
);
|
|
|
|
TcpClose (Tcb);
|
|
}
|
|
|
|
/**
|
|
Timeout handler for 2MSL timer.
|
|
|
|
@param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
|
|
|
|
**/
|
|
VOID
|
|
Tcp2MSLTimeout (
|
|
IN OUT TCP_CB *Tcb
|
|
)
|
|
{
|
|
DEBUG (
|
|
(DEBUG_WARN,
|
|
"Tcp2MSLTimeout: connection closed because TIME_WAIT timer timeouts for TCB %p\n",
|
|
Tcb)
|
|
);
|
|
|
|
TcpClose (Tcb);
|
|
}
|
|
|
|
/**
|
|
Update the timer status and the next expire time according to the timers
|
|
to expire in a specific future time slot.
|
|
|
|
@param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
|
|
|
|
**/
|
|
VOID
|
|
TcpUpdateTimer (
|
|
IN OUT TCP_CB *Tcb
|
|
)
|
|
{
|
|
UINT16 Index;
|
|
|
|
//
|
|
// Don't use a too large value to init NextExpire
|
|
// since mTcpTick wraps around as sequence no does.
|
|
//
|
|
Tcb->NextExpire = TCP_EXPIRE_TIME;
|
|
TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON);
|
|
|
|
for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) {
|
|
if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) &&
|
|
TCP_TIME_LT (Tcb->Timer[Index], mTcpTick + Tcb->NextExpire)
|
|
)
|
|
{
|
|
Tcb->NextExpire = TCP_SUB_TIME (Tcb->Timer[Index], mTcpTick);
|
|
TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Enable a TCP timer.
|
|
|
|
@param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
|
|
@param[in] Timer The index of the timer to be enabled.
|
|
@param[in] TimeOut The timeout value of this timer.
|
|
|
|
**/
|
|
VOID
|
|
TcpSetTimer (
|
|
IN OUT TCP_CB *Tcb,
|
|
IN UINT16 Timer,
|
|
IN UINT32 TimeOut
|
|
)
|
|
{
|
|
TCP_SET_TIMER (Tcb->EnabledTimer, Timer);
|
|
Tcb->Timer[Timer] = mTcpTick + TimeOut;
|
|
|
|
TcpUpdateTimer (Tcb);
|
|
}
|
|
|
|
/**
|
|
Clear one TCP timer.
|
|
|
|
@param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
|
|
@param[in] Timer The index of the timer to be cleared.
|
|
|
|
**/
|
|
VOID
|
|
TcpClearTimer (
|
|
IN OUT TCP_CB *Tcb,
|
|
IN UINT16 Timer
|
|
)
|
|
{
|
|
TCP_CLEAR_TIMER (Tcb->EnabledTimer, Timer);
|
|
TcpUpdateTimer (Tcb);
|
|
}
|
|
|
|
/**
|
|
Clear all TCP timers.
|
|
|
|
@param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
|
|
|
|
**/
|
|
VOID
|
|
TcpClearAllTimer (
|
|
IN OUT TCP_CB *Tcb
|
|
)
|
|
{
|
|
Tcb->EnabledTimer = 0;
|
|
TcpUpdateTimer (Tcb);
|
|
}
|
|
|
|
/**
|
|
Enable the window prober timer and set the timeout value.
|
|
|
|
@param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
|
|
|
|
**/
|
|
VOID
|
|
TcpSetProbeTimer (
|
|
IN OUT TCP_CB *Tcb
|
|
)
|
|
{
|
|
if (!Tcb->ProbeTimerOn) {
|
|
Tcb->ProbeTime = Tcb->Rto;
|
|
Tcb->ProbeTimerOn = TRUE;
|
|
} else {
|
|
Tcb->ProbeTime <<= 1;
|
|
}
|
|
|
|
if (Tcb->ProbeTime < TCP_RTO_MIN) {
|
|
Tcb->ProbeTime = TCP_RTO_MIN;
|
|
} else if (Tcb->ProbeTime > TCP_RTO_MAX) {
|
|
Tcb->ProbeTime = TCP_RTO_MAX;
|
|
}
|
|
|
|
TcpSetTimer (Tcb, TCP_TIMER_PROBE, Tcb->ProbeTime);
|
|
}
|
|
|
|
/**
|
|
Enable the keepalive timer and set the timeout value.
|
|
|
|
@param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
|
|
|
|
**/
|
|
VOID
|
|
TcpSetKeepaliveTimer (
|
|
IN OUT TCP_CB *Tcb
|
|
)
|
|
{
|
|
if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE)) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Set the timer to KeepAliveIdle if either
|
|
// 1. the keepalive timer is off
|
|
// 2. The keepalive timer is on, but the idle
|
|
// is less than KeepAliveIdle, that means the
|
|
// connection is alive since our last probe.
|
|
//
|
|
if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_KEEPALIVE) ||
|
|
(Tcb->Idle < Tcb->KeepAliveIdle)
|
|
)
|
|
{
|
|
TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAliveIdle);
|
|
Tcb->KeepAliveProbes = 0;
|
|
} else {
|
|
TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAlivePeriod);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Heart beat timer handler.
|
|
|
|
@param[in] Context Context of the timer event, ignored.
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
TcpTickingDpc (
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
LIST_ENTRY *Entry;
|
|
LIST_ENTRY *Next;
|
|
TCP_CB *Tcb;
|
|
INT16 Index;
|
|
|
|
mTcpTick++;
|
|
|
|
//
|
|
// Don't use LIST_FOR_EACH, which isn't delete safe.
|
|
//
|
|
for (Entry = mTcpRunQue.ForwardLink; Entry != &mTcpRunQue; Entry = Next) {
|
|
Next = Entry->ForwardLink;
|
|
|
|
Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
|
|
|
|
if (Tcb->State == TCP_CLOSED) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// The connection is doing RTT measurement.
|
|
//
|
|
if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {
|
|
Tcb->RttMeasure++;
|
|
}
|
|
|
|
Tcb->Idle++;
|
|
|
|
if (Tcb->DelayedAck != 0) {
|
|
TcpSendAck (Tcb);
|
|
}
|
|
|
|
if ((Tcb->IpInfo->IpVersion == IP_VERSION_6) && (Tcb->Tick > 0)) {
|
|
Tcb->Tick--;
|
|
}
|
|
|
|
//
|
|
// No timer is active or no timer expired
|
|
//
|
|
if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON) || ((--Tcb->NextExpire) > 0)) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Call the timeout handler for each expired timer.
|
|
//
|
|
for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) {
|
|
if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) && TCP_TIME_LEQ (Tcb->Timer[Index], mTcpTick)) {
|
|
//
|
|
// disable the timer before calling the handler
|
|
// in case the handler enables it again.
|
|
//
|
|
TCP_CLEAR_TIMER (Tcb->EnabledTimer, Index);
|
|
mTcpTimerHandler[Index](Tcb);
|
|
|
|
//
|
|
// The Tcb may have been deleted by the timer, or
|
|
// no other timer is set.
|
|
//
|
|
if ((Next->BackLink != Entry) || (Tcb->EnabledTimer == 0)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the Tcb still exist or some timer is set, update the timer
|
|
//
|
|
if (Index == TCP_TIMER_NUMBER) {
|
|
TcpUpdateTimer (Tcb);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Heart beat timer handler, queues the DPC at TPL_CALLBACK.
|
|
|
|
@param[in] Event Timer event signaled, ignored.
|
|
@param[in] Context Context of the timer event, ignored.
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
TcpTicking (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
QueueDpc (TPL_CALLBACK, TcpTickingDpc, Context);
|
|
}
|