From 61cf6fa9e99f9128a7dfc4759e100fc093d65271 Mon Sep 17 00:00:00 2001 From: CaS Date: Wed, 5 Nov 2003 18:26:09 +0000 Subject: [PATCH] Add support for server side ssl connections. git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@18055 72102866-910b-0410-8b05-ffd578937521 --- ChangeLog | 5 + Headers/Foundation/NSFileHandle.h | 7 +- SSL/GSSSLHandle.m | 201 ++++++++++++++++++++++++------ Source/NSFileHandle.m | 8 ++ 4 files changed, 184 insertions(+), 37 deletions(-) diff --git a/ChangeLog b/ChangeLog index 62ae54c23..556cb668c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2003-11-05 18:26 Richard Frith-Macdonald + + * SSL/GSSSLHandle.m: Add support for server end handles. + * Source/NSFileHandle.m: New -sslAccept method for servers. + 2003-11-05 14:49 Alexander Malmberg * Source/NSSerializer.m (serializeToInfo): Only save a string as diff --git a/Headers/Foundation/NSFileHandle.h b/Headers/Foundation/NSFileHandle.h index f10d7455c..77b77891f 100644 --- a/Headers/Foundation/NSFileHandle.h +++ b/Headers/Foundation/NSFileHandle.h @@ -148,14 +148,19 @@ GS_EXPORT NSString * const NSFileHandleOperationException; /* * Where OpenSSL is available, you can use the subclass returned by +sslClass * to handle SSL connections. + * The -sslAccept method is used to do SSL handlshake and start an + * encrypted session on a channel where the connection was initiated + * from the far end. * The -sslConnect method is used to do SSL handlshake and start an - * encrypted session. + * encrypted session on a channel where the connection was initiated + * from the near end.. * The -sslDisconnect method is used to end the encrypted session. * The -sslSetCertificate:privateKey:PEMpasswd: method is used to * establish a client certificate before starting an encrypted session. */ @interface NSFileHandle (GNUstepOpenSSL) + (Class) sslClass; +- (BOOL) sslAccept; - (BOOL) sslConnect; - (void) sslDisconnect; - (void) sslSetCertificate: (NSString*)certFile diff --git a/SSL/GSSSLHandle.m b/SSL/GSSSLHandle.m index bed2e39af..d745b4d07 100644 --- a/SSL/GSSSLHandle.m +++ b/SSL/GSSSLHandle.m @@ -48,6 +48,8 @@ while it is an Objective-C reserved keyword. */ #define id id_x_ #include + #include + #include #undef id #include @@ -81,12 +83,53 @@ #endif #include +static NSString* +sslError(int err, int e) +{ + NSString *str; + + SSL_load_error_strings(); + + switch (err) + { + case SSL_ERROR_NONE: + str = @"No error: really helpful"; + break; + case SSL_ERROR_ZERO_RETURN: + str = @"Zero Return error"; + break; + case SSL_ERROR_WANT_READ: + str = @"Want Read Error"; + break; + case SSL_ERROR_WANT_WRITE: + str = @"Want Write Error"; + break; + case SSL_ERROR_WANT_X509_LOOKUP: + str = @"Want X509 Lookup Error"; + break; + case SSL_ERROR_SYSCALL: + str = [NSString stringWithFormat: @"Syscall error %d - %s", + e, GSLastErrorStr(e)]; + break; + case SSL_ERROR_SSL: + str = @"SSL Error: really helpful"; + break; + default: + str = @"Standard system error: really helpful"; + break; + } + return str; +} + + @interface GSSSLHandle : GSFileHandle { SSL_CTX *ctx; SSL *ssl; BOOL connected; } + +- (BOOL) sslAccept; - (BOOL) sslConnect; - (void) sslDisconnect; - (void) sslSetCertificate: (NSString*)certFile @@ -100,6 +143,18 @@ if (self == [GSSSLHandle class]) { SSL_library_init(); + + /* + * If there is no /dev/urandom for ssl to use, we must seed the + * random number generator ourselves. + */ + if (![[NSFileManager defaultManager] fileExistsAtPath: @"/dev/urandom"]) + { + const char *inf; + + inf = [[[NSProcessInfo processInfo] globallyUniqueString] UTF8String]; + RAND_seed(inf, strlen(inf)); + } } } @@ -124,6 +179,89 @@ return [super read: buf length: len]; } +- (BOOL) sslAccept +{ + int ret; + int err; + NSRunLoop *loop; + + if (connected == YES) + { + return YES; /* Already connected. */ + } + if (isStandardFile == YES) + { + NSLog(@"Attempt to make ssl connection to a standard file"); + return NO; + } + + /* + * Ensure we have a context and handle to connect with. + */ + if (ctx == 0) + { + ctx = SSL_CTX_new(SSLv23_server_method()); + } + if (ssl == 0) + { + ssl = SSL_new(ctx); + } + + loop = [NSRunLoop currentRunLoop]; + ret = SSL_set_fd(ssl, descriptor); + if (ret == 1) + { + [loop runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]]; + ret = SSL_accept(ssl); + } + if (ret != 1) + { + int e = errno; + NSDate *final; + NSDate *when; + NSTimeInterval last = 0.0; + NSTimeInterval limit = 0.1; + + final = [[NSDate alloc] initWithTimeIntervalSinceNow: 20.0]; + when = [NSDate alloc]; + + err = SSL_get_error(ssl, ret); + while ((err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) + && [final timeIntervalSinceNow] > 0.0) + { + NSTimeInterval tmp = limit; + + limit += last; + last = tmp; + when = [when initWithTimeIntervalSinceNow: limit]; + [loop runUntilDate: when]; + ret = SSL_accept(ssl); + if (ret != 1) + { + e = errno; + err = SSL_get_error(ssl, ret); + } + else + { + err = SSL_ERROR_NONE; + } + } + RELEASE(when); + RELEASE(final); + if (err != SSL_ERROR_NONE) + { + NSString *str = sslError(err, e); + + NSLog(@"unable to accept SSL connection from %@:%@ - %@", + address, service, str); +ERR_print_errors_fp(stderr); + return NO; + } + } + connected = YES; + return YES; +} + - (BOOL) sslConnect { int ret; @@ -152,10 +290,13 @@ ssl = SSL_new(ctx); } - ret = SSL_set_fd(ssl, descriptor); loop = [NSRunLoop currentRunLoop]; - [loop runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]]; - ret = SSL_connect(ssl); + ret = SSL_set_fd(ssl, descriptor); + if (ret == 1) + { + [loop runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]]; + ret = SSL_connect(ssl); + } if (ret != 1) { int e = errno; @@ -192,38 +333,11 @@ RELEASE(final); if (err != SSL_ERROR_NONE) { - NSString *str; + NSString *str = sslError(err, e); - switch (err) - { - case SSL_ERROR_NONE: - str = @"No error: really helpful"; - break; - case SSL_ERROR_ZERO_RETURN: - str = @"Zero Return error"; - break; - case SSL_ERROR_WANT_READ: - str = @"Want Read Error"; - break; - case SSL_ERROR_WANT_WRITE: - str = @"Want Write Error"; - break; - case SSL_ERROR_WANT_X509_LOOKUP: - str = @"Want X509 Lookup Error"; - break; - case SSL_ERROR_SYSCALL: - str = [NSString stringWithFormat: @"Syscall error %d - %s", - e, GSLastErrorStr(e)]; - break; - case SSL_ERROR_SSL: - str = @"SSL Error: really helpful"; - break; - default: - str = @"Standard system error: really helpful"; - break; - } NSLog(@"unable to make SSL connection to %@:%@ - %@", address, service, str); +ERR_print_errors_fp(stderr); return NO; } } @@ -255,6 +369,8 @@ privateKey: (NSString*)privateKey PEMpasswd: (NSString*)PEMpasswd { + int ret; + if (isStandardFile == YES) { NSLog(@"Attempt to set ssl certificate for a standard file"); @@ -265,19 +381,32 @@ */ if (ctx == 0) { - ctx = SSL_CTX_new(SSLv23_client_method()); + ctx = SSL_CTX_new(SSLv23_method()); } if ([PEMpasswd length] > 0) { - SSL_CTX_set_default_passwd_cb_userdata(ctx, (char*)[PEMpasswd cString]); + SSL_CTX_set_default_passwd_cb_userdata(ctx, + (char*)[PEMpasswd UTF8String]); } if ([certFile length] > 0) { - SSL_CTX_use_certificate_file(ctx, [certFile cString], X509_FILETYPE_PEM); + ret = SSL_CTX_use_certificate_file(ctx, [certFile UTF8String], + X509_FILETYPE_PEM); + if (ret != 1) + { + NSLog(@"Failed to set certificate file to %@ - %@", + certFile, sslError(ERR_get_error(), errno)); + } } if ([privateKey length] > 0) { - SSL_CTX_use_PrivateKey_file(ctx, [privateKey cString], X509_FILETYPE_PEM); + ret = SSL_CTX_use_PrivateKey_file(ctx, [privateKey UTF8String], + X509_FILETYPE_PEM); + if (ret != 1) + { + NSLog(@"Failed to set private key file to %@ - %@", + privateKey, sslError(ERR_get_error(), errno)); + } } } diff --git a/Source/NSFileHandle.m b/Source/NSFileHandle.m index 9c99e9b33..02ff4ef92 100644 --- a/Source/NSFileHandle.m +++ b/Source/NSFileHandle.m @@ -617,6 +617,14 @@ NSString * const NSFileHandleOperationException return NSFileHandle_ssl_class; } +/** + * Establishes an SSL connection from the system that the handle is talking to. + */ +- (BOOL) sslAccept +{ + return NO; +} + /** * Establishes an SSL connection to the system that the handle is talking to. */