Index: src/OFTLSStream.h ================================================================== --- src/OFTLSStream.h +++ src/OFTLSStream.h @@ -32,11 +32,11 @@ typedef enum { /** @brief An unknown error. */ OFTLSStreamErrorCodeUnknown, /** @brief Initialization of the TLS context failed. */ OFTLSStreamErrorCodeInitializationFailed, - /** @brief Verification of the certificate failed. */ + /** @brief Failed to verify certificate. */ OFTLSStreamErrorCodeCertificateVerificationFailed, /** @brief The certificate has an untrusted or unknown issuer. */ OFTLSStreamErrorCodeCertificateIssuerUntrusted, /** @brief The certificate is for a different name. */ OFTLSStreamErrorCodeCertificateNameMismatch, Index: src/OFTLSStream.m ================================================================== --- src/OFTLSStream.m +++ src/OFTLSStream.m @@ -52,11 +52,11 @@ { switch (errorCode) { case OFTLSStreamErrorCodeInitializationFailed: return @"Initialization of TLS context failed"; case OFTLSStreamErrorCodeCertificateVerificationFailed: - return @"Verification of the certificate failed"; + return @"Failed to verify certificate"; case OFTLSStreamErrorCodeCertificateIssuerUntrusted: return @"The certificate has an untrusted or unknown issuer"; case OFTLSStreamErrorCodeCertificateNameMismatch: return @"The certificate is for a different name"; case OFTLSStreamErrorCodeCertificatedExpired: Index: src/tls/OFOpenSSLTLSStream.m ================================================================== --- src/tls/OFOpenSSLTLSStream.m +++ src/tls/OFOpenSSLTLSStream.m @@ -21,10 +21,12 @@ #include #import "OFOpenSSLTLSStream.h" #import "OFData.h" + +#include #import "OFAlreadyOpenException.h" #import "OFInitializationFailedException.h" #import "OFNotOpenException.h" #import "OFReadFailedException.h" @@ -33,10 +35,48 @@ #define bufferSize OFOpenSSLTLSStreamBufferSize int _ObjFWTLS_reference; static SSL_CTX *clientContext; + +static OFTLSStreamErrorCode +verifyResultToErrorCode(const SSL *SSL_) +{ + switch (SSL_get_verify_result(SSL_)) { + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: + case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: + case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: + case X509_V_ERR_CERT_UNTRUSTED: + return OFTLSStreamErrorCodeCertificateIssuerUntrusted; + case X509_V_ERR_HOSTNAME_MISMATCH: + return OFTLSStreamErrorCodeCertificateNameMismatch; + case X509_V_ERR_CERT_NOT_YET_VALID: + case X509_V_ERR_CERT_HAS_EXPIRED: + return OFTLSStreamErrorCodeCertificatedExpired; + case X509_V_ERR_CERT_REVOKED: + return OFTLSStreamErrorCodeCertificateRevoked; + } + + return OFTLSStreamErrorCodeCertificateVerificationFailed; +} + +static OFTLSStreamErrorCode +errToErrorCode(const SSL *SSL_) +{ + unsigned long err = ERR_get_error(); + + switch (ERR_GET_LIB(err)) { + case ERR_LIB_SSL: + switch (ERR_GET_REASON(err)) { + case SSL_R_CERTIFICATE_VERIFY_FAILED: + return verifyResultToErrorCode(SSL_); + } + } + + return OFTLSStreamErrorCodeUnknown; +} @implementation OFOpenSSLTLSStream + (void)load { if (OFTLSStreamImplementation == Nil) @@ -112,10 +152,11 @@ size_t bytesRead; if (!_handshakeDone) @throw [OFNotOpenException exceptionWithObject: self]; + ERR_clear_error(); ret = SSL_read_ex(_SSL, buffer, length, &bytesRead); while (BIO_ctrl_pending(_writeBIO) > 0) { int tmp = BIO_read(_writeBIO, _buffer, bufferSize); @@ -143,10 +184,11 @@ if (e.errNo == EWOULDBLOCK || e.errNo != EAGAIN) return 0; } } + ERR_clear_error(); ret = SSL_read_ex(_SSL, buffer, length, &bytesRead); while (BIO_ctrl_pending(_writeBIO) > 0) { int tmp = BIO_read(_writeBIO, _buffer, bufferSize); @@ -174,10 +216,12 @@ int ret; size_t bytesWritten; if (!_handshakeDone) @throw [OFNotOpenException exceptionWithObject: self]; + + ERR_clear_error(); if ((ret = SSL_write_ex(_SSL, buffer, length, &bytesWritten)) != 1) { /* FIXME: Translate error to errNo */ int errNo = 0; @@ -270,10 +314,11 @@ exceptionWithStream: self host: host errorCode: initFailedErrorCode]; } + ERR_clear_error(); status = SSL_do_handshake(_SSL); while (BIO_ctrl_pending(_writeBIO) > 0) { int tmp = BIO_read(_writeBIO, _buffer, bufferSize); @@ -298,12 +343,17 @@ [_underlyingStream asyncWriteData: [OFData data] runLoopMode: runLoopMode]; [_delegate retain]; objc_autoreleasePoolPop(pool); return; + case SSL_ERROR_SSL: + exception = [OFTLSHandshakeFailedException + exceptionWithStream: self + host: host + errorCode: errToErrorCode(_SSL)]; + break; default: - /* FIXME: Map to better errors */ exception = [OFTLSHandshakeFailedException exceptionWithStream: self host: host errorCode: OFTLSStreamErrorCodeUnknown]; break; @@ -331,10 +381,11 @@ OFEnsure(length <= INT_MAX); OFEnsure(BIO_write(_readBIO, buffer, (int)length) == (int)length); + ERR_clear_error(); status = SSL_do_handshake(_SSL); while (BIO_ctrl_pending(_writeBIO) > 0) { int tmp = BIO_read(_writeBIO, buffer, bufferSize); @@ -354,10 +405,16 @@ OFRunLoopMode runLoopMode = [OFRunLoop currentRunLoop].currentMode; [_underlyingStream asyncWriteData: [OFData data] runLoopMode: runLoopMode]; return false; + case SSL_ERROR_SSL: + exception = [OFTLSHandshakeFailedException + exceptionWithStream: self + host: _host + errorCode: errToErrorCode(_SSL)]; + break; default: exception = [OFTLSHandshakeFailedException exceptionWithStream: self host: _host errorCode: unknownErrorCode]; @@ -395,10 +452,11 @@ [_underlyingStream writeBuffer: _buffer length: tmp]; [_underlyingStream flushWriteBuffer]; } + ERR_clear_error(); status = SSL_do_handshake(_SSL); while (BIO_ctrl_pending(_writeBIO) > 0) { int tmp = BIO_read(_writeBIO, _buffer, bufferSize); @@ -420,10 +478,16 @@ length: bufferSize runLoopMode: runLoopMode]; return nil; case SSL_ERROR_WANT_WRITE: return data; + case SSL_ERROR_SSL: + exception = [OFTLSHandshakeFailedException + exceptionWithStream: self + host: _host + errorCode: errToErrorCode(_SSL)]; + break; default: exception = [OFTLSHandshakeFailedException exceptionWithStream: self host: _host errorCode: unknownErrorCode];