mirror of
https://github.com/gnustep/libs-base.git
synced 2025-05-31 16:50:58 +00:00
more tls reorganisation
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@35605 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
c2808eb88e
commit
f05d94e9e3
6 changed files with 921 additions and 482 deletions
|
@ -2,6 +2,12 @@
|
||||||
|
|
||||||
* Source/NSUserDefaults.m: Fix error synchronising from changes in
|
* Source/NSUserDefaults.m: Fix error synchronising from changes in
|
||||||
database ... was not sending the notification to say we had updated.
|
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 <rfm@gnu.org>
|
2012-09-25 Richard Frith-Macdonald <rfm@gnu.org>
|
||||||
|
|
||||||
|
|
|
@ -279,15 +279,15 @@ GS_EXPORT NSString * const NSFileHandleOperationException;
|
||||||
* <desc>The path to a PEM encoded certificate used to identify this end
|
* <desc>The path to a PEM encoded certificate used to identify this end
|
||||||
* of the connection. This option <em>must</em> be set for handing an
|
* of the connection. This option <em>must</em> be set for handing an
|
||||||
* incoming connection, but is optional for outgoing connections.<br />
|
* incoming connection, but is optional for outgoing connections.<br />
|
||||||
* This must be used in conjunction with GSTLSPrivateKeyFile.
|
* This must be used in conjunction with GSTLSCertificateKeyFile.
|
||||||
* </desc>
|
* </desc>
|
||||||
* <term>GSTLSPrivateKeyFile</term>
|
* <term>GSTLSCertificateKeyFile</term>
|
||||||
* <desc>The path to a PEM encoded key used to unlock the certificate
|
* <desc>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
|
* file for the connection. The key in the file may or may not be
|
||||||
* encrypted, but if it is encrypted you must specify
|
* encrypted, but if it is encrypted you must specify
|
||||||
* GSTLSPrivateKeyPassword.
|
* GSTLSCertificateKeyPassword.
|
||||||
* </desc>
|
* </desc>
|
||||||
* <term>GSTLSPrivateKeyPassword</term>
|
* <term>GSTLSCertificateKeyPassword</term>
|
||||||
* <desc>A string to be used as the password to decrypt a key which was
|
* <desc>A string to be used as the password to decrypt a key which was
|
||||||
* specified using GSTLSKeyPassword.
|
* specified using GSTLSKeyPassword.
|
||||||
* </desc>
|
* </desc>
|
||||||
|
@ -305,12 +305,12 @@ GS_EXPORT NSString * const GSTLSCertificateFile;
|
||||||
/** Dictionary key for the path to a PEM encoded private key used
|
/** Dictionary key for the path to a PEM encoded private key used
|
||||||
* to unlock the certificate used by this end of a connection.
|
* 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
|
/** Dictionary key for the password used to decrypt the key file used
|
||||||
* to unlock the certificate used by this end of a connection.
|
* to unlock the certificate used by this end of a connection.
|
||||||
*/
|
*/
|
||||||
GS_EXPORT NSString * const GSTLSPrivateKeyPassword;
|
GS_EXPORT NSString * const GSTLSCertificateKeyPassword;
|
||||||
|
|
||||||
// GNUstep Notification names.
|
// GNUstep Notification names.
|
||||||
|
|
||||||
|
|
|
@ -353,8 +353,7 @@ GSPrivateSockaddrSetup(NSString *machine, uint16_t port,
|
||||||
@interface GSTLSHandler : GSStreamHandler
|
@interface GSTLSHandler : GSStreamHandler
|
||||||
{
|
{
|
||||||
@public
|
@public
|
||||||
gnutls_session_t session;
|
GSTLSSession *session;
|
||||||
gnutls_certificate_credentials_t certcred;
|
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@ -381,7 +380,7 @@ GSTLSPull(gnutls_transport_ptr_t handle, void *buffer, size_t len)
|
||||||
e = EAGAIN; // Tell GNUTLS this would block.
|
e = EAGAIN; // Tell GNUTLS this would block.
|
||||||
}
|
}
|
||||||
#if HAVE_GNUTLS_TRANSPORT_SET_ERRNO
|
#if HAVE_GNUTLS_TRANSPORT_SET_ERRNO
|
||||||
gnutls_transport_set_errno (tls->session, e);
|
gnutls_transport_set_errno (tls->session->session, e);
|
||||||
#else
|
#else
|
||||||
errno = e; // Not thread-safe
|
errno = e; // Not thread-safe
|
||||||
#endif
|
#endif
|
||||||
|
@ -412,7 +411,7 @@ GSTLSPush(gnutls_transport_ptr_t handle, const void *buffer, size_t len)
|
||||||
e = EAGAIN; // Tell GNUTLS this would block.
|
e = EAGAIN; // Tell GNUTLS this would block.
|
||||||
}
|
}
|
||||||
#if HAVE_GNUTLS_TRANSPORT_SET_ERRNO
|
#if HAVE_GNUTLS_TRANSPORT_SET_ERRNO
|
||||||
gnutls_transport_set_errno (tls->session, e);
|
gnutls_transport_set_errno (tls->session->session, e);
|
||||||
#else
|
#else
|
||||||
errno = e; // Not thread-safe
|
errno = e; // Not thread-safe
|
||||||
#endif
|
#endif
|
||||||
|
@ -459,20 +458,15 @@ GSTLSPush(gnutls_transport_ptr_t handle, const void *buffer, size_t len)
|
||||||
|
|
||||||
- (void) bye
|
- (void) bye
|
||||||
{
|
{
|
||||||
if (active == YES || handshake == YES)
|
handshake = NO;
|
||||||
{
|
active = NO;
|
||||||
active = NO;
|
[session disconnect];
|
||||||
handshake = NO;
|
|
||||||
gnutls_bye (session, GNUTLS_SHUT_RDWR);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) dealloc
|
- (void) dealloc
|
||||||
{
|
{
|
||||||
[self bye];
|
[self bye];
|
||||||
gnutls_db_remove_session (session);
|
DESTROY(session);
|
||||||
gnutls_deinit (session);
|
|
||||||
gnutls_certificate_free_credentials (certcred);
|
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -485,28 +479,16 @@ GSTLSPush(gnutls_transport_ptr_t handle, const void *buffer, size_t len)
|
||||||
{
|
{
|
||||||
if (active == NO)
|
if (active == NO)
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (handshake == NO)
|
if (handshake == NO)
|
||||||
{
|
{
|
||||||
/* Set flag to say we are now doing a handshake.
|
/* Set flag to say we are now doing a handshake.
|
||||||
*/
|
*/
|
||||||
handshake = YES;
|
handshake = YES;
|
||||||
}
|
}
|
||||||
ret = gnutls_handshake (session);
|
if ([session handshake] == YES)
|
||||||
if (ret < 0)
|
|
||||||
{
|
{
|
||||||
NSDebugMLLog(@"NSStream",
|
handshake = NO; // Handshake is now complete.
|
||||||
@"Handshake status %d", ret);
|
active = [session active]; // The TLS session is now active.
|
||||||
if (GSDebugSet(@"NSStream") == YES)
|
|
||||||
{
|
|
||||||
gnutls_perror(ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
handshake = NO; // Handshake is now complete.
|
|
||||||
active = YES; // 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
|
- (id) initWithInput: (GSSocketInputStream*)i
|
||||||
output: (GSSocketOutputStream*)o
|
output: (GSSocketOutputStream*)o
|
||||||
{
|
{
|
||||||
NSString *proto = [i propertyForKey: NSStreamSocketSecurityLevelKey];
|
NSString *proto;
|
||||||
|
NSDictionary *opts;
|
||||||
BOOL server = NO;
|
BOOL server = NO;
|
||||||
|
|
||||||
|
proto = [i propertyForKey: NSStreamSocketSecurityLevelKey];
|
||||||
/* FIXME
|
/* FIXME
|
||||||
if ([[o propertyForKey: NSStreamSocketCertificateServerKey] boolValue] == YES)
|
if ([[o propertyForKey: NSStreamSocketCertificateServerKey] boolValue] == YES)
|
||||||
{
|
{
|
||||||
|
@ -539,119 +523,22 @@ GSTLSPush(gnutls_transport_ptr_t handle, const void *buffer, size_t len)
|
||||||
DESTROY(self);
|
DESTROY(self);
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
if ([proto isEqualToString: NSStreamSocketSecurityLevelNone] == YES)
|
opts = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||||
{
|
proto, NSStreamSocketSecurityLevelKey,
|
||||||
// proto = NSStreamSocketSecurityLevelNone;
|
nil];
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((self = [super initWithInput: i output: o]) == nil)
|
if ((self = [super initWithInput: i output: o]) == nil)
|
||||||
{
|
{
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
session = [[GSTLSSession alloc] initWithOptions: opts
|
||||||
|
direction: (server ? NO : YES)
|
||||||
|
transport: (void*)self
|
||||||
|
push: GSTLSPush
|
||||||
|
pull: GSTLSPull
|
||||||
|
host: nil];
|
||||||
initialised = YES;
|
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;
|
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
|
- (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
|
- (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
|
- (NSInteger) write: (const uint8_t *)buffer maxLength: (NSUInteger)len
|
||||||
{
|
{
|
||||||
return gnutls_record_send (session, buffer, len);
|
return [session write: buffer length: len];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -42,29 +42,10 @@
|
||||||
#include <gcrypt.h>
|
#include <gcrypt.h>
|
||||||
#undef id
|
#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
|
/* 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
|
@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.<br />
|
|
||||||
* 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
|
@end
|
||||||
|
|
||||||
/* This class provides the current autogenerated Diffie Hellman parameters
|
/* This class provides the current autogenerated Diffie Hellman parameters
|
||||||
|
@ -122,5 +103,78 @@
|
||||||
- (gnutls_x509_privkey_t) key;
|
- (gnutls_x509_privkey_t) key;
|
||||||
@end
|
@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).<br />
|
||||||
|
*/
|
||||||
|
- (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
|
#endif
|
||||||
|
|
||||||
|
|
881
Source/GSTLS.m
881
Source/GSTLS.m
|
@ -31,6 +31,8 @@
|
||||||
#import "Foundation/NSException.h"
|
#import "Foundation/NSException.h"
|
||||||
#import "Foundation/NSLock.h"
|
#import "Foundation/NSLock.h"
|
||||||
#import "Foundation/NSNotification.h"
|
#import "Foundation/NSNotification.h"
|
||||||
|
#import "Foundation/NSProcessInfo.h"
|
||||||
|
#import "Foundation/NSStream.h"
|
||||||
#import "Foundation/NSThread.h"
|
#import "Foundation/NSThread.h"
|
||||||
#import "Foundation/NSUserDefaults.h"
|
#import "Foundation/NSUserDefaults.h"
|
||||||
|
|
||||||
|
@ -38,9 +40,15 @@
|
||||||
|
|
||||||
#import "GSPrivate.h"
|
#import "GSPrivate.h"
|
||||||
|
|
||||||
|
/* Constants to control TLS/SSL (options).
|
||||||
|
*/
|
||||||
|
NSString * const GSTLSCAFile = @"GSTLSCAFile";
|
||||||
NSString * const GSTLSCertificateFile = @"GSTLSCertificateFile";
|
NSString * const GSTLSCertificateFile = @"GSTLSCertificateFile";
|
||||||
NSString * const GSTLSPrivateKeyFile = @"GSTLSPrivateKeyFile";
|
NSString * const GSTLSCertificateKeyFile = @"GSTLSCertificateKeyFile";
|
||||||
NSString * const GSTLSPrivateKeyPassword = @"GSTLSPrivateKeyPassword";
|
NSString * const GSTLSCertificateKeyPassword = @"GSTLSCertificateKeyPassword";
|
||||||
|
NSString * const GSTLSDebug = @"GSTLSDebug";
|
||||||
|
NSString * const GSTLSCAVerify = @"GSTLSCAVerify";
|
||||||
|
|
||||||
|
|
||||||
#if defined(HAVE_GNUTLS)
|
#if defined(HAVE_GNUTLS)
|
||||||
|
|
||||||
|
@ -82,6 +90,33 @@ GSTLSLog(int level, const char *msg)
|
||||||
NSLog(@"%s", 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 NSString *cipherList = nil;
|
||||||
|
|
||||||
static gnutls_anon_client_credentials_t anoncred;
|
static gnutls_anon_client_credentials_t anoncred;
|
||||||
|
@ -93,8 +128,36 @@ static gnutls_anon_client_credentials_t anoncred;
|
||||||
|
|
||||||
+ (void) _defaultsChanged: (NSNotification*)n
|
+ (void) _defaultsChanged: (NSNotification*)n
|
||||||
{
|
{
|
||||||
|
NSString *str;
|
||||||
|
|
||||||
cipherList
|
cipherList
|
||||||
= [[NSUserDefaults standardUserDefaults] stringForKey: @"GSCipherList"];
|
= [[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
|
+ (void) initialize
|
||||||
|
@ -106,11 +169,35 @@ static gnutls_anon_client_credentials_t anoncred;
|
||||||
if (beenHere == NO)
|
if (beenHere == NO)
|
||||||
{
|
{
|
||||||
NSUserDefaults *defs;
|
NSUserDefaults *defs;
|
||||||
|
NSProcessInfo *pi;
|
||||||
|
NSString *str;
|
||||||
|
|
||||||
beenHere = YES;
|
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];
|
defs = [NSUserDefaults standardUserDefaults];
|
||||||
cipherList = [defs stringForKey: @"GSCipherList"];
|
|
||||||
[[NSNotificationCenter defaultCenter]
|
[[NSNotificationCenter defaultCenter]
|
||||||
addObserver: self
|
addObserver: self
|
||||||
selector: @selector(_defaultsChanged:)
|
selector: @selector(_defaultsChanged:)
|
||||||
|
@ -132,109 +219,12 @@ static gnutls_anon_client_credentials_t anoncred;
|
||||||
/* Enable gnutls logging via NSLog
|
/* Enable gnutls logging via NSLog
|
||||||
*/
|
*/
|
||||||
gnutls_global_set_log_function (GSTLSLog);
|
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 <GSTLSOwner> owner;
|
|
||||||
NSHost *host;
|
|
||||||
NSDictionary *options;
|
|
||||||
|
|
||||||
/* read hostname */
|
|
||||||
owner = (id<GSTLSOwner>)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
|
@end
|
||||||
|
|
||||||
@implementation GSTLSDHParams
|
@implementation GSTLSDHParams
|
||||||
|
@ -711,5 +701,692 @@ static NSMutableDictionary *privateKeyCache1 = nil;
|
||||||
}
|
}
|
||||||
@end
|
@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
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -851,11 +851,11 @@ NSString * const NSFileHandleOperationException
|
||||||
}
|
}
|
||||||
if (nil != privateKey)
|
if (nil != privateKey)
|
||||||
{
|
{
|
||||||
[opts setObject: privateKey forKey: GSTLSPrivateKeyFile];
|
[opts setObject: privateKey forKey: GSTLSCertificateKeyFile];
|
||||||
}
|
}
|
||||||
if (nil != PEMpasswd)
|
if (nil != PEMpasswd)
|
||||||
{
|
{
|
||||||
[opts setObject: PEMpasswd forKey: GSTLSPrivateKeyPassword];
|
[opts setObject: PEMpasswd forKey: GSTLSCertificateKeyPassword];
|
||||||
}
|
}
|
||||||
err = [self sslSetOptions: opts];
|
err = [self sslSetOptions: opts];
|
||||||
if (nil != err)
|
if (nil != err)
|
||||||
|
@ -879,14 +879,9 @@ NSString * const NSFileHandleOperationException
|
||||||
|
|
||||||
@interface GSTLSHandle : GSFileHandle
|
@interface GSTLSHandle : GSFileHandle
|
||||||
{
|
{
|
||||||
GSTLSDHParams *dhParams;
|
|
||||||
@public
|
@public
|
||||||
gnutls_session_t session;
|
NSDictionary *opts;
|
||||||
gnutls_certificate_credentials_t certcred;
|
GSTLSSession *session;
|
||||||
BOOL active; // able to read/write
|
|
||||||
BOOL handshake; // performing handshake
|
|
||||||
BOOL outgoing; // handshake direction
|
|
||||||
BOOL setup; // have set certificate
|
|
||||||
}
|
}
|
||||||
- (void) sslDisconnect;
|
- (void) sslDisconnect;
|
||||||
- (BOOL) sslHandshakeEstablished: (BOOL*)result outgoing: (BOOL)isOutgoing;
|
- (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 (result < 0)
|
||||||
{
|
{
|
||||||
#if HAVE_GNUTLS_TRANSPORT_SET_ERRNO
|
#if HAVE_GNUTLS_TRANSPORT_SET_ERRNO
|
||||||
gnutls_transport_set_errno (tls->session, errno);
|
gnutls_transport_set_errno (tls->session->session, errno);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -928,7 +923,7 @@ GSTLSHandlePush(gnutls_transport_ptr_t handle, const void *buffer, size_t len)
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
{
|
{
|
||||||
#if HAVE_GNUTLS_TRANSPORT_SET_ERRNO
|
#if HAVE_GNUTLS_TRANSPORT_SET_ERRNO
|
||||||
gnutls_transport_set_errno (tls->session, errno);
|
gnutls_transport_set_errno (tls->session->session, errno);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -950,46 +945,38 @@ GSTLSHandlePush(gnutls_transport_ptr_t handle, const void *buffer, size_t len)
|
||||||
[super closeFile];
|
[super closeFile];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void) dealloc
|
||||||
|
{
|
||||||
|
DESTROY(opts);
|
||||||
|
DESTROY(session);
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
- (void) finalize
|
- (void) finalize
|
||||||
{
|
{
|
||||||
[self sslDisconnect];
|
[self sslDisconnect];
|
||||||
if (YES == setup)
|
|
||||||
{
|
|
||||||
setup = NO;
|
|
||||||
gnutls_certificate_free_credentials (certcred);
|
|
||||||
}
|
|
||||||
[super finalize];
|
[super finalize];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSInteger) read: (void*)buf length: (NSUInteger)len
|
- (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];
|
return [super read: buf length: len];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) sslDisconnect
|
- (void) sslDisconnect
|
||||||
{
|
{
|
||||||
if (YES == active || YES == handshake)
|
[session disconnect];
|
||||||
{
|
|
||||||
active = NO;
|
|
||||||
handshake = NO;
|
|
||||||
gnutls_bye (session, GNUTLS_SHUT_RDWR);
|
|
||||||
gnutls_db_remove_session (session);
|
|
||||||
gnutls_deinit (session);
|
|
||||||
}
|
|
||||||
DESTROY(dhParams);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL) sslHandshakeEstablished: (BOOL*)result outgoing: (BOOL)isOutgoing
|
- (BOOL) sslHandshakeEstablished: (BOOL*)result outgoing: (BOOL)isOutgoing
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
|
|
||||||
NSAssert(0 != result, NSInvalidArgumentException);
|
NSAssert(0 != result, NSInvalidArgumentException);
|
||||||
|
|
||||||
if (YES == active)
|
if (YES == [session active])
|
||||||
{
|
{
|
||||||
return YES; /* Already connected. */
|
return YES; /* Already connected. */
|
||||||
}
|
}
|
||||||
|
@ -997,125 +984,31 @@ GSTLSHandlePush(gnutls_transport_ptr_t handle, const void *buffer, size_t len)
|
||||||
if (YES == isStandardFile)
|
if (YES == isStandardFile)
|
||||||
{
|
{
|
||||||
NSLog(@"Attempt to perform ssl handshake with a standard file");
|
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.
|
/* Set the handshake direction so we know how to set up the connection.
|
||||||
*/
|
*/
|
||||||
outgoing = isOutgoing;
|
if (nil == session)
|
||||||
|
|
||||||
if (NO == setup)
|
|
||||||
{
|
{
|
||||||
[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
|
return NO; // Need more.
|
||||||
|
|
||||||
/* 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.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
handshake = NO; // Handshake is now complete.
|
*result = [session active];
|
||||||
active = YES; // The TLS session is now active.
|
return YES;
|
||||||
{
|
|
||||||
ret = [GSTLSObject verify: session];
|
|
||||||
if (ret < 0)
|
|
||||||
{
|
|
||||||
NSLog(@"unable to verify SSL connection to %@:%@ - %s",
|
|
||||||
address, service, gnutls_strerror(ret));
|
|
||||||
// active = NO;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*result = active;
|
|
||||||
}
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString*) sslSetOptions: (NSDictionary*)options
|
- (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";
|
return @"Attempt to set ssl options for a standard file";
|
||||||
}
|
}
|
||||||
|
ASSIGNCOPY(opts, options);
|
||||||
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
|
|
||||||
|
|
||||||
}
|
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSInteger) write: (const void*)buf length: (NSUInteger)len
|
- (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];
|
return [super write: buf length: len];
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue