252 lines
6.3 KiB
C++
252 lines
6.3 KiB
C++
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "ICEAuthTransaction.h"
|
|
#include "ICEStunAttributes.h"
|
|
#include "ICEMD5.h"
|
|
#include "ICELog.h"
|
|
|
|
using namespace ice;
|
|
|
|
#define LOG_SUBSYSTEM "ICE"
|
|
|
|
AuthTransaction::AuthTransaction()
|
|
:Transaction(), mActive(false), mComposed(false), mConformsToKeepaliveSchedule(true),
|
|
mCredentialsEncoded(false)
|
|
{
|
|
}
|
|
|
|
AuthTransaction::~AuthTransaction()
|
|
{
|
|
}
|
|
|
|
void AuthTransaction::init()
|
|
{
|
|
mConformsToKeepaliveSchedule = false;
|
|
if (mRealm.size() && mNonce.size())
|
|
buildAuthenticatedMsg();
|
|
else
|
|
{
|
|
std::shared_ptr<StunMessage> msg(new StunMessage());
|
|
msg->setTransactionId(mTransactionID);
|
|
setInitialRequest(*msg);
|
|
mComposed = false;
|
|
mOutgoingMsgQueue.push_back(msg);
|
|
}
|
|
}
|
|
|
|
void AuthTransaction::buildAuthenticatedMsg()
|
|
{
|
|
// Setup key for MessageIntegrity
|
|
std::string key = mUsername + ":" + mRealm + ":" + mPassword;
|
|
md5Bin(key.c_str(), key.size(), mKey);
|
|
|
|
// Create new authenticated message
|
|
std::shared_ptr<StunMessage> newMsg( new StunMessage() );
|
|
|
|
// Optional - generate new transaction ID
|
|
// mTransactionID = StunMessage::TransactionID::GenerateNew();
|
|
|
|
newMsg->setTransactionId( mTransactionID );
|
|
|
|
// Add USERNAME
|
|
newMsg->usernameAttr().setValue( mUsername );
|
|
newMsg->realmAttr().setValue( mRealm );
|
|
newMsg->nonceAttr().setValue( mNonce );
|
|
|
|
// Adjust message
|
|
setAuthenticatedRequest( *newMsg );
|
|
|
|
// Ensure MessageIntegrity exists
|
|
newMsg->messageIntegrityAttr().setValue( NULL );
|
|
|
|
// Enqueue msg
|
|
mComposed = false;
|
|
|
|
mOutgoingMsgQueue.clear();
|
|
mOutgoingMsgQueue.push_back( newMsg );
|
|
}
|
|
|
|
bool AuthTransaction::processData(StunMessage& msg, NetworkAddress& address)
|
|
{
|
|
if (msg.transactionId() != mTransactionID)
|
|
return false;
|
|
|
|
// Check for 401 error code
|
|
if (msg.hasAttribute(StunAttribute::ErrorCode))
|
|
{
|
|
ErrorCode& ec = msg.errorCodeAttr();
|
|
|
|
// Get realm value - it must be long term credentials
|
|
if (ec.errorCode() == 401 && (!mCredentialsEncoded || !mLongTerm))
|
|
{
|
|
if (!msg.hasAttribute(StunAttribute::Realm) || !msg.hasAttribute(StunAttribute::Nonce))
|
|
return false;
|
|
|
|
Realm& realm = msg.realmAttr();
|
|
|
|
// Extract realm and nonce
|
|
mRealm = realm.value();
|
|
mNonce = msg.nonceAttr().value();
|
|
|
|
// Change transaction id
|
|
if (mLongTerm)
|
|
{
|
|
mCredentialsEncoded = true;
|
|
generateId();
|
|
}
|
|
ICELogDebug(<< "Server requested long term credentials for realm " << mRealm);
|
|
buildAuthenticatedMsg();
|
|
}
|
|
else
|
|
if (ec.errorCode() == 438 && (msg.hasAttribute(StunAttribute::Realm) || msg.hasAttribute(StunAttribute::Nonce)))
|
|
{
|
|
if (msg.hasAttribute(StunAttribute::Realm))
|
|
mRealm = msg.realmAttr().value();
|
|
if (msg.hasAttribute(StunAttribute::Nonce))
|
|
mNonce = msg.nonceAttr().value();
|
|
|
|
ICELogDebug(<< "Returned error 438, using new nonce");
|
|
if (msg.hasAttribute(StunAttribute::Realm) && msg.hasAttribute(StunAttribute::Nonce))
|
|
{
|
|
if (mLongTerm)
|
|
{
|
|
mCredentialsEncoded = true;
|
|
generateId();
|
|
}
|
|
}
|
|
buildAuthenticatedMsg();
|
|
}
|
|
else
|
|
{
|
|
mErrorCode = ec.errorCode();
|
|
mErrorResponse = ec.errorPhrase();
|
|
mState = Transaction::Failed;
|
|
processError();
|
|
|
|
ICELogError(<<"Stack ID " << mStackID << ". Got error code " << mErrorCode << " for STUN transaction. Error message: " << ec.errorPhrase());
|
|
}
|
|
}
|
|
else
|
|
if (msg.messageClass() == StunMessage::ErrorClass)
|
|
{
|
|
mErrorCode = 0;
|
|
mState = Transaction::Failed;
|
|
processError();
|
|
ICELogError(<<"Stack ID " << mStackID << ". Got ErrorClass response.");
|
|
}
|
|
else
|
|
{
|
|
ICELogDebug(<< "Process STUN success message");
|
|
processSuccessMessage(msg, address);
|
|
mState = Transaction::Success;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
ByteBuffer* AuthTransaction::generateData(bool force)
|
|
{
|
|
if (!mActive)
|
|
{
|
|
init();
|
|
mActive = true;
|
|
}
|
|
|
|
if (!mComposed)
|
|
{
|
|
// Restart retransmission timer as new message will be built
|
|
mRTOTimer.stop();
|
|
mRTOTimer.start();
|
|
|
|
// Get message from outgoing queue
|
|
if (!mOutgoingMsgQueue.empty())
|
|
{
|
|
// Clear buffer for raw data
|
|
mOutgoingData.clear();
|
|
|
|
// Encode next message
|
|
StunMessage& msg = *mOutgoingMsgQueue.front();
|
|
|
|
//ICELogDebug(<< "Stack ID " << mStackID << ". Build message " << msg.GetTransactionID().ToString() << " from transaction " << this->GetComment());
|
|
|
|
if (mShortTerm)
|
|
msg.buildPacket(mOutgoingData, mPassword);
|
|
else
|
|
msg.buildPacket(mOutgoingData, std::string((char*)mKey, 16));
|
|
|
|
// Remove encoded message from msg queue
|
|
mOutgoingMsgQueue.pop_front();
|
|
}
|
|
else
|
|
ICELogDebug(<< "No outgoing message in queue");
|
|
|
|
mComposed = true;
|
|
}
|
|
|
|
return Transaction::generateData(force);
|
|
}
|
|
|
|
int AuthTransaction::errorCode()
|
|
{
|
|
return mErrorCode;
|
|
}
|
|
|
|
std::string AuthTransaction::errorResponse()
|
|
{
|
|
return mErrorResponse;
|
|
}
|
|
|
|
void AuthTransaction::restart()
|
|
{
|
|
// Clear outgoing data
|
|
mOutgoingData.clear();
|
|
mOutgoingMsgQueue.clear();
|
|
|
|
mState = Transaction::Running;
|
|
|
|
// Mark "message is not built yet"
|
|
mComposed = false;
|
|
|
|
// Mark "first request is not sent yet"
|
|
mActive = false;
|
|
|
|
// Reset retransmission timer
|
|
mRTOTimer.stop();
|
|
mRTOTimer.start();
|
|
mCredentialsEncoded = false;
|
|
mConformsToKeepaliveSchedule = true;
|
|
}
|
|
|
|
bool AuthTransaction::hasToRunNow()
|
|
{
|
|
bool result = Transaction::hasToRunNow();
|
|
if (!result && keepalive())
|
|
{
|
|
result |= mState == /*TransactionState::*/Running && !mConformsToKeepaliveSchedule;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::string AuthTransaction::realm() const
|
|
{
|
|
return mRealm;
|
|
}
|
|
|
|
void AuthTransaction::setRealm(std::string realm)
|
|
{
|
|
mRealm = realm;
|
|
}
|
|
|
|
std::string AuthTransaction::nonce() const
|
|
{
|
|
return mNonce;
|
|
}
|
|
|
|
void AuthTransaction::setNonce(std::string nonce)
|
|
{
|
|
mNonce = nonce;
|
|
}
|