reSIProcate/repro  9694
Public Member Functions | Private Member Functions | Private Attributes
repro::DigestAuthenticator Class Reference

#include <DigestAuthenticator.hxx>

Inheritance diagram for repro::DigestAuthenticator:
Inheritance graph
[legend]
Collaboration diagram for repro::DigestAuthenticator:
Collaboration graph
[legend]

List of all members.

Public Member Functions

 DigestAuthenticator (ProxyConfig &config, Dispatcher *authRequestDispatcher)
 ~DigestAuthenticator ()
virtual processor_action_t process (RequestContext &)

Private Member Functions

bool authorizedForThisIdentity (const resip::Data &user, const resip::Data &realm, resip::Uri &fromUri)
resip::NameAddr getDefaultIdentity (const resip::Data &user, const resip::Data &realm, resip::NameAddr &from)
void challengeRequest (RequestContext &, bool stale=false)
processor_action_t requestUserAuthInfo (RequestContext &, resip::Data &realm)
virtual resip::Data getRealm (RequestContext &)

Private Attributes

DispatchermAuthRequestDispatcher
bool mNoIdentityHeaders
resip::Data mHttpHostname
int mHttpPort
bool mUseAuthInt
bool mRejectBadNonces

Detailed Description

Definition at line 16 of file DigestAuthenticator.hxx.


Constructor & Destructor Documentation

DigestAuthenticator::DigestAuthenticator ( ProxyConfig config,
Dispatcher authRequestDispatcher 
)

Definition at line 30 of file DigestAuthenticator.cxx.

                                                                            :
   Processor("DigestAuthenticator"),
   mAuthRequestDispatcher(authRequestDispatcher),
   mNoIdentityHeaders(config.getConfigBool("DisableIdentity", false)),
   mHttpHostname(config.getConfigData("HttpHostname", "")),
   mHttpPort(config.getConfigInt("HttpPort", 5080)),
   mUseAuthInt(!config.getConfigBool("DisableAuthInt", false)),
   mRejectBadNonces(config.getConfigBool("RejectBadNonces", false))
{
}
DigestAuthenticator::~DigestAuthenticator ( )

Definition at line 42 of file DigestAuthenticator.cxx.

{
}

Member Function Documentation

bool DigestAuthenticator::authorizedForThisIdentity ( const resip::Data user,
const resip::Data realm,
resip::Uri fromUri 
) [private]

Definition at line 296 of file DigestAuthenticator.cxx.

{
   // !rwm! good enough for now.  TODO eventually consult a database to see what
   // combinations of user/realm combos are authorized for an identity
   if (fromUri.host() == realm)
   {
      if ((fromUri.user() == user) || (fromUri.user() == "anonymous"))
         return true;

      // Now try the form where the username parameter in the auth
      // header is the full fromUri, e.g.
      //    Proxy-Authorization: Digest username="user@domain" ...
      //
      if (fromUri.getAorNoPort() == user)
         return true;
   }

   // catch-all: access denied
   return false;
}
void DigestAuthenticator::challengeRequest ( repro::RequestContext rc,
bool  stale = false 
) [private]

Definition at line 330 of file DigestAuthenticator.cxx.

{
   SipMessage &sipMessage = rc.getOriginalRequest();
   Data realm = getRealm(rc);

   SipMessage *challenge = Helper::makeProxyChallenge(sipMessage, realm, 
                                                      mUseAuthInt /*auth-int*/, stale);
   rc.sendResponse(*challenge);
   delete challenge;
}
NameAddr DigestAuthenticator::getDefaultIdentity ( const resip::Data user,
const resip::Data realm,
resip::NameAddr from 
) [private]

Definition at line 319 of file DigestAuthenticator.cxx.

{
   NameAddr defaultIdentity;
   defaultIdentity.displayName() = from.displayName();
   defaultIdentity.uri().scheme() = from.uri().scheme();
   defaultIdentity.uri().user() = user;
   defaultIdentity.uri().host() = realm;
   return defaultIdentity;
}
resip::Data DigestAuthenticator::getRealm ( RequestContext rc) [private, virtual]

Definition at line 396 of file DigestAuthenticator.cxx.

{
   Data realm;

   Proxy &proxy = rc.getProxy();
   SipMessage& sipMessage = rc.getOriginalRequest();

   // (1) Check Preferred Identity
   if (sipMessage.exists(h_PPreferredIdentities))
   {
      // !abr! Add this when we get a chance
      // find the fist sip or sips P-Preferred-Identity header
      // for (;;)
      // {
      //    if ((i->uri().scheme() == Symbols::SIP) || (i->uri().scheme() == Symbols::SIPS))
      //    {
      //       return i->uri().host();
      //    }
      // }
   }

   // (2) Check From domain
   if (proxy.isMyDomain(sipMessage.header(h_From).uri().host()))
   {
      return sipMessage.header(h_From).uri().host();
   }

   // (3) Check Top Route Header
   if (sipMessage.exists(h_Routes) &&
         sipMessage.header(h_Routes).size()!=0 &&
         sipMessage.header(h_Routes).front().isWellFormed())
   {
      // !abr! Add this when we get a chance
   }

   // (4) Punt: Use Request URI
   return sipMessage.header(h_RequestLine).uri().host();
}
repro::Processor::processor_action_t DigestAuthenticator::process ( repro::RequestContext rc) [virtual]

Implements repro::Processor.

Definition at line 47 of file DigestAuthenticator.cxx.

{
   DebugLog(<< "Monkey handling request: " << *this << "; reqcontext = " << rc);
   
   Message *message = rc.getCurrentEvent();
   
   SipMessage *sipMessage = dynamic_cast<SipMessage*>(message);
   UserInfoMessage *userInfo = dynamic_cast<UserInfoMessage*>(message);
   Proxy &proxy = rc.getProxy();
   
   if (sipMessage)
   {
      if (sipMessage->method() == ACK ||
          sipMessage->method() == BYE)
      {
         // Don't challenge ACK and BYE requests
         return Continue;
      }

      if (sipMessage->exists(h_ProxyAuthorizations))
      {
         Auths &authHeaders = sipMessage->header(h_ProxyAuthorizations);
         
         // if we find a Proxy-Authorization header for a realm we handle, 
         // asynchronously fetch the relevant userAuthInfo from the database
         for (Auths::iterator i = authHeaders.begin() ; i != authHeaders.end() ; ++i)
         {
            // !rwm!  TODO sometime we need to have a separate isMyRealm() function
            if (proxy.isMyDomain(i->param(p_realm)))
            {
               return requestUserAuthInfo(rc, i->param(p_realm));
            }
         }
      }
      
      // if there was no Proxy-Auth header already, and the request is purportedly From
      // one of our domains, send a challenge, unless this is from a trusted node in one
      // of "our" domains (ex: from a gateway).
      //
      // Note that other monkeys can still challenge the request later if needed 
      // for other reasons (for example, the StaticRoute monkey)
      if(!sipMessage->header(h_From).isWellFormed() ||
         sipMessage->header(h_From).isAllContacts() )
      {
         InfoLog(<<"Malformed From header: cannot get realm to challenge with. Rejecting.");
         rc.sendResponse(*auto_ptr<SipMessage>
                         (Helper::makeResponse(*sipMessage, 400, "Malformed From header")));
         return SkipAllChains;         
      }
      
      if (proxy.isMyDomain(sipMessage->header(h_From).uri().host()))
      {
         if (!rc.getKeyValueStore().getBoolValue(IsTrustedNode::mFromTrustedNodeKey))
         {
               challengeRequest(rc, false);
               return SkipAllChains;
         }
      }
   }
   else if (userInfo)
   {
      // Handle response from user authentication database
      sipMessage = &rc.getOriginalRequest();
      const Data& a1 = userInfo->A1();
      const Data& realm = userInfo->realm();
      const Data& user = userInfo->user();
      InfoLog (<< "Received user auth info for " << user << " at realm " << realm 
               <<  " a1 is " << a1);

      pair<Helper::AuthResult,Data> result =
         Helper::advancedAuthenticateRequest(*sipMessage, realm, a1, 3000); // was 15

//      Auths &authHeaders = sipMessage->header(h_ProxyAuthorizations);
      switch (result.first)
      {
         case Helper::Failed:
            InfoLog (<< "Authentication failed for " << user << " at realm " << realm << ". Sending 403");
            rc.sendResponse(*auto_ptr<SipMessage>
                            (Helper::makeResponse(*sipMessage, 403, "Authentication Failed")));
            return SkipAllChains;
        
            // !abr! Eventually, this should just append a counter to
            // the nonce, and increment it on each challenge. 
            // If this count is smaller than some reasonable limit,
            // then we re-challenge; otherwise, we send a 403 instead.

         case Helper::Authenticated:
            InfoLog (<< "Authentication ok for " << user);
            
            // Delete the Proxy-Auth header for this realm.  
            // other Proxy-Auth headers might be needed by a downsteram node
/*            
            Auths::iterator i = authHeaders.begin();
            Auths::iterator j = authHeaders.begin();
            while( i != authHeaders.end() )
            {
               if (proxy.isMyDomain(i->param(p_realm)))
               {
                  j = i++;
                  authHeaders.erase(j);
               }
               else
               {
                  ++i;
               }
            }
*/            
            if(!sipMessage->header(h_From).isWellFormed() ||
               sipMessage->header(h_From).isAllContacts())
            {
               InfoLog(<<"From header is malformed in"
                              " digest response.");
               rc.sendResponse(*auto_ptr<SipMessage>
                               (Helper::makeResponse(*sipMessage, 400, "Malformed From header")));
               return SkipAllChains;               
            }
            
            if (authorizedForThisIdentity(user, realm, sipMessage->header(h_From).uri()))
            {
               rc.setDigestIdentity(user);

               if(rc.getProxy().isPAssertedIdentityProcessingEnabled())
               {
                  if (sipMessage->exists(h_PPreferredIdentities))
                  {
                     // Ensure any P-AssertedIdentities present are removed (note: this is an illegal condidition)
                     sipMessage->remove(h_PAssertedIdentities);

                     // TODO - when we have a concept of multiple identities per user
                     // find the first sip or sips P-Preferred-Identity header  and the first tel
                     // bool haveSip = false;
                     // bool haveTel = false;
                     // for (;;)
                     // {
                     //    if ((i->uri().scheme() == Symbols::SIP) || (i->uri().scheme() == Symbols::SIPS))
                     //    {
                     //       if (haveSip)
                     //       {
                     //          continue;   // skip all but the first sip: or sips: URL
                     //       }
                     //       haveSip = true;
                     //
                     //       if (knownSipIdentity( user, realm, i->uri() )  // should be NameAddr?
                     //       {
                     //          sipMessage->header(h_PAssertedIdentities).push_back( i->uri() );
                     //       }
                     //       else
                     //       {
                     //          sipMessage->header(h_PAssertedIdentities).push_back(getDefaultIdentity(user, realm));
                     //       }
                     //    }
                     //    else if ((i->uri().scheme() == Symbols::TEL))
                     //    {
                     //       if (haveTel)
                     //       {
                     //          continue;  // skip all but the first tel: URL
                     //       }
                     //       haveTel = true;
                     //
                     //       if (knownTelIdentity( user, realm, i->uri() ))
                     //       {
                     //          sipMessage->header(h_PAssertedIdentities).push_back( i->uri() );
                     //       }
                     //    }
                     // }

                     // We currently don't do anything special with the P-Peferred-Identity hint - just
                     // add default identity
                     sipMessage->header(h_PAssertedIdentities).push_back(getDefaultIdentity(user, realm, sipMessage->header(h_From)));

                     // Remove the P-Preferered-Identity header
                     sipMessage->remove(h_PPreferredIdentities);
                  }
                  else
                  {
                     if (!sipMessage->exists(h_PAssertedIdentities))
                     {
                        sipMessage->header(h_PAssertedIdentities).push_back(getDefaultIdentity(user, realm, sipMessage->header(h_From)));
                     }
                     // else  TODO
                     //  - should implement guidlines in RFC5876 4.5 - whereby the proxy should remove 
                     //        ignored URI's (ie. a 2nd SIP, SIPS or TEL URI, unknown scheme)
                  }
               }            
            
#if defined(USE_SSL)
               if(!mNoIdentityHeaders)
               {
                  static Data http("http://" + mHttpHostname + ":" + Data(mHttpPort) + "/cert?domain=");
                  // .bwc. Leave pre-existing Identity headers alone.
                  if(!sipMessage->exists(h_Identity))
                  {
                     sipMessage->header(h_Identity).value() = Data::Empty;  // This is a signal to have the TransportSelector fill in the identity header
                     if(sipMessage->exists(h_IdentityInfo))
                     {
                        InfoLog(<<"Somebody sent us a"
                              " request with an Identity-Info, but no Identity"
                              " header. Removing it.");
                        if(!sipMessage->header(h_IdentityInfo).isWellFormed())
                        {
                           InfoLog(<<"...and this "
                              "Identity-Info header was malformed!");
                        }

                        sipMessage->remove(h_IdentityInfo);
                     }
                     
                     sipMessage->header(h_IdentityInfo).uri() = http + realm;
                     InfoLog (<< "Identity-Info=" << sipMessage->header(h_IdentityInfo).uri());
                  }
               }
#endif
            }
            else
            {
               // !rwm! The user is trying to forge a request.  Respond with a 403
               InfoLog (<< "User: " << user << " at realm: " << realm << 
                           " trying to forge request from: " << sipMessage->header(h_From).uri());
               rc.sendResponse(*auto_ptr<SipMessage>
                               (Helper::makeResponse(*sipMessage, 403)));
               return SkipAllChains;               
            }
            
            return Continue;

         case Helper::Expired:
            InfoLog (<< "Authentication expired for " << user);
            challengeRequest(rc, true);
            return SkipAllChains;

         case Helper::BadlyFormed:
            InfoLog (<< "Authentication nonce badly formed for " << user);
            if(mRejectBadNonces)
            {
               rc.sendResponse(*auto_ptr<SipMessage>
                            (Helper::makeResponse(*sipMessage, 403, "Where on earth did you get that nonce?")));
            }
            else
            {
               challengeRequest(rc, true);
            }
            return SkipAllChains;
      }
   }

   return Continue;
}
repro::Processor::processor_action_t DigestAuthenticator::requestUserAuthInfo ( repro::RequestContext rc,
resip::Data realm 
) [private]

