reSIProcate/stack  9694
Public Types | Public Member Functions | Static Public Member Functions | Private Member Functions | Private Attributes
resip::TlsConnection Class Reference

#include <TlsConnection.hxx>

Inheritance diagram for resip::TlsConnection:
Inheritance graph
[legend]
Collaboration diagram for resip::TlsConnection:
Collaboration graph
[legend]

List of all members.

Public Types

enum  TlsState { Initial, Broken, Handshaking, Up }
typedef enum
resip::TlsConnection::TlsState 
TlsState

Public Member Functions

 RESIP_HeapCount (TlsConnection)
 TlsConnection (Transport *transport, const Tuple &who, Socket fd, Security *security, bool server, Data domain, SecurityTypes::SSLType sslType, Compression &compression)
virtual ~TlsConnection ()
int read (char *buf, const int count)
 pure virtual, but need concrete Connection for book-ends of lists
int write (const char *buf, const int count)
 pure virtual, but need concrete Connection for book-ends of lists
virtual bool hasDataToRead ()
 always true -- always add to fdset as read ready
virtual bool isGood ()
 has valid connection
virtual bool isWritable ()
virtual bool transportWrite ()
void getPeerNames (std::list< Data > &peerNames) const

Static Public Member Functions

static const char * fromState (TlsState)

Private Member Functions

 TlsConnection ()
 No default c'tor.
void computePeerName ()
Data getPeerNamesData () const
TlsState checkState ()

Private Attributes

bool mServer
SecuritymSecurity
SecurityTypes::SSLType mSslType
Data mDomain
TlsState mTlsState
bool mHandShakeWantsRead
SSL * mSsl
BIO * mBio
std::list< BaseSecurity::PeerNamemPeerNames

Detailed Description

Definition at line 32 of file TlsConnection.hxx.


Member Typedef Documentation


Member Enumeration Documentation

Enumerator:
Initial 
Broken 
Handshaking 
Up 

Definition at line 54 of file TlsConnection.hxx.


Constructor & Destructor Documentation

TlsConnection::TlsConnection ( Transport transport,
const Tuple who,
Socket  fd,
Security security,
bool  server,
Data  domain,
SecurityTypes::SSLType  sslType,
Compression compression 
)

Definition at line 28 of file TlsConnection.cxx.

References DebugLog, resip::Data::empty(), ErrLog, resip::TlsTransport::getClientVerificationMode(), resip::TlsTransport::getCtx(), InfoLog, Initial, resip::SecurityTypes::Mandatory, mBio, mDomain, mHandShakeWantsRead, mSecurity, mServer, mSsl, mTlsState, resip::SecurityTypes::None, resip::SecurityTypes::Optional, and resip::ConnectionBase::transport().

                                                        :
   Connection(transport,tuple, fd, compression),
   mServer(server),
   mSecurity(security),
   mSslType( sslType ),
   mDomain(domain)
{
#if defined(USE_SSL)
   InfoLog (<< "Creating TLS connection for domain " 
            << mDomain 
            << " " << tuple 
            << " on " << fd);

   mSsl = NULL;
   mBio= NULL;
  
   if (mServer)
   {
      DebugLog( << "Trying to form TLS connection - acting as server" );
      if ( mDomain.empty() )
      {
         ErrLog(<< "Tranport was not created with a server domain so can not act as server" ); 
         throw Security::Exception("Trying to act as server but no domain specified",
                                   __FILE__,__LINE__);
      }
   }
   else
   {
      DebugLog( << "Trying to form TLS connection - acting as client" );
   }
   assert( mSecurity );

   TlsTransport *t = dynamic_cast<TlsTransport*>(transport);
   assert(t);

   SSL_CTX* ctx=t->getCtx();
   assert(ctx);
   
   mSsl = SSL_new(ctx);
   assert(mSsl);

   assert( mSecurity );

   if(mServer)
   {
      // clear SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE set in SSL_CTX if we are a server
      int verify_mode;
      switch(t->getClientVerificationMode())
      {
      case SecurityTypes::None:
         verify_mode = SSL_VERIFY_NONE;
         DebugLog(<< "Not expecting client certificate" );
         break;
      case SecurityTypes::Optional:
         verify_mode = SSL_VERIFY_PEER;
         DebugLog(<< "Optional client certificate mode" );
         break;
      case SecurityTypes::Mandatory:
         verify_mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
         DebugLog(<< "Mandatory client certificate mode" );
         break;
      default:
         assert( 0 );
      }
      SSL_set_verify(mSsl, verify_mode, 0);
   }

   mBio = BIO_new_socket((int)fd,0/*close flag*/);
   assert( mBio );
   
   SSL_set_bio( mSsl, mBio, mBio );

   mTlsState = Initial;
   mHandShakeWantsRead = false;

#endif // USE_SSL   
}

