3
#include "resip/dum/ChallengeInfo.hxx"
4
#include "resip/dum/DumFeature.hxx"
5
#include "resip/dum/DumFeatureChain.hxx"
6
#include "resip/dum/ServerAuthManager.hxx"
7
#include "resip/dum/DialogUsageManager.hxx"
8
#include "resip/dum/TargetCommand.hxx"
9
#include "rutil/Logger.hxx"
10
#include "resip/dum/UserAuthInfo.hxx"
11
#include "resip/stack/Helper.hxx"
12
#include "rutil/WinLeakCheck.hxx"
14
#define RESIPROCATE_SUBSYSTEM Subsystem::DUM
16
using namespace resip;
19
ServerAuthManager::ServerAuthManager(DialogUsageManager& dum, TargetCommand::Target& target) :
20
DumFeature(dum, target)
25
ServerAuthManager::~ServerAuthManager()
27
InfoLog(<< "~ServerAuthManager: " << mMessages.size() << " messages in memory when destroying.");
30
// !bwc! We absolutely, positively, MUST NOT throw here. This is because in
31
// DialogUsageManager::process(), we do not know if a DumFeature has taken
32
// ownership of msg until we get a return. If we throw, the ownership of msg
33
// is unknown. This is unacceptable.
34
DumFeature::ProcessingResult
35
ServerAuthManager::process(Message* msg)
37
SipMessage* sipMsg = dynamic_cast<SipMessage*>(msg);
41
//!dcm! -- unecessary happens in handle
42
switch ( handle(sipMsg) )
44
case ServerAuthManager::Challenged:
45
InfoLog(<< "ServerAuth challenged request " << sipMsg->brief());
46
return DumFeature::ChainDoneAndEventDone;
47
case ServerAuthManager::RequestedInfo:
48
InfoLog(<< "ServerAuth requested info (requiresChallenge) " << sipMsg->brief());
49
return DumFeature::EventTaken;
50
case ServerAuthManager::RequestedCredentials:
51
InfoLog(<< "ServerAuth requested credentials " << sipMsg->brief());
52
return DumFeature::EventTaken;
53
case ServerAuthManager::Rejected:
54
InfoLog(<< "ServerAuth rejected request " << sipMsg->brief());
55
return DumFeature::ChainDoneAndEventDone;
56
default: // includes Skipped
57
return DumFeature::FeatureDone;
61
ChallengeInfo* challengeInfo = dynamic_cast<ChallengeInfo*>(msg);
64
InfoLog(<< "ServerAuth got ChallengeInfo " << challengeInfo->brief());
65
MessageMap::iterator it = mMessages.find(challengeInfo->getTransactionId());
66
assert(it != mMessages.end());
67
std::auto_ptr<SipMessage> sipMsg(it->second);
70
if(challengeInfo->isFailed())
72
// some kind of failure occurred while checking whether a
73
// challenge is required
74
InfoLog(<< "ServerAuth requiresChallenge() async failed");
75
SharedPtr<SipMessage> response(new SipMessage);
76
Helper::makeResponse(*response, *sipMsg, 500, "Server Internal Error");
78
return DumFeature::ChainDoneAndEventDone;
81
if(challengeInfo->isChallengeRequired())
83
issueChallenge(sipMsg.get());
84
InfoLog(<< "ServerAuth challenged request (after async) " << sipMsg->brief());
85
return DumFeature::ChainDoneAndEventDone;
89
// challenge is not required, re-instate original message
90
postCommand(auto_ptr<Message>(sipMsg));
91
return FeatureDoneAndEventDone;
95
UserAuthInfo* userAuth = dynamic_cast<UserAuthInfo*>(msg);
98
//InfoLog(<< "Got UserAuthInfo");
99
UserAuthInfo* userAuth = dynamic_cast<UserAuthInfo*>(msg);
102
Message* result = handleUserAuthInfo(userAuth);
105
postCommand(auto_ptr<Message>(result));
106
return FeatureDoneAndEventDone;
110
InfoLog(<< "ServerAuth rejected request " << *userAuth);
111
return ChainDoneAndEventDone;
119
ServerAuthManager::handleUserAuthInfo(UserAuthInfo* userAuth)
123
MessageMap::iterator it = mMessages.find(userAuth->getTransactionId());
124
assert(it != mMessages.end());
125
SipMessage* requestWithAuth = it->second;
128
InfoLog( << "Checking for auth result in realm=" << userAuth->getRealm()
129
<< " A1=" << userAuth->getA1());
131
if (userAuth->getMode() == UserAuthInfo::UserUnknown ||
132
(userAuth->getMode() == UserAuthInfo::RetrievedA1 && userAuth->getA1().empty()))
134
InfoLog (<< "User unknown " << userAuth->getUser() << " in " << userAuth->getRealm());
135
SharedPtr<SipMessage> response(new SipMessage);
136
Helper::makeResponse(*response, *requestWithAuth, 404, "User unknown.");
138
onAuthFailure(BadCredentials, *requestWithAuth);
139
delete requestWithAuth;
143
if (userAuth->getMode() == UserAuthInfo::Error)
145
InfoLog (<< "Error in auth procedure for " << userAuth->getUser() << " in " << userAuth->getRealm());
146
SharedPtr<SipMessage> response(new SipMessage);
147
Helper::makeResponse(*response, *requestWithAuth, 503, "Server Error.");
149
onAuthFailure(Error, *requestWithAuth);
150
delete requestWithAuth;
155
bool digestAccepted = (userAuth->getMode() == UserAuthInfo::DigestAccepted);
156
if(userAuth->getMode() == UserAuthInfo::RetrievedA1)
158
//!dcm! -- need to handle stale/unit test advancedAuthenticateRequest
159
//!dcm! -- delta? deal with.
160
std::pair<Helper::AuthResult,Data> resPair =
161
Helper::advancedAuthenticateRequest(*requestWithAuth,
162
userAuth->getRealm(),
165
proxyAuthenticationMode());
167
switch (resPair.first)
169
case Helper::Authenticated:
170
digestAccepted = true;
173
// digestAccepted = false; // already false by default
175
case Helper::BadlyFormed:
176
if(rejectBadNonces())
178
InfoLog (<< "Authentication nonce badly formed for " << userAuth->getUser());
180
SharedPtr<SipMessage> response(new SipMessage);
181
Helper::makeResponse(*response, *requestWithAuth, 403, "Invalid nonce");
183
onAuthFailure(InvalidRequest, *requestWithAuth);
184
delete requestWithAuth;
192
case Helper::Expired:
200
if(stale || userAuth->getMode() == UserAuthInfo::Stale)
202
InfoLog (<< "Nonce expired for " << userAuth->getUser());
204
issueChallenge(requestWithAuth);
205
delete requestWithAuth;
211
if (authorizedForThisIdentity(userAuth->getUser(), userAuth->getRealm(),
212
requestWithAuth->header(h_From).uri()))
214
InfoLog (<< "Authorized request for " << userAuth->getRealm());
215
onAuthSuccess(*requestWithAuth);
216
return requestWithAuth;
220
// !rwm! The user is trying to forge a request. Respond with a 403
221
InfoLog (<< "User: " << userAuth->getUser() << " at realm: " << userAuth->getRealm() <<
222
" trying to forge request from: " << requestWithAuth->header(h_From).uri());
224
SharedPtr<SipMessage> response(new SipMessage);
225
Helper::makeResponse(*response, *requestWithAuth, 403, "Invalid user name provided");
227
onAuthFailure(InvalidRequest, *requestWithAuth);
228
delete requestWithAuth;
234
// Handles digestAccepted == false, DigestNotAccepted and any other
235
// case that is not recognised by the foregoing logic
237
InfoLog (<< "Invalid password provided for " << userAuth->getUser() << " in " << userAuth->getRealm());
238
InfoLog (<< " a1 hash of password from db was " << userAuth->getA1() );
240
SharedPtr<SipMessage> response(new SipMessage);
241
Helper::makeResponse(*response, *requestWithAuth, 403, "Invalid password provided");
243
onAuthFailure(BadCredentials, *requestWithAuth);
244
delete requestWithAuth;
251
ServerAuthManager::useAuthInt() const
258
ServerAuthManager::proxyAuthenticationMode() const
265
ServerAuthManager::rejectBadNonces() const
271
ServerAuthManager::AsyncBool
272
ServerAuthManager::requiresChallenge(const SipMessage& msg)
279
ServerAuthManager::authorizedForThisIdentity(const resip::Data &user,
280
const resip::Data &realm,
283
// !rwm! good enough for now. TODO eventually consult a database to see what
284
// combinations of user/realm combos are authorized for an identity
286
// First try the form where the username parameter in the auth
287
// header is just the username component of the fromUri
289
if ((fromUri.user() == user) && (fromUri.host() == realm))
292
// Now try the form where the username parameter in the auth
293
// header is the full fromUri, e.g.
294
// Proxy-Authorization: Digest username="user@domain" ...
296
if ((fromUri.getAorNoPort() == user) && (fromUri.host() == realm))
299
// catch-all: access denied
305
ServerAuthManager::getChallengeRealm(const SipMessage& msg)
307
return msg.header(h_RequestLine).uri().host();
312
ServerAuthManager::isMyRealm(const Data& realm)
314
return mDum.isMyDomain(realm);
318
// return true if request has been consumed
319
ServerAuthManager::Result
320
ServerAuthManager::handle(SipMessage* sipMsg)
322
//InfoLog( << "trying to do auth" );
323
if (sipMsg->isRequest() &&
324
sipMsg->header(h_RequestLine).method() != ACK &&
325
sipMsg->header(h_RequestLine).method() != CANCEL) // Do not challenge ACKs or CANCELs
327
ParserContainer<Auth>* auths;
328
if (proxyAuthenticationMode())
330
if(!sipMsg->exists(h_ProxyAuthorizations))
332
return issueChallengeIfRequired(sipMsg);
334
auths = &sipMsg->header(h_ProxyAuthorizations);
338
if(!sipMsg->exists(h_Authorizations))
340
return issueChallengeIfRequired(sipMsg);
342
auths = &sipMsg->header(h_Authorizations);
347
for(Auths::iterator it = auths->begin(); it != auths->end(); it++)
349
if (isMyRealm(it->param(p_realm)))
351
InfoLog (<< "Requesting credential for "
352
<< it->param(p_username) << " @ " << it->param(p_realm));
354
requestCredential(it->param(p_username),
358
sipMsg->getTransactionId());
359
mMessages[sipMsg->getTransactionId()] = sipMsg;
360
return RequestedCredentials;
364
InfoLog (<< "Didn't find matching realm ");
365
return issueChallengeIfRequired(sipMsg);
367
catch(BaseException& e)
369
InfoLog (<< "Invalid auth header provided " << e);
370
SharedPtr<SipMessage> response(new SipMessage);
371
Helper::makeResponse(*response, *sipMsg, 400, "Invalid auth header");
373
onAuthFailure(InvalidRequest, *sipMsg);
380
ServerAuthManager::Result
381
ServerAuthManager::issueChallengeIfRequired(SipMessage *sipMsg)
383
// Is challenge required for this message
384
AsyncBool required = requiresChallenge(*sipMsg);
390
mMessages[sipMsg->getTransactionId()] = sipMsg;
391
return RequestedInfo;
394
issueChallenge(sipMsg);
400
ServerAuthManager::issueChallenge(SipMessage *sipMsg)
402
//assume TransactionUser has matched/repaired a realm
403
SharedPtr<SipMessage> challenge(Helper::makeChallenge(*sipMsg,
404
getChallengeRealm(*sipMsg),
407
proxyAuthenticationMode()));
409
InfoLog (<< "Sending challenge to " << sipMsg->brief());
410
mDum.send(challenge);
414
ServerAuthManager::onAuthSuccess(const SipMessage& msg)
416
// sub class may want to create a log entry
420
ServerAuthManager::onAuthFailure(AuthFailureReason reason, const SipMessage& msg)
422
// sub class may want to create a log entry
426
/* ====================================================================
427
* The Vovida Software License, Version 1.0
429
* Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
431
* Redistribution and use in source and binary forms, with or without
432
* modification, are permitted provided that the following conditions
435
* 1. Redistributions of source code must retain the above copyright
436
* notice, this list of conditions and the following disclaimer.
438
* 2. Redistributions in binary form must reproduce the above copyright
439
* notice, this list of conditions and the following disclaimer in
440
* the documentation and/or other materials provided with the
443
* 3. The names "VOCAL", "Vovida Open Communication Application Library",
444
* and "Vovida Open Communication Application Library (VOCAL)" must
445
* not be used to endorse or promote products derived from this
446
* software without prior written permission. For written
447
* permission, please contact vocal@vovida.org.
449
* 4. Products derived from this software may not be called "VOCAL", nor
450
* may "VOCAL" appear in their name, without prior written
451
* permission of Vovida Networks, Inc.
453
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
454
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
455
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
456
* NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
457
* NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
458
* IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
459
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
460
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
461
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
462
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
463
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
464
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
467
* ====================================================================
469
* This software consists of voluntary contributions made by Vovida
470
* Networks, Inc. and many individuals on behalf of Vovida Networks,
471
* Inc. For more information on Vovida Networks, Inc., please see
472
* <http://www.vovida.org/>.