From f05d94e9e356e79a6263fe67f19e0ba9ed873e04 Mon Sep 17 00:00:00 2001 From: rfm Date: Wed, 26 Sep 2012 15:23:24 +0000 Subject: [PATCH] more tls reorganisation git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@35605 72102866-910b-0410-8b05-ffd578937521 --- ChangeLog | 6 + Headers/Foundation/NSFileHandle.h | 12 +- Source/GSSocketStream.m | 161 +----- Source/GSTLS.h | 94 +++- Source/GSTLS.m | 881 ++++++++++++++++++++++++++---- Source/NSFileHandle.m | 249 ++------- 6 files changed, 921 insertions(+), 482 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1ef7ce25c..e716334b4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,12 @@ * Source/NSUserDefaults.m: Fix error synchronising from changes in database ... was not sending the notification to say we had updated. + * Headers/Foundation/NSFileHandle.h: + * Source/GSSocketStream.m: + * Source/GSTLS.h: + * Source/GSTLS.m: + * Source/NSFileHandle.m: + More TLS reorganisation and adding diagnostics 2012-09-25 Richard Frith-Macdonald diff --git a/Headers/Foundation/NSFileHandle.h b/Headers/Foundation/NSFileHandle.h index dc79c7b57..34b011cb3 100644 --- a/Headers/Foundation/NSFileHandle.h +++ b/Headers/Foundation/NSFileHandle.h @@ -279,15 +279,15 @@ GS_EXPORT NSString * const NSFileHandleOperationException; * The path to a PEM encoded certificate used to identify this end * of the connection. This option must be set for handing an * incoming connection, but is optional for outgoing connections.
- * This must be used in conjunction with GSTLSPrivateKeyFile. + * This must be used in conjunction with GSTLSCertificateKeyFile. *
- * GSTLSPrivateKeyFile + * GSTLSCertificateKeyFile * The path to a PEM encoded key used to unlock the certificate * file for the connection. The key in the file may or may not be * encrypted, but if it is encrypted you must specify - * GSTLSPrivateKeyPassword. + * GSTLSCertificateKeyPassword. * - * GSTLSPrivateKeyPassword + * GSTLSCertificateKeyPassword * A string to be used as the password to decrypt a key which was * specified using GSTLSKeyPassword. * @@ -305,12 +305,12 @@ GS_EXPORT NSString * const GSTLSCertificateFile; /** Dictionary key for the path to a PEM encoded private key used * to unlock the certificate used by this end of a connection. */ -GS_EXPORT NSString * const GSTLSPrivateKeyFile; +GS_EXPORT NSString * const GSTLSCertificateKeyFile; /** Dictionary key for the password used to decrypt the key file used * to unlock the certificate used by this end of a connection. */ -GS_EXPORT NSString * const GSTLSPrivateKeyPassword; +GS_EXPORT NSString * const GSTLSCertificateKeyPassword; // GNUstep Notification names. diff --git a/Source/GSSocketStream.m b/Source/GSSocketStream.m index c9226dde6..20f2589db 100644 --- a/Source/GSSocketStream.m +++ b/Source/GSSocketStream.m @@ -353,8 +353,7 @@ GSPrivateSockaddrSetup(NSString *machine, uint16_t port, @interface GSTLSHandler : GSStreamHandler { @public - gnutls_session_t session; - gnutls_certificate_credentials_t certcred; + GSTLSSession *session; } @end @@ -381,7 +380,7 @@ GSTLSPull(gnutls_transport_ptr_t handle, void *buffer, size_t len) e = EAGAIN; // Tell GNUTLS this would block. } #if HAVE_GNUTLS_TRANSPORT_SET_ERRNO - gnutls_transport_set_errno (tls->session, e); + gnutls_transport_set_errno (tls->session->session, e); #else errno = e; // Not thread-safe #endif @@ -412,7 +411,7 @@ GSTLSPush(gnutls_transport_ptr_t handle, const void *buffer, size_t len) e = EAGAIN; // Tell GNUTLS this would block. } #if HAVE_GNUTLS_TRANSPORT_SET_ERRNO - gnutls_transport_set_errno (tls->session, e); + gnutls_transport_set_errno (tls->session->session, e); #else errno = e; // Not thread-safe #endif @@ -459,20 +458,15 @@ GSTLSPush(gnutls_transport_ptr_t handle, const void *buffer, size_t len) - (void) bye { - if (active == YES || handshake == YES) - { - active = NO; - handshake = NO; - gnutls_bye (session, GNUTLS_SHUT_RDWR); - } + handshake = NO; + active = NO; + [session disconnect]; } - (void) dealloc { [self bye]; - gnutls_db_remove_session (session); - gnutls_deinit (session); - gnutls_certificate_free_credentials (certcred); + DESTROY(session); [super dealloc]; } @@ -485,28 +479,16 @@ GSTLSPush(gnutls_transport_ptr_t handle, const void *buffer, size_t len) { if (active == NO) { - int ret; - if (handshake == NO) { /* Set flag to say we are now doing a handshake. */ handshake = YES; } - ret = gnutls_handshake (session); - if (ret < 0) + if ([session handshake] == YES) { - NSDebugMLLog(@"NSStream", - @"Handshake status %d", ret); - if (GSDebugSet(@"NSStream") == YES) - { - gnutls_perror(ret); - } - } - else - { - handshake = NO; // Handshake is now complete. - active = YES; // The TLS session is now active. + handshake = NO; // Handshake is now complete. + active = [session active]; // The TLS session is now active. } } } @@ -514,9 +496,11 @@ GSTLSPush(gnutls_transport_ptr_t handle, const void *buffer, size_t len) - (id) initWithInput: (GSSocketInputStream*)i output: (GSSocketOutputStream*)o { - NSString *proto = [i propertyForKey: NSStreamSocketSecurityLevelKey]; + NSString *proto; + NSDictionary *opts; BOOL server = NO; + proto = [i propertyForKey: NSStreamSocketSecurityLevelKey]; /* FIXME if ([[o propertyForKey: NSStreamSocketCertificateServerKey] boolValue] == YES) { @@ -539,119 +523,22 @@ GSTLSPush(gnutls_transport_ptr_t handle, const void *buffer, size_t len) DESTROY(self); return nil; } - if ([proto isEqualToString: NSStreamSocketSecurityLevelNone] == YES) - { - // proto = NSStreamSocketSecurityLevelNone; - DESTROY(self); - return nil; - } - else if ([proto isEqualToString: NSStreamSocketSecurityLevelSSLv2] == YES) - { - // proto = NSStreamSocketSecurityLevelSSLv2; - GSOnceMLog(@"NSStreamSocketSecurityLevelTLSv2 is insecure ..." - @" not implemented"); - DESTROY(self); - return nil; - } - else if ([proto isEqualToString: NSStreamSocketSecurityLevelSSLv3] == YES) - { - proto = NSStreamSocketSecurityLevelSSLv3; - } - else if ([proto isEqualToString: NSStreamSocketSecurityLevelTLSv1] == YES) - { - proto = NSStreamSocketSecurityLevelTLSv1; - } - else - { - proto = NSStreamSocketSecurityLevelNegotiatedSSL; - } + opts = [NSDictionary dictionaryWithObjectsAndKeys: + proto, NSStreamSocketSecurityLevelKey, + nil]; if ((self = [super initWithInput: i output: o]) == nil) { return nil; } + session = [[GSTLSSession alloc] initWithOptions: opts + direction: (server ? NO : YES) + transport: (void*)self + push: GSTLSPush + pull: GSTLSPull + host: nil]; initialised = YES; - /* Configure this session to support certificate based - * operation. - */ - gnutls_certificate_allocate_credentials (&certcred); - - /* FIXME ... should get the trusted authority certificates - * from somewhere sensible to validate the remote end! - */ - gnutls_certificate_set_x509_trust_file - (certcred, "ca.pem", GNUTLS_X509_FMT_PEM); - - /* Initialise session and set default priorities foir key exchange. - */ - if (server) - { - gnutls_init (&session, GNUTLS_SERVER); - /* FIXME ... need to set up DH information and key/certificate. */ - } - else - { - gnutls_init (&session, GNUTLS_CLIENT); - } - gnutls_set_default_priority (session); - - if ([proto isEqualToString: NSStreamSocketSecurityLevelTLSv1] == YES) - { -#if GNUTLS_VERSION_NUMBER < 0x020C00 - const int proto_prio[4] = { -#if defined(GNUTLS_TLS1_2) - GNUTLS_TLS1_2, -#endif - GNUTLS_TLS1_1, - GNUTLS_TLS1_0, - 0 }; - gnutls_protocol_set_priority (session, proto_prio); -#else - gnutls_priority_set_direct(session, - "NORMAL:-VERS-SSL3.0:+VERS-TLS-ALL", NULL); -#endif - } - if ([proto isEqualToString: NSStreamSocketSecurityLevelSSLv3] == YES) - { -#if GNUTLS_VERSION_NUMBER < 0x020C00 - const int proto_prio[2] = { - GNUTLS_SSL3, - 0 }; - gnutls_protocol_set_priority (session, proto_prio); -#else - gnutls_priority_set_direct(session, - "NORMAL:-VERS-TLS-ALL:+VERS-SSL3.0", NULL); -#endif - } - -/* - { - const int kx_prio[] = { - GNUTLS_KX_RSA, - GNUTLS_KX_RSA_EXPORT, - GNUTLS_KX_DHE_RSA, - GNUTLS_KX_DHE_DSS, - GNUTLS_KX_ANON_DH, - 0 }; - gnutls_kx_set_priority (session, kx_prio); - gnutls_credentials_set (session, GNUTLS_CRD_ANON, anoncred); - } - */ - - /* Set certificate credentials for this session. - */ - gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, certcred); - - /* Set transport layer to use our low level stream code. - */ -#if GNUTLS_VERSION_NUMBER < 0x020C00 - gnutls_transport_set_lowat (session, 0); -#endif - gnutls_transport_set_pull_function (session, GSTLSPull); - gnutls_transport_set_push_function (session, GSTLSPush); - gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t)self); - return self; } @@ -667,7 +554,7 @@ GSTLSPush(gnutls_transport_ptr_t handle, const void *buffer, size_t len) - (NSInteger) read: (uint8_t *)buffer maxLength: (NSUInteger)len { - return gnutls_record_recv (session, buffer, len); + return [session read: buffer length: len]; } - (void) stream: (NSStream*)stream handleEvent: (NSStreamEvent)event @@ -719,7 +606,7 @@ GSTLSPush(gnutls_transport_ptr_t handle, const void *buffer, size_t len) - (NSInteger) write: (const uint8_t *)buffer maxLength: (NSUInteger)len { - return gnutls_record_send (session, buffer, len); + return [session write: buffer length: len]; } @end diff --git a/Source/GSTLS.h b/Source/GSTLS.h index ef87c8855..dd4ba39e4 100644 --- a/Source/GSTLS.h +++ b/Source/GSTLS.h @@ -42,29 +42,10 @@ #include #undef id -@protocol GSTLSOwner - -/* Returns the optins dictionary set for this session. - */ -- (NSDictionary*) options; - -/* Returns the host this session should be connected to. - */ -- (NSHost*) remoteHost; - -@end - /* This class is used to ensure that the GNUTLS system is initialised - * and thread-safe. It also provides session verification. + * and thread-safe. */ @interface GSTLSObject : NSObject - -/* Performs verification for the supplied session and returns a GNUTLS - * error code in the event of verification failure or zero on success.
- * The ponter set in the session with gnutls_session_set_ptr() must be - * the owner of the session and must conform to the GSTLSOwner protocol. - */ -+ (int) verify: (gnutls_session_t)session; @end /* This class provides the current autogenerated Diffie Hellman parameters @@ -122,5 +103,78 @@ - (gnutls_x509_privkey_t) key; @end + +/* Declare a pointer to a function to be used for I/O + */ +typedef ssize_t (*GSTLSIOR)(gnutls_transport_ptr_t, void *, size_t); +typedef ssize_t (*GSTLSIOW)(gnutls_transport_ptr_t, const void *, size_t); + +/* This class encapsulates a session to a remote system. + * Sessions are created with a direction and an options dictionary, + * defining how they will operate. The handle, pushFunc and pullFunc + * provide the I/O mechanism, and the host specifies the host that the + * session is connected to. + */ +@interface GSTLSSession : GSTLSObject +{ + NSDictionary *opts; + NSHost *host; + GSTLSPrivateKey *key; + GSTLSCertificateList *list; + GSTLSDHParams *dhParams; + gnutls_certificate_credentials_t certcred; + BOOL outgoing; + BOOL active; + BOOL handshake; + BOOL setup; +@public + gnutls_session_t session; +} ++ (GSTLSSession*) sessionWithOptions: (NSDictionary*)options + direction: (BOOL)isOutgoing + transport: (void*)handle + push: (GSTLSIOW)pushFunc + pull: (GSTLSIOR)pullFunc + host: (NSHost*)remote; + +- (id) initWithOptions: (NSDictionary*)options + direction: (BOOL)isOutgoing + transport: (void*)handle + push: (GSTLSIOW)pushFunc + pull: (GSTLSIOR)pullFunc + host: (NSHost*)remote; + +/* Return YES if the session is active (handshake has succeeded and the + * session has not been disconnected), NO otherwise. + */ +- (BOOL) active; + +/* Disconnects and closes down the session. + */ +- (void) disconnect; + +/* Try to complete a handshake ... return YES if complete, NO if we need + * to try again (would have to wait for the remote end).
+ */ +- (BOOL) handshake; + +/* Read data from the session. + */ +- (NSInteger) read: (void*)buf length: (NSUInteger)len; + +/** Get a report of the SSL/TLS status of the current session. + */ +- (NSString*) sessionInfo; + +/* Write data to the session. + */ +- (NSInteger) write: (const void*)buf length: (NSUInteger)len; + +/* For internal use to verify the remmote system's vertificate. + * Returns 0 on success, negative on failure. + */ +- (int) verify; +@end + #endif diff --git a/Source/GSTLS.m b/Source/GSTLS.m index e55e496a3..f511424f6 100644 --- a/Source/GSTLS.m +++ b/Source/GSTLS.m @@ -31,6 +31,8 @@ #import "Foundation/NSException.h" #import "Foundation/NSLock.h" #import "Foundation/NSNotification.h" +#import "Foundation/NSProcessInfo.h" +#import "Foundation/NSStream.h" #import "Foundation/NSThread.h" #import "Foundation/NSUserDefaults.h" @@ -38,9 +40,15 @@ #import "GSPrivate.h" +/* Constants to control TLS/SSL (options). + */ +NSString * const GSTLSCAFile = @"GSTLSCAFile"; NSString * const GSTLSCertificateFile = @"GSTLSCertificateFile"; -NSString * const GSTLSPrivateKeyFile = @"GSTLSPrivateKeyFile"; -NSString * const GSTLSPrivateKeyPassword = @"GSTLSPrivateKeyPassword"; +NSString * const GSTLSCertificateKeyFile = @"GSTLSCertificateKeyFile"; +NSString * const GSTLSCertificateKeyPassword = @"GSTLSCertificateKeyPassword"; +NSString * const GSTLSDebug = @"GSTLSDebug"; +NSString * const GSTLSCAVerify = @"GSTLSCAVerify"; + #if defined(HAVE_GNUTLS) @@ -82,6 +90,33 @@ GSTLSLog(int level, const char *msg) NSLog(@"%s", msg); } +/* The caFile variable holds the location of the file containing the default + * certificate authorities to be used by our system. + * The hard-coded value can be overridden by the GS_TLS_CA_FILE environment + * variable, which in turn will be overridden by the GSTLSCAFile user + * default string. + */ +static NSString *caFile = @"/etc/ssl/certs/ca-certificates.crt"; + +/* The verifyServer variable tells us if connections to a remote server should + * (by default) verify its certificate against trusted authorities. + * The hard-coded value can be overridden by the GS_TLS_CA_VERIFY environment + * variable, which in turn will be overridden by the GSTLSCAVerify user + * default string. + * Any option set for a specific session overrides this default + */ +static BOOL verifyServer = NO; + +/* The globalDebug variable turns on gnutls debug. The hard-code value is + * overridden by GS_TLS_DEBUG, which in turn can be overridden by the + * GSTLSDebug user default. This is an integer debug level with higher + * values producing more debug output. Usually levels higher than 1 are + * too verbose and not useful unless you have the gnutls source code to hand. + * NB. The GSTLSDebug session option is a boolean to turn on extra debug for + * a particular session to be produced on verification failure. + */ +static int globalDebug = 0; + static NSString *cipherList = nil; static gnutls_anon_client_credentials_t anoncred; @@ -93,8 +128,36 @@ static gnutls_anon_client_credentials_t anoncred; + (void) _defaultsChanged: (NSNotification*)n { + NSString *str; + cipherList = [[NSUserDefaults standardUserDefaults] stringForKey: @"GSCipherList"]; + + /* The GSTLSCAFile user default overrides the builtin value or the + * GS_TLS_CA_FILE environment variable. + */ + str = [[NSUserDefaults standardUserDefaults] stringForKey: GSTLSCAFile]; + if (nil != str) + { + ASSIGN(caFile, str); + } + + str = [[NSUserDefaults standardUserDefaults] stringForKey: GSTLSCAVerify]; + if (nil != str) + { + verifyServer = [str boolValue]; + } + + str = [[NSUserDefaults standardUserDefaults] stringForKey: GSTLSDebug]; + if (nil != str) + { + globalDebug = [str intValue]; + } + if (globalDebug < 0) + { + globalDebug = 0; + } + gnutls_global_set_log_level(globalDebug); } + (void) initialize @@ -106,11 +169,35 @@ static gnutls_anon_client_credentials_t anoncred; if (beenHere == NO) { NSUserDefaults *defs; + NSProcessInfo *pi; + NSString *str; beenHere = YES; + /* Let the GS_TLS_CA_FILE environment variable override the + * default certificate authority location. + */ + pi = [NSProcessInfo processInfo]; + str = [[pi environment] objectForKey: @"GS_TLS_CA_FILE"]; + if (nil != str) + { + ASSIGN(caFile, str); + } + + str = [[pi environment] objectForKey: @"GS_TLS_CA_VERIFY"]; + if (nil != str) + { + verifyServer = [str boolValue]; + } + + str = [[pi environment] objectForKey: @"GS_TLS_DEBUG"]; + if (nil != str) + { + globalDebug = [str intValue]; + } + defs = [NSUserDefaults standardUserDefaults]; - cipherList = [defs stringForKey: @"GSCipherList"]; + [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(_defaultsChanged:) @@ -132,109 +219,12 @@ static gnutls_anon_client_credentials_t anoncred; /* Enable gnutls logging via NSLog */ gnutls_global_set_log_function (GSTLSLog); + + [self _defaultsChanged: nil]; } } } -+ (int) verify: (gnutls_session_t)session -{ - unsigned int status; - const gnutls_datum_t *cert_list; - unsigned int cert_list_size; - int ret; - gnutls_x509_crt_t cert; - id owner; - NSHost *host; - NSDictionary *options; - - /* read hostname */ - owner = (id)gnutls_session_get_ptr(session); - host = [owner remoteHost]; - options = [owner options]; - - /* This verification function uses the trusted CAs in the credentials - * structure. So you must have installed one or more CA certificates. - */ - ret = gnutls_certificate_verify_peers2 (session, &status); - if (ret < 0) - { - NSLog(@"Error"); - return GNUTLS_E_CERTIFICATE_ERROR; - } - - if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) - NSLog(@"The certificate hasn't got a known issuer."); - - if (status & GNUTLS_CERT_REVOKED) - NSLog(@"The certificate has been revoked."); - -/* - if (status & GNUTLS_CERT_EXPIRED) - NSLog(@"The certificate has expired"); - - if (status & GNUTLS_CERT_NOT_ACTIVATED) - NSLog(@"The certificate is not yet activated"); -*/ - - if (status & GNUTLS_CERT_INVALID) - { - NSLog(@"The certificate is not trusted."); - return GNUTLS_E_CERTIFICATE_ERROR; - } - - /* Up to here the process is the same for X.509 certificates and - * OpenPGP keys. From now on X.509 certificates are assumed. This can - * be easily extended to work with openpgp keys as well. - */ - if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509) - return GNUTLS_E_CERTIFICATE_ERROR; - - if (gnutls_x509_crt_init (&cert) < 0) - { - NSLog(@"error in initialization"); - return GNUTLS_E_CERTIFICATE_ERROR; - } - - cert_list = gnutls_certificate_get_peers (session, &cert_list_size); - if (cert_list == NULL) - { - NSLog(@"No certificate was found!"); - return GNUTLS_E_CERTIFICATE_ERROR; - } - - if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0) - { - NSLog(@"error parsing certificate"); - return GNUTLS_E_CERTIFICATE_ERROR; - } - - if (nil != host) - { - NSEnumerator *enumerator = [[host names] objectEnumerator]; - BOOL found = NO; - NSString *name; - - while (nil != (name = [enumerator nextObject])) - { - if (0 == gnutls_x509_crt_check_hostname(cert, [name UTF8String])) - { - found = YES; - break; - } - } - if (NO == found) - { - NSLog(@"The certificate's owner does not match host '%@'", host); - gnutls_x509_crt_deinit (cert); - return GNUTLS_E_CERTIFICATE_ERROR; - } - } - - gnutls_x509_crt_deinit (cert); - - return 0; // Verified -} - @end @implementation GSTLSDHParams @@ -711,5 +701,692 @@ static NSMutableDictionary *privateKeyCache1 = nil; } @end +@implementation GSTLSSession + ++ (GSTLSSession*) sessionWithOptions: (NSDictionary*)options + direction: (BOOL)isOutgoing + transport: (void*)handle + push: (GSTLSIOW)pushFunc + pull: (GSTLSIOR)pullFunc + host: (NSHost*)host +{ + GSTLSSession *sess; + + sess = [[self alloc] initWithOptions: options + direction: isOutgoing + transport: handle + push: pushFunc + pull: pullFunc + host: host]; + return [sess autorelease]; +} + +- (BOOL) active +{ + return active; +} + +- (void) dealloc +{ + [self finalize]; + DESTROY(opts); + DESTROY(host); + DESTROY(list); + DESTROY(key); + DESTROY(dhParams); + [super dealloc]; +} + +- (void) disconnect +{ + if (YES == active || YES == handshake) + { + active = NO; + handshake = NO; + gnutls_bye(session, GNUTLS_SHUT_RDWR); + } + if (YES == setup) + { + setup = NO; + gnutls_db_remove_session(session); + gnutls_deinit(session); + gnutls_certificate_free_credentials(certcred); + } +} + +- (void) finalize +{ + [self disconnect]; + [super finalize]; +} + +- (id) initWithOptions: (NSDictionary*)options + direction: (BOOL)isOutgoing + transport: (void*)handle + push: (GSTLSIOW)pushFunc + pull: (GSTLSIOR)pullFunc + host: (NSHost*)remote +{ + if (nil != (self = [super init])) + { + NSString *certFile; + NSString *privateKey; + NSString *PEMpasswd; + NSString *pri; + NSString *str; + int ret; + BOOL debug = (globalDebug > 0) ? YES : NO; + + opts = [options copy]; + host = [remote copy]; + outgoing = isOutgoing ? YES : NO; + + if (NO == debug) + { + debug = [[opts objectForKey: GSTLSDebug] boolValue]; + } + + /* Now initialise session and set it up. It's simplest to always + * allocate a credentials structure at this point (and get rid of + * it when the session is disconnected) too. + */ + gnutls_certificate_allocate_credentials(&certcred); + if (YES == outgoing) + { + gnutls_init(&session, GNUTLS_CLIENT); + } + else + { + gnutls_init(&session, GNUTLS_SERVER); + + /* We don't request any certificate from the client. + * If we did we would need to verify it. + */ + gnutls_certificate_server_set_request(session, GNUTLS_CERT_IGNORE); + } + setup = YES; + + /* Set the default trusted authority certificates. + */ + if ([caFile length] > 0) + { + const char *path = [caFile fileSystemRepresentation]; + int ret; + + ret = gnutls_certificate_set_x509_trust_file(certcred, + path, GNUTLS_X509_FMT_PEM); + if (ret < 0) + { + NSLog(@"Problem loading trusted authorities from %@: %s", + caFile, gnutls_strerror(ret)); + } + else if (0 == ret && YES == debug) + { + NSLog(@"No certificates processed from %@", caFile); + } + } + + /* Load any specified trusted authority certificates. + */ + str = [opts objectForKey: GSTLSCAFile]; + if ([str length] > 0) + { + const char *path = [str fileSystemRepresentation]; + int ret; + + ret = gnutls_certificate_set_x509_trust_file(certcred, + path, GNUTLS_X509_FMT_PEM); + if (ret < 0) + { + NSLog(@"Problem loading trusted authorities from %@: %s", + str, gnutls_strerror(ret)); + } + else if (0 == ret) + { + NSLog(@"No certificates processed from %@", str); + } + } + +/* + gnutls_certificate_set_x509_crl_file + (certcred, "crl.pem", GNUTLS_X509_FMT_PEM); + gnutls_certificate_set_verify_function (certcred, + _verify_certificate_callback); + +*/ + + certFile = [opts objectForKey: GSTLSCertificateFile]; + privateKey = [opts objectForKey: GSTLSCertificateKeyFile]; + PEMpasswd = [opts objectForKey: GSTLSCertificateKeyPassword]; + + if (nil != privateKey) + { + key = [[GSTLSPrivateKey keyFromFile: privateKey + withPassword: PEMpasswd] retain]; + if (nil == key) + { + [self release]; + return nil; + } + } + + if (nil != certFile) + { + list = [[GSTLSCertificateList listFromFile: certFile] retain]; + if (nil == list) + { + [self release]; + return nil; + } + } + + if (nil != list) + { + ret = gnutls_certificate_set_x509_key(certcred, + [list certificateList], [list count], [key key]); + if (ret < 0) + { + NSLog(@"Unable to set certificate for session: %s", + gnutls_strerror(ret)); + [self release]; + return nil; + } +/* + else if (NO == outgoing) + { + dhParams = [[GSTLSDHParams current] retain]; + gnutls_certificate_set_dh_params (certcred, [dhParams params]); + } +*/ + } + + gnutls_set_default_priority(session); + pri = [opts objectForKey: NSStreamSocketSecurityLevelKey]; + if ([pri isEqualToString: NSStreamSocketSecurityLevelNone] == YES) + { + // pri = NSStreamSocketSecurityLevelNone; + GSOnceMLog(@"NSStreamSocketSecurityLevelNone is insecure ..." + @" not implemented"); + DESTROY(self); + return nil; + } + else if ([pri isEqualToString: NSStreamSocketSecurityLevelSSLv2] == YES) + { + // pri = NSStreamSocketSecurityLevelSSLv2; + GSOnceMLog(@"NSStreamSocketSecurityLevelTLSv2 is insecure ..." + @" not implemented"); + DESTROY(self); + return nil; + } + else if ([pri isEqualToString: NSStreamSocketSecurityLevelSSLv3] == YES) + { +#if GNUTLS_VERSION_NUMBER < 0x020C00 + const int proto_prio[2] = { + GNUTLS_SSL3, + 0 }; + gnutls_protocol_set_priority (session, proto_prio); +#else + gnutls_priority_set_direct(session, + "NORMAL:-VERS-TLS-ALL:+VERS-SSL3.0", NULL); +#endif + } + else if ([pri isEqualToString: NSStreamSocketSecurityLevelTLSv1] == YES) + { +#if GNUTLS_VERSION_NUMBER < 0x020C00 + const int proto_prio[4] = { +#if defined(GNUTLS_TLS1_2) + GNUTLS_TLS1_2, +#endif + GNUTLS_TLS1_1, + GNUTLS_TLS1_0, + 0 }; + gnutls_protocol_set_priority (session, proto_prio); +#else + gnutls_priority_set_direct(session, + "NORMAL:-VERS-SSL3.0:+VERS-TLS-ALL", NULL); +#endif + } + + /* Set certificate credentials for this session. + */ + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, certcred); + + /* Set transport layer to use + */ +#if GNUTLS_VERSION_NUMBER < 0x020C00 + gnutls_transport_set_lowat (session, 0); +#endif + gnutls_transport_set_pull_function(session, pullFunc); + gnutls_transport_set_push_function(session, pushFunc); + gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t)handle); + } + + return self; +} + +- (BOOL) handshake +{ + int ret; + + if (YES == active || NO == setup) + { + return YES; // Handshake completed or impossible. + } + + handshake = YES; + ret = gnutls_handshake(session); + if (ret < 0) + { + if (gnutls_error_is_fatal(ret)) + { + NSLog(@"unable to make SSL connection: %s", + gnutls_strerror(ret)); + [self disconnect]; + return YES; // Failed ... not active. + } + else + { + if (GSDebugSet(@"NSStream") == YES) + { + gnutls_perror(ret); + } + return NO; // Non-fatal error needs a retry. + } + } + else + { + NSString *str; + BOOL shouldVerify = NO; + + active = YES; // The TLS session is now active. + handshake = NO; // Handshake is over. + + if (YES == outgoing) + { + shouldVerify = verifyServer; // Verify remote server? + } + str = [opts objectForKey: GSTLSCAVerify]; + if (nil != str) + { + shouldVerify = [str boolValue]; + } + + if (globalDebug > 1) + { + NSLog(@"Before verify:\n%@", [self sessionInfo]); + } + if (YES == shouldVerify) + { + ret = [self verify]; + if (ret < 0) + { + if (globalDebug > 0 + || YES == [[opts objectForKey: GSTLSDebug] boolValue]) + { + NSLog(@"unable to verify SSL connection - %s", + gnutls_strerror(ret)); + NSLog(@"%@", [self sessionInfo]); + } + [self disconnect]; + } + } + return YES; // Handshake complete + } +} + +- (NSInteger) read: (void*)buf length: (NSUInteger)len +{ + return gnutls_record_recv(session, buf, len); +} + +- (NSInteger) write: (const void*)buf length: (NSUInteger)len +{ + return gnutls_record_send (session, buf, len); +} + +/* Copied/based on the public domain code provided by gnutls + * to print the session ... I've left in details for features + * we don't yet support. + */ +- (NSString*) sessionInfo +{ + NSMutableString *str; + const char *tmp; + gnutls_credentials_type_t cred; + gnutls_kx_algorithm_t kx; + int dhe; + int ecdh; + + dhe = ecdh = 0; + str = [NSMutableString stringWithCapacity: 2000]; + + /* get the key exchange's algorithm name + */ + kx = gnutls_kx_get(session); + tmp = gnutls_kx_get_name(kx); + [str appendFormat: _(@"- Key Exchange: %s\n"), tmp]; + + /* Check the authentication type used and switch to the appropriate. + */ + cred = gnutls_auth_get_type(session); + switch (cred) + { + case GNUTLS_CRD_IA: + [str appendString: _(@"- TLS/IA session\n")]; + break; + + case GNUTLS_CRD_SRP: +#ifdef ENABLE_SRP + [str appendFormat: _(@"- SRP session with username %s\n"), + gnutls_srp_server_get_username(session)]; +#endif + break; + + case GNUTLS_CRD_PSK: +#if 0 + /* This returns NULL in server side. + */ + if (gnutls_psk_client_get_hint (session) != NULL) + { + [str appendFormat: _(@"- PSK authentication. PSK hint '%s'\n"), + gnutls_psk_client_get_hint(session)]; + } + /* This returns NULL in client side. + */ + if (gnutls_psk_server_get_username (session) != NULL) + { + [str appendFormat: _(@"- PSK authentication. Connected as '%s'\n"), + gnutls_psk_server_get_username(session)]; + } + + if (GNUTLS_KX_ECDHE_PSK == kx) + { + dhe = 0; + ecdh = 1; + } + else if (GNUTLS_KX_DHE_PSK == kx) + { + dhe = 1; + ecdh = 0; + } +#endif + break; + + case GNUTLS_CRD_ANON: /* anonymous authentication */ +#if 0 + [str appendFormat: _(@"- Anonymous authentication.\n")]; + if (GNUTLS_KX_ANON_ECDH == kx) + { + dhe = 0; + ecdh = 1; + } + else if (GNUTLS_KX_ANON_DH == kx) + { + dhe = 1; + ecdh = 0; + } +#endif + break; + + case GNUTLS_CRD_CERTIFICATE: /* certificate authentication */ + { + unsigned int cert_list_size = 0; + const gnutls_datum *cert_list; + gnutls_x509_crt cert; + + /* Check if we have been using ephemeral Diffie-Hellman. + */ + if (GNUTLS_KX_DHE_RSA == kx || GNUTLS_KX_DHE_DSS == kx) + { + dhe = 1; + ecdh = 0; + } +#if 0 + if (GNUTLS_KX_ECDHE_RSA == kx || GNUTLS_KX_ECDHE_ECDSA == kx) + { + dhe = 0; + ecdh = 1; + } +#endif + + /* if the certificate list is available, then + * print some information about it. + */ + cert_list = gnutls_certificate_get_peers(session, &cert_list_size); + if (cert_list_size > 0 + && gnutls_certificate_type_get(session) == GNUTLS_CRT_X509) + { + char dn[128]; + char serial[40]; + size_t dn_size = sizeof(dn); + size_t serial_size = sizeof(serial); + time_t expiret; + time_t activet; + int algo; + unsigned int bits; + int i; + int cert_num; + + for (cert_num = 0; cert_num < cert_list_size; cert_num++) + { + gnutls_x509_crt_init(&cert); + /* NB. the list of peer certificate is in memory in native + * format (DER) rather than the normal file format (PEM). + */ + gnutls_x509_crt_import(cert, + &cert_list[cert_num], GNUTLS_X509_FMT_DER); + + [str appendFormat: _(@"- Certificate %d info:\n"), cert_num]; + + expiret = gnutls_x509_crt_get_expiration_time(cert); + activet = gnutls_x509_crt_get_activation_time(cert); + [str appendFormat: _(@"- Certificate is valid since: %s"), + ctime(&activet)]; + [str appendFormat: _(@"- Certificate expires: %s"), + ctime (&expiret)]; + +#if 0 + { + char digest[20]; + size_t digest_size = sizeof(digest); + if (gnutls_x509_fingerprint(GNUTLS_DIG_MD5, + &cert_list[0], digest, &digest_size) >= 0) + { + [str appendString: _(@"- Certificate fingerprint: ")]; + for (i = 0; i < digest_size; i++) + { + [str appendFormat: @"%.2x ", (unsigned char)digest[i]]; + } + [str appendString: @"\n"]; + } + } +#endif + + if (gnutls_x509_crt_get_serial(cert, serial, &serial_size) >= 0) + { + [str appendString: _(@"- Certificate serial number: ")]; + for (i = 0; i < serial_size; i++) + { + [str appendFormat: @"%.2x ", (unsigned char)serial[i]]; + } + [str appendString: @"\n"]; + } + + [str appendString: _(@"- Certificate public key: ")]; + algo = gnutls_x509_crt_get_pk_algorithm(cert, &bits); + if (GNUTLS_PK_RSA == algo) + { + [str appendString: _(@"RSA\n")]; + [str appendFormat: _(@"- Modulus: %d bits\n"), bits]; + } + else if (GNUTLS_PK_DSA == algo) + { + [str appendString: _(@"DSA\n")]; + [str appendFormat: _(@"- Exponent: %d bits\n"), bits]; + } + else + { + [str appendString: _(@"UNKNOWN\n")]; + } + + [str appendFormat: _(@"- Certificate version: #%d\n"), + gnutls_x509_crt_get_version(cert)]; + + gnutls_x509_crt_get_dn(cert, dn, &dn_size); + [str appendFormat: @"- Certificate DN: %s\n", dn]; + + gnutls_x509_crt_get_issuer_dn (cert, dn, &dn_size); + [str appendFormat: _(@"- Certificate Issuer's DN: %s\n"), dn]; + + gnutls_x509_crt_deinit(cert); + } + } + } + break; + } /* switch */ + +#if 0 + if (ecdh != 0) + { + [str appendFormat: _(@"- Ephemeral ECDH using curve %s\n"), + gnutls_ecc_curve_get_name(gnutls_ecc_curve_get(session))]; + } +#endif + if (dhe != 0) + { + [str appendFormat: _(@"- Ephemeral DH using prime of %d bits\n"), + gnutls_dh_get_prime_bits(session)]; + } + + /* print the protocol's name (ie TLS 1.0) + */ + tmp = gnutls_protocol_get_name(gnutls_protocol_get_version(session)); + [str appendFormat: _(@"- Protocol: %s\n"), tmp]; + + /* print the certificate type of the peer. + * ie X.509 + */ + tmp = gnutls_certificate_type_get_name(gnutls_certificate_type_get(session)); + [str appendFormat: _(@"- Certificate Type: %s\n"), tmp]; + + /* print the compression algorithm (if any) + */ + tmp = gnutls_compression_get_name(gnutls_compression_get(session)); + [str appendFormat: _(@"- Compression: %s\n"), tmp]; + + /* print the name of the cipher used. + * ie 3DES. + */ + tmp = gnutls_cipher_get_name(gnutls_cipher_get(session)); + [str appendFormat: _(@"- Cipher: %s\n"), tmp]; + + /* Print the MAC algorithms name. + * ie SHA1 + */ + tmp = gnutls_mac_get_name(gnutls_mac_get(session)); + [str appendFormat: _(@"- MAC: %s\n"), tmp]; + + return str; +} + +- (int) verify +{ + BOOL debug = (globalDebug > 0) ? YES : NO; + unsigned int status; + const gnutls_datum_t *cert_list; + unsigned int cert_list_size; + int ret; + gnutls_x509_crt_t cert; + + if (NO == debug) + { + debug = [[opts objectForKey: GSTLSDebug] boolValue]; + } + + /* This verification function uses the trusted CAs in the credentials + * structure. So you must have installed one or more CA certificates. + */ + ret = gnutls_certificate_verify_peers2 (session, &status); + if (ret < 0) + { + NSLog(@"Error %s", gnutls_strerror(ret)); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) + NSLog(@"The certificate hasn't got a known issuer."); + + if (status & GNUTLS_CERT_REVOKED) + NSLog(@"The certificate has been revoked."); + +/* + if (status & GNUTLS_CERT_EXPIRED) + NSLog(@"The certificate has expired"); + + if (status & GNUTLS_CERT_NOT_ACTIVATED) + NSLog(@"The certificate is not yet activated"); +*/ + + if (status & GNUTLS_CERT_INVALID) + { + NSLog(@"The certificate is not trusted."); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + /* Up to here the process is the same for X.509 certificates and + * OpenPGP keys. From now on X.509 certificates are assumed. This can + * be easily extended to work with openpgp keys as well. + */ + if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509) + return GNUTLS_E_CERTIFICATE_ERROR; + + if (gnutls_x509_crt_init (&cert) < 0) + { + NSLog(@"error in initialization"); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + cert_list = gnutls_certificate_get_peers (session, &cert_list_size); + if (cert_list == NULL) + { + NSLog(@"No certificate was found!"); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0) + { + NSLog(@"error parsing certificate"); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + if (nil != host) + { + NSEnumerator *enumerator = [[host names] objectEnumerator]; + BOOL found = NO; + NSString *name; + + while (nil != (name = [enumerator nextObject])) + { + if (0 == gnutls_x509_crt_check_hostname(cert, [name UTF8String])) + { + found = YES; + break; + } + } + if (NO == found) + { + NSLog(@"The certificate's owner does not match host '%@'", host); + gnutls_x509_crt_deinit (cert); + return GNUTLS_E_CERTIFICATE_ERROR; + } + } + + gnutls_x509_crt_deinit (cert); + + return 0; // Verified +} + +@end + #endif diff --git a/Source/NSFileHandle.m b/Source/NSFileHandle.m index 4736c85f9..ee8b4f719 100644 --- a/Source/NSFileHandle.m +++ b/Source/NSFileHandle.m @@ -851,11 +851,11 @@ NSString * const NSFileHandleOperationException } if (nil != privateKey) { - [opts setObject: privateKey forKey: GSTLSPrivateKeyFile]; + [opts setObject: privateKey forKey: GSTLSCertificateKeyFile]; } if (nil != PEMpasswd) { - [opts setObject: PEMpasswd forKey: GSTLSPrivateKeyPassword]; + [opts setObject: PEMpasswd forKey: GSTLSCertificateKeyPassword]; } err = [self sslSetOptions: opts]; if (nil != err) @@ -879,14 +879,9 @@ NSString * const NSFileHandleOperationException @interface GSTLSHandle : GSFileHandle { - GSTLSDHParams *dhParams; @public - gnutls_session_t session; - gnutls_certificate_credentials_t certcred; - BOOL active; // able to read/write - BOOL handshake; // performing handshake - BOOL outgoing; // handshake direction - BOOL setup; // have set certificate + NSDictionary *opts; + GSTLSSession *session; } - (void) sslDisconnect; - (BOOL) sslHandshakeEstablished: (BOOL*)result outgoing: (BOOL)isOutgoing; @@ -908,7 +903,7 @@ GSTLSHandlePull(gnutls_transport_ptr_t handle, void *buffer, size_t len) if (result < 0) { #if HAVE_GNUTLS_TRANSPORT_SET_ERRNO - gnutls_transport_set_errno (tls->session, errno); + gnutls_transport_set_errno (tls->session->session, errno); #endif } return result; @@ -928,7 +923,7 @@ GSTLSHandlePush(gnutls_transport_ptr_t handle, const void *buffer, size_t len) if (result < 0) { #if HAVE_GNUTLS_TRANSPORT_SET_ERRNO - gnutls_transport_set_errno (tls->session, errno); + gnutls_transport_set_errno (tls->session->session, errno); #endif } return result; @@ -950,46 +945,38 @@ GSTLSHandlePush(gnutls_transport_ptr_t handle, const void *buffer, size_t len) [super closeFile]; } +- (void) dealloc +{ + DESTROY(opts); + DESTROY(session); + [super dealloc]; +} + - (void) finalize { [self sslDisconnect]; - if (YES == setup) - { - setup = NO; - gnutls_certificate_free_credentials (certcred); - } [super finalize]; } - (NSInteger) read: (void*)buf length: (NSUInteger)len { - if (YES == active) + if (YES == [session active]) { - return gnutls_record_recv (session, buf, len); + return [session read: buf length: len]; } return [super read: buf length: len]; } - (void) sslDisconnect { - if (YES == active || YES == handshake) - { - active = NO; - handshake = NO; - gnutls_bye (session, GNUTLS_SHUT_RDWR); - gnutls_db_remove_session (session); - gnutls_deinit (session); - } - DESTROY(dhParams); + [session disconnect]; } - (BOOL) sslHandshakeEstablished: (BOOL*)result outgoing: (BOOL)isOutgoing { - int ret; - NSAssert(0 != result, NSInvalidArgumentException); - if (YES == active) + if (YES == [session active]) { return YES; /* Already connected. */ } @@ -997,125 +984,31 @@ GSTLSHandlePush(gnutls_transport_ptr_t handle, const void *buffer, size_t len) if (YES == isStandardFile) { NSLog(@"Attempt to perform ssl handshake with a standard file"); - return NO; + return YES; } /* Set the handshake direction so we know how to set up the connection. */ - outgoing = isOutgoing; - - if (NO == setup) + if (nil == session) { - [self sslSetCertificate: nil privateKey: nil PEMpasswd: nil]; + session = [[GSTLSSession alloc] initWithOptions: opts + direction: isOutgoing + transport: (void*)self + push: GSTLSHandlePush + pull: GSTLSHandlePull + host: nil]; } - if (NO == handshake) + if (NO == [session handshake]) { - handshake = YES; // Set flag to say a handshake is in progress - - /* Now initialise session and set it up - */ - if (YES == outgoing) - { - gnutls_init (&session, GNUTLS_CLIENT); - } - else - { - gnutls_init (&session, GNUTLS_SERVER); - - /* We don't request any certificate from the client. - * If we did we would need to verify it. - */ - gnutls_certificate_server_set_request (session, GNUTLS_CERT_IGNORE); - - /* FIXME ... need to set up DH information and key/certificate. */ - } - gnutls_set_default_priority (session); - -#if GNUTLS_VERSION_NUMBER < 0x020C00 - { - const int proto_prio[2] = { - GNUTLS_SSL3, - 0 }; - gnutls_protocol_set_priority (session, proto_prio); - } -#else - gnutls_priority_set_direct(session, - "NORMAL:-VERS-TLS-ALL:+VERS-SSL3.0", NULL); -#endif - -/* - { - const int kx_prio[] = { - GNUTLS_KX_RSA, - GNUTLS_KX_RSA_EXPORT, - GNUTLS_KX_DHE_RSA, - GNUTLS_KX_DHE_DSS, - GNUTLS_KX_ANON_DH, - 0 }; - gnutls_kx_set_priority (session, kx_prio); - gnutls_credentials_set (session, GNUTLS_CRD_ANON, anoncred); - } - */ - - - /* Set certificate credentials for this session. - */ - gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, certcred); - - /* Set transport layer to use our low level stream code. - */ -#if GNUTLS_VERSION_NUMBER < 0x020C00 - gnutls_transport_set_lowat (session, 0); -#endif - gnutls_transport_set_pull_function (session, GSTLSHandlePull); - gnutls_transport_set_push_function (session, GSTLSHandlePush); - gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t)self); - - [self setNonBlocking: YES]; - } - - if (outgoing != isOutgoing) - { - [NSException raise: NSInvalidArgumentException - format: @"Attempt to change direction of TLS handshake"]; - } - - ret = gnutls_handshake (session); - if (ret < 0) - { - if (gnutls_error_is_fatal(ret)) - { - NSLog(@"unable to make SSL connection to %@:%@ - %s", - address, service, gnutls_strerror(ret)); - *result = NO; - } - else - { - if (GSDebugSet(@"NSStream") == YES) - { - gnutls_perror(ret); - } - return NO; // Non-fatal error needs a retry. - } + return NO; // Need more. } else { - handshake = NO; // Handshake is now complete. - active = YES; // The TLS session is now active. -{ - ret = [GSTLSObject verify: session]; - if (ret < 0) - { - NSLog(@"unable to verify SSL connection to %@:%@ - %s", - address, service, gnutls_strerror(ret)); - // active = NO; + *result = [session active]; + return YES; } } - *result = active; - } - return YES; -} - (NSString*) sslSetOptions: (NSDictionary*)options { @@ -1123,93 +1016,15 @@ GSTLSHandlePush(gnutls_transport_ptr_t handle, const void *buffer, size_t len) { return @"Attempt to set ssl options for a standard file"; } - - if (NO == setup) - { - NSString *certFile; - NSString *privateKey; - NSString *PEMpasswd; - GSTLSPrivateKey *key = nil; - GSTLSCertificateList *list = nil; - int ret; - - certFile = [options objectForKey: GSTLSCertificateFile]; - privateKey = [options objectForKey: GSTLSPrivateKeyFile]; - PEMpasswd = [options objectForKey: GSTLSPrivateKeyPassword]; - - if (nil != privateKey) - { - key = [GSTLSPrivateKey keyFromFile: privateKey - withPassword: PEMpasswd]; - if (nil == key) - { - return @"Unable to load key file"; - } - } - - if (nil != certFile) - { - list = [GSTLSCertificateList listFromFile: certFile]; - if (nil == list) - { - return @"Unable to load certificate file"; - } - } - - setup = YES; - - /* Configure this session to support certificate based - * operation. - */ - gnutls_certificate_allocate_credentials (&certcred); - - /* FIXME ... should get the trusted authority certificates - * from somewhere sensible to validate the remote end! - */ - gnutls_certificate_set_x509_trust_file - (certcred, "ca.pem", GNUTLS_X509_FMT_PEM); - -/* - gnutls_certificate_set_x509_crl_file - (certcred, "crl.pem", GNUTLS_X509_FMT_PEM); - gnutls_certificate_set_verify_function (certcred, - _verify_certificate_callback); - -*/ - - if (nil != list) - { - ret = gnutls_certificate_set_x509_key (certcred, - [list certificateList], [list count], [key key]); - if (ret < 0) - { - return [NSString stringWithFormat: - @"Unable to set certificate for session: %s", - gnutls_strerror(ret)]; - } - else if (NO == outgoing) - { - dhParams = [[GSTLSDHParams current] retain]; - gnutls_certificate_set_dh_params (certcred, [dhParams params]); - } - } - -#if 0 - if (nil != cipherList) - { - SSL_CTX_set_cipher_list(ctx, [cipherList UTF8String]); - } -#endif - - } + ASSIGNCOPY(opts, options); return nil; } - (NSInteger) write: (const void*)buf length: (NSUInteger)len { - if (YES == active) + if (YES == [session active]) { - return gnutls_record_send (session, buf, len); + return [session write: buf length: len]; } return [super write: buf length: len]; }