mirror of
https://github.com/resiprocate/resiprocate.git
synced 2026-01-12 00:05:02 +08:00
Allow applications to get TLS connection/handshake errors
- Expand ConnectionTerminated event message to include a failureString with additional diagnostic information - Add static callback so that application can see SSL verify callback errors (BaseSecurity::SSLVerifyErrorFuncPtr) Other Related Changes: - reduce number of log statements during SSL/TLS errors - but maintain same level of information - cleaned up Security.cxx verifyCallback - cleaned up TlsConnection::handleOpenSSLErrorQueue logging - cleaned up code formatting in TlsConnection - refactored Transport::error, so that error string can be extracted without logging - found and fixed 2 memory leaks from SSL_get_peer_certificate calls (DtlsSocket and TlsConnection - missing X509_free calls)
This commit is contained in:
@@ -2,4 +2,7 @@
|
||||
indent_style=space
|
||||
|
||||
[*.{hxx,cxx,hpp,cpp,h,c}]
|
||||
indent_size=3
|
||||
indent_size=3
|
||||
cpp_indent_case_labels = true
|
||||
cpp_indent_case_contents = true
|
||||
cpp_indent_case_contents_when_block = false
|
||||
@@ -211,7 +211,7 @@ DtlsSocket::getRemoteFingerprint(char *fprint)
|
||||
return false;
|
||||
|
||||
computeFingerprint(x,fprint);
|
||||
|
||||
X509_free(x);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,13 @@ static unsigned int FailedSubscriptionRetryTime = 60;
|
||||
|
||||
namespace resip
|
||||
{
|
||||
#if defined (USE_SSL)
|
||||
void SSLVerifyErrorCallback(X509* cert, int errorCode, int errorDepth, const char* errorString)
|
||||
{
|
||||
InfoLog(<< "SSLVerifyErrorCallback: errorCode=" << errorCode << ", errorDepth=" << errorDepth << ", errorString=" << errorString);
|
||||
}
|
||||
#endif
|
||||
|
||||
class ClientAppDialogSetFactory : public AppDialogSetFactory
|
||||
{
|
||||
public:
|
||||
@@ -143,6 +150,7 @@ BasicClientUserAgent::BasicClientUserAgent(int argc, char** argv) :
|
||||
addTransport(TCP, mTcpPort);
|
||||
#if defined(USE_SSL)
|
||||
addTransport(TLS, mTlsPort);
|
||||
BaseSecurity::SSLVerifyErrorFuncPtr = &SSLVerifyErrorCallback;
|
||||
#endif
|
||||
#if defined(USE_DTLS)
|
||||
addTransport(DTLS, mDtlsPort);
|
||||
@@ -584,6 +592,7 @@ int
|
||||
BasicClientUserAgent::onRequestRetry(ClientRegistrationHandle h, int retryMinimum, const SipMessage& msg)
|
||||
{
|
||||
mRegHandle = h;
|
||||
mStack->logDnsCache();
|
||||
if(mShuttingdown)
|
||||
{
|
||||
return -1;
|
||||
|
||||
@@ -496,8 +496,13 @@ Connection::checkConnectionTimedout()
|
||||
if (errNum == ETIMEDOUT || errNum == EHOSTUNREACH ||
|
||||
errNum == ECONNREFUSED || errNum == ECONNABORTED)
|
||||
{
|
||||
InfoLog(<< "Exception on socket " << mWho.mFlowKey << " code: " << errNum << "; closing connection");
|
||||
setFailureReason(TransportFailure::ConnectionException, errNum);
|
||||
Data failureString;
|
||||
{
|
||||
DataStream ds(failureString);
|
||||
ds << "Exception on socket " << mWho.mFlowKey << ", errNum=" << errNum << ", err=" << strerror(errNum) << "; closing connection";
|
||||
}
|
||||
InfoLog(<< failureString);
|
||||
setFailureReason(TransportFailure::ConnectionException, errNum, failureString);
|
||||
delete this;
|
||||
return true;
|
||||
}
|
||||
@@ -529,8 +534,14 @@ Connection::processPollEvent(FdPollEventMask mask)
|
||||
{
|
||||
Socket fd = getSocket();
|
||||
int errNum = getSocketError(fd);
|
||||
InfoLog(<< "Exception on socket " << fd << " code: " << errNum << "; closing connection");
|
||||
setFailureReason(TransportFailure::ConnectionException, errNum);
|
||||
Data failureString;
|
||||
{
|
||||
DataStream ds(failureString);
|
||||
ds << "Exception on socket " << fd << ", errNum=" << errNum << ", err=" << strerror(errNum) << "; closing connection";
|
||||
}
|
||||
|
||||
InfoLog(<< failureString);
|
||||
setFailureReason(TransportFailure::ConnectionException, errNum, failureString);
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ ConnectionBase::~ConnectionBase()
|
||||
{
|
||||
if(mTransport)
|
||||
{
|
||||
mTransport->flowTerminated(mWho, mFailureReason, mFailureSubCode);
|
||||
mTransport->flowTerminated(mWho, mFailureReason, mFailureSubCode, mFailureString);
|
||||
}
|
||||
|
||||
while (!mOutstandingSends.empty())
|
||||
@@ -118,12 +118,13 @@ ConnectionBase::~ConnectionBase()
|
||||
}
|
||||
|
||||
void
|
||||
ConnectionBase::setFailureReason(TransportFailure::FailureReason failReason, int subCode)
|
||||
ConnectionBase::setFailureReason(TransportFailure::FailureReason failReason, int subCode, const Data& failureString)
|
||||
{
|
||||
if ( failReason > mFailureReason )
|
||||
if (failReason > mFailureReason)
|
||||
{
|
||||
mFailureReason = failReason;
|
||||
mFailureSubCode = subCode;
|
||||
mFailureString = failureString;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ class ConnectionBase
|
||||
Data::size_type mSendPos;
|
||||
std::list<SendData*> mOutstandingSends; // !jacob! intrusive queue?
|
||||
|
||||
void setFailureReason(TransportFailure::FailureReason failReason, int subCode);
|
||||
void setFailureReason(TransportFailure::FailureReason failReason, int subCode, const Data& failureString);
|
||||
|
||||
virtual ~ConnectionBase();
|
||||
// no value semantics
|
||||
@@ -108,8 +108,9 @@ class ConnectionBase
|
||||
virtual void onSingleCRLF(){}
|
||||
Transport* mTransport;
|
||||
Tuple mWho;
|
||||
TransportFailure::FailureReason mFailureReason;
|
||||
TransportFailure::FailureReason mFailureReason;
|
||||
int mFailureSubCode;
|
||||
Data mFailureString;
|
||||
Compression &mCompression;
|
||||
osc::Stack *mSigcompStack;
|
||||
osc::TcpStream *mSigcompFramer;
|
||||
|
||||
@@ -14,28 +14,33 @@ class ConnectionTerminated : public TransactionMessage
|
||||
public:
|
||||
RESIP_HeapCount(ConnectionTerminated);
|
||||
|
||||
ConnectionTerminated(const Tuple& flow, TransportFailure::FailureReason failureReason, int failureSubCode) :
|
||||
mFlow(flow), mFailureReason(failureReason), mFailureSubCode(failureSubCode)
|
||||
ConnectionTerminated(const Tuple& flow, TransportFailure::FailureReason failureReason, int failureSubCode, const Data& failureString) :
|
||||
mFlow(flow), mFailureReason(failureReason), mFailureSubCode(failureSubCode), mFailureString(failureString)
|
||||
{
|
||||
}
|
||||
virtual const Data& getTransactionId() const { resip_assert(0); return Data::Empty; }
|
||||
virtual bool isClientTransaction() const { resip_assert(0); return false; }
|
||||
virtual Message* clone() const { return new ConnectionTerminated(mFlow, mFailureReason, mFailureSubCode); }
|
||||
virtual Message* clone() const { return new ConnectionTerminated(mFlow, mFailureReason, mFailureSubCode, mFailureString); }
|
||||
virtual EncodeStream& encode(EncodeStream& strm) const { return encodeBrief(strm); }
|
||||
virtual EncodeStream& encodeBrief(EncodeStream& str) const
|
||||
{
|
||||
return str << "ConnectionTerminated: flow=" << mFlow << ", failureReason=" << TransportFailure::failureReasonToString(mFailureReason) << ", failureSubCode=" << mFailureSubCode;
|
||||
return str << "ConnectionTerminated: flow=" << mFlow
|
||||
<< ", failureReason=" << TransportFailure::failureReasonToString(mFailureReason)
|
||||
<< ", failureSubCode=" << mFailureSubCode
|
||||
<< ", failureString=" << mFailureString;
|
||||
}
|
||||
|
||||
FlowKey getFlowKey() const { return mFlow.mFlowKey; }
|
||||
const Tuple& getFlow() const { return mFlow; }
|
||||
TransportFailure::FailureReason getFailureReason() const { return mFailureReason; }
|
||||
int getFailureSubCode() const { return mFailureSubCode; }
|
||||
const Data& getFailureString() const { return mFailureString; }
|
||||
|
||||
private:
|
||||
const Tuple mFlow;
|
||||
TransportFailure::FailureReason mFailureReason;
|
||||
int mFailureSubCode;
|
||||
const Data mFailureString;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -62,9 +62,13 @@ TcpConnection::read( char* buf, int count )
|
||||
break;
|
||||
}
|
||||
|
||||
InfoLog (<< "Failed read on " << getSocket() << " " << strerror(e));
|
||||
Transport::error(e);
|
||||
setFailureReason(TransportFailure::ConnectionException, e+2000);
|
||||
Data failureString;
|
||||
{
|
||||
DataStream ds(failureString);
|
||||
ds << "Failed read on " << getSocket() << ", errNum=" << e << ", err=" << Transport::errorToString(e);
|
||||
}
|
||||
InfoLog (<< failureString);
|
||||
setFailureReason(TransportFailure::ConnectionException, e+2000, failureString);
|
||||
return -1;
|
||||
}
|
||||
else if (bytesRead == 0)
|
||||
|
||||
@@ -124,131 +124,141 @@ Transport::onReload()
|
||||
void
|
||||
Transport::error(int e)
|
||||
{
|
||||
if (e != EAGAIN)
|
||||
{
|
||||
InfoLog(<< errorToString(e));
|
||||
}
|
||||
}
|
||||
|
||||
Data
|
||||
Transport::errorToString(int e)
|
||||
{
|
||||
Data errorString;
|
||||
DataStream ds(errorString);
|
||||
switch (e)
|
||||
{
|
||||
case EAGAIN:
|
||||
//InfoLog (<< "No data ready to read" << strerror(e));
|
||||
ds << "No data ready to read: " << strerror(e);
|
||||
break;
|
||||
case EINTR:
|
||||
InfoLog (<< "The call was interrupted by a signal before any data was read : " << strerror(e));
|
||||
ds << "The call was interrupted by a signal before any data was read: " << strerror(e);
|
||||
break;
|
||||
case EIO:
|
||||
InfoLog (<< "I/O error : " << strerror(e));
|
||||
ds << "I/O error: " << strerror(e);
|
||||
break;
|
||||
case EBADF:
|
||||
InfoLog (<< "fd is not a valid file descriptor or is not open for reading : " << strerror(e));
|
||||
ds << "fd is not a valid file descriptor or is not open for reading: " << strerror(e);
|
||||
break;
|
||||
case EINVAL:
|
||||
InfoLog (<< "fd is attached to an object which is unsuitable for reading : " << strerror(e));
|
||||
ds << "fd is attached to an object which is unsuitable for reading: " << strerror(e);
|
||||
break;
|
||||
case EFAULT:
|
||||
InfoLog (<< "buf is outside your accessible address space : " << strerror(e));
|
||||
ds << "buf is outside your accessible address space: " << strerror(e);
|
||||
break;
|
||||
|
||||
#if defined(WIN32)
|
||||
case WSAENETDOWN:
|
||||
InfoLog (<<" The network subsystem has failed. ");
|
||||
ds << "The network subsystem has failed.";
|
||||
break;
|
||||
case WSAEFAULT:
|
||||
InfoLog (<<" The buf or from parameters are not part of the user address space, "
|
||||
"or the fromlen parameter is too small to accommodate the peer address. ");
|
||||
ds << "The buf or from parameters are not part of the user address space, "
|
||||
"or the fromlen parameter is too small to accommodate the peer address.";
|
||||
break;
|
||||
case WSAEINTR:
|
||||
InfoLog (<<" The (blocking) call was canceled through WSACancelBlockingCall. ");
|
||||
ds << "The (blocking) call was canceled through WSACancelBlockingCall.";
|
||||
break;
|
||||
case WSAEINPROGRESS:
|
||||
InfoLog (<<" A blocking Windows Sockets 1.1 call is in progress, or the "
|
||||
"service provider is still processing a callback function. ");
|
||||
ds << "A blocking Windows Sockets 1.1 call is in progress, or the "
|
||||
"service provider is still processing a callback function.";
|
||||
break;
|
||||
case WSAEINVAL:
|
||||
InfoLog (<<" The socket has not been bound with bind, or an unknown flag was specified, "
|
||||
"or MSG_OOB was specified for a socket with SO_OOBINLINE enabled, "
|
||||
"or (for byte stream-style sockets only) len was zero or negative. ");
|
||||
ds << "The socket has not been bound with bind, or an unknown flag was specified, "
|
||||
"or MSG_OOB was specified for a socket with SO_OOBINLINE enabled, "
|
||||
"or (for byte stream-style sockets only) len was zero or negative.";
|
||||
break;
|
||||
case WSAEISCONN :
|
||||
InfoLog (<<"The socket is connected. This function is not permitted with a connected socket, "
|
||||
"whether the socket is connection-oriented or connectionless. ");
|
||||
ds << "The socket is connected. This function is not permitted with a connected socket, "
|
||||
"whether the socket is connection-oriented or connectionless.";
|
||||
break;
|
||||
case WSAENETRESET:
|
||||
InfoLog (<<" The connection has been broken due to the keep-alive activity "
|
||||
"detecting a failure while the operation was in progress. ");
|
||||
ds << "The connection has been broken due to the keep-alive activity "
|
||||
"detecting a failure while the operation was in progress.";
|
||||
break;
|
||||
case WSAENOTSOCK :
|
||||
InfoLog (<<"The descriptor is not a socket. ");
|
||||
case WSAENOTSOCK:
|
||||
ds << "The descriptor is not a socket.";
|
||||
break;
|
||||
case WSAEOPNOTSUPP:
|
||||
InfoLog (<<" MSG_OOB was specified, but the socket is not stream-style such as type "
|
||||
"SOCK_STREAM, OOB data is not supported in the communication domain associated with this socket, "
|
||||
"or the socket is unidirectional and supports only send operations. ");
|
||||
ds << " MSG_OOB was specified, but the socket is not stream-style such as type "
|
||||
"SOCK_STREAM, OOB data is not supported in the communication domain associated with this socket, "
|
||||
"or the socket is unidirectional and supports only send operations.";
|
||||
break;
|
||||
case WSAESHUTDOWN:
|
||||
InfoLog (<<"The socket has been shut down; it is not possible to recvfrom on a socket after "
|
||||
"shutdown has been invoked with how set to SD_RECEIVE or SD_BOTH. ");
|
||||
ds << "The socket has been shut down; it is not possible to recvfrom on a socket after "
|
||||
"shutdown has been invoked with how set to SD_RECEIVE or SD_BOTH.";
|
||||
break;
|
||||
case WSAEMSGSIZE:
|
||||
InfoLog (<<" The message was too large to fit into the specified buffer and was truncated. ");
|
||||
ds << "The message was too large to fit into the specified buffer and was truncated.";
|
||||
break;
|
||||
case WSAETIMEDOUT:
|
||||
InfoLog (<<" The connection has been dropped, because of a network failure or because the "
|
||||
"system on the other end went down without notice. ");
|
||||
ds << "The connection has been dropped, because of a network failure or because the "
|
||||
"system on the other end went down without notice.";
|
||||
break;
|
||||
case WSAECONNRESET :
|
||||
InfoLog (<<"Connection reset ");
|
||||
ds << "Connection reset.";
|
||||
break;
|
||||
|
||||
case WSAEWOULDBLOCK:
|
||||
DebugLog (<<"Would Block ");
|
||||
ds << "Would block.";
|
||||
break;
|
||||
|
||||
case WSAEHOSTUNREACH:
|
||||
InfoLog (<<"A socket operation was attempted to an unreachable host ");
|
||||
ds << "A socket operation was attempted to an unreachable host.";
|
||||
break;
|
||||
case WSANOTINITIALISED:
|
||||
InfoLog (<<"Either the application has not called WSAStartup or WSAStartup failed. "
|
||||
"The application may be accessing a socket that the current active task does not own (that is, trying to share a socket between tasks),"
|
||||
"or WSACleanup has been called too many times. ");
|
||||
ds << "Either the application has not called WSAStartup or WSAStartup failed. "
|
||||
"The application may be accessing a socket that the current active task does not own (that is, trying to share a socket between tasks),"
|
||||
"or WSACleanup has been called too many times.";
|
||||
break;
|
||||
case WSAEACCES:
|
||||
InfoLog (<<"An attempt was made to access a socket in a way forbidden by its access permissions ");
|
||||
ds << "An attempt was made to access a socket in a way forbidden by its access permissions.";
|
||||
break;
|
||||
case WSAENOBUFS:
|
||||
InfoLog (<<"An operation on a socket could not be performed because the system lacked sufficient "
|
||||
"buffer space or because a queue was full");
|
||||
ds << "An operation on a socket could not be performed because the system lacked sufficient "
|
||||
"buffer space or because a queue was full.";
|
||||
break;
|
||||
case WSAENOTCONN:
|
||||
InfoLog (<<"A request to send or receive data was disallowed because the socket is not connected "
|
||||
"and (when sending on a datagram socket using sendto) no address was supplied");
|
||||
ds << "A request to send or receive data was disallowed because the socket is not connected "
|
||||
"and (when sending on a datagram socket using sendto) no address was supplied.";
|
||||
break;
|
||||
case WSAECONNABORTED:
|
||||
InfoLog (<<"An established connection was aborted by the software in your host computer, possibly "
|
||||
"due to a data transmission time-out or protocol error");
|
||||
ds << "An established connection was aborted by the software in your host computer, possibly "
|
||||
"due to a data transmission time-out or protocol error.";
|
||||
break;
|
||||
case WSAEADDRNOTAVAIL:
|
||||
InfoLog (<<"The requested address is not valid in its context. This normally results from an attempt to "
|
||||
"bind to an address that is not valid for the local computer");
|
||||
ds << "The requested address is not valid in its context. This normally results from an attempt to "
|
||||
"bind to an address that is not valid for the local computer.";
|
||||
break;
|
||||
case WSAEAFNOSUPPORT:
|
||||
InfoLog (<<"An address incompatible with the requested protocol was used");
|
||||
ds << "An address incompatible with the requested protocol was used.";
|
||||
break;
|
||||
case WSAEDESTADDRREQ:
|
||||
InfoLog (<<"A required address was omitted from an operation on a socket");
|
||||
ds << "A required address was omitted from an operation on a socket.";
|
||||
break;
|
||||
case WSAENETUNREACH:
|
||||
InfoLog (<<"A socket operation was attempted to an unreachable network");
|
||||
ds << "A socket operation was attempted to an unreachable network.";
|
||||
break;
|
||||
|
||||
#endif
|
||||
|
||||
default:
|
||||
InfoLog (<< "Some other error (" << e << "): " << strerror(e));
|
||||
ds << "Some other error (" << e << "): " << strerror(e);
|
||||
break;
|
||||
}
|
||||
ds.flush();
|
||||
return errorString;
|
||||
}
|
||||
|
||||
void
|
||||
Transport::flowTerminated(const Tuple& flow, TransportFailure::FailureReason failureReason, int failureSubCode)
|
||||
Transport::flowTerminated(const Tuple& flow, TransportFailure::FailureReason failureReason, int failureSubCode, const Data& failureString)
|
||||
{
|
||||
mStateMachineFifo.add(new ConnectionTerminated(flow, failureReason, failureSubCode));
|
||||
mStateMachineFifo.add(new ConnectionTerminated(flow, failureReason, failureSubCode, failureString));
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -228,7 +228,7 @@ class Transport : public FdSetIOObserver
|
||||
Posts a ConnectionTerminated message to TransactionMessage
|
||||
Fifo.
|
||||
*/
|
||||
void flowTerminated(const Tuple& flow, TransportFailure::FailureReason failureReason, int failureSubCode);
|
||||
void flowTerminated(const Tuple& flow, TransportFailure::FailureReason failureReason, int failureSubCode, const Data& reasonString);
|
||||
void keepAlivePong(const Tuple& flow);
|
||||
|
||||
/**
|
||||
@@ -250,6 +250,7 @@ class Transport : public FdSetIOObserver
|
||||
@param e the socket error number
|
||||
*/
|
||||
static void error(int e);
|
||||
static Data errorToString(int e);
|
||||
|
||||
// These methods are used by the TransportSelector
|
||||
const Data& interfaceName() const { return mInterface; }
|
||||
|
||||
@@ -97,29 +97,41 @@ getAor(const Data& filename, const Security::PEMType &pemType )
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
|
||||
static int
|
||||
verifyCallback(int iInCode, X509_STORE_CTX *pInStore)
|
||||
verifyCallback(int iInCode, X509_STORE_CTX* pInStore)
|
||||
{
|
||||
char cBuf1[257];
|
||||
char cBuf2[1024];
|
||||
X509 *pErrCert;
|
||||
int iErr = 0;
|
||||
int iDepth = 0;
|
||||
pErrCert = X509_STORE_CTX_get_current_cert(pInStore);
|
||||
iErr = X509_STORE_CTX_get_error(pInStore);
|
||||
iDepth = X509_STORE_CTX_get_error_depth(pInStore);
|
||||
|
||||
if (NULL != pErrCert)
|
||||
X509_NAME_oneline(X509_get_subject_name(pErrCert),cBuf1,256);
|
||||
|
||||
snprintf(cBuf2, 1023, ", iErr='%s' depth=%d %s\n", X509_verify_cert_error_string(iErr), iDepth, cBuf1);
|
||||
if(!iInCode)
|
||||
if (!iInCode)
|
||||
{
|
||||
ErrLog(<< "Error when verifying peer's chain of certificates: " << X509_verify_cert_error_string(X509_STORE_CTX_get_error(pInStore)) << cBuf2 );
|
||||
DebugLog(<<"additional validation checks may have failed but only one is ever logged - please check peer certificate carefully");
|
||||
char subjectNameBuf[257];
|
||||
X509* cert = X509_STORE_CTX_get_current_cert(pInStore); // Doesn't appear to require X509_free call
|
||||
int errorCode = X509_STORE_CTX_get_error(pInStore);
|
||||
// This is a nonnegative integer representing where in the certificate chain the error occurred.
|
||||
// If it is zero it occurred in the end entity certificate, one if it is the certificate which
|
||||
// signed the end entity certificate and so on.
|
||||
int errorDepth = X509_STORE_CTX_get_error_depth(pInStore);
|
||||
|
||||
if (NULL != cert)
|
||||
{
|
||||
X509_NAME_oneline(X509_get_subject_name(cert), subjectNameBuf, 256);
|
||||
}
|
||||
else
|
||||
{
|
||||
subjectNameBuf[0] = '\0';
|
||||
}
|
||||
|
||||
Data errorString;
|
||||
{
|
||||
DataStream ds(errorString);
|
||||
ds << "Error when verifying peer's chain of certificates: '" << X509_verify_cert_error_string(errorCode) << "', errorDepth=" << errorDepth << ", subjectName=[" << subjectNameBuf << "]";
|
||||
}
|
||||
if (BaseSecurity::SSLVerifyErrorFuncPtr != nullptr)
|
||||
{
|
||||
(*BaseSecurity::SSLVerifyErrorFuncPtr)(cert, errorCode, errorDepth, errorString.c_str());
|
||||
}
|
||||
ErrLog(<< errorString);
|
||||
}
|
||||
|
||||
|
||||
return iInCode;
|
||||
}
|
||||
|
||||
@@ -145,6 +157,9 @@ BaseSecurity::CipherList BaseSecurity::StrongestSuite("HIGH:-COMPLEMENTOFDEFAULT
|
||||
long BaseSecurity::OpenSSLCTXSetOptions = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
|
||||
long BaseSecurity::OpenSSLCTXClearOptions = 0;
|
||||
|
||||
BaseSecurity::SSLVerifyErrorFuncPtrType BaseSecurity::SSLVerifyErrorFuncPtr = nullptr;
|
||||
|
||||
|
||||
Security::Security(const CipherList& cipherSuite, const Data& defaultPrivateKeyPassPhrase, const Data& dHParamsFilename) :
|
||||
BaseSecurity(cipherSuite, defaultPrivateKeyPassPhrase, dHParamsFilename)
|
||||
{
|
||||
|
||||
@@ -55,7 +55,7 @@ class BaseSecurity
|
||||
{
|
||||
public:
|
||||
CipherList(){}
|
||||
CipherList(const Data& cipherList) : mCipherList(cipherList) {}
|
||||
CipherList(const Data& cipherList) : mCipherList(cipherList) {}
|
||||
Data cipherList() const { return mCipherList; }
|
||||
private:
|
||||
Data mCipherList;
|
||||
@@ -67,7 +67,7 @@ class BaseSecurity
|
||||
{
|
||||
SubjectAltName,
|
||||
CommonName
|
||||
}NameType;
|
||||
} NameType;
|
||||
|
||||
struct PeerName
|
||||
{
|
||||
@@ -90,7 +90,17 @@ class BaseSecurity
|
||||
static long OpenSSLCTXSetOptions;
|
||||
static long OpenSSLCTXClearOptions;
|
||||
|
||||
BaseSecurity(const CipherList& cipherSuite = StrongestSuite, const Data& defaultPrivateKeyPassPhrase = Data::Empty, const Data& dHParamsFilename = Data::Empty);
|
||||
/*
|
||||
* Set this callback function to receive a callback whenever the OpenSSL certificate
|
||||
* verify callback reports a certificate validation error. It can be used by an application
|
||||
* to get access to the validation failure, outside of the resip logs.
|
||||
*/
|
||||
typedef void(*SSLVerifyErrorFuncPtrType)(X509* cert, int errorCode, int errorDepth, const char* errorString);
|
||||
static SSLVerifyErrorFuncPtrType SSLVerifyErrorFuncPtr;
|
||||
|
||||
BaseSecurity(const CipherList& cipherSuite = StrongestSuite,
|
||||
const Data& defaultPrivateKeyPassPhrase = Data::Empty,
|
||||
const Data& dHParamsFilename = Data::Empty);
|
||||
virtual ~BaseSecurity();
|
||||
|
||||
// used to initialize the openssl library
|
||||
|
||||
@@ -28,121 +28,114 @@ using namespace resip;
|
||||
|
||||
#define RESIPROCATE_SUBSYSTEM Subsystem::TRANSPORT
|
||||
|
||||
inline bool handleOpenSSLErrorQueue(int ret, unsigned long err, const char* op)
|
||||
inline bool
|
||||
handleOpenSSLErrorQueue(int ret, unsigned long err, const char* op)
|
||||
{
|
||||
bool hadReason = false;
|
||||
ErrLog(<< op << " error=" << err << ", ret=" << ret);
|
||||
while (true)
|
||||
{
|
||||
const char* file;
|
||||
int line;
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x30000000L
|
||||
unsigned long code = ERR_get_error_line(&file,&line);
|
||||
unsigned long code = ERR_get_error_line(&file, &line);
|
||||
#else
|
||||
unsigned long code = ERR_get_error_all(&file, &line, NULL, NULL, NULL);
|
||||
#endif
|
||||
if ( code == 0 )
|
||||
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 );
|
||||
ERR_error_string_n(code, buf, sizeof(buf));
|
||||
ErrLog(<< " " << buf << " (file=" << file << ", line=" << line << ")");
|
||||
hadReason = true;
|
||||
}
|
||||
ErrLog( << "Got TLS " << op << " error=" << err << " ret=" << ret );
|
||||
if(!hadReason)
|
||||
{
|
||||
WarningLog(<<"no reason found with ERR_get_error_line");
|
||||
}
|
||||
return hadReason;
|
||||
}
|
||||
|
||||
TlsConnection::TlsConnection( Transport* transport, const Tuple& tuple,
|
||||
Socket fd, Security* security,
|
||||
bool server, Data domain, SecurityTypes::SSLType sslType ,
|
||||
Compression &compression) :
|
||||
Connection(transport,tuple, fd, compression, server),
|
||||
TlsConnection::TlsConnection(Transport* transport, const Tuple& tuple,
|
||||
Socket fd, Security* security,
|
||||
bool server, Data domain, SecurityTypes::SSLType sslType,
|
||||
Compression& compression) :
|
||||
Connection(transport, tuple, fd, compression, server),
|
||||
mServer(server),
|
||||
mSecurity(security),
|
||||
mSslType( sslType ),
|
||||
mSslType(sslType),
|
||||
mDomain(domain)
|
||||
{
|
||||
#if defined(USE_SSL)
|
||||
InfoLog (<< "Creating TLS connection for domain "
|
||||
<< mDomain
|
||||
<< " " << tuple
|
||||
<< " on " << fd);
|
||||
InfoLog(<< "Creating TLS connection for domain " << mDomain << " " << tuple << " on " << fd);
|
||||
|
||||
mSsl = NULL;
|
||||
mBio= NULL;
|
||||
|
||||
mBio = NULL;
|
||||
|
||||
if (mServer)
|
||||
{
|
||||
DebugLog( << "Trying to form TLS connection - acting as server" );
|
||||
if ( mDomain.empty() )
|
||||
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" );
|
||||
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__);
|
||||
__FILE__, __LINE__);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugLog( << "Trying to form TLS connection - acting as client" );
|
||||
DebugLog(<< "Trying to form TLS connection - acting as client");
|
||||
}
|
||||
resip_assert( mSecurity );
|
||||
resip_assert(mSecurity);
|
||||
|
||||
TlsBaseTransport *t = dynamic_cast<TlsBaseTransport*>(transport);
|
||||
TlsBaseTransport* t = dynamic_cast<TlsBaseTransport*>(transport);
|
||||
resip_assert(t);
|
||||
|
||||
SSL_CTX* ctx=t->getCtx();
|
||||
SSL_CTX* ctx = t->getCtx();
|
||||
resip_assert(ctx);
|
||||
|
||||
|
||||
mSsl = SSL_new(ctx);
|
||||
resip_assert(mSsl);
|
||||
|
||||
resip_assert( mSecurity );
|
||||
resip_assert(mSecurity);
|
||||
|
||||
if(mServer)
|
||||
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())
|
||||
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:
|
||||
resip_assert( 0 );
|
||||
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:
|
||||
resip_assert(0);
|
||||
}
|
||||
SSL_set_verify(mSsl, verify_mode, 0);
|
||||
}
|
||||
|
||||
mBio = BIO_new_socket((int)fd,0/*close flag*/);
|
||||
if( !mBio )
|
||||
mBio = BIO_new_socket((int)fd, 0/*close flag*/);
|
||||
if (!mBio)
|
||||
{
|
||||
throw Transport::Exception("Failed to create OpenSSL BIO for socket",
|
||||
__FILE__, __LINE__);
|
||||
}
|
||||
|
||||
SSL_set_bio( mSsl, mBio, mBio );
|
||||
|
||||
SSL_set_bio(mSsl, mBio, mBio);
|
||||
|
||||
mTlsState = Initial;
|
||||
mHandShakeWantsRead = false;
|
||||
|
||||
#endif // USE_SSL
|
||||
#endif // USE_SSL
|
||||
}
|
||||
|
||||
TlsConnection::~TlsConnection()
|
||||
@@ -150,7 +143,7 @@ TlsConnection::~TlsConnection()
|
||||
#if defined(USE_SSL)
|
||||
ERR_clear_error();
|
||||
int ret = SSL_shutdown(mSsl);
|
||||
if(ret < 0)
|
||||
if (ret < 0)
|
||||
{
|
||||
int err = SSL_get_error(mSsl, ret);
|
||||
switch (err)
|
||||
@@ -158,22 +151,19 @@ TlsConnection::~TlsConnection()
|
||||
case SSL_ERROR_WANT_READ:
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
case SSL_ERROR_NONE:
|
||||
{
|
||||
// WANT_READ or WANT_WRITE can arise for bi-directional shutdown on
|
||||
// non-blocking sockets, safe to ignore
|
||||
StackLog( << "Got TLS shutdown error condition of " << err );
|
||||
}
|
||||
// WANT_READ or WANT_WRITE can arise for bi-directional shutdown on
|
||||
// non-blocking sockets, safe to ignore
|
||||
StackLog(<< "Got TLS shutdown error condition of " << err);
|
||||
break;
|
||||
|
||||
default:
|
||||
ErrLog(<<"Unexpected error in SSL_shutdown");
|
||||
handleOpenSSLErrorQueue(ret, err, "SSL_shutdown");
|
||||
}
|
||||
}
|
||||
SSL_free(mSsl);
|
||||
#endif // USE_SSL
|
||||
#endif // USE_SSL
|
||||
}
|
||||
|
||||
|
||||
const char*
|
||||
TlsConnection::fromState(TlsConnection::TlsState s)
|
||||
{
|
||||
@@ -184,6 +174,7 @@ TlsConnection::fromState(TlsConnection::TlsState s)
|
||||
case Broken: return "Broken"; break;
|
||||
case Up: return "Up"; break;
|
||||
}
|
||||
resip_assert(false);
|
||||
return "????";
|
||||
}
|
||||
|
||||
@@ -197,116 +188,128 @@ TlsConnection::checkState()
|
||||
{
|
||||
return mTlsState;
|
||||
}
|
||||
|
||||
int ok=0;
|
||||
|
||||
|
||||
int handshakeRet = 0;
|
||||
|
||||
ERR_clear_error();
|
||||
|
||||
|
||||
if (mTlsState != Handshaking)
|
||||
{
|
||||
if (mServer)
|
||||
{
|
||||
InfoLog( << "TLS handshake starting (Server mode)" );
|
||||
InfoLog(<< "TLS handshake starting (Server mode)");
|
||||
SSL_set_accept_state(mSsl);
|
||||
mTlsState = Handshaking;
|
||||
}
|
||||
else
|
||||
{
|
||||
InfoLog( << "TLS handshake starting (client mode)" );
|
||||
InfoLog(<< "TLS handshake starting (client mode)");
|
||||
/* OpenSSL version < 0.9.8f does not support SSL_set_tlsext_host_name() */
|
||||
#if defined(SSL_set_tlsext_host_name)
|
||||
DebugLog ( << "TLS SNI extension in Client Hello: " << who().getTargetDomain());
|
||||
SSL_set_tlsext_host_name(mSsl,who().getTargetDomain().c_str()); // set the SNI hostname
|
||||
DebugLog(<< "TLS SNI extension in Client Hello: " << who().getTargetDomain());
|
||||
SSL_set_tlsext_host_name(mSsl, who().getTargetDomain().c_str()); // set the SNI hostname
|
||||
#endif
|
||||
SSL_set_connect_state(mSsl);
|
||||
mTlsState = Handshaking;
|
||||
}
|
||||
|
||||
InfoLog( << "TLS connected" );
|
||||
InfoLog(<< "TLS connected");
|
||||
mTlsState = Handshaking;
|
||||
}
|
||||
|
||||
mHandShakeWantsRead = false;
|
||||
ok = SSL_do_handshake(mSsl);
|
||||
|
||||
if ( ok <= 0 )
|
||||
handshakeRet = SSL_do_handshake(mSsl);
|
||||
|
||||
if (handshakeRet <= 0)
|
||||
{
|
||||
int err = SSL_get_error(mSsl,ok);
|
||||
|
||||
int err = SSL_get_error(mSsl, handshakeRet);
|
||||
|
||||
switch (err)
|
||||
{
|
||||
case SSL_ERROR_WANT_READ:
|
||||
StackLog( << "TLS handshake want read" );
|
||||
StackLog(<< "TLS handshake want read");
|
||||
mHandShakeWantsRead = true;
|
||||
return mTlsState;
|
||||
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
StackLog( << "TLS handshake want write" );
|
||||
StackLog(<< "TLS handshake want write");
|
||||
ensureWritable();
|
||||
return mTlsState;
|
||||
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
StackLog( << "TLS connection closed cleanly");
|
||||
StackLog(<< "TLS connection closed cleanly");
|
||||
return mTlsState;
|
||||
|
||||
case SSL_ERROR_WANT_CONNECT:
|
||||
StackLog( << "BIO not connected, try later");
|
||||
StackLog(<< "BIO not connected, try later");
|
||||
return mTlsState;
|
||||
|
||||
case SSL_ERROR_WANT_ACCEPT:
|
||||
StackLog( << "TLS connection want accept" );
|
||||
StackLog(<< "TLS connection want accept");
|
||||
return mTlsState;
|
||||
|
||||
case SSL_ERROR_WANT_X509_LOOKUP:
|
||||
DebugLog( << "Try later / SSL_ERROR_WANT_X509_LOOKUP");
|
||||
DebugLog(<< "Try later / SSL_ERROR_WANT_X509_LOOKUP");
|
||||
return mTlsState;
|
||||
|
||||
default:
|
||||
if(err == SSL_ERROR_SYSCALL)
|
||||
TransportFailure::FailureReason failureReason = TransportFailure::ConnectionException;
|
||||
int failureSubCode = 0;
|
||||
Data failureString;
|
||||
DataStream ds(failureString);
|
||||
ds << "TLS handshake failed: ";
|
||||
if (err == SSL_ERROR_SYSCALL)
|
||||
{
|
||||
int e = getErrno();
|
||||
switch(e)
|
||||
switch (e)
|
||||
{
|
||||
case EINTR:
|
||||
case EAGAIN:
|
||||
#if EAGAIN != EWOULDBLOCK
|
||||
case EWOULDBLOCK: // Treat EGAIN and EWOULDBLOCK as the same: http://stackoverflow.com/questions/7003234/which-systems-define-eagain-and-ewouldblock-as-different-values
|
||||
#endif
|
||||
StackLog( << "try later");
|
||||
StackLog(<< "try later: " << e);
|
||||
return mTlsState;
|
||||
}
|
||||
ErrLog( << "socket error " << e);
|
||||
Transport::error(e);
|
||||
if(e == 0)
|
||||
ds << "socket error=" << e << ": " << Transport::errorToString(e);
|
||||
failureSubCode = e;
|
||||
if (e == 0)
|
||||
{
|
||||
TlsBaseTransport *t = dynamic_cast<TlsBaseTransport*>(transport());
|
||||
TlsBaseTransport* t = dynamic_cast<TlsBaseTransport*>(transport());
|
||||
resip_assert(t);
|
||||
if(mServer && t->getClientVerificationMode() != SecurityTypes::None)
|
||||
if (mServer && t->getClientVerificationMode() != SecurityTypes::None)
|
||||
{
|
||||
DebugLog(<<"client may have disconnected to prompt for user certificate, because it can't supply a certificate (verification mode == " << (t->getClientVerificationMode() == SecurityTypes::Mandatory?"Mandatory":"Optional") << " for this transport) or because it does not support using client certificates over WebSockets");
|
||||
ds << ", client may have disconnected to prompt for user certificate, because it can't supply a certificate (verification mode == " << (t->getClientVerificationMode() == SecurityTypes::Mandatory ? "Mandatory" : "Optional") << " for this transport) or because it does not support using client certificates";
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (err == SSL_ERROR_SSL)
|
||||
{
|
||||
WarningLog(<< "SSL cipher or certificate failure SSL_ERROR_SSL");
|
||||
ds << "SSL cipher or certificate failure(SSL_ERROR_SSL): ";
|
||||
int verifyErrorCode = SSL_get_verify_result(mSsl);
|
||||
switch (verifyErrorCode)
|
||||
{
|
||||
case X509_V_OK:
|
||||
if (SSL_get_peer_certificate(mSsl))
|
||||
case X509_V_OK:
|
||||
{
|
||||
DebugLog(<< "peer supplied a certificate, it has either not been checked or it was checked successfully");
|
||||
// OpenSSL docs: If no peer certificate was presented, the returned result code is X509_V_OK.
|
||||
// This is because no verification error occurred, it does however not indicate success.
|
||||
X509* peerCert = SSL_get_peer_certificate(mSsl);
|
||||
if (peerCert)
|
||||
{
|
||||
ds << "peer supplied a certificate, it has either not been checked or it was checked successfully";
|
||||
X509_free(peerCert);
|
||||
}
|
||||
else
|
||||
{
|
||||
ds << "no peer supplied certificate";
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugLog(<< "no peer supplied certificate");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ErrLog(<< "peer certificate validation failure: " << X509_verify_cert_error_string(verifyErrorCode));
|
||||
DebugLog(<< "additional validation checks may have failed but only one is ever logged - please check peer certificate carefully");
|
||||
break;
|
||||
|
||||
default:
|
||||
ds << "peer certificate validation failure: " << X509_verify_cert_error_string(verifyErrorCode)
|
||||
<< " (additional validation checks may have failed but only one is ever logged - please check peer certificate carefully)";
|
||||
break;
|
||||
}
|
||||
if (mServer)
|
||||
{
|
||||
@@ -314,29 +317,29 @@ TlsConnection::checkState()
|
||||
resip_assert(t);
|
||||
if (t->getClientVerificationMode() == SecurityTypes::Mandatory)
|
||||
{
|
||||
ErrLog(<< "Mandatory client certificate verification required, protocol failed, client did not send a certificate or it was not valid");
|
||||
ds << "; mandatory client certificate verification required";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ErrLog(<< "Server did not present any certificate to us, certificate invalid or protocol did not reach certificate exchange");
|
||||
}
|
||||
setFailureReason(TransportFailure::CertValidationFailure, verifyErrorCode);
|
||||
failureReason = TransportFailure::CertValidationFailure;
|
||||
failureSubCode = verifyErrorCode;
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugLog(<<"unrecognised/unhandled SSL_get_error result: " << err);
|
||||
ds << "unhandled SSL_get_error result: " << err;
|
||||
failureSubCode = err;
|
||||
}
|
||||
ErrLog( << "TLS handshake failed ");
|
||||
handleOpenSSLErrorQueue(ok, err, "SSL_do_handshake");
|
||||
ds.flush();
|
||||
ErrLog(<< failureString);
|
||||
setFailureReason(failureReason, failureSubCode, failureString);
|
||||
handleOpenSSLErrorQueue(handshakeRet, err, "SSL_do_handshake");
|
||||
mBio = NULL;
|
||||
mTlsState = Broken;
|
||||
return mTlsState;
|
||||
}
|
||||
}
|
||||
else // ok > 1
|
||||
else // handshakeRet > 1
|
||||
{
|
||||
InfoLog( << "TLS connected" );
|
||||
InfoLog(<< "TLS connected");
|
||||
}
|
||||
|
||||
// force peer name to get checked and perhaps cert loaded
|
||||
@@ -346,45 +349,50 @@ TlsConnection::checkState()
|
||||
if (!mServer)
|
||||
{
|
||||
bool matches = false;
|
||||
for(std::list<BaseSecurity::PeerName>::iterator it = mPeerNames.begin(); it != mPeerNames.end(); it++)
|
||||
for (std::list<BaseSecurity::PeerName>::iterator it = mPeerNames.begin(); it != mPeerNames.end(); it++)
|
||||
{
|
||||
if(BaseSecurity::matchHostName(it->mName, who().getTargetDomain()))
|
||||
if (BaseSecurity::matchHostName(it->mName, who().getTargetDomain()))
|
||||
{
|
||||
matches=true;
|
||||
break;
|
||||
matches = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!matches)
|
||||
if (!matches)
|
||||
{
|
||||
mTlsState = Broken;
|
||||
mBio = NULL;
|
||||
ErrLog (<< "Certificate name mismatch: trying to connect to <"
|
||||
<< who().getTargetDomain()
|
||||
<< "> remote cert domain(s) are <"
|
||||
<< getPeerNamesData() << ">" );
|
||||
setFailureReason(TransportFailure::CertNameMismatch, 0);
|
||||
Data failureString;
|
||||
{
|
||||
DataStream ds(failureString);
|
||||
ds << "Certificate name mismatch: trying to connect to <"
|
||||
<< who().getTargetDomain()
|
||||
<< "> remote cert domain(s) are <"
|
||||
<< getPeerNamesData() << ">";
|
||||
}
|
||||
ErrLog(<< failureString);
|
||||
setFailureReason(TransportFailure::CertNameMismatch, 0, failureString);
|
||||
return mTlsState;
|
||||
}
|
||||
}
|
||||
|
||||
InfoLog( << "TLS handshake done for peer " << getPeerNamesData());
|
||||
InfoLog(<< "TLS handshake done for peer " << getPeerNamesData());
|
||||
mTlsState = Up;
|
||||
if (!mOutstandingSends.empty())
|
||||
{
|
||||
ensureWritable();
|
||||
}
|
||||
#endif // USE_SSL
|
||||
#endif // USE_SSL
|
||||
return mTlsState;
|
||||
}
|
||||
|
||||
int
|
||||
TlsConnection::read(char* buf, int count )
|
||||
int
|
||||
TlsConnection::read(char* buf, int count)
|
||||
{
|
||||
#if defined(USE_SSL)
|
||||
resip_assert( mSsl );
|
||||
resip_assert( buf );
|
||||
resip_assert(mSsl);
|
||||
resip_assert(buf);
|
||||
|
||||
switch(checkState())
|
||||
switch (checkState())
|
||||
{
|
||||
case Broken:
|
||||
return -1;
|
||||
@@ -398,10 +406,10 @@ TlsConnection::read(char* buf, int count )
|
||||
|
||||
if (!mBio)
|
||||
{
|
||||
DebugLog( << "Got TLS read bad bio " );
|
||||
DebugLog(<< "Got TLS read bad bio ");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (!isGood())
|
||||
{
|
||||
return -1;
|
||||
@@ -421,7 +429,7 @@ TlsConnection::read(char* buf, int count )
|
||||
//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;
|
||||
@@ -436,8 +444,8 @@ TlsConnection::read(char* buf, int count )
|
||||
// and that -1 was retryable. You should instead call SSL_get_error() to find out if it's retryable."
|
||||
int err = SSL_get_error(mSsl, bytesPending);
|
||||
if (err != SSL_ERROR_WANT_READ &&
|
||||
err != SSL_ERROR_WANT_WRITE &&
|
||||
err != SSL_ERROR_NONE)
|
||||
err != SSL_ERROR_WANT_WRITE &&
|
||||
err != SSL_ERROR_NONE)
|
||||
{
|
||||
// Not a retryable error, put the error return code into bytesRead to
|
||||
// be used in the conditional block later in this method.
|
||||
@@ -460,29 +468,29 @@ TlsConnection::read(char* buf, int count )
|
||||
|
||||
if (bytesRead <= 0)
|
||||
{
|
||||
int err = SSL_get_error(mSsl,bytesRead);
|
||||
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 );
|
||||
StackLog(<< "Got TLS read got condition of " << err);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
{
|
||||
DebugLog( << "Got SSL_ERROR_ZERO_RETURN (TLS shutdown by peer)");
|
||||
DebugLog(<< "Got SSL_ERROR_ZERO_RETURN (TLS shutdown by peer)");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
handleOpenSSLErrorQueue(bytesRead, err, "SSL_read");
|
||||
if(err == 5)
|
||||
if (err == 5)
|
||||
{
|
||||
WarningLog(<<"err=5 sometimes indicates that intermediate certificates may be missing from local PEM file");
|
||||
WarningLog(<< "err=5 sometimes indicates that intermediate certificates may be missing from local PEM file");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -490,7 +498,7 @@ TlsConnection::read(char* buf, int count )
|
||||
}
|
||||
resip_assert(0);
|
||||
}
|
||||
StackLog(<<"SSL bytesRead="<<bytesRead);
|
||||
StackLog(<< "SSL bytesRead=" << bytesRead);
|
||||
return bytesRead;
|
||||
#endif // USE_SSL
|
||||
return -1;
|
||||
@@ -506,32 +514,30 @@ TlsConnection::transportWrite()
|
||||
checkState();
|
||||
if (mTlsState == Handshaking)
|
||||
{
|
||||
DebugLog(<< "Transportwrite--Handshaking--remove from write: " << mHandShakeWantsRead);
|
||||
DebugLog(<< "transportWrite in Handshaking state, remove from write: " << (mHandShakeWantsRead ? "true" : "false"));
|
||||
return mHandShakeWantsRead;
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugLog(<< "Transportwrite--Handshake complete, in " << fromState(mTlsState) << " calling write");
|
||||
return false;
|
||||
DebugLog(<< "transportWrite in Initial state (Handshake complete), calling write");
|
||||
}
|
||||
case Up:
|
||||
case Broken:
|
||||
DebugLog(<< "Transportwrite--" << fromState(mTlsState) << " fall through to write");
|
||||
return false;
|
||||
default:
|
||||
DebugLog(<< "transportWrite in " << fromState(mTlsState) << " state, calling write");
|
||||
}
|
||||
resip_assert(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
int
|
||||
TlsConnection::write( const char* buf, int count )
|
||||
int
|
||||
TlsConnection::write(const char* buf, int count)
|
||||
{
|
||||
#if defined(USE_SSL)
|
||||
resip_assert( mSsl );
|
||||
resip_assert( buf );
|
||||
resip_assert(mSsl);
|
||||
resip_assert(buf);
|
||||
int ret;
|
||||
|
||||
switch(checkState())
|
||||
|
||||
switch (checkState())
|
||||
{
|
||||
case Broken:
|
||||
return -1;
|
||||
@@ -539,34 +545,34 @@ TlsConnection::write( const char* buf, int count )
|
||||
case Up:
|
||||
break;
|
||||
default:
|
||||
DebugLog( << "Tried to Tls write - but connection is not Up" );
|
||||
DebugLog(<< "Tried to Tls write, but connection state is not Up");
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!mBio)
|
||||
{
|
||||
DebugLog( << "Got TLS write bad bio " );
|
||||
DebugLog(<< "Got TLS write bad bio");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = SSL_write(mSsl,(const char*)buf,count);
|
||||
if (ret < 0 )
|
||||
|
||||
ret = SSL_write(mSsl, (const char*)buf, count);
|
||||
if (ret < 0)
|
||||
{
|
||||
int err = SSL_get_error(mSsl,ret);
|
||||
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 );
|
||||
StackLog(<< "Got TLS write got condition of " << err);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
{
|
||||
DebugLog( << "Got SSL_ERROR_ZERO_RETURN (TLS shutdown by peer)");
|
||||
DebugLog(<< "Got SSL_ERROR_ZERO_RETURN (TLS shutdown by peer)");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
@@ -580,21 +586,19 @@ TlsConnection::write( const char* buf, int count )
|
||||
}
|
||||
|
||||
Data monkey(Data::Borrow, buf, count);
|
||||
|
||||
StackLog( << "Did TLS write " << ret << " " << count << " " << "[[" << monkey << "]]" );
|
||||
StackLog(<< "Did TLS write " << ret << " " << count << " " << "[[" << monkey << "]]");
|
||||
|
||||
return ret;
|
||||
#endif // USE_SSL
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
bool
|
||||
TlsConnection::hasDataToRead() // has data that can be read
|
||||
{
|
||||
#if defined(USE_SSL)
|
||||
//hack (for now)
|
||||
if(mTlsState == Initial)
|
||||
if (mTlsState == Initial)
|
||||
return false;
|
||||
|
||||
if (checkState() != Up)
|
||||
@@ -604,31 +608,30 @@ TlsConnection::hasDataToRead() // has data that can be read
|
||||
|
||||
int p = SSL_pending(mSsl);
|
||||
//DebugLog(<<"hasDataToRead(): " <<p);
|
||||
return (p>0);
|
||||
return (p > 0);
|
||||
#else // USE_SSL
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
bool
|
||||
TlsConnection::isGood() // has data that can be read
|
||||
{
|
||||
#if defined(USE_SSL)
|
||||
if ( mBio == 0 )
|
||||
if (mBio == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int mode = SSL_get_shutdown(mSsl);
|
||||
if ( mode < 0 )
|
||||
if (mode < 0)
|
||||
{
|
||||
int err = SSL_get_error(mSsl, mode);
|
||||
handleOpenSSLErrorQueue(mode, err, "SSL_get_shutdown");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( mode != 0 )
|
||||
if (mode != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -637,11 +640,11 @@ TlsConnection::isGood() // has data that can be read
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TlsConnection::isWritable()
|
||||
bool
|
||||
TlsConnection::isWritable()
|
||||
{
|
||||
#if defined(USE_SSL)
|
||||
switch(mTlsState)
|
||||
switch (mTlsState)
|
||||
{
|
||||
case Handshaking:
|
||||
return mHandShakeWantsRead ? false : true;
|
||||
@@ -652,15 +655,16 @@ TlsConnection::isWritable()
|
||||
return false;
|
||||
}
|
||||
//dragos -- to remove
|
||||
DebugLog( << "Current state: " << fromState(mTlsState));
|
||||
DebugLog(<< "Current state: " << fromState(mTlsState));
|
||||
//end dragos
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
void TlsConnection::getPeerNames(std::list<Data> &peerNames) const
|
||||
void
|
||||
TlsConnection::getPeerNames(std::list<Data>& peerNames) const
|
||||
{
|
||||
for(std::list<BaseSecurity::PeerName>::const_iterator it = mPeerNames.begin(); it != mPeerNames.end(); it++)
|
||||
for (std::list<BaseSecurity::PeerName>::const_iterator it = mPeerNames.begin(); it != mPeerNames.end(); it++)
|
||||
{
|
||||
peerNames.push_back(it->mName);
|
||||
}
|
||||
@@ -670,9 +674,9 @@ Data
|
||||
TlsConnection::getPeerNamesData() const
|
||||
{
|
||||
Data peerNamesString;
|
||||
for(std::list<BaseSecurity::PeerName>::const_iterator it = mPeerNames.begin(); it != mPeerNames.end(); it++)
|
||||
for (std::list<BaseSecurity::PeerName>::const_iterator it = mPeerNames.begin(); it != mPeerNames.end(); it++)
|
||||
{
|
||||
if(it == mPeerNames.begin())
|
||||
if (it == mPeerNames.begin())
|
||||
{
|
||||
peerNamesString += it->mName;
|
||||
}
|
||||
@@ -694,62 +698,62 @@ TlsConnection::computePeerName()
|
||||
|
||||
if (!mBio)
|
||||
{
|
||||
ErrLog( << "bad bio" );
|
||||
ErrLog(<< "bad bio");
|
||||
return;
|
||||
}
|
||||
|
||||
// print session info
|
||||
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) << " " );
|
||||
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 )
|
||||
if (!cert)
|
||||
{
|
||||
DebugLog(<< "No peer certificate in TLS connection" );
|
||||
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;
|
||||
DebugLog(<< "Peer certificate in TLS connection is not valid");
|
||||
X509_free(cert); cert = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
TlsBaseTransport *t = dynamic_cast<TlsBaseTransport*>(mTransport);
|
||||
TlsBaseTransport* t = dynamic_cast<TlsBaseTransport*>(mTransport);
|
||||
resip_assert(t);
|
||||
|
||||
mPeerNames.clear();
|
||||
BaseSecurity::getCertNames(cert, mPeerNames, t->isUseEmailAsSIP());
|
||||
if(mPeerNames.empty())
|
||||
if (mPeerNames.empty())
|
||||
{
|
||||
ErrLog(<< "Invalid certificate: no subjectAltName/CommonName found");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!mServer)
|
||||
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++)
|
||||
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 ) )
|
||||
if (!mSecurity->hasDomainCert(it->mName))
|
||||
{
|
||||
mSecurity->addDomainCertDER(it->mName,derCert);
|
||||
mSecurity->addDomainCertDER(it->mName, derCert);
|
||||
}
|
||||
}
|
||||
OPENSSL_free(buf); buf=NULL;
|
||||
OPENSSL_free(buf); buf = NULL;
|
||||
}
|
||||
|
||||
X509_free(cert); cert=NULL;
|
||||
X509_free(cert); cert = NULL;
|
||||
#endif // USE_SSL
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user