reSIProcate/repro  9694
DigestAuthenticator.cxx
Go to the documentation of this file.
00001 
00002 #if defined(HAVE_CONFIG_H)
00003   #include "config.h"
00004 #endif
00005 
00006 #include "rutil/DnsUtil.hxx"
00007 #include "resip/stack/Message.hxx"
00008 #include "resip/stack/SipMessage.hxx"
00009 #include "resip/stack/Auth.hxx"
00010 #include "resip/stack/Helper.hxx"
00011 #include "rutil/Logger.hxx"
00012 
00013 #include "repro/monkeys/DigestAuthenticator.hxx"
00014 #include "repro/monkeys/IsTrustedNode.hxx"
00015 #include "repro/RequestContext.hxx"
00016 #include "repro/Proxy.hxx"
00017 #include "repro/UserInfoMessage.hxx"
00018 #include "repro/UserStore.hxx"
00019 #include "repro/Dispatcher.hxx"
00020 #include "resip/stack/SipStack.hxx"
00021 #include "rutil/ParseBuffer.hxx"
00022 #include "rutil/WinLeakCheck.hxx"
00023 
00024 #define RESIPROCATE_SUBSYSTEM resip::Subsystem::REPRO
00025 
00026 using namespace resip;
00027 using namespace repro;
00028 using namespace std;
00029 
00030 DigestAuthenticator::DigestAuthenticator(ProxyConfig& config,
00031                                          Dispatcher* authRequestDispatcher) :
00032    Processor("DigestAuthenticator"),
00033    mAuthRequestDispatcher(authRequestDispatcher),
00034    mNoIdentityHeaders(config.getConfigBool("DisableIdentity", false)),
00035    mHttpHostname(config.getConfigData("HttpHostname", "")),
00036    mHttpPort(config.getConfigInt("HttpPort", 5080)),
00037    mUseAuthInt(!config.getConfigBool("DisableAuthInt", false)),
00038    mRejectBadNonces(config.getConfigBool("RejectBadNonces", false))
00039 {
00040 }
00041 
00042 DigestAuthenticator::~DigestAuthenticator()
00043 {
00044 }
00045 
00046 repro::Processor::processor_action_t
00047 DigestAuthenticator::process(repro::RequestContext &rc)
00048 {
00049    DebugLog(<< "Monkey handling request: " << *this << "; reqcontext = " << rc);
00050    
00051    Message *message = rc.getCurrentEvent();
00052    
00053    SipMessage *sipMessage = dynamic_cast<SipMessage*>(message);
00054    UserInfoMessage *userInfo = dynamic_cast<UserInfoMessage*>(message);
00055    Proxy &proxy = rc.getProxy();
00056    
00057    if (sipMessage)
00058    {
00059       if (sipMessage->method() == ACK ||
00060           sipMessage->method() == BYE)
00061       {
00062          // Don't challenge ACK and BYE requests
00063          return Continue;
00064       }
00065 
00066       if (sipMessage->exists(h_ProxyAuthorizations))
00067       {
00068          Auths &authHeaders = sipMessage->header(h_ProxyAuthorizations);
00069          
00070          // if we find a Proxy-Authorization header for a realm we handle, 
00071          // asynchronously fetch the relevant userAuthInfo from the database
00072          for (Auths::iterator i = authHeaders.begin() ; i != authHeaders.end() ; ++i)
00073          {
00074             // !rwm!  TODO sometime we need to have a separate isMyRealm() function
00075             if (proxy.isMyDomain(i->param(p_realm)))
00076             {
00077                return requestUserAuthInfo(rc, i->param(p_realm));
00078             }
00079          }
00080       }
00081       
00082       // if there was no Proxy-Auth header already, and the request is purportedly From
00083       // one of our domains, send a challenge, unless this is from a trusted node in one
00084       // of "our" domains (ex: from a gateway).
00085       //
00086       // Note that other monkeys can still challenge the request later if needed 
00087       // for other reasons (for example, the StaticRoute monkey)
00088       if(!sipMessage->header(h_From).isWellFormed() ||
00089          sipMessage->header(h_From).isAllContacts() )
00090       {
00091          InfoLog(<<"Malformed From header: cannot get realm to challenge with. Rejecting.");
00092          rc.sendResponse(*auto_ptr<SipMessage>
00093                          (Helper::makeResponse(*sipMessage, 400, "Malformed From header")));
00094          return SkipAllChains;         
00095       }
00096       
00097       if (proxy.isMyDomain(sipMessage->header(h_From).uri().host()))
00098       {
00099          if (!rc.getKeyValueStore().getBoolValue(IsTrustedNode::mFromTrustedNodeKey))
00100          {
00101                challengeRequest(rc, false);
00102                return SkipAllChains;
00103          }
00104       }
00105    }
00106    else if (userInfo)
00107    {
00108       // Handle response from user authentication database
00109       sipMessage = &rc.getOriginalRequest();
00110       const Data& a1 = userInfo->A1();
00111       const Data& realm = userInfo->realm();
00112       const Data& user = userInfo->user();
00113       InfoLog (<< "Received user auth info for " << user << " at realm " << realm 
00114                <<  " a1 is " << a1);
00115 
00116       pair<Helper::AuthResult,Data> result =
00117          Helper::advancedAuthenticateRequest(*sipMessage, realm, a1, 3000); // was 15
00118 
00119 //      Auths &authHeaders = sipMessage->header(h_ProxyAuthorizations);
00120       switch (result.first)
00121       {
00122          case Helper::Failed:
00123             InfoLog (<< "Authentication failed for " << user << " at realm " << realm << ". Sending 403");
00124             rc.sendResponse(*auto_ptr<SipMessage>
00125                             (Helper::makeResponse(*sipMessage, 403, "Authentication Failed")));
00126             return SkipAllChains;
00127         
00128             // !abr! Eventually, this should just append a counter to
00129             // the nonce, and increment it on each challenge. 
00130             // If this count is smaller than some reasonable limit,
00131             // then we re-challenge; otherwise, we send a 403 instead.
00132 
00133          case Helper::Authenticated:
00134             InfoLog (<< "Authentication ok for " << user);
00135             
00136             // Delete the Proxy-Auth header for this realm.  
00137             // other Proxy-Auth headers might be needed by a downsteram node
00138 /*            
00139             Auths::iterator i = authHeaders.begin();
00140             Auths::iterator j = authHeaders.begin();
00141             while( i != authHeaders.end() )
00142             {
00143                if (proxy.isMyDomain(i->param(p_realm)))
00144                {
00145                   j = i++;
00146                   authHeaders.erase(j);
00147                }
00148                else
00149                {
00150                   ++i;
00151                }
00152             }
00153 */            
00154             if(!sipMessage->header(h_From).isWellFormed() ||
00155                sipMessage->header(h_From).isAllContacts())
00156             {
00157                InfoLog(<<"From header is malformed in"
00158                               " digest response.");
00159                rc.sendResponse(*auto_ptr<SipMessage>
00160                                (Helper::makeResponse(*sipMessage, 400, "Malformed From header")));
00161                return SkipAllChains;               
00162             }
00163             
00164             if (authorizedForThisIdentity(user, realm, sipMessage->header(h_From).uri()))
00165             {
00166                rc.setDigestIdentity(user);
00167 
00168                if(rc.getProxy().isPAssertedIdentityProcessingEnabled())
00169                {
00170                   if (sipMessage->exists(h_PPreferredIdentities))
00171                   {
00172                      // Ensure any P-AssertedIdentities present are removed (note: this is an illegal condidition)
00173                      sipMessage->remove(h_PAssertedIdentities);
00174 
00175                      // TODO - when we have a concept of multiple identities per user
00176                      // find the first sip or sips P-Preferred-Identity header  and the first tel
00177                      // bool haveSip = false;
00178                      // bool haveTel = false;
00179                      // for (;;)
00180                      // {
00181                      //    if ((i->uri().scheme() == Symbols::SIP) || (i->uri().scheme() == Symbols::SIPS))
00182                      //    {
00183                      //       if (haveSip)
00184                      //       {
00185                      //          continue;   // skip all but the first sip: or sips: URL
00186                      //       }
00187                      //       haveSip = true;
00188                      //
00189                      //       if (knownSipIdentity( user, realm, i->uri() )  // should be NameAddr?
00190                      //       {
00191                      //          sipMessage->header(h_PAssertedIdentities).push_back( i->uri() );
00192                      //       }
00193                      //       else
00194                      //       {
00195                      //          sipMessage->header(h_PAssertedIdentities).push_back(getDefaultIdentity(user, realm));
00196                      //       }
00197                      //    }
00198                      //    else if ((i->uri().scheme() == Symbols::TEL))
00199                      //    {
00200                      //       if (haveTel)
00201                      //       {
00202                      //          continue;  // skip all but the first tel: URL
00203                      //       }
00204                      //       haveTel = true;
00205                      //
00206                      //       if (knownTelIdentity( user, realm, i->uri() ))
00207                      //       {
00208                      //          sipMessage->header(h_PAssertedIdentities).push_back( i->uri() );
00209                      //       }
00210                      //    }
00211                      // }
00212 
00213                      // We currently don't do anything special with the P-Peferred-Identity hint - just
00214                      // add default identity
00215                      sipMessage->header(h_PAssertedIdentities).push_back(getDefaultIdentity(user, realm, sipMessage->header(h_From)));
00216 
00217                      // Remove the P-Preferered-Identity header
00218                      sipMessage->remove(h_PPreferredIdentities);
00219                   }
00220                   else
00221                   {
00222                      if (!sipMessage->exists(h_PAssertedIdentities))
00223                      {
00224                         sipMessage->header(h_PAssertedIdentities).push_back(getDefaultIdentity(user, realm, sipMessage->header(h_From)));
00225                      }
00226                      // else  TODO
00227                      //  - should implement guidlines in RFC5876 4.5 - whereby the proxy should remove 
00228                      //        ignored URI's (ie. a 2nd SIP, SIPS or TEL URI, unknown scheme)
00229                   }
00230                }            
00231             
00232 #if defined(USE_SSL)
00233                if(!mNoIdentityHeaders)
00234                {
00235                   static Data http("http://" + mHttpHostname + ":" + Data(mHttpPort) + "/cert?domain=");
00236                   // .bwc. Leave pre-existing Identity headers alone.
00237                   if(!sipMessage->exists(h_Identity))
00238                   {
00239                      sipMessage->header(h_Identity).value() = Data::Empty;  // This is a signal to have the TransportSelector fill in the identity header
00240                      if(sipMessage->exists(h_IdentityInfo))
00241                      {
00242                         InfoLog(<<"Somebody sent us a"
00243                               " request with an Identity-Info, but no Identity"
00244                               " header. Removing it.");
00245                         if(!sipMessage->header(h_IdentityInfo).isWellFormed())
00246                         {
00247                            InfoLog(<<"...and this "
00248                               "Identity-Info header was malformed!");
00249                         }
00250 
00251                         sipMessage->remove(h_IdentityInfo);
00252                      }
00253                      
00254                      sipMessage->header(h_IdentityInfo).uri() = http + realm;
00255                      InfoLog (<< "Identity-Info=" << sipMessage->header(h_IdentityInfo).uri());
00256                   }
00257                }
00258 #endif
00259             }
00260             else
00261             {
00262                // !rwm! The user is trying to forge a request.  Respond with a 403
00263                InfoLog (<< "User: " << user << " at realm: " << realm << 
00264                            " trying to forge request from: " << sipMessage->header(h_From).uri());
00265                rc.sendResponse(*auto_ptr<SipMessage>
00266                                (Helper::makeResponse(*sipMessage, 403)));
00267                return SkipAllChains;               
00268             }
00269             
00270             return Continue;
00271 
00272          case Helper::Expired:
00273             InfoLog (<< "Authentication expired for " << user);
00274             challengeRequest(rc, true);
00275             return SkipAllChains;
00276 
00277          case Helper::BadlyFormed:
00278             InfoLog (<< "Authentication nonce badly formed for " << user);
00279             if(mRejectBadNonces)
00280             {
00281                rc.sendResponse(*auto_ptr<SipMessage>
00282                             (Helper::makeResponse(*sipMessage, 403, "Where on earth did you get that nonce?")));
00283             }
00284             else
00285             {
00286                challengeRequest(rc, true);
00287             }
00288             return SkipAllChains;
00289       }
00290    }
00291 
00292    return Continue;
00293 }
00294 
00295 bool
00296 DigestAuthenticator::authorizedForThisIdentity(const resip::Data &user, const resip::Data &realm, 
00297                                                 resip::Uri &fromUri)
00298 {
00299    // !rwm! good enough for now.  TODO eventually consult a database to see what
00300    // combinations of user/realm combos are authorized for an identity
00301    if (fromUri.host() == realm)
00302    {
00303       if ((fromUri.user() == user) || (fromUri.user() == "anonymous"))
00304          return true;
00305 
00306       // Now try the form where the username parameter in the auth
00307       // header is the full fromUri, e.g.
00308       //    Proxy-Authorization: Digest username="user@domain" ...
00309       //
00310       if (fromUri.getAorNoPort() == user)
00311          return true;
00312    }
00313 
00314    // catch-all: access denied
00315    return false;
00316 }
00317 
00318 NameAddr
00319 DigestAuthenticator::getDefaultIdentity(const resip::Data &user, const resip::Data &realm, resip::NameAddr &from)
00320 {
00321    NameAddr defaultIdentity;
00322    defaultIdentity.displayName() = from.displayName();
00323    defaultIdentity.uri().scheme() = from.uri().scheme();
00324    defaultIdentity.uri().user() = user;
00325    defaultIdentity.uri().host() = realm;
00326    return defaultIdentity;
00327 }
00328 
00329 void
00330 DigestAuthenticator::challengeRequest(repro::RequestContext &rc,
00331                                       bool stale)
00332 {
00333    SipMessage &sipMessage = rc.getOriginalRequest();
00334    Data realm = getRealm(rc);
00335 
00336    SipMessage *challenge = Helper::makeProxyChallenge(sipMessage, realm, 
00337                                                       mUseAuthInt /*auth-int*/, stale);
00338    rc.sendResponse(*challenge);
00339    delete challenge;
00340 }
00341 
00342 
00343 repro::Processor::processor_action_t
00344 DigestAuthenticator::requestUserAuthInfo(repro::RequestContext &rc, resip::Data &realm)
00345 {
00346    Message *message = rc.getCurrentEvent();
00347    SipMessage *sipMessage = dynamic_cast<SipMessage*>(message);
00348    assert(sipMessage);
00349 
00350 
00351    // Extract the user from the appropriate Proxy-Authorization header
00352    Auths &authorizationHeaders = sipMessage->header(h_ProxyAuthorizations); 
00353    Auths::iterator i;
00354    Data user;
00355 
00356    for (i = authorizationHeaders.begin();
00357         i != authorizationHeaders.end(); i++)
00358    {
00359       if (    i->exists(p_realm) && 
00360               i->param(p_realm) == realm
00361               &&  i->exists(p_username))
00362       {
00363          user = i->param(p_username);
00364 
00365          InfoLog (<< "Request user auth info for "  << user
00366                   << " at realm " << realm);
00367          break;
00368       }
00369    }
00370 
00371    if (!user.empty())
00372    {
00373       UserInfoMessage* async = new UserInfoMessage(*this, rc.getTransactionId(), &(rc.getProxy()));
00374       async->user()=user;
00375       async->realm()=realm;
00376       if(sipMessage->header(h_From).isWellFormed())
00377       {
00378          async->domain()=sipMessage->header(h_From).uri().host();
00379       }
00380       else
00381       {
00382          async->domain()=realm;
00383       }
00384       std::auto_ptr<ApplicationMessage> app(async);
00385       mAuthRequestDispatcher->post(app);
00386       return WaitingForEvent;
00387    }
00388    else
00389    {
00390       challengeRequest(rc, false);
00391       return SkipAllChains;
00392    }
00393 }
00394 
00395 resip::Data
00396 DigestAuthenticator::getRealm(RequestContext &rc)
00397 {
00398    Data realm;
00399 
00400    Proxy &proxy = rc.getProxy();
00401    SipMessage& sipMessage = rc.getOriginalRequest();
00402 
00403    // (1) Check Preferred Identity
00404    if (sipMessage.exists(h_PPreferredIdentities))
00405    {
00406       // !abr! Add this when we get a chance
00407       // find the fist sip or sips P-Preferred-Identity header
00408       // for (;;)
00409       // {
00410       //    if ((i->uri().scheme() == Symbols::SIP) || (i->uri().scheme() == Symbols::SIPS))
00411       //    {
00412       //       return i->uri().host();
00413       //    }
00414       // }
00415    }
00416 
00417    // (2) Check From domain
00418    if (proxy.isMyDomain(sipMessage.header(h_From).uri().host()))
00419    {
00420       return sipMessage.header(h_From).uri().host();
00421    }
00422 
00423    // (3) Check Top Route Header
00424    if (sipMessage.exists(h_Routes) &&
00425          sipMessage.header(h_Routes).size()!=0 &&
00426          sipMessage.header(h_Routes).front().isWellFormed())
00427    {
00428       // !abr! Add this when we get a chance
00429    }
00430 
00431    // (4) Punt: Use Request URI
00432    return sipMessage.header(h_RequestLine).uri().host();
00433 }
00434 
00435 
00436 /* ====================================================================
00437  * The Vovida Software License, Version 1.0 
00438  * 
00439  * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
00440  * 
00441  * Redistribution and use in source and binary forms, with or without
00442  * modification, are permitted provided that the following conditions
00443  * are met:
00444  * 
00445  * 1. Redistributions of source code must retain the above copyright
00446  *    notice, this list of conditions and the following disclaimer.
00447  * 
00448  * 2. Redistributions in binary form must reproduce the above copyright
00449  *    notice, this list of conditions and the following disclaimer in
00450  *    the documentation and/or other materials provided with the
00451  *    distribution.
00452  * 
00453  * 3. The names "VOCAL", "Vovida Open Communication Application Library",
00454  *    and "Vovida Open Communication Application Library (VOCAL)" must
00455  *    not be used to endorse or promote products derived from this
00456  *    software without prior written permission. For written
00457  *    permission, please contact vocal@vovida.org.
00458  *
00459  * 4. Products derived from this software may not be called "VOCAL", nor
00460  *    may "VOCAL" appear in their name, without prior written
00461  *    permission of Vovida Networks, Inc.
00462  * 
00463  * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
00464  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00465  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
00466  * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
00467  * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
00468  * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
00469  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00470  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00471  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
00472  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00473  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
00474  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
00475  * DAMAGE.
00476  * 
00477  * ====================================================================
00478  * 
00479  * This software consists of voluntary contributions made by Vovida
00480  * Networks, Inc. and many individuals on behalf of Vovida Networks,
00481  * Inc.  For more information on Vovida Networks, Inc., please see
00482  * <http://www.vovida.org/>.
00483  *
00484  */