Here is the call graph for this function:

TlsConnection::~TlsConnection ( ) [virtual]

Definition at line 109 of file TlsConnection.cxx.

References mSsl.

{
#if defined(USE_SSL)
   SSL_shutdown(mSsl);
   SSL_free(mSsl);
#endif // USE_SSL   
}
resip::TlsConnection::TlsConnection ( ) [private]

No default c'tor.


Member Function Documentation

TlsConnection::TlsState TlsConnection::checkState ( ) [private]

Definition at line 132 of file TlsConnection.cxx.

References Broken, resip::TransportFailure::CertNameMismatch, resip::TransportFailure::CertValidationFailure, computePeerName(), resip::Connection::ensureWritable(), ErrLog, resip::Transport::error(), resip::getErrno(), getPeerNamesData(), Handshaking, InfoLog, resip::BaseSecurity::matchHostName(), mBio, resip::ConnectionBase::mFailureReason, mHandShakeWantsRead, resip::ConnectionBase::mOutstandingSends, mPeerNames, mServer, mSsl, mTlsState, StackLog, Up, and resip::ConnectionBase::who().

Referenced by hasDataToRead(), read(), transportWrite(), and write().

{
#if defined(USE_SSL)
   //DebugLog(<<"state is " << fromTlsState(mTlsState));

   if (mTlsState == Up || mTlsState == Broken)
   {
      return mTlsState;
   }
   
   int ok=0;
   
   ERR_clear_error();
   
   if (mTlsState != Handshaking)
   {
      if (mServer)
      {
         InfoLog( << "TLS handshake starting (Server mode)" );
         SSL_set_accept_state(mSsl);
         mTlsState = Handshaking;
      }
      else
      {
         InfoLog( << "TLS handshake starting (client mode)" );
         SSL_set_connect_state(mSsl);
         mTlsState = Handshaking;
      }

      InfoLog( << "TLS connected" ); 
      mTlsState = Handshaking;
   }

   mHandShakeWantsRead = false;
   ok = SSL_do_handshake(mSsl);
      
   if ( ok <= 0 )
   {
      int err = SSL_get_error(mSsl,ok);
         
      switch (err)
      {
         case SSL_ERROR_WANT_READ:
            StackLog( << "TLS handshake want read" );
            mHandShakeWantsRead = true;

            return mTlsState;
         case SSL_ERROR_WANT_WRITE:
            StackLog( << "TLS handshake want write" );
            ensureWritable();
            return mTlsState;

         case SSL_ERROR_ZERO_RETURN:
            StackLog( << "TLS connection closed cleanly");
            return mTlsState;

         case SSL_ERROR_WANT_CONNECT:
            StackLog( << "BIO not connected, try later");
            return mTlsState;

#if  ( OPENSSL_VERSION_NUMBER >= 0x0090702fL )
         case SSL_ERROR_WANT_ACCEPT:
            StackLog( << "TLS connection want accept" );
            return mTlsState;
#endif

         case SSL_ERROR_WANT_X509_LOOKUP:
            StackLog( << "Try later");
            return mTlsState;
         default:
            if(err == SSL_ERROR_SYSCALL)
            {
               int e = getErrno();
               switch(e)
               {
                  case EINTR:
                  case EAGAIN:
                     StackLog( << "try later");
                     return mTlsState;
               }
               ErrLog( << "socket error " << e);
               Transport::error(e);
            }
            else if (err == SSL_ERROR_SSL)
            {
               mFailureReason = TransportFailure::CertValidationFailure;
            }
            ErrLog( << "TLS handshake failed ");
            while (true)
            {
               const char* file;
               int line;

               unsigned long code = ERR_get_error_line(&file,&line);
               if ( code == 0 )
               {
                  break;
               }

               char buf[256];
               ERR_error_string_n(code,buf,sizeof(buf));
               ErrLog( << buf  );
               ErrLog( << "Error code = "
                        << code << " file=" << file << " line=" << line );
            }
            mBio = NULL;
            mTlsState = Broken;
            return mTlsState;
      }
   }
   else // ok > 1
   {
      InfoLog( << "TLS connected" );
   }

   // force peer name to get checked and perhaps cert loaded
   computePeerName();

   //post-connection verification: check that certificate name matches domain name
   if (!mServer)
   {
      bool matches = false;
      for(std::list<BaseSecurity::PeerName>::iterator it = mPeerNames.begin(); it != mPeerNames.end(); it++)
      {
         if(BaseSecurity::matchHostName(it->mName, who().getTargetDomain()))
         {
             matches=true;
             break;
         }
      }
      if(!matches)
      {
         mTlsState = Broken;
         mBio = NULL;
         ErrLog (<< "Certificate name mismatch: trying to connect to <" 
                 << who().getTargetDomain()
                 << "> remote cert domain(s) are <" 
                 << getPeerNamesData() << ">" );
         mFailureReason = TransportFailure::CertNameMismatch;         
         return mTlsState;
      }
   }

   InfoLog( << "TLS handshake done for peer " << getPeerNamesData()); 
   mTlsState = Up;
   if (!mOutstandingSends.empty())
   {
      ensureWritable();
   }
#endif // USE_SSL   
   return mTlsState;
}