Definition at line 344 of file DigestAuthenticator.cxx.

{
   Message *message = rc.getCurrentEvent();
   SipMessage *sipMessage = dynamic_cast<SipMessage*>(message);
   assert(sipMessage);


   // Extract the user from the appropriate Proxy-Authorization header
   Auths &authorizationHeaders = sipMessage->header(h_ProxyAuthorizations); 
   Auths::iterator i;
   Data user;

   for (i = authorizationHeaders.begin();
        i != authorizationHeaders.end(); i++)
   {
      if (    i->exists(p_realm) && 
              i->param(p_realm) == realm
              &&  i->exists(p_username))
      {
         user = i->param(p_username);

         InfoLog (<< "Request user auth info for "  << user
                  << " at realm " << realm);
         break;
      }
   }

   if (!user.empty())
   {
      UserInfoMessage* async = new UserInfoMessage(*this, rc.getTransactionId(), &(rc.getProxy()));
      async->user()=user;
      async->realm()=realm;
      if(sipMessage->header(h_From).isWellFormed())
      {
         async->domain()=sipMessage->header(h_From).uri().host();
      }
      else
      {
         async->domain()=realm;
      }
      std::auto_ptr<ApplicationMessage> app(async);
      mAuthRequestDispatcher->post(app);
      return WaitingForEvent;
   }
   else
   {
      challengeRequest(rc, false);
      return SkipAllChains;
   }
}

Member Data Documentation

Definition at line 31 of file DigestAuthenticator.hxx.

Definition at line 33 of file DigestAuthenticator.hxx.

Definition at line 34 of file DigestAuthenticator.hxx.

Definition at line 32 of file DigestAuthenticator.hxx.

Definition at line 36 of file DigestAuthenticator.hxx.

Definition at line 35 of file DigestAuthenticator.hxx.


The documentation for this class was generated from the following files: