Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -1887,12 +1887,12 @@ AC_CHECK_FUNC(SSLHandshake, [ tls_support="Secure Transport" TLS_LIBS="-framework Foundation $TLS_LIBS" TLS_LIBS="-framework Security $TLS_LIBS" - AC_SUBST(OF_SECURE_TRANSPORT_TLS_STREAM_M, - "OFSecureTransportTLSStream.m") + AC_SUBST(USE_SRCS_SECURETRANSPORT, + '${SRCS_SECURETRANSPORT}') AC_CHECK_FUNCS(SSLCreateContext) ], []) LIBS="$old_LIBS" @@ -1915,12 +1915,11 @@ AC_CHECK_LIB($ssl, SSL_set1_host, [ AC_CHECK_HEADER(openssl/ssl.h, [ tls_support="OpenSSL" TLS_LIBS="-l$ssl -l$crypto $TLS_LIBS" - AC_SUBST(OF_OPENSSL_TLS_STREAM_M, - "OFOpenSSLTLSStream.m") + AC_SUBST(USE_SRCS_OPENSSL, '${SRCS_OPENSSL}') old_LIBS="$LIBS" LIBS="$TLS_LIBS $LIBS" AC_CHECK_FUNCS(SSL_has_pending) LIBS="$old_LIBS" @@ -1933,11 +1932,11 @@ PKG_CHECK_MODULES(gnutls, [gnutls >= 3.5.0], [ tls_support="GnuTLS" TLS_CPPFLAGS="$gnutls_CFLAGS $TLS_CPPFLAGS" TLS_LIBS="$gnutls_LIBS $TLS_LIBS" - AC_SUBST(OF_GNUTLS_TLS_STREAM_M, "OFGnuTLSTLSStream.m") + AC_SUBST(USE_SRCS_GNUTLS, '${SRCS_GNUTLS}') ], [ dnl Disable default action-if-not-found, which exits dnl configure with an error. : ]) @@ -1948,12 +1947,11 @@ AC_CHECK_HEADER(mbedtls/ssl.h, [ tls_support="Mbed TLS" TLS_LIBS="-lmbedx509 -lmbedcrypto $TLS_LIBS" TLS_LIBS="-lmbedtls $TLS_LIBS" - AC_SUBST(OF_MBEDTLS_TLS_STREAM_M, - "OFMbedTLSTLSStream.m") + AC_SUBST(USE_SRCS_MBEDTLS, '${SRCS_MBEDTLS}') ]) ], [], [-lmbedx509 -lmbedcrypto]) ]) AS_IF([test x"$tls_support" != x"no"], [ Index: extra.mk.in ================================================================== --- extra.mk.in +++ extra.mk.in @@ -66,18 +66,14 @@ OFHASH_LIBS = @OFHTTP_LIBS@ OFHTTP = @OFHTTP@ OFHTTP_LIBS = @OFHTTP_LIBS@ OF_BLOCK_TESTS_M = @OF_BLOCK_TESTS_M@ OF_EPOLL_KERNEL_EVENT_OBSERVER_M = @OF_EPOLL_KERNEL_EVENT_OBSERVER_M@ -OF_GNUTLS_TLS_STREAM_M = @OF_GNUTLS_TLS_STREAM_M@ OF_HTTP_CLIENT_TESTS_M = @OF_HTTP_CLIENT_TESTS_M@ OF_KQUEUE_KERNEL_EVENT_OBSERVER_M = @OF_KQUEUE_KERNEL_EVENT_OBSERVER_M@ -OF_MBEDTLS_TLS_STREAM_M = @OF_MBEDTLS_TLS_STREAM_M@ -OF_OPENSSL_TLS_STREAM_M = @OF_OPENSSL_TLS_STREAM_M@ OF_POLL_KERNEL_EVENT_OBSERVER_M = @OF_POLL_KERNEL_EVENT_OBSERVER_M@ OF_SCTP_SOCKET_M = @OF_SCTP_SOCKET_M@ -OF_SECURE_TRANSPORT_TLS_STREAM_M = @OF_SECURE_TRANSPORT_TLS_STREAM_M@ OF_SELECT_KERNEL_EVENT_OBSERVER_M = @OF_SELECT_KERNEL_EVENT_OBSERVER_M@ REEXPORT_RUNTIME = @REEXPORT_RUNTIME@ REEXPORT_RUNTIME_FRAMEWORK = @REEXPORT_RUNTIME_FRAMEWORK@ RUNTIME = @RUNTIME@ RUNTIME_ARC_TESTS_M = @RUNTIME_ARC_TESTS_M@ @@ -99,16 +95,20 @@ UNICODE_M = @UNICODE_M@ USE_INCLUDES_ATOMIC = @USE_INCLUDES_ATOMIC@ USE_SRCS_APPLETALK = @USE_SRCS_APPLETALK@ USE_SRCS_EVDEV = @USE_SRCS_EVDEV@ USE_SRCS_FILES = @USE_SRCS_FILES@ +USE_SRCS_GNUTLS = @USE_SRCS_GNUTLS@ USE_SRCS_IPX = @USE_SRCS_IPX@ +USE_SRCS_MBEDTLS = @USE_SRCS_MBEDTLS@ USE_SRCS_NINTENDO_3DS = @USE_SRCS_NINTENDO_3DS@ USE_SRCS_NINTENDO_DS = @USE_SRCS_NINTENDO_DS@ USE_SRCS_NINTENDO_SWITCH = @USE_SRCS_NINTENDO_SWITCH@ +USE_SRCS_OPENSSL = @USE_SRCS_OPENSSL@ USE_SRCS_PLUGINS = @USE_SRCS_PLUGINS@ USE_SRCS_SCTP = @USE_SRCS_SCTP@ +USE_SRCS_SECURETRANSPORT = @USE_SRCS_SECURETRANSPORT@ USE_SRCS_SOCKETS = @USE_SRCS_SOCKETS@ USE_SRCS_SUBPROCESSES = @USE_SRCS_SUBPROCESSES@ USE_SRCS_TAGGED_POINTERS = @USE_SRCS_TAGGED_POINTERS@ USE_SRCS_THREADS = @USE_SRCS_THREADS@ USE_SRCS_UNIX_SOCKETS = @USE_SRCS_UNIX_SOCKETS@ Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -150,10 +150,11 @@ OFTCPSocket.m \ OFTLSStream.m \ OFTXTDNSResourceRecord.m \ OFUDPSocket.m \ OFURIDNSResourceRecord.m \ + OFX509Certificate.m \ ${USE_SRCS_APPLETALK} \ ${USE_SRCS_IPX} \ ${USE_SRCS_SCTP} \ ${USE_SRCS_UNIX_SOCKETS} SRCS_APPLETALK = OFDDPSocket.m Index: src/OFTLSStream.h ================================================================== --- src/OFTLSStream.h +++ src/OFTLSStream.h @@ -22,11 +22,13 @@ OF_ASSUME_NONNULL_BEGIN /** @file */ +@class OFArray OF_GENERIC(ObjectType); @class OFTLSStream; +@class OFX509Certificate; /** * @brief An enum representing an error of an OFTLSStream. */ typedef enum { @@ -50,10 +52,11 @@ * @protocol OFTLSStreamDelegate OFTLSStream.h ObjFW/ObjFW.h * * A delegate for OFTLSStream. */ @protocol OFTLSStreamDelegate +@optional /** * @brief A method which is called when a TLS stream performed the client * handshake. * * @param stream The TLS stream which performed the handshake @@ -62,10 +65,21 @@ * success */ - (void)stream: (OFTLSStream *)stream didPerformClientHandshakeWithHost: (OFString *)host exception: (nullable id)exception; + +/** + * @brief A method which is called when a TLS stream performed the server + * handshake. + * + * @param stream The TLS stream which performed the handshake + * @param exception An exception that occurred during the handshake, or nil on + * success + */ +- (void)streamDidPerformServerHandshake: (OFTLSStream *)stream + exception: (nullable id)exception; @end /** * @class OFTLSStream OFTLSStream.h ObjFW/ObjFW.h * @@ -86,11 +100,12 @@ OFReadyForWritingObserving> { OFStream *_underlyingStream; bool _verifiesCertificates; - OF_RESERVE_IVARS(OFTLSStream, 4) + OFArray OF_GENERIC(OFX509Certificate *) *_Nullable _certificateChain; + OF_RESERVE_IVARS(OFTLSStream, 3) } /** * @brief The underlying stream. */ @@ -109,10 +124,16 @@ /** * @brief Whether certificates are verified. Default is true. */ @property (nonatomic) bool verifiesCertificates; +/** + * @brief The certificate chain to use. + */ +@property OF_NULLABLE_PROPERTY (copy, nonatomic) + OFArray OF_GENERIC(OFX509Certificate *) *certificateChain; + - (instancetype)init OF_UNAVAILABLE; /** * @brief Creates a new TLS stream with the specified stream as its underlying * stream. @@ -167,10 +188,38 @@ * @param host The host to perform the handshake with * @throw OFTLSHandshakeFailedException The TLS handshake failed * @throw OFAlreadyOpenException The handshake was already performed */ - (void)performClientHandshakeWithHost: (OFString *)host; + +/** + * @brief Asynchronously performs the TLS server handshake and calls the + * delegate afterwards. + * + * @throw OFTLSHandshakeFailedException The TLS handshake failed + * @throw OFAlreadyOpenException The handshake was already performed + */ +- (void)asyncPerformServerHandshake; + +/** + * @brief Asynchronously performs the TLS server handshake and calls the + * delegate afterwards. + * + * @param runLoopMode The run loop mode in which to perform the async handshake + * + * @throw OFTLSHandshakeFailedException The TLS handshake failed + * @throw OFAlreadyOpenException The handshake was already performed + */ +- (void)asyncPerformServerHandshakeWithRunLoopMode: (OFRunLoopMode)runLoopMode; + +/** + * @brief Performs the TLS server handshake. + * + * @throw OFTLSHandshakeFailedException The TLS handshake failed + * @throw OFAlreadyOpenException The handshake was already performed + */ +- (void)performServerHandshake; @end #ifdef __cplusplus extern "C" { #endif Index: src/OFTLSStream.m ================================================================== --- src/OFTLSStream.m +++ src/OFTLSStream.m @@ -18,10 +18,11 @@ */ #include "config.h" #import "OFTLSStream.h" +#import "OFArray.h" #import "OFDate.h" #import "OFNotImplementedException.h" #import "OFTLSHandshakeFailedException.h" @@ -78,10 +79,17 @@ - (void)stream: (OFTLSStream *)stream didPerformClientHandshakeWithHost: (OFString *)host exception: (id)exception { + _done = true; + _exception = [exception retain]; +} + +- (void)streamDidPerformServerHandshake: (OFTLSStream *)stream + exception: (id)exception +{ _done = true; _exception = [exception retain]; } @end @@ -142,10 +150,23 @@ [_underlyingStream release]; _underlyingStream = nil; [super close]; } + +- (void)setCertificateChain: + (OFArray OF_GENERIC(OFX509Certificate *) *)certificateChain +{ + OFArray OF_GENERIC(OFX509Certificate *) *old = _certificateChain; + _certificateChain = [certificateChain copy]; + [old release]; +} + +- (OFArray OF_GENERIC(OFX509Certificate *) *)certificateChain +{ + return _certificateChain; +} - (size_t)lowlevelReadIntoBuffer: (void *)buffer length: (size_t)length { OF_UNRECOGNIZED_SELECTOR } @@ -192,10 +213,45 @@ _delegate = handshakeDelegate; [self asyncPerformClientHandshakeWithHost: host runLoopMode: handshakeRunLoopMode]; + while (!handshakeDelegate->_done) + [runLoop runMode: handshakeRunLoopMode beforeDate: nil]; + + /* Cleanup */ + [runLoop runMode: handshakeRunLoopMode beforeDate: [OFDate date]]; + + _delegate = delegate; + + if (handshakeDelegate->_exception != nil) + @throw handshakeDelegate->_exception; + + objc_autoreleasePoolPop(pool); +} + +- (void)asyncPerformServerHandshake +{ + [self asyncPerformServerHandshakeWithRunLoopMode: OFDefaultRunLoopMode]; +} + +- (void)asyncPerformServerHandshakeWithRunLoopMode: (OFRunLoopMode)runLoopMode +{ + OF_UNRECOGNIZED_SELECTOR +} + +- (void)performServerHandshake +{ + void *pool = objc_autoreleasePoolPush(); + id delegate = _delegate; + OFTLSStreamHandshakeDelegate *handshakeDelegate = + [[[OFTLSStreamHandshakeDelegate alloc] init] autorelease]; + OFRunLoop *runLoop = [OFRunLoop currentRunLoop]; + + _delegate = handshakeDelegate; + [self asyncPerformServerHandshakeWithRunLoopMode: handshakeRunLoopMode]; + while (!handshakeDelegate->_done) [runLoop runMode: handshakeRunLoopMode beforeDate: nil]; /* Cleanup */ [runLoop runMode: handshakeRunLoopMode beforeDate: [OFDate date]]; ADDED src/OFX509Certificate.h Index: src/OFX509Certificate.h ================================================================== --- /dev/null +++ src/OFX509Certificate.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2008-2024 Jonathan Schleifer + * + * All rights reserved. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3.0 only, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * version 3.0 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3.0 along with this program. If not, see + * . + */ + +#import "OFObject.h" + +OF_ASSUME_NONNULL_BEGIN + +/** + * @class OFX509Certificate OFX509Certificate.h ObjFW/ObjFW.h + * + * @brief An X.509 certificate. + */ +@interface OFX509Certificate: OFObject +{ + OF_RESERVE_IVARS(OFX509Certificate, 4) +} +@end + +OF_ASSUME_NONNULL_END ADDED src/OFX509Certificate.m Index: src/OFX509Certificate.m ================================================================== --- /dev/null +++ src/OFX509Certificate.m @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2008-2024 Jonathan Schleifer + * + * All rights reserved. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3.0 only, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * version 3.0 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3.0 along with this program. If not, see + * . + */ + +#include "config.h" + +#import "OFX509Certificate.h" + +#import "OFNotImplementedException.h" + +Class OFX509CertificateImplementation = Nil; + +@implementation OFX509Certificate ++ (instancetype)alloc +{ + if (self == [OFX509Certificate class]) { + if (OFX509CertificateImplementation != Nil) + return [OFX509CertificateImplementation alloc]; + + @throw [OFNotImplementedException exceptionWithSelector: _cmd + object: self]; + } + + return [super alloc]; +} +@end Index: src/tls/Makefile ================================================================== --- src/tls/Makefile +++ src/tls/Makefile @@ -8,14 +8,19 @@ LIB_MAJOR = ${OBJFWTLS_LIB_MAJOR} LIB_MINOR = ${OBJFWTLS_LIB_MINOR} LIB_PATCH = ${OBJFWTLS_LIB_PATCH} INCLUDES := ObjFWTLS.h -SRCS = ${OF_GNUTLS_TLS_STREAM_M} \ - ${OF_MBEDTLS_TLS_STREAM_M} \ - ${OF_OPENSSL_TLS_STREAM_M} \ - ${OF_SECURE_TRANSPORT_TLS_STREAM_M} +SRCS = ${USE_SRCS_GNUTLS} \ + ${USE_SRCS_MBEDTLS} \ + ${USE_SRCS_OPENSSL} \ + ${USE_SRCS_SECURETRANSPORT} + +SRCS_GNUTLS = OFGnuTLSTLSStream.m +SRCS_MBEDTLS = OFMbedTLSTLSStream.m +SRCS_OPENSSL = OFOpenSSLTLSStream.m +SRCS_SECURETRANSPORT = OFSecureTransportTLSStream.m includesubdir = ObjFWTLS include ../../buildsys.mk