Here is the call graph for this function:

void TlsConnection::computePeerName ( ) [private]

Definition at line 568 of file TlsConnection.cxx.

References resip::BaseSecurity::addDomainCertDER(), DebugLog, ErrLog, resip::BaseSecurity::getCertNames(), resip::BaseSecurity::hasDomainCert(), InfoLog, resip::TlsTransport::isUseEmailAsSIP(), len, mBio, mPeerNames, mSecurity, mServer, mSsl, and resip::ConnectionBase::mTransport.

Referenced by checkState().

{
#if defined(USE_SSL)
   Data commonName;

   assert(mSsl);

   if (!mBio)
   {
      ErrLog( << "bad bio" );
      return;
   }

   // print session infor       
   const SSL_CIPHER *ciph;
   ciph=SSL_get_current_cipher(mSsl);
   InfoLog( << "TLS sessions set up with " 
            <<  SSL_get_version(mSsl) << " "
            <<  SSL_CIPHER_get_version(ciph) << " "
            <<  SSL_CIPHER_get_name(ciph) << " " );

   // get the certificate if other side has one 
   X509* cert = SSL_get_peer_certificate(mSsl);
   if ( !cert )
   {
      DebugLog(<< "No peer certificate in TLS connection" );
      return;
   }

   // check that this certificate is valid 
   if (X509_V_OK != SSL_get_verify_result(mSsl))
   {
      DebugLog(<< "Peer certificate in TLS connection is not valid" );
      X509_free(cert); cert=NULL;
      return;
   }

   TlsTransport *t = dynamic_cast<TlsTransport*>(mTransport);
   assert(t);

   mPeerNames.clear();
   BaseSecurity::getCertNames(cert, mPeerNames,
      t->isUseEmailAsSIP());
   if(mPeerNames.empty())
   {
      ErrLog(<< "Invalid certificate: no subjectAltName/CommonName found");
      return;
   }

   if(!mServer)
   {
      // add the certificate to the Security store
      unsigned char* buf = NULL;
      int len = i2d_X509( cert, &buf );
      Data derCert( buf, len );
      for(std::list<BaseSecurity::PeerName>::iterator it = mPeerNames.begin(); it != mPeerNames.end(); it++)
      {
         if ( !mSecurity->hasDomainCert( it->mName ) )
         {
            mSecurity->addDomainCertDER(it->mName,derCert);
         }
      }
      OPENSSL_free(buf); buf=NULL;
   }

   X509_free(cert); cert=NULL;
#endif // USE_SSL
}

Here is the call graph for this function:

const char * TlsConnection::fromState ( TlsConnection::TlsState  s) [static]

Definition at line 119 of file TlsConnection.cxx.

References Broken, Handshaking, Initial, and Up.

Referenced by isWritable(), and transportWrite().

{
   switch(s)
   {
      case Initial: return "Initial"; break;
      case Handshaking: return "Handshaking"; break;
      case Broken: return "Broken"; break;
      case Up: return "Up"; break;
   }
   return "????";
}
void TlsConnection::getPeerNames ( std::list< Data > &  peerNames) const

Definition at line 540 of file TlsConnection.cxx.

References mPeerNames.

Referenced by resip::ConnectionBase::preparseNewBytes().

