[reSIProcate] Changes to support server authentication
Hello,
I've attached changes to support UAS-side authentication.
The diff for Helper::authenticateRequest is not as bad it the diff may
appear. I simply put the auth header check for 407 and 401 at the beginning
and the rest of the method is the same, just removed some indentation.
Comments?
Thanks,
Liz
Elizabeth Clark
Bridgewater Systems Corporation
Phone : (613) 591-9104 x2776
E-mail : mailto:elizabeth@xxxxxxxxxxxxxxxxxxxxxx
Index: Helper.cxx
===================================================================
--- Helper.cxx (revision 3877)
+++ Helper.cxx (working copy)
@@ -576,99 +576,110 @@
{
DebugLog(<< "Authenticating: realm=" << realm << " expires=" <<
expiresDelta);
//DebugLog(<< request);
-
+
+ const ParserContainer<Auth>* auths= 0;
if (request.exists(h_ProxyAuthorizations))
{
- const ParserContainer<Auth>& auths =
request.header(h_ProxyAuthorizations);
- for (ParserContainer<Auth>::const_iterator i = auths.begin(); i !=
auths.end(); i++)
+ auths = &(request.header(h_ProxyAuthorizations));
+ }
+ else if( request.exists(h_Authorizations) )
+ {
+ auths = &(request.header(h_Authorizations));
+ }
+ else
+ {
+ DebugLog (<< "No authentication headers. Failing request.");
+ return Failed;
+ }
+
+ ParserContainer<Auth>::const_iterator i = auths->begin();
+ for (; i != auths->end(); i++)
+ {
+ if (i->exists(p_realm) &&
+ i->exists(p_nonce) &&
+ i->exists(p_response) &&
+ i->param(p_realm) == realm)
{
- if (i->exists(p_realm) &&
- i->exists(p_nonce) &&
- i->exists(p_response) &&
- i->param(p_realm) == realm)
+ ParseBuffer pb(i->param(p_nonce).data(), i->param(p_nonce).size());
+ if (!pb.eof() && !isdigit(*pb.position()))
{
- ParseBuffer pb(i->param(p_nonce).data(), i->param(p_nonce).size());
- if (!pb.eof() && !isdigit(*pb.position()))
- {
- DebugLog(<< "Invalid nonce; expected timestamp.");
- return BadlyFormed;
- }
- const char* anchor = pb.position();
- pb.skipToChar(Symbols::COLON[0]);
+ DebugLog(<< "Invalid nonce; expected timestamp.");
+ return BadlyFormed;
+ }
+ const char* anchor = pb.position();
+ pb.skipToChar(Symbols::COLON[0]);
- if (pb.eof())
+ if (pb.eof())
+ {
+ DebugLog(<< "Invalid nonce; expected timestamp terminator.");
+ return BadlyFormed;
+ }
+
+ Data then;
+ pb.data(then, anchor);
+ if (expiresDelta > 0)
+ {
+ int now = (int)(Timer::getTimeMs()/1000);
+ if (then.convertInt() + expiresDelta < now)
{
- DebugLog(<< "Invalid nonce; expected timestamp terminator.");
- return BadlyFormed;
+ DebugLog(<< "Nonce has expired.");
+ return Expired;
}
+ }
+ if (i->param(p_nonce) != makeNonce(request, then))
+ {
+ InfoLog(<< "Not my nonce.");
+ return Failed;
+ }
- Data then;
- pb.data(then, anchor);
- if (expiresDelta > 0)
+ if (i->exists(p_qop))
+ {
+ if (i->param(p_qop) == Symbols::auth)
{
- int now = (int)(Timer::getTimeMs()/1000);
- if (then.convertInt() + expiresDelta < now)
+ if (i->param(p_response) ==
makeResponseMD5(i->param(p_username),
+ password,
+ realm,
+
getMethodName(request.header(h_RequestLine).getMethod()),
+ i->param(p_uri),
+ i->param(p_nonce),
+ i->param(p_qop),
+ i->param(p_cnonce),
+ i->param(p_nc)))
{
- DebugLog(<< "Nonce has expired.");
- return Expired;
+ return Authenticated;
}
- }
- if (i->param(p_nonce) != makeNonce(request, then))
- {
- InfoLog(<< "Not my nonce.");
- return Failed;
- }
-
- if (i->exists(p_qop))
- {
- if (i->param(p_qop) == Symbols::auth)
- {
- if (i->param(p_response) ==
makeResponseMD5(i->param(p_username),
- password,
- realm,
-
getMethodName(request.header(h_RequestLine).getMethod()),
- i->param(p_uri),
-
i->param(p_nonce),
- i->param(p_qop),
-
i->param(p_cnonce),
- i->param(p_nc)))
- {
- return Authenticated;
- }
- else
- {
- return Failed;
- }
- }
else
{
- InfoLog (<< "Unsupported qop=" << i->param(p_qop));
return Failed;
}
}
- else if (i->param(p_response) ==
makeResponseMD5(i->param(p_username),
- password,
- realm,
-
getMethodName(request.header(h_RequestLine).getMethod()),
- i->param(p_uri),
-
i->param(p_nonce)))
- {
- return Authenticated;
- }
else
{
+ InfoLog (<< "Unsupported qop=" << i->param(p_qop));
return Failed;
}
}
+ else if (i->param(p_response) == makeResponseMD5(i->param(p_username),
+ password,
+ realm,
+
getMethodName(request.header(h_RequestLine).getMethod()),
+ i->param(p_uri),
+ i->param(p_nonce)))
+ {
+ return Authenticated;
+ }
else
{
- return BadlyFormed;
+ return Failed;
}
}
- return BadlyFormed;
+ else
+ {
+ return BadlyFormed;
+ }
}
- DebugLog (<< "No authentication headers. Failing request.");
- return Failed;
+ return BadlyFormed;
+
}
SipMessage*
@@ -727,6 +738,24 @@
return response;
}
+SipMessage*
+Helper::makeUserChallenge(const SipMessage& request, const Data& realm, bool
useAuth)
+{
+ Auth auth;
+ auth.scheme() = "Digest";
+ Data timestamp((int)(Timer::getTimeMs()/1000));
+ auth.param(p_nonce) = makeNonce(request, timestamp);
+ auth.param(p_algorithm) = "MD5";
+ auth.param(p_realm) = realm;
+ if (useAuth)
+ {
+ auth.param(p_qopOptions) = "auth,auth-int";
+ }
+ SipMessage *response = Helper::makeResponse(request, 401);
+ response->header(h_WWWAuthenticates).push_back(auth);
+ return response;
+}
+
void updateNonceCount(unsigned int& nonceCount, Data& nonceCountString)
{
if (!nonceCountString.empty())
Helper::ContentsSecAttrs::ContentsSecAttrs()
Index: Helper.hxx
===================================================================
--- Helper.hxx (revision 3877)
+++ Helper.hxx (working copy)
@@ -115,6 +115,11 @@
const Data& realm,
bool useAuth = true);
+ // create a 401 response with WWW-Authenticate header filled in
+ static SipMessage* makeUserChallenge(const SipMessage& request,
+ const Data& realm,
+ bool useAuth = true);
+
// adds authorization headers in reponse to the 401 or 407--currently
// only supports md5, the only qop supported is auth.
static SipMessage& addAuthorization(SipMessage& request,
Index: dum/DialogUsageManager.cxx
===================================================================
--- dum/DialogUsageManager.cxx (revision 3877)
+++ dum/DialogUsageManager.cxx (working copy)
@@ -701,13 +701,10 @@
return true;
}
}
-
- if ( mServerAuthManager.get() )
- {
- if ( mServerAuthManager->handle(*sipMsg) )
- {
- return true;
- }
+ if ( !authenticateRequest(*sipMsg) )
+ {
+ DebugLog (<< "Failed authentication" << *sipMsg);
+ return true;
}
if (queueForIdentityCheck(sipMsg))
{
@@ -1011,6 +1008,26 @@
}
bool
+DialogUsageManager::authenticateRequest(const SipMessage& request)
+{
+ if( !mServerAuthManager.get() )
+ {
+ return true;
+ }
+
+ if ( !mServerAuthManager->handle(request) )
+ {
+
+ SipMessage* challenge = mServerAuthManager->makeChallenge(request);
+ sendResponse(*challenge);
+ delete challenge;
+ return false;
+ }
+
+ return true;
+}
+
+bool
DialogUsageManager::mergeRequest(const SipMessage& request)
{
assert(request.isRequest());
Index: dum/ServerAuthManager.cxx
===================================================================
--- dum/ServerAuthManager.cxx (revision 3877)
+++ dum/ServerAuthManager.cxx (working copy)
@@ -1,13 +1,87 @@
#include "ServerAuthManager.hxx"
+#include "resiprocate/Helper.hxx"
+#include "resiprocate/dum/Profile.hxx"
+#include "resiprocate/os/Data.hxx"
+#include "resiprocate/SipMessage.hxx"
#include <cassert>
#define RESIPROCATE_SUBSYSTEM Subsystem::DUM
using namespace resip;
+ServerAuthManager::ServerAuthManager(Profile& profile)
+ : mProfile(profile),
+ mExpiresDelta(0) //never expires, by default
+{
+}
+
+ServerAuthManager::~ServerAuthManager()
+{
+}
+
+void ServerAuthManager::setExpiresDelta(unsigned int expires)
+{
+ mExpiresDelta = expires;
+}
+
bool
-ServerAuthManager::handle(const SipMessage& response)
+ServerAuthManager::handle(const SipMessage& request)
{
- assert(0);
- return true;
+ // Determine if the request needs to be authenticated
+ if( !requiresAuthentication(request) )
+ {
+ return true;
+ }
+
+ // locate the credentials for this request
+ Profile::DigestCredential credential =
mProfile.getDigestCredential(getAuthRealm(request));
+
+ if( credential.realm.empty() )
+ {
+ //hmmm no digest credentials for this realm, no auth required then
+ return true;
+ }
+
+ Helper::AuthResult auth = Helper::authenticateRequest(request,
+ credential.realm,
+ credential.password,
+ mExpiresDelta);
+
+ switch( auth )
+ {
+ case Helper::Authenticated:
+ return true;
+ case Helper::Failed:
+ case Helper::BadlyFormed:
+ case Helper::Expired:
+ default:
+ break;
+ }
+
+ return false;
}
+
+SipMessage*
+ServerAuthManager::makeChallenge(const SipMessage& request)
+{
+ Data realm = getAuthRealm(request);
+
+ Profile::DigestCredential credential = mProfile.getDigestCredential(realm);
+
+ return Helper::makeUserChallenge(request, credential.realm, true);
+
+}
+
+bool
+ServerAuthManager::requiresAuthentication(const SipMessage& request)
+{
+ return true;
+}
+
+const Data&
+ServerAuthManager::getAuthRealm(const SipMessage& request)
+{
+ return request.header(h_To).uri().host();
+}
+
+
Index: dum/DialogUsageManager.hxx
===================================================================
--- dum/DialogUsageManager.hxx (revision 3877)
+++ dum/DialogUsageManager.hxx (working copy)
@@ -264,6 +264,7 @@
bool validateContent(const SipMessage& request);
bool validateAccept(const SipMessage& request);
bool validateTo(const SipMessage& request);
+ bool authenticateRequest(const SipMessage& request);
bool mergeRequest(const SipMessage& request);
void processPublish(const SipMessage& publish);
Index: dum/ServerAuthManager.hxx
===================================================================
--- dum/ServerAuthManager.hxx (revision 3877)
+++ dum/ServerAuthManager.hxx (working copy)
@@ -5,17 +5,43 @@
{
class SipMessage;
class Profile;
+class Data;
class ServerAuthManager
{
public:
ServerAuthManager(Profile& profile);
- // return true if request is authorized
+ virtual ~ServerAuthManager();
+
+ // return true if request is authenticated
bool handle(const SipMessage& request);
+ // creates the 401 challenge response
+ SipMessage* makeChallenge(const SipMessage& request);
+
+ // Set the amount of time the nonce is valid
+ // The default is 0 (never expires).
+ void setExpiresDelta(unsigned int expires);
+
+ protected:
+
+ // Determine if request requires authentication
+ // Returns true, meaning all requests require authentication.
+ // Sub-class may override to decide which requests need autthentication.
+ virtual bool requiresAuthentication(const SipMessage& request);
+
+ // get the realm required to authenticate the request
+ // Returns request aor realm.
+ // Sub-class may override it it uses a different realm scope.
+ virtual const Data& getAuthRealm(const SipMessage& request);
+
private:
Profile& mProfile;
+
+ // the amount of time the nonce is considered valid
+ unsigned int mExpiresDelta;
+
};