reSIProcate/DialogUsageManager  9694
ServerAuthManager.cxx
Go to the documentation of this file.
00001 #include <cassert>
00002 
00003 #include "resip/dum/ChallengeInfo.hxx"
00004 #include "resip/dum/DumFeature.hxx"
00005 #include "resip/dum/DumFeatureChain.hxx"
00006 #include "resip/dum/ServerAuthManager.hxx"
00007 #include "resip/dum/DialogUsageManager.hxx"
00008 #include "resip/dum/TargetCommand.hxx"
00009 #include "rutil/Logger.hxx"
00010 #include "resip/dum/UserAuthInfo.hxx"
00011 #include "resip/stack/Helper.hxx"
00012 #include "rutil/WinLeakCheck.hxx"
00013 
00014 #define RESIPROCATE_SUBSYSTEM Subsystem::DUM
00015 
00016 using namespace resip;
00017 using namespace std;
00018 
00019 ServerAuthManager::ServerAuthManager(DialogUsageManager& dum, TargetCommand::Target& target) :
00020    DumFeature(dum, target)
00021 {
00022 }
00023 
00024 
00025 ServerAuthManager::~ServerAuthManager()
00026 {
00027    InfoLog(<< "~ServerAuthManager:  " << mMessages.size() << " messages in memory when destroying.");
00028 }
00029 
00030 // !bwc! We absolutely, positively, MUST NOT throw here. This is because in
00031 // DialogUsageManager::process(), we do not know if a DumFeature has taken
00032 // ownership of msg until we get a return. If we throw, the ownership of msg
00033 // is unknown. This is unacceptable.
00034 DumFeature::ProcessingResult 
00035 ServerAuthManager::process(Message* msg)
00036 {
00037    SipMessage* sipMsg = dynamic_cast<SipMessage*>(msg);
00038 
00039    if (sipMsg)
00040    {
00042       switch ( handle(sipMsg) )
00043       {
00044          case ServerAuthManager::Challenged:
00045             InfoLog(<< "ServerAuth challenged request " << sipMsg->brief());
00046             return DumFeature::ChainDoneAndEventDone;            
00047          case ServerAuthManager::RequestedInfo:
00048             InfoLog(<< "ServerAuth requested info (requiresChallenge) " << sipMsg->brief());
00049             return DumFeature::EventTaken;
00050          case ServerAuthManager::RequestedCredentials:
00051             InfoLog(<< "ServerAuth requested credentials " << sipMsg->brief());
00052             return DumFeature::EventTaken;
00053          case ServerAuthManager::Rejected:
00054             InfoLog(<< "ServerAuth rejected request " << sipMsg->brief());
00055             return DumFeature::ChainDoneAndEventDone;            
00056          default:   // includes Skipped
00057             return DumFeature::FeatureDone;            
00058       }
00059    }
00060 
00061    ChallengeInfo* challengeInfo = dynamic_cast<ChallengeInfo*>(msg);
00062    if(challengeInfo)
00063    {
00064       InfoLog(<< "ServerAuth got ChallengeInfo " << challengeInfo->brief());
00065       MessageMap::iterator it = mMessages.find(challengeInfo->getTransactionId());
00066       assert(it != mMessages.end());
00067       std::auto_ptr<SipMessage> sipMsg(it->second);
00068       mMessages.erase(it);
00069 
00070       if(challengeInfo->isFailed()) 
00071       {
00072         // some kind of failure occurred while checking whether a 
00073         // challenge is required
00074         InfoLog(<< "ServerAuth requiresChallenge() async failed");
00075         SharedPtr<SipMessage> response(new SipMessage);
00076         Helper::makeResponse(*response, *sipMsg, 500, "Server Internal Error");
00077         mDum.send(response);
00078         return DumFeature::ChainDoneAndEventDone;
00079       }
00080 
00081       if(challengeInfo->isChallengeRequired()) 
00082       {
00083         issueChallenge(sipMsg.get());
00084         InfoLog(<< "ServerAuth challenged request (after async) " << sipMsg->brief());
00085         return DumFeature::ChainDoneAndEventDone;
00086       } 
00087       else 
00088       {
00089         // challenge is not required, re-instate original message
00090         postCommand(auto_ptr<Message>(sipMsg));
00091         return FeatureDoneAndEventDone;
00092       }
00093    }
00094 
00095    UserAuthInfo* userAuth = dynamic_cast<UserAuthInfo*>(msg);
00096    if (userAuth)
00097    {
00098       //InfoLog(<< "Got UserAuthInfo");
00099       UserAuthInfo* userAuth = dynamic_cast<UserAuthInfo*>(msg);
00100       if (userAuth)
00101       {
00102          Message* result = handleUserAuthInfo(userAuth);
00103          if (result)
00104          {
00105             postCommand(auto_ptr<Message>(result));
00106             return FeatureDoneAndEventDone;
00107          }
00108          else
00109          {
00110             InfoLog(<< "ServerAuth rejected request " << *userAuth);
00111             return ChainDoneAndEventDone;            
00112          }
00113       }
00114    }
00115    return FeatureDone;   
00116 }
00117 
00118 SipMessage*
00119 ServerAuthManager::handleUserAuthInfo(UserAuthInfo* userAuth)
00120 {
00121    assert(userAuth);
00122 
00123    MessageMap::iterator it = mMessages.find(userAuth->getTransactionId());
00124    assert(it != mMessages.end());
00125    SipMessage* requestWithAuth = it->second;
00126    mMessages.erase(it);
00127 
00128    InfoLog( << "Checking for auth result in realm=" << userAuth->getRealm() 
00129             << " A1=" << userAuth->getA1());
00130 
00131    if (userAuth->getMode() == UserAuthInfo::UserUnknown || 
00132        (userAuth->getMode() == UserAuthInfo::RetrievedA1 && userAuth->getA1().empty()))
00133    {
00134       InfoLog (<< "User unknown " << userAuth->getUser() << " in " << userAuth->getRealm());
00135       SharedPtr<SipMessage> response(new SipMessage);
00136       Helper::makeResponse(*response, *requestWithAuth, 404, "User unknown.");
00137       mDum.send(response);
00138       onAuthFailure(BadCredentials, *requestWithAuth);
00139       delete requestWithAuth;
00140       return 0;
00141    }
00142 
00143    if (userAuth->getMode() == UserAuthInfo::Error)
00144    {
00145       InfoLog (<< "Error in auth procedure for " << userAuth->getUser() << " in " << userAuth->getRealm());
00146       SharedPtr<SipMessage> response(new SipMessage);
00147       Helper::makeResponse(*response, *requestWithAuth, 503, "Server Error.");
00148       mDum.send(response);
00149       onAuthFailure(Error, *requestWithAuth);
00150       delete requestWithAuth;
00151       return 0;
00152    }
00153 
00154    bool stale = false;
00155    bool digestAccepted = (userAuth->getMode() == UserAuthInfo::DigestAccepted);
00156    if(userAuth->getMode() == UserAuthInfo::RetrievedA1)
00157    {
00160       std::pair<Helper::AuthResult,Data> resPair = 
00161          Helper::advancedAuthenticateRequest(*requestWithAuth, 
00162                                              userAuth->getRealm(),
00163                                              userAuth->getA1(),
00164                                              3000,
00165                                              proxyAuthenticationMode());
00166 
00167       switch (resPair.first) 
00168       {
00169          case Helper::Authenticated:
00170             digestAccepted = true;
00171             break;
00172          case Helper::Failed:
00173             // digestAccepted = false;   // already false by default
00174             break;
00175          case Helper::BadlyFormed:
00176             if(rejectBadNonces())
00177             {
00178                InfoLog (<< "Authentication nonce badly formed for " << userAuth->getUser());
00179    
00180                SharedPtr<SipMessage> response(new SipMessage);
00181                Helper::makeResponse(*response, *requestWithAuth, 403, "Invalid nonce");
00182                mDum.send(response);
00183                onAuthFailure(InvalidRequest, *requestWithAuth);
00184                delete requestWithAuth;
00185                return 0;
00186             }
00187             else
00188             {
00189                stale=true;
00190             }
00191             break;
00192          case Helper::Expired:
00193             stale = true;
00194             break;
00195          default:
00196             break;
00197       }
00198    }
00199 
00200    if(stale || userAuth->getMode() == UserAuthInfo::Stale) 
00201    {
00202       InfoLog (<< "Nonce expired for " << userAuth->getUser());
00203 
00204       issueChallenge(requestWithAuth);
00205       delete requestWithAuth;
00206       return 0;
00207    }
00208 
00209    if(digestAccepted)
00210    {
00211       if (authorizedForThisIdentity(userAuth->getUser(), userAuth->getRealm(),
00212                                     requestWithAuth->header(h_From).uri()))
00213       {
00214          InfoLog (<< "Authorized request for " << userAuth->getRealm());
00215          onAuthSuccess(*requestWithAuth);
00216          return requestWithAuth;
00217       }
00218       else
00219       {
00220          // !rwm! The user is trying to forge a request.  Respond with a 403
00221          InfoLog (<< "User: " << userAuth->getUser() << " at realm: " << userAuth->getRealm() <<
00222                   " trying to forge request from: " << requestWithAuth->header(h_From).uri());
00223 
00224          SharedPtr<SipMessage> response(new SipMessage);
00225          Helper::makeResponse(*response, *requestWithAuth, 403, "Invalid user name provided");
00226          mDum.send(response);
00227          onAuthFailure(InvalidRequest, *requestWithAuth);
00228          delete requestWithAuth;
00229          return 0;
00230       }
00231    } 
00232    else 
00233    {
00234       // Handles digestAccepted == false, DigestNotAccepted and any other
00235       // case that is not recognised by the foregoing logic
00236 
00237       InfoLog (<< "Invalid password provided for " << userAuth->getUser() << " in " << userAuth->getRealm());
00238       InfoLog (<< "  a1 hash of password from db was " << userAuth->getA1() );
00239 
00240       SharedPtr<SipMessage> response(new SipMessage);
00241       Helper::makeResponse(*response, *requestWithAuth, 403, "Invalid password provided");
00242       mDum.send(response);
00243       onAuthFailure(BadCredentials, *requestWithAuth);
00244       delete requestWithAuth;
00245       return 0;
00246    }
00247 }
00248 
00249             
00250 bool
00251 ServerAuthManager::useAuthInt() const
00252 {
00253    return false;
00254 }
00255 
00256 
00257 bool
00258 ServerAuthManager::proxyAuthenticationMode() const
00259 {
00260    return true;
00261 }
00262 
00263 
00264 bool
00265 ServerAuthManager::rejectBadNonces() const
00266 {
00267    return true;
00268 }
00269 
00270 
00271 ServerAuthManager::AsyncBool
00272 ServerAuthManager::requiresChallenge(const SipMessage& msg)
00273 {
00274    return True;  
00275 }
00276 
00277 
00278 bool
00279 ServerAuthManager::authorizedForThisIdentity(const resip::Data &user, 
00280                                                const resip::Data &realm, 
00281                                                 resip::Uri &fromUri)
00282 {
00283    // !rwm! good enough for now.  TODO eventually consult a database to see what
00284    // combinations of user/realm combos are authorized for an identity
00285 
00286    // First try the form where the username parameter in the auth
00287    // header is just the username component of the fromUri
00288    //
00289    if ((fromUri.user() == user) && (fromUri.host() == realm))
00290       return true;
00291 
00292    // Now try the form where the username parameter in the auth
00293    // header is the full fromUri, e.g.
00294    //    Proxy-Authorization: Digest username="user@domain" ...
00295    //
00296    if ((fromUri.getAorNoPort() == user) && (fromUri.host() == realm))
00297       return true;
00298 
00299    // catch-all: access denied
00300    return false;
00301 }
00302 
00303 
00304 const Data& 
00305 ServerAuthManager::getChallengeRealm(const SipMessage& msg)
00306 {
00307    return msg.header(h_RequestLine).uri().host();
00308 }
00309 
00310 
00311 bool
00312 ServerAuthManager::isMyRealm(const Data& realm)
00313 {
00314    return mDum.isMyDomain(realm);
00315 }
00316 
00317 
00318 // return true if request has been consumed 
00319 ServerAuthManager::Result
00320 ServerAuthManager::handle(SipMessage* sipMsg)
00321 {
00322    //InfoLog( << "trying to do auth" );
00323    if (sipMsg->isRequest() && 
00324        sipMsg->header(h_RequestLine).method() != ACK && 
00325        sipMsg->header(h_RequestLine).method() != CANCEL)  // Do not challenge ACKs or CANCELs
00326    {
00327       ParserContainer<Auth>* auths;
00328       if (proxyAuthenticationMode())
00329       {
00330          if(!sipMsg->exists(h_ProxyAuthorizations))
00331          {
00332             return issueChallengeIfRequired(sipMsg);
00333          }
00334          auths = &sipMsg->header(h_ProxyAuthorizations);
00335       }
00336       else
00337       {
00338          if(!sipMsg->exists(h_Authorizations))
00339          {
00340             return issueChallengeIfRequired(sipMsg);
00341          }
00342          auths = &sipMsg->header(h_Authorizations);
00343       }
00344  
00345       try
00346       {
00347          for(Auths::iterator it = auths->begin(); it != auths->end(); it++)
00348          {
00349             if (isMyRealm(it->param(p_realm)))
00350             {
00351                InfoLog (<< "Requesting credential for " 
00352                         << it->param(p_username) << " @ " << it->param(p_realm));
00353                
00354                requestCredential(it->param(p_username),
00355                                  it->param(p_realm), 
00356                                  *sipMsg,
00357                                   *it,
00358                                  sipMsg->getTransactionId());
00359                mMessages[sipMsg->getTransactionId()] = sipMsg;
00360                return RequestedCredentials;
00361             }
00362          }
00363 
00364          InfoLog (<< "Didn't find matching realm ");
00365          return issueChallengeIfRequired(sipMsg);
00366       }
00367       catch(BaseException& e)
00368       {
00369          InfoLog (<< "Invalid auth header provided " << e);
00370          SharedPtr<SipMessage> response(new SipMessage);
00371          Helper::makeResponse(*response, *sipMsg, 400, "Invalid auth header");
00372          mDum.send(response);
00373          onAuthFailure(InvalidRequest, *sipMsg);
00374          return Rejected;
00375       }
00376    }
00377    return Skipped;
00378 }
00379 
00380 ServerAuthManager::Result
00381 ServerAuthManager::issueChallengeIfRequired(SipMessage *sipMsg) 
00382 {
00383    // Is challenge required for this message
00384    AsyncBool required = requiresChallenge(*sipMsg);
00385    switch(required) 
00386    {
00387      case False:
00388         return Skipped;
00389      case Async:
00390         mMessages[sipMsg->getTransactionId()] = sipMsg;
00391         return RequestedInfo;
00392      case True:
00393      default:
00394         issueChallenge(sipMsg);
00395         return Challenged;
00396    }
00397 }
00398 
00399 void
00400 ServerAuthManager::issueChallenge(SipMessage *sipMsg) 
00401 {
00402   //assume TransactionUser has matched/repaired a realm
00403   SharedPtr<SipMessage> challenge(Helper::makeChallenge(*sipMsg,
00404                                                         getChallengeRealm(*sipMsg), 
00405                                                         useAuthInt(), 
00406                                                         false /*stale*/,
00407                                                         proxyAuthenticationMode()));
00408 
00409   InfoLog (<< "Sending challenge to " << sipMsg->brief());
00410   mDum.send(challenge);
00411 }
00412 
00413 void 
00414 ServerAuthManager::onAuthSuccess(const SipMessage& msg) 
00415 {
00416    // sub class may want to create a log entry
00417 }
00418 
00419 void 
00420 ServerAuthManager::onAuthFailure(AuthFailureReason reason, const SipMessage& msg) 
00421 {
00422    // sub class may want to create a log entry
00423 }
00424 
00425 
00426 /* ====================================================================
00427  * The Vovida Software License, Version 1.0 
00428  * 
00429  * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
00430  * 
00431  * Redistribution and use in source and binary forms, with or without
00432  * modification, are permitted provided that the following conditions
00433  * are met:
00434  * 
00435  * 1. Redistributions of source code must retain the above copyright
00436  *    notice, this list of conditions and the following disclaimer.
00437  * 
00438  * 2. Redistributions in binary form must reproduce the above copyright
00439  *    notice, this list of conditions and the following disclaimer in
00440  *    the documentation and/or other materials provided with the
00441  *    distribution.
00442  * 
00443  * 3. The names "VOCAL", "Vovida Open Communication Application Library",
00444  *    and "Vovida Open Communication Application Library (VOCAL)" must
00445  *    not be used to endorse or promote products derived from this
00446  *    software without prior written permission. For written
00447  *    permission, please contact vocal@vovida.org.
00448  *
00449  * 4. Products derived from this software may not be called "VOCAL", nor
00450  *    may "VOCAL" appear in their name, without prior written
00451  *    permission of Vovida Networks, Inc.
00452  * 
00453  * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
00454  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00455  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
00456  * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
00457  * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
00458  * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
00459  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00460  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00461  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
00462  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00463  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
00464  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
00465  * DAMAGE.
00466  * 
00467  * ====================================================================
00468  * 
00469  * This software consists of voluntary contributions made by Vovida
00470  * Networks, Inc. and many individuals on behalf of Vovida Networks,
00471  * Inc.  For more information on Vovida Networks, Inc., please see
00472  * <http://www.vovida.org/>.
00473  *
00474  */