diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/net/tls/gnutls/TLSSocket_GnuTLS.cpp | 14 | ||||
-rwxr-xr-x | src/net/tls/openssl/TLSSocket_OpenSSL.cpp | 14 | ||||
-rw-r--r-- | src/platforms/posix/posixSocket.cpp | 95 | ||||
-rw-r--r-- | src/platforms/windows/windowsSocket.cpp | 67 | ||||
-rw-r--r-- | src/security/cert/defaultCertificateVerifier.cpp | 15 | ||||
-rw-r--r-- | src/security/cert/gnutls/X509Certificate_GnuTLS.cpp | 6 | ||||
-rwxr-xr-x | src/security/cert/openssl/X509Certificate_OpenSSL.cpp | 68 | ||||
-rw-r--r-- | src/security/sasl/SASLSocket.cpp | 12 |
8 files changed, 286 insertions, 5 deletions
diff --git a/src/net/tls/gnutls/TLSSocket_GnuTLS.cpp b/src/net/tls/gnutls/TLSSocket_GnuTLS.cpp index 477f655b..37381881 100644 --- a/src/net/tls/gnutls/TLSSocket_GnuTLS.cpp +++ b/src/net/tls/gnutls/TLSSocket_GnuTLS.cpp @@ -116,6 +116,18 @@ TLSSocket::size_type TLSSocket_GnuTLS::getBlockSize() const } +const string TLSSocket_GnuTLS::getPeerName() const +{ + return m_wrapped->getPeerName(); +} + + +const string TLSSocket_OpenSSL::getPeerAddress() const +{ + return m_wrapped->getPeerAddress(); +} + + void TLSSocket_GnuTLS::receive(string& buffer) { const int size = receiveRaw(m_buffer, sizeof(m_buffer)); @@ -262,7 +274,7 @@ void TLSSocket_GnuTLS::handshake(ref <timeoutHandler> toHandler) if (certs == NULL) throw exceptions::tls_exception("No peer certificate."); - m_session->getCertificateVerifier()->verify(certs); + m_session->getCertificateVerifier()->verify(certs, getPeerName()); m_connected = true; } diff --git a/src/net/tls/openssl/TLSSocket_OpenSSL.cpp b/src/net/tls/openssl/TLSSocket_OpenSSL.cpp index 25937e32..f37d9a33 100755 --- a/src/net/tls/openssl/TLSSocket_OpenSSL.cpp +++ b/src/net/tls/openssl/TLSSocket_OpenSSL.cpp @@ -160,6 +160,18 @@ TLSSocket::size_type TLSSocket_OpenSSL::getBlockSize() const } +const string TLSSocket_OpenSSL::getPeerName() const +{ + return m_wrapped->getPeerName(); +} + + +const string TLSSocket_OpenSSL::getPeerAddress() const +{ + return m_wrapped->getPeerAddress(); +} + + void TLSSocket_OpenSSL::receive(string& buffer) { const size_type size = receiveRaw(m_buffer, sizeof(m_buffer)); @@ -239,7 +251,7 @@ void TLSSocket_OpenSSL::handshake(ref <timeoutHandler> toHandler) if (certs == NULL) throw exceptions::tls_exception("No peer certificate."); - m_session->getCertificateVerifier()->verify(certs); + m_session->getCertificateVerifier()->verify(certs, getPeerName()); m_connected = true; } diff --git a/src/platforms/posix/posixSocket.cpp b/src/platforms/posix/posixSocket.cpp index b3e33f49..1a7fb7a3 100644 --- a/src/platforms/posix/posixSocket.cpp +++ b/src/platforms/posix/posixSocket.cpp @@ -104,6 +104,8 @@ void posixSocket::connect(const vmime::string& address, const vmime::port_t port throw vmime::exceptions::connection_error("Cannot resolve address."); } + m_serverAddress = address; + // Connect to host int sock = -1; struct ::addrinfo* res = res0; @@ -268,6 +270,8 @@ void posixSocket::connect(const vmime::string& address, const vmime::port_t port ::memcpy(reinterpret_cast <char*>(&addr.sin_addr), hostInfo->h_addr, hostInfo->h_length); } + m_serverAddress = address; + // Get a new socket m_desc = ::socket(AF_INET, SOCK_STREAM, 0); @@ -331,6 +335,97 @@ void posixSocket::disconnect() } +static bool isNumericAddress(const char* address) +{ + +#if VMIME_HAVE_GETADDRINFO + + struct addrinfo hint, *info = NULL; + memset(&hint, 0, sizeof(hint)); + + hint.ai_family = AF_UNSPEC; + hint.ai_flags = AI_NUMERICHOST; + + if (getaddrinfo(address, 0, &hint, &info) == 0) + { + freeaddrinfo(info); + return true; + } + else + { + return false; + } + +#else + + return inet_addr(address) != INADDR_NONE; + +#endif + +} + + +const string posixSocket::getPeerAddress() const +{ + // Get address of connected peer + sockaddr peer; + socklen_t peerLen = sizeof(peer); + + getpeername(m_desc, reinterpret_cast <sockaddr*>(&peer), &peerLen); + + // Convert to numerical presentation format + char numericAddress[1024]; + + if (inet_ntop(peer.sa_family, &peer, numericAddress, sizeof(numericAddress)) != NULL) + return string(numericAddress); + + return ""; // should not happen +} + + +const string posixSocket::getPeerName() const +{ + // Get address of connected peer + sockaddr peer; + socklen_t peerLen = sizeof(peer); + + getpeername(m_desc, reinterpret_cast <sockaddr*>(&peer), &peerLen); + + // If server address as specified when connecting is a numeric + // address, try to get a host name for it + if (isNumericAddress(m_serverAddress.c_str())) + { + +#if VMIME_HAVE_GETNAMEINFO + + char host[NI_MAXHOST + 1]; + char service[NI_MAXSERV + 1]; + + if (getnameinfo(reinterpret_cast <sockaddr *>(&peer), peerLen, + host, sizeof(host), service, sizeof(service), + /* flags */ NI_NAMEREQD) == 0) + { + return string(host); + } + +#else + + struct hostent *hp; + + if ((hp = gethostbyaddr(reinterpret_cast <const void *>(&peer), + sizeof(peer), peer.sa_family)) != NULL) + { + return string(hp->h_name); + } + +#endif + + } + + return m_serverAddress; +} + + posixSocket::size_type posixSocket::getBlockSize() const { return 16384; // 16 KB diff --git a/src/platforms/windows/windowsSocket.cpp b/src/platforms/windows/windowsSocket.cpp index dc6f1b47..abc16d70 100644 --- a/src/platforms/windows/windowsSocket.cpp +++ b/src/platforms/windows/windowsSocket.cpp @@ -91,6 +91,8 @@ void windowsSocket::connect(const vmime::string& address, const vmime::port_t po memcpy(reinterpret_cast <char*>(&addr.sin_addr), hostInfo->h_addr, hostInfo->h_length); } + m_serverAddress = address; + // Get a new socket m_desc = ::socket(AF_INET, SOCK_STREAM, 0); @@ -156,6 +158,71 @@ void windowsSocket::disconnect() } +static bool isNumericAddress(const char* address) +{ + struct addrinfo hint, *info = NULL; + memset(&hint, 0, sizeof(hint)); + + hint.ai_family = AF_UNSPEC; + hint.ai_flags = AI_NUMERICHOST; + + if (getaddrinfo(address, 0, &hint, &info) == 0) + { + freeaddrinfo(info); + return true; + } + else + { + return false; + } +} + + +const string windowsSocket::getPeerAddress() const +{ + // Get address of connected peer + sockaddr peer; + socklen_t peerLen = sizeof(peer); + + getpeername(m_desc, reinterpret_cast <sockaddr*>(&peer), &peerLen); + + // Convert to numerical presentation format + char numericAddress[1024]; + + if (inet_ntop(peer.sa_family, &peer, numericAddress, sizeof(numericAddress)) != NULL) + return string(numericAddress); + + return ""; // should not happen +} + + +const string windowsSocket::getPeerName() const +{ + // Get address of connected peer + sockaddr peer; + socklen_t peerLen = sizeof(peer); + + getpeername(m_desc, reinterpret_cast <sockaddr*>(&peer), &peerLen); + + // If server address as specified when connecting is a numeric + // address, try to get a host name for it + if (isNumericAddress(m_serverAddress.c_str())) + { + char host[NI_MAXHOST + 1]; + char service[NI_MAXSERV + 1]; + + if (getnameinfo(reinterpret_cast <sockaddr *>(&peer), peerLen, + host, sizeof(host), service, sizeof(service), + /* flags */ NI_NAMEREQD) == 0) + { + return string(host); + } + } + + return m_serverAddress; +} + + windowsSocket::size_type windowsSocket::getBlockSize() const { return 16384; // 16 KB diff --git a/src/security/cert/defaultCertificateVerifier.cpp b/src/security/cert/defaultCertificateVerifier.cpp index 6fde5519..65f5f476 100644 --- a/src/security/cert/defaultCertificateVerifier.cpp +++ b/src/security/cert/defaultCertificateVerifier.cpp @@ -50,7 +50,8 @@ defaultCertificateVerifier::defaultCertificateVerifier(const defaultCertificateV } -void defaultCertificateVerifier::verify(ref <certificateChain> chain) +void defaultCertificateVerifier::verify + (ref <certificateChain> chain, const string& hostname) { if (chain->getCount() == 0) return; @@ -58,13 +59,14 @@ void defaultCertificateVerifier::verify(ref <certificateChain> chain) const string type = chain->getAt(0)->getType(); if (type == "X.509") - verifyX509(chain); + verifyX509(chain, hostname); else throw exceptions::unsupported_certificate_type(type); } -void defaultCertificateVerifier::verifyX509(ref <certificateChain> chain) +void defaultCertificateVerifier::verifyX509 + (ref <certificateChain> chain, const string& hostname) { // For every certificate in the chain, verify that the certificate // has been issued by the next certificate in the chain @@ -141,6 +143,13 @@ void defaultCertificateVerifier::verifyX509(ref <certificateChain> chain) throw exceptions::certificate_verification_exception ("Cannot verify certificate against trusted certificates."); } + + // Ensure the first certificate's subject name matches server hostname + if (!firstCert->verifyHostName(hostname)) + { + throw exceptions::certificate_verification_exception + ("Server identity cannot be verified."); + } } diff --git a/src/security/cert/gnutls/X509Certificate_GnuTLS.cpp b/src/security/cert/gnutls/X509Certificate_GnuTLS.cpp index 633004ff..b3f939ec 100644 --- a/src/security/cert/gnutls/X509Certificate_GnuTLS.cpp +++ b/src/security/cert/gnutls/X509Certificate_GnuTLS.cpp @@ -181,6 +181,12 @@ bool X509Certificate_GnuTLS::verify(ref <const X509Certificate> caCert_) const } +bool X509Certificate_GnuTLS::verifyHostName(const string& hostname) const +{ + return gnutls_x509_crt_check_hostname(m_data->cert, hostname.c_str()) != 0; +} + + const datetime X509Certificate_GnuTLS::getActivationDate() const { const time_t t = gnutls_x509_crt_get_activation_time(m_data->cert); diff --git a/src/security/cert/openssl/X509Certificate_OpenSSL.cpp b/src/security/cert/openssl/X509Certificate_OpenSSL.cpp index e47a19a4..3f171a4f 100755 --- a/src/security/cert/openssl/X509Certificate_OpenSSL.cpp +++ b/src/security/cert/openssl/X509Certificate_OpenSSL.cpp @@ -41,6 +41,8 @@ #include "vmime/exception.hpp" #include <openssl/x509.h> +#include <openssl/x509v3.h> +#include <openssl/conf.h> #include <openssl/bio.h> #include <openssl/pem.h> #include <openssl/err.h> @@ -330,6 +332,72 @@ bool X509Certificate_OpenSSL::verify(ref <const X509Certificate> caCert_) const } +bool X509Certificate_OpenSSL::verifyHostName(const string& hostname) const +{ + // First, check subject common name against hostname + char CNBuffer[1024]; + CNBuffer[sizeof(CNBuffer - 1)] = '\0'; + + X509_NAME* xname = X509_get_subject_name(m_data->cert); + + if (X509_NAME_get_text_by_NID(xname, NID_commonName, CNBuffer, sizeof(CNBuffer)) != -1) + { + if (strcasecmp(CNBuffer, hostname.c_str()) == 0) + return true; + } + + // Now, look in subject alternative names + for (int i = 0, extCount = X509_get_ext_count(m_data->cert) ; i < extCount ; ++i) + { + X509_EXTENSION* ext = X509_get_ext(m_data->cert, i); + const char* extStr = OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(ext))); + + if (strcmp(extStr, "subjectAltName") == 0) + { + const X509V3_EXT_METHOD* method; + + if ((method = X509V3_EXT_get(ext)) != NULL) + { + const unsigned char* extVal = ext->value->data; + void *extValStr; + + if (method->it) + { + extValStr = ASN1_item_d2i + (NULL, &extVal, ext->value->length, ASN1_ITEM_ptr(method->it)); + } + else + { + extValStr = method->d2i + (NULL, &extVal, ext->value->length); + } + + if (extValStr && method->i2v) + { + STACK_OF(CONF_VALUE)* val = method->i2v(method, extValStr, NULL); + + for (int j = 0 ; j < sk_CONF_VALUE_num(val) ; ++j) + { + CONF_VALUE* cnf = sk_CONF_VALUE_value(val, j); + + if ((strcasecmp(cnf->name, "DNS") == 0 && + strcasecmp(cnf->value, hostname.c_str()) == 0) + || + (strncasecmp(cnf->name, "IP", 2) == 0 && + strcasecmp(cnf->value, hostname.c_str()) == 0)) + { + return true; + } + } + } + } + } + } + + return false; +} + + const datetime X509Certificate_OpenSSL::convertX509Date(void* time) const { char* buffer; diff --git a/src/security/sasl/SASLSocket.cpp b/src/security/sasl/SASLSocket.cpp index c3498d22..a4b0ea56 100644 --- a/src/security/sasl/SASLSocket.cpp +++ b/src/security/sasl/SASLSocket.cpp @@ -81,6 +81,18 @@ SASLSocket::size_type SASLSocket::getBlockSize() const } +const string SASLSocket::getPeerName() const +{ + return m_wrapped->getPeerName(); +} + + +const string SASLSocket::getPeerAddress() const +{ + return m_wrapped->getPeerAddress(); +} + + void SASLSocket::receive(string& buffer) { const size_type n = receiveRaw(m_recvBuffer, sizeof(m_recvBuffer)); |