diff --git a/ChangeLog b/ChangeLog index 77bcfbf24..c49efbd7b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2013-09-18 Richard Frith-Macdonald + + * Source/GSTLS.h: + * Source/GSTLS.m: + * Source/NSFileHandle.m: + * Headers/Foundation/NSFileHandle.h: + Add mechanism to set the contents of TLS certificate and key files + in memory rather than reading from disk. Expose TLS property keys + in the NSFileHandle header to allow more control over certificates. + 2013-09-10 Richard Frith-Macdonald * configure.ac: Check for another unicode header diff --git a/Headers/Foundation/NSFileHandle.h b/Headers/Foundation/NSFileHandle.h index e49fe1484..56f8f24dc 100644 --- a/Headers/Foundation/NSFileHandle.h +++ b/Headers/Foundation/NSFileHandle.h @@ -273,7 +273,9 @@ GS_EXPORT NSString * const NSFileHandleOperationException; * Sets options to be used to configure this channel before the handshake.
* Returns nil on success, or an error message if some options could not * be set.
- * Expects key value pairs with the follwiing names/meanings: + * You may use the same options as property settings with the GNUstep + * implementation of NSStream.
+ * Expects key value pairs with the follwing names/meanings: * * GSTLSCAFile * A string identifying the full path to the file containing any @@ -324,8 +326,22 @@ GS_EXPORT NSString * const NSFileHandleOperationException; */ - (NSString*) sslSetOptions: (NSDictionary*)options; +/** Sets the known (cached) data content for the specified file name.
+ * Calling this with a nil data object will remove any existing value + * from the cache.
+ * You may use this method to control what data is used for specified + * file names when those file names are used as a result of SSL/TLS + * options being set for a file handle or stream. + */ ++ (void) setData: (NSData*)data forTLSFile: (NSString*)fileName; + @end +/** Dictionary key for the path to a PEM encoded certificate authority + * file. + */ +GS_EXPORT NSString * const GSTLSCAFile; + /** Dictionary key for the path to a PEM encoded certificate used * to identify this end of a connection. */ @@ -341,6 +357,27 @@ GS_EXPORT NSString * const GSTLSCertificateKeyFile; */ GS_EXPORT NSString * const GSTLSCertificateKeyPassword; +/** Dictionary key for a boolean to enable TLS debug for a session. + */ +GS_EXPORT NSString * const GSTLSDebug; + +/** Dictionary key for a GNUTLS priority setting for a session. + */ +GS_EXPORT NSString * const GSTLSPriority; + +/** Dictionary key for a list of hosts to use in certificate verification. + */ +GS_EXPORT NSString * const GSTLSRemoteHosts; + +/** Dictionary key for the path to a PEM encoded certificate revocation + * file. + */ +GS_EXPORT NSString * const GSTLSRevokeFile; + +/** Dictionary key for a boolean to enable certificate verification. + */ +GS_EXPORT NSString * const GSTLSVerify; + // GNUstep Notification names. /** @@ -360,6 +397,7 @@ GS_EXPORT NSString * const GSFileHandleWriteCompletionNotification; * operation. */ GS_EXPORT NSString * const GSFileHandleNotificationError; + #endif #if defined(__cplusplus) diff --git a/Source/GSTLS.h b/Source/GSTLS.h index fd744c6ee..6b85e3e99 100644 --- a/Source/GSTLS.h +++ b/Source/GSTLS.h @@ -53,9 +53,27 @@ extern NSString * const GSTLSVerify; #undef id /* This class is used to ensure that the GNUTLS system is initialised - * and thread-safe. + * and thread-safe. It also provides a mechanism to save certificate + * and key information in memory by associating a 'filename' with the + * cached data. */ @interface GSTLSObject : NSObject + +/** Returns either the cached data for this file name (if any), or the + * result of calling [NSData+dataWithContentsOfFile:] if there is no + * cached data.
+ * This method is used internally to load certificates and keys. + */ ++ (NSData*) dataForTLSFile: (NSString*)fileName; + +/** Sets the known (cached) data content for the specified file name.
+ * Calling this with a nil data object will remove any existing value + * from the cache.
+ * You may use this method to control what data is used for specified + * file names. + */ ++ (void) setData: (NSData*)data forTLSFile: (NSString*)fileName; + @end /* This class provides the current autogenerated Diffie Hellman parameters diff --git a/Source/GSTLS.m b/Source/GSTLS.m index 1bf043e62..c374035d0 100644 --- a/Source/GSTLS.m +++ b/Source/GSTLS.m @@ -152,6 +152,9 @@ static gnutls_anon_client_credentials_t anoncred; */ @implementation GSTLSObject +static NSLock *fileLock = nil; +static NSMutableDictionary *fileMap = nil; + + (void) _defaultsChanged: (NSNotification*)n { NSString *str; @@ -214,6 +217,25 @@ static gnutls_anon_client_credentials_t anoncred; gnutls_global_set_log_level(globalDebug); } ++ (NSData*) dataForTLSFile: (NSString*)fileName +{ + NSData *result; + + if (NO == [fileName isKindOfClass: [NSString class]]) + { + [NSException raise: NSInvalidArgumentException + format: @"[GSTLS+dataForTLSFile:] called with bad file name"]; + } + [fileLock lock]; + result = [[fileMap objectForKey: fileName] retain]; + [fileLock unlock]; + if (nil == result) + { + return [NSData dataWithContentsOfFile: fileName]; + } + return [result autorelease]; +} + + (void) initialize { if ([GSTLSObject class] == self) @@ -230,6 +252,9 @@ static gnutls_anon_client_credentials_t anoncred; bundle = [NSBundle bundleForClass: [NSObject class]]; + fileLock = [NSLock new]; + fileMap = [NSMutableDictionary new]; + /* Let the GS_TLS_CA_FILE environment variable override the * default certificate authority location. */ @@ -309,6 +334,23 @@ static gnutls_anon_client_credentials_t anoncred; } } ++ (void) setData: (NSData*)data forTLSFile: (NSString*)fileName +{ + if (nil != data && NO == [data isKindOfClass: [NSData class]]) + { + [NSException raise: NSInvalidArgumentException + format: @"[GSTLS+setData:forTLSFile:] called with bad data"]; + } + if (NO == [fileName isKindOfClass: [NSString class]]) + { + [NSException raise: NSInvalidArgumentException + format: @"[GSTLS+setData:forTLSFile:] called with bad file"]; + } + [fileLock lock]; + [fileMap setObject: data forKey: fileName]; + [fileLock unlock]; +} + @end @implementation GSTLSDHParams @@ -440,7 +482,7 @@ static GSTLSDHParams *paramsCurrent = nil; int ret; gnutls_datum_t datum; - data = [NSData dataWithContentsOfFile: f]; + data = [[self class] dataForTLSFile: f]; if (nil == data) { NSLog(@"Unable to read DF params file '%@'", f); @@ -629,7 +671,7 @@ static NSMutableDictionary *certificateListCache = nil; unsigned int count = 100; gnutls_x509_crt_t crts[count]; - data = [NSData dataWithContentsOfFile: f]; + data = [[self class] dataForTLSFile: f]; if (nil == data) { NSLog(@"Unable to read certificate file '%@'", f); @@ -832,7 +874,7 @@ static NSMutableDictionary *privateKeyCache1 = nil; int ret; gnutls_datum_t datum; - data = [NSData dataWithContentsOfFile: f]; + data = [[self class] dataForTLSFile: f]; if (nil == data) { NSLog(@"Unable to read private key file '%@'", f); diff --git a/Source/NSFileHandle.m b/Source/NSFileHandle.m index 30eac4e3e..6e839a639 100644 --- a/Source/NSFileHandle.m +++ b/Source/NSFileHandle.m @@ -710,6 +710,12 @@ NSString * const NSFileHandleOperationException @end @implementation NSFileHandle (GNUstepTLS) + ++ (void) setData: (NSData*)data forTLSFile: (NSString*)fileName +{ + [GSTLSObject setData: data forTLSFile: fileName]; +} + /** * returns the concrete class used to implement SSL/TLS connections. */