{
   for(std::list<BaseSecurity::PeerName>::const_iterator it = mPeerNames.begin(); it != mPeerNames.end(); it++)
{
      peerNames.push_back(it->mName);
   }
}
Data TlsConnection::getPeerNamesData ( ) const [private]

Definition at line 549 of file TlsConnection.cxx.

References mPeerNames.

Referenced by checkState().

{
   Data peerNamesString;
   for(std::list<BaseSecurity::PeerName>::const_iterator it = mPeerNames.begin(); it != mPeerNames.end(); it++)
   {
      if(it == mPeerNames.begin())
      {
         peerNamesString += it->mName;
      }
      else
      {
         peerNamesString += ", " + it->mName;
      }
   }
   return peerNamesString;
}
bool TlsConnection::hasDataToRead ( ) [virtual]

always true -- always add to fdset as read ready

Reimplemented from resip::Connection.

Definition at line 479 of file TlsConnection.cxx.

References checkState(), Initial, mSsl, mTlsState, and Up.

{
#if defined(USE_SSL)
   //hack (for now)
   if(mTlsState == Initial)
      return false;

   if (checkState() != Up)
   {
      return false;
   }

   int p = SSL_pending(mSsl);
   //DebugLog(<<"hasDataToRead(): " <<p);
   return (p>0);
#else // USE_SSL
   return false;
#endif 
}

Here is the call graph for this function:

bool TlsConnection::isGood ( ) [virtual]

has valid connection

Reimplemented from resip::Connection.

Definition at line 501 of file TlsConnection.cxx.

References mBio, and mSsl.

Referenced by isWritable(), and read().

{
#if defined(USE_SSL)
   if ( mBio == 0 )
   {
      return false;
   }

   int mode = SSL_get_shutdown(mSsl);
   if ( mode != 0 ) 
   {
      return false;
   }

#endif       
   return true;
}
bool TlsConnection::isWritable ( ) [virtual]

Reimplemented from resip::Connection.

Definition at line 520 of file TlsConnection.cxx.

References DebugLog, fromState(), Handshaking, Initial, isGood(), mHandShakeWantsRead, mTlsState, and Up.

{
#if defined(USE_SSL)
   switch(mTlsState)
   {
      case Handshaking:
         return mHandShakeWantsRead ? false : true;
      case Initial:
      case Up:
         return isGood();
      default:
         return false;
   }
   //dragos -- to remove
   DebugLog( << "Current state: " << fromState(mTlsState));
   //end dragos
#endif 
   return false;
}

Here is the call graph for this function:

int TlsConnection::read ( char *  ,
const int   
) [virtual]

pure virtual, but need concrete Connection for book-ends of lists

Reimplemented from resip::Connection.

Definition at line 287 of file TlsConnection.cxx.

References resip::Data::Borrow, Broken, checkState(), DebugLog, ErrLog, resip::ConnectionBase::getWriteBufferForExtraBytes(), isGood(), mBio, mSsl, StackLog, and Up.

Referenced by main().

{
#if defined(USE_SSL)
   assert( mSsl ); 
   assert( buf );

   switch(checkState())
   {
      case Broken:
         return -1;
         break;
      case Up:
         break;
      default:
         return 0;
         break;
   }

   if (!mBio)
   {
      DebugLog( << "Got TLS read bad bio  " );
      return 0;
   }
      
   if ( !isGood() )
   {
      return -1;
   }

   int bytesRead = SSL_read(mSsl,buf,count);
   StackLog(<< "SSL_read returned " << bytesRead << " bytes [" << Data(Data::Borrow, buf, (bytesRead > 0)?(bytesRead):(0)) << "]");

   int bytesPending = SSL_pending(mSsl);

   if ((bytesRead > 0) && (bytesPending > 0))
   {
      char* buffer = getWriteBufferForExtraBytes(bytesPending);
      if (buffer)
      {
         StackLog(<< "reading remaining buffered bytes");
         bytesPending = SSL_read(mSsl, buffer, bytesPending);
         StackLog(<< "SSL_read returned  " << bytesPending << " bytes [" << Data(Data::Borrow, buffer, (bytesPending > 0)?(bytesPending):(0)) << "]");
         
         if (bytesPending > 0)
         {
            bytesRead += bytesPending;
         }
         else
         {
            bytesRead = bytesPending;
         }
      }
      else
      {
         assert(0);
      }
   }

   if (bytesRead <= 0)
   {
      int err = SSL_get_error(mSsl,bytesRead);
      switch (err)
      {
         case SSL_ERROR_WANT_READ:
         case SSL_ERROR_WANT_WRITE:
         case SSL_ERROR_NONE:
         {
            StackLog( << "Got TLS read got condition of " << err  );
            return 0;
         }
         break;
         default:
         {
            char buf[256];
            ERR_error_string_n(err,buf,sizeof(buf));
            ErrLog( << "Got TLS read ret=" << bytesRead << " error=" << err  << " " << buf  );
            return -1;
         }
         break;
      }
      assert(0);
   }
   StackLog(<<"SSL bytesRead="<<bytesRead);
   return bytesRead;
#endif // USE_SSL
   return -1;
}

Here is the call graph for this function:

resip::TlsConnection::RESIP_HeapCount ( TlsConnection  )
bool TlsConnection::transportWrite ( ) [virtual]

Reimplemented from resip::Connection.

Definition at line 376 of file TlsConnection.cxx.

References Broken, checkState(), DebugLog, fromState(), Handshaking, Initial, mHandShakeWantsRead, mTlsState, and Up.

{
   switch(mTlsState)
   {
      case Handshaking:
      case Initial:
         checkState();
         if (mTlsState == Handshaking)
         {
            DebugLog(<< "Transportwrite--Handshaking--remove from write: " << mHandShakeWantsRead);
            return mHandShakeWantsRead;
         }
         else
         {
            DebugLog(<< "Transportwrite--Handshake complete, in " << fromState(mTlsState) << " calling write");
            return false;
         }
      case Up:
      case Broken:
         DebugLog(<< "Transportwrite--" << fromState(mTlsState) << " fall through to write");
         return false;
   }
   assert(0);
   return false;
}

Here is the call graph for this function:

int TlsConnection::write ( const char *  ,
const int   
) [virtual]

pure virtual, but need concrete Connection for book-ends of lists

Reimplemented from resip::Connection.

Definition at line 403 of file TlsConnection.cxx.

References resip::Data::Borrow, Broken, checkState(), DebugLog, ErrLog, mBio, mSsl, StackLog, and Up.

Referenced by main().

{
#if defined(USE_SSL)
   assert( mSsl );
   assert( buf );
   int ret;
 
   switch(checkState())
   {
      case Broken:
         return -1;
         break;
      case Up:
         break;
      default:
         DebugLog( << "Tried to Tls write - but connection is not Up"  );
         return 0;
         break;
   }

   if (!mBio)
   {
      DebugLog( << "Got TLS write bad bio "  );
      return 0;
   }
        
   ret = SSL_write(mSsl,(const char*)buf,count);
   if (ret < 0 )
   {
      int err = SSL_get_error(mSsl,ret);
      switch (err)
      {
         case SSL_ERROR_WANT_READ:
         case SSL_ERROR_WANT_WRITE:
         case SSL_ERROR_NONE:
         {
            StackLog( << "Got TLS write got condition of " << err  );
            return 0;
         }
         break;
         default:
         {
            while (true)
            {
               const char* file;
               int line;
               
               unsigned long code = ERR_get_error_line(&file,&line);
               if ( code == 0 )
               {
                  break;
               }
               
               char buf[256];
               ERR_error_string_n(code,buf,sizeof(buf));
               ErrLog( << buf  );
               DebugLog( << "Error code = " << code << " file=" << file << " line=" << line );
            }
            ErrLog( << "Got TLS write error=" << err << " ret=" << ret  );
            return -1;
         }
         break;
      }
   }

   Data monkey(Data::Borrow, buf, count);

   StackLog( << "Did TLS write " << ret << " " << count << " " << "[[" << monkey << "]]" );

   return ret;
#endif // USE_SSL
   return -1;
}

Here is the call graph for this function:


Member Data Documentation

Definition at line 73 of file TlsConnection.hxx.

Referenced by checkState(), computePeerName(), isGood(), read(), TlsConnection(), and write().

Definition at line 67 of file TlsConnection.hxx.

Referenced by TlsConnection().

Definition at line 70 of file TlsConnection.hxx.

Referenced by checkState(), isWritable(), TlsConnection(), and transportWrite().

Definition at line 74 of file TlsConnection.hxx.

Referenced by checkState(), computePeerName(), getPeerNames(), and getPeerNamesData().

Definition at line 65 of file TlsConnection.hxx.

Referenced by computePeerName(), and TlsConnection().

Definition at line 64 of file TlsConnection.hxx.

Referenced by checkState(), computePeerName(), and TlsConnection().

Definition at line 66 of file TlsConnection.hxx.


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