libi2pd: fix parsing crashes

This commit is contained in:
jpk68
2026-08-23 13:27:13 -04:00
parent 41cb59c7a7
commit bf594a742e
6 changed files with 78 additions and 26 deletions
+7 -2
View File
@@ -243,9 +243,9 @@ namespace datagram
return;
}
}
if (offset > len)
if (offset > len || signatureLen > len - offset)
{
LogPrint (eLogWarning, "Datagram: datagram2 is too short ", len, " expected ", offset);
LogPrint (eLogWarning, "Datagram: datagram2 is too short ", len, " expected ", offset + signatureLen);
return;
}
if (!verified)
@@ -261,6 +261,11 @@ namespace datagram
}
signatureLen = transientVerifier->GetSignatureLen ();
}
if (offset > len || signatureLen > len - offset)
{
LogPrint (eLogWarning, "Datagram: datagram2 is too short ", len, " expected ", offset + signatureLen);
return;
}
std::vector<uint8_t> signedData (len + 32 - identityLen - signatureLen);
memcpy (signedData.data (), identity.GetIdentHash (), 32);
memcpy (signedData.data () + 32, buf + identityLen, signedData.size () - 32);
+5
View File
@@ -1338,6 +1338,11 @@ namespace transport
LogPrint (eLogError, "NTCP2: I2NP block is too long ", size);
break;
}
if (size < 9)
{
LogPrint (eLogError, "NTCP2: I2NP block is too short ", size);
break;
}
auto nextMsg = (frame[offset] == eI2NPTunnelData) ? NewI2NPTunnelMessage (true) : NewI2NPMessage (size);
nextMsg->len = nextMsg->offset + size + 7; // 7 more bytes for full I2NP header
if (nextMsg->len <= nextMsg->maxLen)
+30 -17
View File
@@ -804,7 +804,7 @@ namespace data
void NetDb::HandleDatabaseStoreMsg (std::shared_ptr<const I2NPMessage> m)
{
const uint8_t * buf = m->GetPayload ();
size_t len = m->GetSize ();
size_t len = m->GetPayloadLength ();
if (len < DATABASE_STORE_HEADER_SIZE)
{
LogPrint (eLogError, "NetDb: Database store msg is too short ", len, ". Dropped");
@@ -964,6 +964,12 @@ namespace data
LogPrint (eLogDebug, "NetDb: DatabaseLookup for ", key, " received flags=", (int)flag);
uint8_t lookupType = flag & DATABASE_LOOKUP_TYPE_FLAGS_MASK;
size_t minLen = 65 + ((flag & DATABASE_LOOKUP_DELIVERY_FLAG) ? 4 : 0) + 2;
if (msg->GetPayloadLength () < minLen)
{
LogPrint (eLogWarning, "NetDb: DatabaseLookup message is too short for its flags");
return;
}
const uint8_t * excluded = buf + 65;
uint32_t replyTunnelID = 0;
if (flag & DATABASE_LOOKUP_DELIVERY_FLAG) //reply to tunnel
@@ -1061,27 +1067,34 @@ namespace data
// encryption might be used though tunnel only
if (flag & (DATABASE_LOOKUP_ENCRYPTION_FLAG | DATABASE_LOOKUP_ECIES_FLAG)) // encrypted reply requested
{
const uint8_t * sessionKey = excluded;
const uint8_t numTags = excluded[32];
if (numTags)
// session key (32) + numTags (1), then the tag itself: 8 bytes for ECIES, 32 for ElGamal
size_t tagLen = (flag & DATABASE_LOOKUP_ECIES_FLAG) ? 8 : 32;
if ((size_t)(excluded - buf) + 33 + tagLen > msg->GetPayloadLength ())
LogPrint (eLogWarning, "NetDb: DatabaseLookup reply key block is truncated");
else
{
if (flag & DATABASE_LOOKUP_ECIES_FLAG)
const uint8_t * sessionKey = excluded;
const uint8_t numTags = excluded[32];
if (numTags)
{
uint64_t tag;
memcpy (&tag, excluded + 33, 8);
replyMsg = i2p::garlic::WrapECIESX25519Message (replyMsg, sessionKey, tag);
if (flag & DATABASE_LOOKUP_ECIES_FLAG)
{
uint64_t tag;
memcpy (&tag, excluded + 33, 8);
replyMsg = i2p::garlic::WrapECIESX25519Message (replyMsg, sessionKey, tag);
}
else
{
const i2p::garlic::SessionTag sessionTag(excluded + 33); // take first tag
i2p::garlic::ElGamalAESSession garlic (sessionKey, sessionTag);
replyMsg = garlic.WrapSingleMessage (replyMsg);
}
if (!replyMsg)
LogPrint (eLogError, "NetDb: Failed to wrap message");
}
else
{
const i2p::garlic::SessionTag sessionTag(excluded + 33); // take first tag
i2p::garlic::ElGamalAESSession garlic (sessionKey, sessionTag);
replyMsg = garlic.WrapSingleMessage (replyMsg);
}
if (!replyMsg)
LogPrint (eLogError, "NetDb: Failed to wrap message");
LogPrint(eLogWarning, "NetDb: Encrypted reply requested but no tags provided");
}
else
LogPrint(eLogWarning, "NetDb: Encrypted reply requested but no tags provided");
}
bool direct = true;
if (!i2p::transport::transports.IsConnected (replyIdent))
+1 -1
View File
@@ -324,7 +324,7 @@ namespace data
}
else if (address->transportStyle == eTransportSSU2)
{
if (value[0] >= '3' && value[0] <= '4') // only 3,4 allowed, TODO: support multiple values
if (value.size () == 1 && value[0] >= '3' && value[0] <= '4') // only 3,4 allowed, TODO: support multiple values
address->v = value[0] - '0';
}
}
+26 -4
View File
@@ -1707,9 +1707,9 @@ namespace transport
uint32_t SSU2Session::SendData (const uint8_t * buf, size_t len, uint8_t flags)
{
if (len < 8)
if (len < 8 || len + 16 > SSU2_MAX_PACKET_SIZE)
{
LogPrint (eLogWarning, "SSU2: Data message payload is too short ", (int)len);
LogPrint (eLogWarning, "SSU2: Data message payload length ", (int)len, " is out of range");
return 0;
}
Header header;
@@ -1721,7 +1721,11 @@ namespace transport
uint8_t nonce[12];
CreateNonce (m_SendPacketNum, nonce);
uint8_t payload[SSU2_MAX_PACKET_SIZE];
m_Server.AEADChaCha20Poly1305Encrypt (buf, len, header.buf, 16, m_KeyDataSend, nonce, payload, SSU2_MAX_PACKET_SIZE);
if (!m_Server.AEADChaCha20Poly1305Encrypt (buf, len, header.buf, 16, m_KeyDataSend, nonce, payload, SSU2_MAX_PACKET_SIZE))
{
LogPrint (eLogError, "SSU2: Data message AEAD encryption failed");
return 0;
}
header.ll[0] ^= CreateHeaderMask (m_Address->i, payload + (len - 8));
header.ll[1] ^= CreateHeaderMask (m_KeyDataSend + 32, payload + (len + 4));
m_Server.Send (header.buf, 16, payload, len + 16, m_RemoteEndpoint);
@@ -1804,6 +1808,12 @@ namespace transport
case eSSU2BlkI2NPMessage:
{
LogPrint (eLogDebug, "SSU2: I2NP message");
if (size < 9)
{
LogPrint (eLogWarning, "SSU2: I2NP message block size ", size, " is too short");
m_IsInvalidMessage = true;
break;
}
auto nextMsg = (buf[offset] == eI2NPTunnelData) ? NewI2NPTunnelMessage (true) : NewI2NPShortMessage ();
if (nextMsg->offset + size + 7 > nextMsg->maxLen) // 7 more bytes for full I2NP header
{
@@ -2157,6 +2167,12 @@ namespace transport
void SSU2Session::HandleFirstFragment (const uint8_t * buf, size_t len)
{
if (len < 9)
{
LogPrint (eLogWarning, "SSU2: First fragment size ", len, " is too short");
m_IsInvalidMessage = true;
return;
}
auto msg = (buf[0] == eI2NPTunnelData) ? NewI2NPTunnelMessage (true) : NewI2NPShortMessage ();
if (msg->offset + len + 7 > msg->maxLen)
{
@@ -2434,13 +2450,19 @@ namespace transport
if (relaySession && relaySession->IsEstablished ())
{
// we are Bob, message from Charlie
size_t maxPayloadSize = relaySession->GetMaxPayloadSize ();
if (len + 3 > maxPayloadSize)
{
LogPrint (eLogWarning, "SSU2: RelayResponse block size ", len, " is too long");
return;
}
auto packet = m_Server.GetSentPacketsPool ().AcquireShared ();
uint8_t * payload = packet->payload;
payload[0] = eSSU2BlkRelayResponse;
htobe16buf (payload + 1, len);
memcpy (payload + 3, buf, len); // forward to Alice as is
packet->payloadSize = len + 3;
packet->payloadSize += CreatePaddingBlock (payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
packet->payloadSize += CreatePaddingBlock (payload + packet->payloadSize, maxPayloadSize - packet->payloadSize);
uint32_t packetNum = relaySession->SendData (packet->payload, packet->payloadSize);
if (m_RemoteVersion >= SSU2_MIN_RELAY_RESPONSE_RESEND_VERSION)
{
+9 -2
View File
@@ -1008,7 +1008,7 @@ namespace stream
else if (numMsgs > m_NumPacketsToSend)
numMsgs = m_NumPacketsToSend;
if (!m_RemoteLeaseSet) m_RemoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ());
if (!m_RemoteLeaseSet && m_RemoteIdentity) m_RemoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ());
if (m_RemoteLeaseSet)
{
if (!m_RoutingSession)
@@ -1082,7 +1082,7 @@ namespace stream
{
// initial packet
m_Status = eStreamStatusOpen;
if (!m_RemoteLeaseSet) m_RemoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ());
if (!m_RemoteLeaseSet && m_RemoteIdentity) m_RemoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ());
if (m_RemoteLeaseSet)
{
m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true, !m_IsIncoming);
@@ -1972,6 +1972,13 @@ namespace stream
bool isLeaseChanged = true;
if (!m_RemoteLeaseSet || m_RemoteLeaseSet->IsExpired ())
{
if (!m_RemoteIdentity)
{
// no remote identity established yet, nothing to look up
m_Status = eStreamStatusClosed;
AsyncClose ();
return;
}
auto remoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ());
if (!remoteLeaseSet)
{