Modified STUN verification functions

The new verification makes verification a function on a message.
It also stores the password used in the request message, so that
it is easily accessible when verifying the response.

Bug: chromium:1177125
Change-Id: I505df4b54214643a28a6b292c4e2262b9d97b097
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/209060
Reviewed-by: Björn Terelius <terelius@webrtc.org>
Commit-Queue: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#33366}
This commit is contained in:
Harald Alvestrand
2021-03-02 08:09:53 +00:00
committed by Commit Bot
parent d99da804d7
commit 07d83c8a9a
12 changed files with 160 additions and 85 deletions

View File

@ -246,6 +246,31 @@ const StunUInt16ListAttribute* StunMessage::GetUnknownAttributes() const {
GetAttribute(STUN_ATTR_UNKNOWN_ATTRIBUTES));
}
StunMessage::IntegrityStatus StunMessage::ValidateMessageIntegrity(
const std::string& password) {
password_ = password;
if (GetByteString(STUN_ATTR_MESSAGE_INTEGRITY)) {
if (ValidateMessageIntegrityOfType(
STUN_ATTR_MESSAGE_INTEGRITY, kStunMessageIntegritySize,
buffer_.c_str(), buffer_.size(), password)) {
integrity_ = IntegrityStatus::kIntegrityOk;
} else {
integrity_ = IntegrityStatus::kIntegrityBad;
}
} else if (GetByteString(STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32)) {
if (ValidateMessageIntegrityOfType(
STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32, kStunMessageIntegrity32Size,
buffer_.c_str(), buffer_.size(), password)) {
integrity_ = IntegrityStatus::kIntegrityOk;
} else {
integrity_ = IntegrityStatus::kIntegrityBad;
}
} else {
integrity_ = IntegrityStatus::kNoIntegrity;
}
return integrity_;
}
bool StunMessage::ValidateMessageIntegrity(const char* data,
size_t size,
const std::string& password) {
@ -353,11 +378,6 @@ bool StunMessage::AddMessageIntegrity(const std::string& password) {
password.size());
}
bool StunMessage::AddMessageIntegrity(const char* key, size_t keylen) {
return AddMessageIntegrityOfType(STUN_ATTR_MESSAGE_INTEGRITY,
kStunMessageIntegritySize, key, keylen);
}
bool StunMessage::AddMessageIntegrity32(absl::string_view password) {
return AddMessageIntegrityOfType(STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32,
kStunMessageIntegrity32Size, password.data(),
@ -395,6 +415,8 @@ bool StunMessage::AddMessageIntegrityOfType(int attr_type,
// Insert correct HMAC into the attribute.
msg_integrity_attr->CopyBytes(hmac, attr_size);
password_.assign(key, keylen);
integrity_ = IntegrityStatus::kIntegrityOk;
return true;
}
@ -473,6 +495,9 @@ bool StunMessage::AddFingerprint() {
}
bool StunMessage::Read(ByteBufferReader* buf) {
// Keep a copy of the buffer data around for later verification.
buffer_.assign(buf->Data(), buf->Length());
if (!buf->ReadUInt16(&type_)) {
return false;
}

View File

@ -16,6 +16,7 @@
#include <stddef.h>
#include <stdint.h>
#include <functional>
#include <memory>
#include <string>
@ -149,15 +150,24 @@ class StunMessage {
StunMessage();
virtual ~StunMessage();
// The verification status of the message. This is checked on parsing,
// or set by AddMessageIntegrity.
enum class IntegrityStatus {
kNotSet,
kNoIntegrity, // Message-integrity attribute missing
kIntegrityOk, // Message-integrity checked OK
kIntegrityBad, // Message-integrity verification failed
};
int type() const { return type_; }
size_t length() const { return length_; }
const std::string& transaction_id() const { return transaction_id_; }
uint32_t reduced_transaction_id() const { return reduced_transaction_id_; }
// Returns true if the message confirms to RFC3489 rather than
// RFC5389. The main difference between two version of the STUN
// RFC5389. The main difference between the two versions of the STUN
// protocol is the presence of the magic cookie and different length
// of transaction ID. For outgoing packets version of the protocol
// of transaction ID. For outgoing packets the version of the protocol
// is determined by the lengths of the transaction ID.
bool IsLegacy() const;
@ -191,19 +201,27 @@ class StunMessage {
// Remote all attributes and releases them.
void ClearAttributes();
// Validates that a raw STUN message has a correct MESSAGE-INTEGRITY value.
// This can't currently be done on a StunMessage, since it is affected by
// padding data (which we discard when reading a StunMessage).
static bool ValidateMessageIntegrity(const char* data,
size_t size,
const std::string& password);
static bool ValidateMessageIntegrity32(const char* data,
size_t size,
const std::string& password);
// Validates that a STUN message has a correct MESSAGE-INTEGRITY value.
// This uses the buffered raw-format message stored by Read().
IntegrityStatus ValidateMessageIntegrity(const std::string& password);
// Returns the current integrity status of the message.
IntegrityStatus integrity() const { return integrity_; }
// Shortcut for checking if integrity is verified.
bool IntegrityOk() const {
return integrity_ == IntegrityStatus::kIntegrityOk;
}
// Returns the password attribute used to set or check the integrity.
// Can only be called after adding or checking the integrity.
std::string password() const {
RTC_DCHECK(integrity_ != IntegrityStatus::kNotSet);
return password_;
}
// Adds a MESSAGE-INTEGRITY attribute that is valid for the current message.
bool AddMessageIntegrity(const std::string& password);
bool AddMessageIntegrity(const char* key, size_t keylen);
// Adds a STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32 attribute that is valid for the
// current message.
@ -244,6 +262,30 @@ class StunMessage {
bool EqualAttributes(const StunMessage* other,
std::function<bool(int type)> attribute_type_mask) const;
// Expose raw-buffer ValidateMessageIntegrity function for testing.
static bool ValidateMessageIntegrityForTesting(const char* data,
size_t size,
const std::string& password) {
return ValidateMessageIntegrity(data, size, password);
}
// Expose raw-buffer ValidateMessageIntegrity function for testing.
static bool ValidateMessageIntegrity32ForTesting(
const char* data,
size_t size,
const std::string& password) {
return ValidateMessageIntegrity32(data, size, password);
}
// Validates that a STUN message in byte buffer form
// has a correct MESSAGE-INTEGRITY value.
// These functions are not recommended and will be deprecated; use
// ValidateMessageIntegrity(password) on the parsed form instead.
static bool ValidateMessageIntegrity(const char* data,
size_t size,
const std::string& password);
static bool ValidateMessageIntegrity32(const char* data,
size_t size,
const std::string& password);
protected:
// Verifies that the given attribute is allowed for this message.
virtual StunAttributeValueType GetAttributeValueType(int type) const;
@ -269,6 +311,10 @@ class StunMessage {
std::string transaction_id_;
uint32_t reduced_transaction_id_;
uint32_t stun_magic_cookie_;
// The original buffer for messages created by Read().
std::string buffer_;
IntegrityStatus integrity_ = IntegrityStatus::kNotSet;
std::string password_;
};
// Base class for all STUN/TURN attributes.

View File

@ -1196,24 +1196,24 @@ TEST_F(StunTest, FailToReadRtcpPacket) {
// Check our STUN message validation code against the RFC5769 test messages.
TEST_F(StunTest, ValidateMessageIntegrity) {
// Try the messages from RFC 5769.
EXPECT_TRUE(StunMessage::ValidateMessageIntegrity(
EXPECT_TRUE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(kRfc5769SampleRequest),
sizeof(kRfc5769SampleRequest), kRfc5769SampleMsgPassword));
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(kRfc5769SampleRequest),
sizeof(kRfc5769SampleRequest), "InvalidPassword"));
EXPECT_TRUE(StunMessage::ValidateMessageIntegrity(
EXPECT_TRUE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(kRfc5769SampleResponse),
sizeof(kRfc5769SampleResponse), kRfc5769SampleMsgPassword));
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(kRfc5769SampleResponse),
sizeof(kRfc5769SampleResponse), "InvalidPassword"));
EXPECT_TRUE(StunMessage::ValidateMessageIntegrity(
EXPECT_TRUE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(kRfc5769SampleResponseIPv6),
sizeof(kRfc5769SampleResponseIPv6), kRfc5769SampleMsgPassword));
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(kRfc5769SampleResponseIPv6),
sizeof(kRfc5769SampleResponseIPv6), "InvalidPassword"));
@ -1222,40 +1222,40 @@ TEST_F(StunTest, ValidateMessageIntegrity) {
ComputeStunCredentialHash(kRfc5769SampleMsgWithAuthUsername,
kRfc5769SampleMsgWithAuthRealm,
kRfc5769SampleMsgWithAuthPassword, &key);
EXPECT_TRUE(StunMessage::ValidateMessageIntegrity(
EXPECT_TRUE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(kRfc5769SampleRequestLongTermAuth),
sizeof(kRfc5769SampleRequestLongTermAuth), key));
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(kRfc5769SampleRequestLongTermAuth),
sizeof(kRfc5769SampleRequestLongTermAuth), "InvalidPassword"));
// Try some edge cases.
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(kStunMessageWithZeroLength),
sizeof(kStunMessageWithZeroLength), kRfc5769SampleMsgPassword));
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(kStunMessageWithExcessLength),
sizeof(kStunMessageWithExcessLength), kRfc5769SampleMsgPassword));
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(kStunMessageWithSmallLength),
sizeof(kStunMessageWithSmallLength), kRfc5769SampleMsgPassword));
// Again, but with the lengths matching what is claimed in the headers.
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(kStunMessageWithZeroLength),
kStunHeaderSize + rtc::GetBE16(&kStunMessageWithZeroLength[2]),
kRfc5769SampleMsgPassword));
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(kStunMessageWithExcessLength),
kStunHeaderSize + rtc::GetBE16(&kStunMessageWithExcessLength[2]),
kRfc5769SampleMsgPassword));
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(kStunMessageWithSmallLength),
kStunHeaderSize + rtc::GetBE16(&kStunMessageWithSmallLength[2]),
kRfc5769SampleMsgPassword));
// Check that a too-short HMAC doesn't cause buffer overflow.
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(kStunMessageWithBadHmacAtEnd),
sizeof(kStunMessageWithBadHmacAtEnd), kRfc5769SampleMsgPassword));
@ -1268,8 +1268,8 @@ TEST_F(StunTest, ValidateMessageIntegrity) {
if (i > 0)
buf[i - 1] ^= 0x01;
EXPECT_EQ(i >= sizeof(buf) - 8,
StunMessage::ValidateMessageIntegrity(buf, sizeof(buf),
kRfc5769SampleMsgPassword));
StunMessage::ValidateMessageIntegrityForTesting(
buf, sizeof(buf), kRfc5769SampleMsgPassword));
}
}
@ -1291,7 +1291,7 @@ TEST_F(StunTest, AddMessageIntegrity) {
rtc::ByteBufferWriter buf1;
EXPECT_TRUE(msg.Write(&buf1));
EXPECT_TRUE(StunMessage::ValidateMessageIntegrity(
EXPECT_TRUE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(buf1.Data()), buf1.Length(),
kRfc5769SampleMsgPassword));
@ -1309,7 +1309,7 @@ TEST_F(StunTest, AddMessageIntegrity) {
rtc::ByteBufferWriter buf3;
EXPECT_TRUE(msg2.Write(&buf3));
EXPECT_TRUE(StunMessage::ValidateMessageIntegrity(
EXPECT_TRUE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(buf3.Data()), buf3.Length(),
kRfc5769SampleMsgPassword));
}
@ -1317,40 +1317,40 @@ TEST_F(StunTest, AddMessageIntegrity) {
// Check our STUN message validation code against the RFC5769 test messages.
TEST_F(StunTest, ValidateMessageIntegrity32) {
// Try the messages from RFC 5769.
EXPECT_TRUE(StunMessage::ValidateMessageIntegrity32(
EXPECT_TRUE(StunMessage::ValidateMessageIntegrity32ForTesting(
reinterpret_cast<const char*>(kSampleRequestMI32),
sizeof(kSampleRequestMI32), kRfc5769SampleMsgPassword));
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32(
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32ForTesting(
reinterpret_cast<const char*>(kSampleRequestMI32),
sizeof(kSampleRequestMI32), "InvalidPassword"));
// Try some edge cases.
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32(
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32ForTesting(
reinterpret_cast<const char*>(kStunMessageWithZeroLength),
sizeof(kStunMessageWithZeroLength), kRfc5769SampleMsgPassword));
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32(
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32ForTesting(
reinterpret_cast<const char*>(kStunMessageWithExcessLength),
sizeof(kStunMessageWithExcessLength), kRfc5769SampleMsgPassword));
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32(
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32ForTesting(
reinterpret_cast<const char*>(kStunMessageWithSmallLength),
sizeof(kStunMessageWithSmallLength), kRfc5769SampleMsgPassword));
// Again, but with the lengths matching what is claimed in the headers.
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32(
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32ForTesting(
reinterpret_cast<const char*>(kStunMessageWithZeroLength),
kStunHeaderSize + rtc::GetBE16(&kStunMessageWithZeroLength[2]),
kRfc5769SampleMsgPassword));
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32(
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32ForTesting(
reinterpret_cast<const char*>(kStunMessageWithExcessLength),
kStunHeaderSize + rtc::GetBE16(&kStunMessageWithExcessLength[2]),
kRfc5769SampleMsgPassword));
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32(
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32ForTesting(
reinterpret_cast<const char*>(kStunMessageWithSmallLength),
kStunHeaderSize + rtc::GetBE16(&kStunMessageWithSmallLength[2]),
kRfc5769SampleMsgPassword));
// Check that a too-short HMAC doesn't cause buffer overflow.
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32(
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32ForTesting(
reinterpret_cast<const char*>(kStunMessageWithBadHmacAtEnd),
sizeof(kStunMessageWithBadHmacAtEnd), kRfc5769SampleMsgPassword));
@ -1363,7 +1363,7 @@ TEST_F(StunTest, ValidateMessageIntegrity32) {
if (i > 0)
buf[i - 1] ^= 0x01;
EXPECT_EQ(i >= sizeof(buf) - 8,
StunMessage::ValidateMessageIntegrity32(
StunMessage::ValidateMessageIntegrity32ForTesting(
buf, sizeof(buf), kRfc5769SampleMsgPassword));
}
}
@ -1384,7 +1384,7 @@ TEST_F(StunTest, AddMessageIntegrity32) {
rtc::ByteBufferWriter buf1;
EXPECT_TRUE(msg.Write(&buf1));
EXPECT_TRUE(StunMessage::ValidateMessageIntegrity32(
EXPECT_TRUE(StunMessage::ValidateMessageIntegrity32ForTesting(
reinterpret_cast<const char*>(buf1.Data()), buf1.Length(),
kRfc5769SampleMsgPassword));
@ -1402,7 +1402,7 @@ TEST_F(StunTest, AddMessageIntegrity32) {
rtc::ByteBufferWriter buf3;
EXPECT_TRUE(msg2.Write(&buf3));
EXPECT_TRUE(StunMessage::ValidateMessageIntegrity32(
EXPECT_TRUE(StunMessage::ValidateMessageIntegrity32ForTesting(
reinterpret_cast<const char*>(buf3.Data()), buf3.Length(),
kRfc5769SampleMsgPassword));
}
@ -1420,14 +1420,14 @@ TEST_F(StunTest, AddMessageIntegrity32AndMessageIntegrity) {
rtc::ByteBufferWriter buf1;
EXPECT_TRUE(msg.Write(&buf1));
EXPECT_TRUE(StunMessage::ValidateMessageIntegrity32(
EXPECT_TRUE(StunMessage::ValidateMessageIntegrity32ForTesting(
reinterpret_cast<const char*>(buf1.Data()), buf1.Length(), "password1"));
EXPECT_TRUE(StunMessage::ValidateMessageIntegrity(
EXPECT_TRUE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(buf1.Data()), buf1.Length(), "password2"));
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32(
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32ForTesting(
reinterpret_cast<const char*>(buf1.Data()), buf1.Length(), "password2"));
EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(buf1.Data()), buf1.Length(), "password1"));
}

View File

@ -480,6 +480,7 @@ void Connection::OnReadPacket(const char* data,
// If this is a STUN response, then update the writable bit.
// Log at LS_INFO if we receive a ping on an unwritable connection.
rtc::LoggingSeverity sev = (!writable() ? rtc::LS_INFO : rtc::LS_VERBOSE);
msg->ValidateMessageIntegrity(remote_candidate().password());
switch (msg->type()) {
case STUN_BINDING_REQUEST:
RTC_LOG_V(sev) << ToString() << ": Received "
@ -505,8 +506,7 @@ void Connection::OnReadPacket(const char* data,
// id's match.
case STUN_BINDING_RESPONSE:
case STUN_BINDING_ERROR_RESPONSE:
if (msg->ValidateMessageIntegrity(data, size,
remote_candidate().password())) {
if (msg->IntegrityOk()) {
requests_.CheckResponse(msg.get());
}
// Otherwise silently discard the response message.
@ -523,8 +523,7 @@ void Connection::OnReadPacket(const char* data,
break;
case GOOG_PING_RESPONSE:
case GOOG_PING_ERROR_RESPONSE:
if (msg->ValidateMessageIntegrity32(data, size,
remote_candidate().password())) {
if (msg->IntegrityOk()) {
requests_.CheckResponse(msg.get());
}
break;

View File

@ -493,7 +493,8 @@ bool Port::GetStunMessage(const char* data,
}
// If ICE, and the MESSAGE-INTEGRITY is bad, fail with a 401 Unauthorized
if (!stun_msg->ValidateMessageIntegrity(data, size, password_)) {
if (stun_msg->ValidateMessageIntegrity(password_) !=
StunMessage::IntegrityStatus::kIntegrityOk) {
RTC_LOG(LS_ERROR) << ToString() << ": Received "
<< StunMethodToString(stun_msg->type())
<< " with bad M-I from " << addr.ToSensitiveString()
@ -559,7 +560,8 @@ bool Port::GetStunMessage(const char* data,
// No stun attributes will be verified, if it's stun indication message.
// Returning from end of the this method.
} else if (stun_msg->type() == GOOG_PING_REQUEST) {
if (!stun_msg->ValidateMessageIntegrity32(data, size, password_)) {
if (stun_msg->ValidateMessageIntegrity(password_) !=
StunMessage::IntegrityStatus::kIntegrityOk) {
RTC_LOG(LS_ERROR) << ToString() << ": Received "
<< StunMethodToString(stun_msg->type())
<< " with bad M-I from " << addr.ToSensitiveString()

View File

@ -1726,9 +1726,8 @@ TEST_F(PortTest, TestSendStunMessage) {
EXPECT_EQ(kDefaultPrflxPriority, priority_attr->value());
EXPECT_EQ("rfrag:lfrag", username_attr->GetString());
EXPECT_TRUE(msg->GetByteString(STUN_ATTR_MESSAGE_INTEGRITY) != NULL);
EXPECT_TRUE(StunMessage::ValidateMessageIntegrity(
lport->last_stun_buf()->data<char>(), lport->last_stun_buf()->size(),
"rpass"));
EXPECT_EQ(StunMessage::IntegrityStatus::kIntegrityOk,
msg->ValidateMessageIntegrity("rpass"));
const StunUInt64Attribute* ice_controlling_attr =
msg->GetUInt64(STUN_ATTR_ICE_CONTROLLING);
ASSERT_TRUE(ice_controlling_attr != NULL);
@ -1767,9 +1766,8 @@ TEST_F(PortTest, TestSendStunMessage) {
ASSERT_TRUE(addr_attr != NULL);
EXPECT_EQ(lport->Candidates()[0].address(), addr_attr->GetAddress());
EXPECT_TRUE(msg->GetByteString(STUN_ATTR_MESSAGE_INTEGRITY) != NULL);
EXPECT_TRUE(StunMessage::ValidateMessageIntegrity(
rport->last_stun_buf()->data<char>(), rport->last_stun_buf()->size(),
"rpass"));
EXPECT_EQ(StunMessage::IntegrityStatus::kIntegrityOk,
msg->ValidateMessageIntegrity("rpass"));
EXPECT_TRUE(msg->GetUInt32(STUN_ATTR_FINGERPRINT) != NULL);
EXPECT_TRUE(StunMessage::ValidateFingerprint(
lport->last_stun_buf()->data<char>(), lport->last_stun_buf()->size()));
@ -1798,9 +1796,8 @@ TEST_F(PortTest, TestSendStunMessage) {
EXPECT_EQ(STUN_ERROR_SERVER_ERROR, error_attr->code());
EXPECT_EQ(std::string(STUN_ERROR_REASON_SERVER_ERROR), error_attr->reason());
EXPECT_TRUE(msg->GetByteString(STUN_ATTR_MESSAGE_INTEGRITY) != NULL);
EXPECT_TRUE(StunMessage::ValidateMessageIntegrity(
rport->last_stun_buf()->data<char>(), rport->last_stun_buf()->size(),
"rpass"));
EXPECT_EQ(StunMessage::IntegrityStatus::kIntegrityOk,
msg->ValidateMessageIntegrity("rpass"));
EXPECT_TRUE(msg->GetUInt32(STUN_ATTR_FINGERPRINT) != NULL);
EXPECT_TRUE(StunMessage::ValidateFingerprint(
lport->last_stun_buf()->data<char>(), lport->last_stun_buf()->size()));

View File

@ -120,6 +120,18 @@ bool StunRequestManager::CheckResponse(StunMessage* msg) {
}
StunRequest* request = iter->second;
// Now that we know the request, we can see if the response is
// integrity-protected or not.
// For some tests, the message integrity is not set in the request.
// Complain, and then don't check.
bool skip_integrity_checking = false;
if (request->msg()->integrity() == StunMessage::IntegrityStatus::kNotSet) {
skip_integrity_checking = true;
} else {
msg->ValidateMessageIntegrity(request->msg()->password());
}
if (!msg->GetNonComprehendedAttributes().empty()) {
// If a response contains unknown comprehension-required attributes, it's
// simply discarded and the transaction is considered failed. See RFC5389
@ -129,6 +141,9 @@ bool StunRequestManager::CheckResponse(StunMessage* msg) {
delete request;
return false;
} else if (msg->type() == GetStunSuccessResponseType(request->type())) {
if (!msg->IntegrityOk() && !skip_integrity_checking) {
return false;
}
request->OnResponse(msg);
} else if (msg->type() == GetStunErrorResponseType(request->type())) {
request->OnErrorResponse(msg);

View File

@ -724,16 +724,6 @@ bool TurnPort::HandleIncomingPacket(rtc::AsyncPacketSocket* socket,
return false;
}
// This must be a response for one of our requests.
// Check success responses, but not errors, for MESSAGE-INTEGRITY.
if (IsStunSuccessResponseType(msg_type) &&
!StunMessage::ValidateMessageIntegrity(data, size, hash())) {
RTC_LOG(LS_WARNING) << ToString()
<< ": Received TURN message with invalid "
"message integrity, msg_type: "
<< msg_type;
return true;
}
request_manager_.CheckResponse(data, size);
return true;

View File

@ -306,7 +306,7 @@ bool TurnServer::GetKey(const StunMessage* msg, std::string* key) {
}
bool TurnServer::CheckAuthorization(TurnServerConnection* conn,
const StunMessage* msg,
StunMessage* msg,
const char* data,
size_t size,
const std::string& key) {
@ -322,14 +322,14 @@ bool TurnServer::CheckAuthorization(TurnServerConnection* conn,
const StunByteStringAttribute* nonce_attr =
msg->GetByteString(STUN_ATTR_NONCE);
// Fail if no M-I.
// Fail if no MESSAGE_INTEGRITY.
if (!mi_attr) {
SendErrorResponseWithRealmAndNonce(conn, msg, STUN_ERROR_UNAUTHORIZED,
STUN_ERROR_REASON_UNAUTHORIZED);
return false;
}
// Fail if there is M-I but no username, nonce, or realm.
// Fail if there is MESSAGE_INTEGRITY but no username, nonce, or realm.
if (!username_attr || !realm_attr || !nonce_attr) {
SendErrorResponse(conn, msg, STUN_ERROR_BAD_REQUEST,
STUN_ERROR_REASON_BAD_REQUEST);
@ -343,9 +343,9 @@ bool TurnServer::CheckAuthorization(TurnServerConnection* conn,
return false;
}
// Fail if bad username or M-I.
// We need |data| and |size| for the call to ValidateMessageIntegrity.
if (key.empty() || !StunMessage::ValidateMessageIntegrity(data, size, key)) {
// Fail if bad MESSAGE_INTEGRITY.
if (key.empty() || msg->ValidateMessageIntegrity(key) !=
StunMessage::IntegrityStatus::kIntegrityOk) {
SendErrorResponseWithRealmAndNonce(conn, msg, STUN_ERROR_UNAUTHORIZED,
STUN_ERROR_REASON_UNAUTHORIZED);
return false;

View File

@ -278,7 +278,7 @@ class TurnServer : public sigslot::has_slots<> {
bool GetKey(const StunMessage* msg, std::string* key);
bool CheckAuthorization(TurnServerConnection* conn,
const StunMessage* msg,
StunMessage* msg,
const char* data,
size_t size,
const std::string& key);

View File

@ -24,5 +24,6 @@ void FuzzOneInput(const uint8_t* data, size_t size) {
std::unique_ptr<cricket::IceMessage> stun_msg(new cricket::IceMessage());
rtc::ByteBufferReader buf(message, size);
stun_msg->Read(&buf);
stun_msg->ValidateMessageIntegrity("");
}
} // namespace webrtc

View File

@ -18,6 +18,6 @@ void FuzzOneInput(const uint8_t* data, size_t size) {
const char* message = reinterpret_cast<const char*>(data);
cricket::StunMessage::ValidateFingerprint(message, size);
cricket::StunMessage::ValidateMessageIntegrity(message, size, "");
cricket::StunMessage::ValidateMessageIntegrityForTesting(message, size, "");
}
} // namespace webrtc