mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-23 09:04:13 +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
bca4db00c9
commit
db9c84ff4e
6 changed files with 921 additions and 482 deletions
|
@ -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 <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
|
||||
* of the connection. This option <em>must</em> be set for handing an
|
||||
* 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>
|
||||
* <term>GSTLSPrivateKeyFile</term>
|
||||
* <term>GSTLSCertificateKeyFile</term>
|
||||
* <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
|
||||
* encrypted, but if it is encrypted you must specify
|
||||
* GSTLSPrivateKeyPassword.
|
||||
* GSTLSCertificateKeyPassword.
|
||||
* </desc>
|
||||
* <term>GSTLSPrivateKeyPassword</term>
|
||||
* <term>GSTLSCertificateKeyPassword</term>
|
||||
* <desc>A string to be used as the password to decrypt a key which was
|
||||
* specified using GSTLSKeyPassword.
|
||||
* </desc>
|
||||
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -42,29 +42,10 @@
|
|||
#include <gcrypt.h>
|
||||
#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.<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
|
||||
|
||||
/* 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).<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
|
||||
|
||||
|
|
881
Source/GSTLS.m
881
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 <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
|
||||
|
||||
@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
|
||||
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue