You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@thrift.apache.org by jf...@apache.org on 2014/04/04 17:42:14 UTC

git commit: THRIFT-2204:SSL client for the cocoa client Client: cocoa Patch: Olivier Van Acker

Repository: thrift
Updated Branches:
  refs/heads/master 6b1027183 -> 9f154150b


THRIFT-2204:SSL client for the cocoa client
Client: cocoa
Patch: Olivier Van Acker

Adds a SSL transport to the cocoa library


Project: http://git-wip-us.apache.org/repos/asf/thrift/repo
Commit: http://git-wip-us.apache.org/repos/asf/thrift/commit/9f154150
Tree: http://git-wip-us.apache.org/repos/asf/thrift/tree/9f154150
Diff: http://git-wip-us.apache.org/repos/asf/thrift/diff/9f154150

Branch: refs/heads/master
Commit: 9f154150b7c52ab2d63dc978782b363a21f4dadb
Parents: 6b10271
Author: jfarrell <jf...@apache.org>
Authored: Fri Apr 4 11:41:15 2014 -0400
Committer: jfarrell <jf...@apache.org>
Committed: Fri Apr 4 11:41:15 2014 -0400

----------------------------------------------------------------------
 lib/cocoa/src/transport/TNSStreamTransport.h  |   6 +-
 lib/cocoa/src/transport/TNSStreamTransport.m  |  14 +-
 lib/cocoa/src/transport/TSSLSocketClient.h    |  40 ++++
 lib/cocoa/src/transport/TSSLSocketClient.m    | 263 +++++++++++++++++++++
 lib/cocoa/src/transport/TSSLSocketException.h |  29 +++
 lib/cocoa/src/transport/TSSLSocketException.m |  42 ++++
 6 files changed, 385 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/thrift/blob/9f154150/lib/cocoa/src/transport/TNSStreamTransport.h
----------------------------------------------------------------------
diff --git a/lib/cocoa/src/transport/TNSStreamTransport.h b/lib/cocoa/src/transport/TNSStreamTransport.h
index d7be40b..8011fb9 100644
--- a/lib/cocoa/src/transport/TNSStreamTransport.h
+++ b/lib/cocoa/src/transport/TNSStreamTransport.h
@@ -21,10 +21,12 @@
 #import "TTransport.h"
 
 @interface TNSStreamTransport : NSObject <TTransport> {
-  NSInputStream * mInput;
-  NSOutputStream * mOutput;
+
 }
 
+@property (nonatomic, strong) NSInputStream * mInput;
+@property (nonatomic, strong) NSOutputStream * mOutput;
+
 - (id) initWithInputStream: (NSInputStream *) input
               outputStream: (NSOutputStream *) output;
 

http://git-wip-us.apache.org/repos/asf/thrift/blob/9f154150/lib/cocoa/src/transport/TNSStreamTransport.m
----------------------------------------------------------------------
diff --git a/lib/cocoa/src/transport/TNSStreamTransport.m b/lib/cocoa/src/transport/TNSStreamTransport.m
index 265e0ba..e2bc249 100644
--- a/lib/cocoa/src/transport/TNSStreamTransport.m
+++ b/lib/cocoa/src/transport/TNSStreamTransport.m
@@ -28,8 +28,8 @@
               outputStream: (NSOutputStream *) output
 {
   self = [super init];
-  mInput = [input retain_stub];
-  mOutput = [output retain_stub];
+  self.mInput = [input retain_stub];
+  self.mOutput = [output retain_stub];
   return self;
 }
 
@@ -45,8 +45,8 @@
 
 - (void) dealloc
 {
-  [mInput release_stub];
-  [mOutput release_stub];
+  [self.mInput release_stub];
+  [self.mOutput release_stub];
   [super dealloc_stub];
 }
 
@@ -56,7 +56,7 @@
   int got = 0;
   int ret = 0;
   while (got < len) {
-    ret = [mInput read: buf+off+got maxLength: len-got];
+    ret = [self.mInput read: buf+off+got maxLength: len-got];
     if (ret <= 0) {
       @throw [TTransportException exceptionWithReason: @"Cannot read. Remote side has closed."];
     }
@@ -71,10 +71,10 @@
   int got = 0;
   int result = 0;
   while (got < length) {
-    result = [mOutput write: data+offset+got maxLength: length-got];
+    result = [self.mOutput write: data+offset+got maxLength: length-got];
     if (result == -1) {
       @throw [TTransportException exceptionWithReason: @"Error writing to transport output stream."
-                                                error: [mOutput streamError]];
+                                                error: [self.mOutput streamError]];
     } else if (result == 0) {
       @throw [TTransportException exceptionWithReason: @"End of output stream."];
     }

http://git-wip-us.apache.org/repos/asf/thrift/blob/9f154150/lib/cocoa/src/transport/TSSLSocketClient.h
----------------------------------------------------------------------
diff --git a/lib/cocoa/src/transport/TSSLSocketClient.h b/lib/cocoa/src/transport/TSSLSocketClient.h
new file mode 100644
index 0000000..44de124
--- /dev/null
+++ b/lib/cocoa/src/transport/TSSLSocketClient.h
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#import <Foundation/Foundation.h>
+#import "TNSStreamTransport.h"
+
+@interface TSSLSocketClient : TNSStreamTransport
+#if TARGET_OS_IPHONE || (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
+<NSStreamDelegate>
+#endif
+{
+    NSInputStream *inputStream;
+    NSOutputStream *outputStream;
+@private
+    NSString *sslHostname;
+    int sd;
+}
+
+- (id) initWithHostname: (NSString *) hostname
+                   port: (int) port;
+
+- (BOOL) isOpen;
+
+@end

http://git-wip-us.apache.org/repos/asf/thrift/blob/9f154150/lib/cocoa/src/transport/TSSLSocketClient.m
----------------------------------------------------------------------
diff --git a/lib/cocoa/src/transport/TSSLSocketClient.m b/lib/cocoa/src/transport/TSSLSocketClient.m
new file mode 100644
index 0000000..db5a2b4
--- /dev/null
+++ b/lib/cocoa/src/transport/TSSLSocketClient.m
@@ -0,0 +1,263 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#import <Foundation/Foundation.h>
+#import <CoreFoundation/CoreFoundation.h>
+#import "TSSLSocketClient.h"
+#import "TSSLSocketException.h"
+#import "TObjective-C.h"
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#if !TARGET_OS_IPHONE
+#import <CoreServices/CoreServices.h>
+#else
+#import <CFNetwork/CFNetwork.h>
+#endif
+
+@implementation TSSLSocketClient
+
+- (id) initWithHostname: (NSString *) hostname
+                   port: (int) port
+{
+    sslHostname = hostname;
+	CFReadStreamRef readStream = NULL;
+	CFWriteStreamRef writeStream = NULL;
+    
+    
+    /* create a socket structure */
+    struct sockaddr_in pin;
+    struct hostent *hp = NULL;
+    for(int i = 0; i < 10; i++) {
+        
+
+        
+        if ((hp = gethostbyname([hostname UTF8String])) == NULL) { 
+            NSLog(@"failed to resolve hostname %@", hostname);
+            herror("resolv");
+            if(i == 9) {
+                @throw [TSSLSocketException exceptionWithReason: @"failed to resolve hostname"];
+            }
+            [NSThread sleepForTimeInterval:0.2];
+        } else {
+            break;
+        }
+    }
+    
+    memset (&pin, 0, sizeof(pin));
+    pin.sin_family = AF_INET;
+    pin.sin_addr.s_addr = ((struct in_addr *) (hp->h_addr))->s_addr;
+    pin.sin_port = htons (port);
+    
+    /* create the socket */
+    if ((sd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
+    {
+        NSLog(@"failed to create socket for host %@:%d", hostname, port);
+        @throw [TSSLSocketException exceptionWithReason: @"failed to create socket"];
+    }
+    
+    /* open a connection */
+    if (connect (sd, (struct sockaddr *) &pin, sizeof(pin)) == -1)
+    {
+        NSLog(@"failed to create conenct to host %@:%d", hostname, port);
+        @throw [TSSLSocketException exceptionWithReason: @"failed to connect"];
+    }
+    CFStreamCreatePairWithSocket(kCFAllocatorDefault, sd, &readStream, &writeStream);
+    
+    CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
+    CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
+    
+	if (readStream && writeStream) {
+        CFReadStreamSetProperty(readStream,
+                                kCFStreamPropertySocketSecurityLevel,
+                                kCFStreamSocketSecurityLevelTLSv1);
+        
+        NSDictionary *settings =
+        [NSDictionary dictionaryWithObjectsAndKeys:
+         (id)kCFBooleanTrue, (id)kCFStreamSSLValidatesCertificateChain,
+         nil];
+        
+        CFReadStreamSetProperty((CFReadStreamRef)readStream,
+                                kCFStreamPropertySSLSettings,
+                                (CFTypeRef)settings);
+        CFWriteStreamSetProperty((CFWriteStreamRef)writeStream,
+                                 kCFStreamPropertySSLSettings,
+                                 (CFTypeRef)settings);
+        
+		inputStream = (bridge_stub NSInputStream *)readStream;
+		[inputStream retain_stub];
+		[inputStream setDelegate:self];
+   		[inputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
+		[inputStream open];
+		
+		outputStream = (bridge_stub NSOutputStream *)writeStream;
+		[outputStream retain_stub];
+		[outputStream setDelegate:self];
+        [outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
+		[outputStream open];
+        
+        
+        CFRelease(readStream);
+        CFRelease(writeStream);
+	}
+    
+    
+	
+	self = [super initWithInputStream: inputStream outputStream: outputStream];
+    
+	return self;
+}
+
+#pragma mark -
+#pragma mark NSStreamDelegate
+- (void)stream:(NSStream *)aStream
+    handleEvent:(NSStreamEvent)eventCode {
+    switch (eventCode) {
+        case NSStreamEventNone:
+            break;
+        case NSStreamEventHasBytesAvailable:
+            break;
+        case NSStreamEventOpenCompleted:
+            break;
+        case NSStreamEventHasSpaceAvailable:
+        {
+            SecPolicyRef policy = SecPolicyCreateSSL(NO, (__bridge CFStringRef)(sslHostname));
+            SecTrustRef trust = NULL;
+            CFArrayRef streamCertificatesRef =
+            CFBridgingRetain((__bridge id)((__bridge CFArrayRef)([aStream propertyForKey:(NSString *) kCFStreamPropertySSLPeerCertificates])));
+            SecTrustCreateWithCertificates(CFBridgingRetain((__bridge id)(streamCertificatesRef)),
+                                           policy,
+                                           &trust);
+            
+            SecTrustResultType trustResultType = kSecTrustResultInvalid;
+            SecTrustEvaluate(trust, &trustResultType);
+            
+            BOOL proceed = NO;
+            switch (trustResultType) {
+                case kSecTrustResultProceed:
+                    proceed = YES;
+                    break;
+                case kSecTrustResultUnspecified:
+                    NSLog(@"Trusted by OS");
+                    proceed = YES;
+                    break;
+                case kSecTrustResultRecoverableTrustFailure:
+                    proceed = recoverFromTrustFailure(trust);
+                    break;
+                case kSecTrustResultDeny:
+                    NSLog(@"Deny");
+                    break;
+                case kSecTrustResultFatalTrustFailure:
+                    NSLog(@"FatalTrustFailure");
+                    break;
+                case kSecTrustResultOtherError:
+                    NSLog(@"OtherError");
+                    break;
+                case kSecTrustResultInvalid:
+                    NSLog(@"Invalid");
+                    break;
+                default:
+                    NSLog(@"Default");
+                    break;
+            }
+            
+            if (trust) {
+                CFRelease(trust);
+            }
+            if (policy) {
+                CFRelease(policy);
+            }
+            if (!proceed) {
+                NSLog(@"Cannot trust certificate. TrustResultType: %u", trustResultType);
+                [aStream close];
+                @throw [TSSLSocketException exceptionWithReason: @"Cannot trust certificate"];
+            }
+        }
+            break;
+        case NSStreamEventErrorOccurred:
+        {
+            NSError *theError = [aStream streamError];
+            NSLog(@"Error occured opening stream: %@", theError);
+//            @throw [TSSLSocketException exceptionWithReason: @"Error occured opening stream" error: theError];
+            break;
+        }
+        case NSStreamEventEndEncountered:
+            break;
+        default:
+            break;
+    }
+}
+
+bool recoverFromTrustFailure(SecTrustRef myTrust)
+{
+    
+    SecTrustResultType trustResult;
+    OSStatus status = SecTrustEvaluate(myTrust, &trustResult);
+    
+    CFAbsoluteTime trustTime,currentTime,timeIncrement,newTime;
+    CFDateRef newDate;
+    if (trustResult == kSecTrustResultRecoverableTrustFailure) {
+        trustTime = SecTrustGetVerifyTime(myTrust);
+        timeIncrement = 31536000;
+        currentTime = CFAbsoluteTimeGetCurrent();
+        newTime = currentTime - timeIncrement;
+        if (trustTime - newTime){
+            newDate = CFDateCreate(NULL, newTime);
+            SecTrustSetVerifyDate(myTrust, newDate);
+            status = SecTrustEvaluate(myTrust, &trustResult);
+        }
+    }
+    if (trustResult != kSecTrustResultProceed) {
+        NSLog(@"Certificate trust failure");
+        return false;
+    }
+    return true;
+}
+
+- (void)close
+{
+    if(self.mInput) {
+        //Close and reset inputstream
+        CFReadStreamSetProperty((__bridge CFReadStreamRef)(self.mInput), kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
+        [self.mInput setDelegate:nil];
+        [self.mInput close];
+        [self.mInput removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
+        self.mInput = nil;
+    }
+    
+    if(self.mOutput) {
+        //Close and reset outputstream
+        CFReadStreamSetProperty((__bridge CFReadStreamRef)(self.mOutput), kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
+        [self.mOutput setDelegate:nil];
+        [self.mOutput close];
+        [self.mOutput removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
+        self.mOutput = nil;
+    }
+}
+
+- (BOOL) isOpen
+{
+    if(sd > 0)
+        return TRUE;
+    else
+        return FALSE;
+}
+
+@end
+

http://git-wip-us.apache.org/repos/asf/thrift/blob/9f154150/lib/cocoa/src/transport/TSSLSocketException.h
----------------------------------------------------------------------
diff --git a/lib/cocoa/src/transport/TSSLSocketException.h b/lib/cocoa/src/transport/TSSLSocketException.h
new file mode 100644
index 0000000..c290b05
--- /dev/null
+++ b/lib/cocoa/src/transport/TSSLSocketException.h
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#import "TTransportException.h"
+
+@interface TSSLSocketException : TTransportException
+
++ (id) exceptionWithReason: (NSString *) reason
+                     error: (NSError *) error;
+
++ (id) exceptionWithReason: (NSString *) reason;
+
+@end

http://git-wip-us.apache.org/repos/asf/thrift/blob/9f154150/lib/cocoa/src/transport/TSSLSocketException.m
----------------------------------------------------------------------
diff --git a/lib/cocoa/src/transport/TSSLSocketException.m b/lib/cocoa/src/transport/TSSLSocketException.m
new file mode 100644
index 0000000..99eadf5
--- /dev/null
+++ b/lib/cocoa/src/transport/TSSLSocketException.m
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#import "TSSLSocketException.h"
+
+@implementation TSSLSocketException
+
++ (id) exceptionWithReason: (NSString *) reason
+                     error: (NSError *) error
+{
+    NSDictionary * userInfo = nil;
+    if (error != nil) {
+        userInfo = [NSDictionary dictionaryWithObject: error forKey: @"error"];
+    }
+    
+    return [super exceptionWithName: @"TSSLSocketException"
+                             reason: reason
+                           userInfo: userInfo];
+}
+
++ (id) exceptionWithReason: (NSString *) reason
+{
+    return [self exceptionWithReason: reason error: nil];
+}
+
+@end