diff --git a/Info.plist b/Info.plist index 453de02..b04c147 100644 --- a/Info.plist +++ b/Info.plist @@ -5,7 +5,7 @@ CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier - zone.heap.${PRODUCT_NAME:rfc1034identifier} + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/ObjOpenSSL.xcodeproj/project.pbxproj b/ObjOpenSSL.xcodeproj/project.pbxproj index 51dda2a..d599e27 100644 --- a/ObjOpenSSL.xcodeproj/project.pbxproj +++ b/ObjOpenSSL.xcodeproj/project.pbxproj @@ -15,6 +15,8 @@ 4B9671B6193E55C800F9F80D /* ObjFW.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B9671B5193E55C800F9F80D /* ObjFW.framework */; }; 4BD0AAEC1341289500445289 /* SSLSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BD0AAEA1341289500445289 /* SSLSocket.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4BD0AAED1341289500445289 /* SSLSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BD0AAEB1341289500445289 /* SSLSocket.m */; }; + 4BDE04741D319BFC0051EDB8 /* SSLConnectionFailedException.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BDE04721D319BFC0051EDB8 /* SSLConnectionFailedException.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4BDE04751D319BFC0051EDB8 /* SSLConnectionFailedException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BDE04731D319BFC0051EDB8 /* SSLConnectionFailedException.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -28,6 +30,8 @@ 4BD0AAE91341286B00445289 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = SOURCE_ROOT; }; 4BD0AAEA1341289500445289 /* SSLSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SSLSocket.h; path = src/SSLSocket.h; sourceTree = SOURCE_ROOT; }; 4BD0AAEB1341289500445289 /* SSLSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SSLSocket.m; path = src/SSLSocket.m; sourceTree = SOURCE_ROOT; }; + 4BDE04721D319BFC0051EDB8 /* SSLConnectionFailedException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SSLConnectionFailedException.h; path = src/SSLConnectionFailedException.h; sourceTree = SOURCE_ROOT; }; + 4BDE04731D319BFC0051EDB8 /* SSLConnectionFailedException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SSLConnectionFailedException.m; path = src/SSLConnectionFailedException.m; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -72,6 +76,8 @@ children = ( 4B1918F41341272300D82152 /* Supporting Files */, 4B4F087713A01EEF00B60C3F /* ObjOpenSSL.h */, + 4BDE04721D319BFC0051EDB8 /* SSLConnectionFailedException.h */, + 4BDE04731D319BFC0051EDB8 /* SSLConnectionFailedException.m */, 4B19F58714D17250005D52DC /* SSLInvalidCertificateException.h */, 4B19F58814D17250005D52DC /* SSLInvalidCertificateException.m */, 4BD0AAEA1341289500445289 /* SSLSocket.h */, @@ -98,6 +104,7 @@ buildActionMask = 2147483647; files = ( 4B4F087813A01EEF00B60C3F /* ObjOpenSSL.h in Headers */, + 4BDE04741D319BFC0051EDB8 /* SSLConnectionFailedException.h in Headers */, 4B19F58B14D17250005D52DC /* SSLInvalidCertificateException.h in Headers */, 4BD0AAEC1341289500445289 /* SSLSocket.h in Headers */, 4B19F58D14D17250005D52DC /* X509Certificate.h in Headers */, @@ -131,7 +138,7 @@ 4B1918E01341272300D82152 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0510; + LastUpgradeCheck = 0730; }; buildConfigurationList = 4B1918E31341272300D82152 /* Build configuration list for PBXProject "ObjOpenSSL" */; compatibilityVersion = "Xcode 3.2"; @@ -165,6 +172,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 4BDE04751D319BFC0051EDB8 /* SSLConnectionFailedException.m in Sources */, 4B19F58C14D17250005D52DC /* SSLInvalidCertificateException.m in Sources */, 4BD0AAED1341289500445289 /* SSLSocket.m in Sources */, 4B19F58E14D17250005D52DC /* X509Certificate.m in Sources */, @@ -177,6 +185,7 @@ 4B1918FA1341272300D82152 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = DEBUG; @@ -224,6 +233,7 @@ "-lcrypto", "-lz", ); + PRODUCT_BUNDLE_IDENTIFIER = "zone.heap.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; WARNING_CFLAGS = ( "-Wall", @@ -263,6 +273,7 @@ "-lcrypto", "-lz", ); + PRODUCT_BUNDLE_IDENTIFIER = "zone.heap.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; WARNING_CFLAGS = ( "-Wall", diff --git a/src/Makefile b/src/Makefile index ed378b1..d25f254 100644 --- a/src/Makefile +++ b/src/Makefile @@ -5,7 +5,8 @@ STATIC_LIB = ${OBJOPENSSL_STATIC_LIB} LIB_MAJOR = 0 LIB_MINOR = 0 -SRCS = SSLInvalidCertificateException.m \ +SRCS = SSLConnectionFailedException.m \ + SSLInvalidCertificateException.m \ SSLSocket.m \ X509Certificate.m diff --git a/src/SSLConnectionFailedException.h b/src/SSLConnectionFailedException.h new file mode 100644 index 0000000..f27c8be --- /dev/null +++ b/src/SSLConnectionFailedException.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2016, Jonathan Schleifer + * + * https://heap.zone/git/?p=objopenssl.git + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice is present in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +@class SSLSocket; + +@interface SSLConnectionFailedException: OFConnectionFailedException +{ + unsigned long _SSLError; + long _verifyResult; +} + +@property (readonly) unsigned long SSLError; +@property (readonly) long verifyResult; + ++ (instancetype)exceptionWithHost: (OFString*)host + port: (uint16_t)port + socket: (SSLSocket*)socket + SSLError: (unsigned long)SSLError; ++ (instancetype)exceptionWithHost: (OFString*)host + port: (uint16_t)port + socket: (SSLSocket*)socket + SSLError: (unsigned long)SSLError + verifyResult: (long)verifyResult; +- initWithHost: (OFString*)host + port: (uint16_t)port + socket: (SSLSocket*)socket + SSLError: (unsigned long)SSLError; +- initWithHost: (OFString*)host + port: (uint16_t)port + socket: (SSLSocket*)socket + SSLError: (unsigned long)SSLError + verifyResult: (long)verifyResult; +@end diff --git a/src/SSLConnectionFailedException.m b/src/SSLConnectionFailedException.m new file mode 100644 index 0000000..994ac28 --- /dev/null +++ b/src/SSLConnectionFailedException.m @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2016, Jonathan Schleifer + * + * https://heap.zone/git/?p=objopenssl.git + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice is present in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#import + +#import "SSLConnectionFailedException.h" + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdocumentation" +#endif + +#include +#include +#include + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + +@implementation SSLConnectionFailedException +@synthesize SSLError = _SSLError, verifyResult = _verifyResult; + ++ (instancetype)exceptionWithHost: (OFString*)host + port: (uint16_t)port + socket: (SSLSocket*)socket + SSLError: (unsigned long)SSLError +{ + return [[[self alloc] initWithHost: host + port: port + socket: socket + SSLError: SSLError] autorelease]; +} + + ++ (instancetype)exceptionWithHost: (OFString*)host + port: (uint16_t)port + socket: (SSLSocket*)socket + SSLError: (unsigned long)SSLError + verifyResult: (long)verifyResult +{ + return [[[self alloc] initWithHost: host + port: port + socket: socket + SSLError: SSLError + verifyResult: verifyResult] autorelease]; +} + +- initWithHost: (OFString*)host + port: (uint16_t)port + socket: (SSLSocket*)socket + SSLError: (unsigned long)SSLError +{ + self = [super initWithHost: host + port: port + socket: socket]; + + _SSLError = SSLError; + + return self; +} + +- initWithHost: (OFString*)host + port: (uint16_t)port + socket: (SSLSocket*)socket + SSLError: (unsigned long)SSLError + verifyResult: (long)verifyResult +{ + self = [super initWithHost: host + port: port + socket: socket]; + + _SSLError = SSLError; + _verifyResult = verifyResult; + + return self; +} + +- (OFString*)description +{ + if (_SSLError != SSL_ERROR_NONE) { + char error[512]; + + ERR_error_string_n(_SSLError, error, 512); + + if (_verifyResult != X509_V_OK) + return [OFString stringWithFormat: + @"A connection to %@ on port %" @PRIu16 @" could " + @"not be established in socket of type %@: " + @"Verification failed: %s [%s]", + _host, _port, [_socket class], + X509_verify_cert_error_string(_verifyResult), + error]; + else + return [OFString stringWithFormat: + @"A connection to %@ on port %" @PRIu16 @" could " + @"not be established in socket of type %@: %s", + _host, _port, [_socket class], error]; + } + + return [super description]; +} +@end diff --git a/src/SSLSocket.m b/src/SSLSocket.m index dec0b9f..eeee4a1 100644 --- a/src/SSLSocket.m +++ b/src/SSLSocket.m @@ -34,6 +34,7 @@ #include #include +#include #include #if defined(__clang__) @@ -46,7 +47,6 @@ #import #import -#import #import #import #import @@ -58,9 +58,11 @@ #import #import "SSLSocket.h" -#import "SSLInvalidCertificateException.h" #import "X509Certificate.h" +#import "SSLConnectionFailedException.h" +#import "SSLInvalidCertificateException.h" + #ifndef INVALID_SOCKET # define INVALID_SOCKET -1 #endif @@ -175,12 +177,16 @@ locking_callback(int mode, int n, const char *file, int line) { of_string_encoding_t encoding; - if ((_SSL = SSL_new(ctx)) == NULL || !SSL_set_fd(_SSL, _socket)) { + if ((_SSL = SSL_new(ctx)) == NULL || SSL_set_fd(_SSL, _socket) != 1) { + unsigned long error = ERR_get_error(); + [super close]; - @throw [OFConnectionFailedException + + @throw [SSLConnectionFailedException exceptionWithHost: host port: port - socket: self]; + socket: self + SSLError: error]; } if (_certificateVerificationEnabled) { @@ -190,11 +196,17 @@ locking_callback(int mode, int n, const char *file, int line) X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); if (X509_VERIFY_PARAM_set1_host(param, - [host UTF8String], [host UTF8StringLength]) == 0) - @throw [OFConnectionFailedException + [host UTF8String], [host UTF8StringLength]) != 1) { + unsigned long error = ERR_get_error(); + + [self close]; + + @throw [SSLConnectionFailedException exceptionWithHost: host port: port - socket: self]; + socket: self + SSLError: error]; + } SSL_set_verify(_SSL, SSL_VERIFY_PEER, NULL); } @@ -208,12 +220,37 @@ locking_callback(int mode, int n, const char *file, int line) SSL_FILETYPE_PEM)) || (_certificateFile != nil && !SSL_use_certificate_file(_SSL, [_certificateFile cStringWithEncoding: encoding], - SSL_FILETYPE_PEM)) || SSL_connect(_SSL) != 1) { + SSL_FILETYPE_PEM))) { + unsigned long error = ERR_get_error(); + [super close]; - @throw [OFConnectionFailedException + + @throw [SSLConnectionFailedException exceptionWithHost: host port: port - socket: self]; + socket: self + SSLError: error]; + } + + if (SSL_connect(_SSL) != 1) { + unsigned long error = ERR_get_error(); + long res; + + [super close]; + + if ((res = SSL_get_verify_result(_SSL)) != X509_V_OK) + @throw [SSLConnectionFailedException + exceptionWithHost: host + port: port + socket: self + SSLError: error + verifyResult: res]; + else + @throw [SSLConnectionFailedException + exceptionWithHost: host + port: port + socket: self + SSLError: error]; } }