1// 2// unit_tests.m 3// openssl 4// 5// Created by J. Osborne on 1/25/10. 6// Copyright 2010 Apple. All rights reserved. 7// 8 9#import "unit_tests.h" 10 11#include <stdarg.h> 12#include <err.h> 13#include <spawn.h> 14#include <openssl/bio.h> 15#include <openssl/ssl.h> 16#include <openssl/err.h> 17#include <crt_externs.h> 18 19static char *client_pem, *signed_pem, *url; 20static int port = 9001; 21 22@implementation unit_tests 23 24void (^comm_test)(BOOL cert_fail, BOOL handshake_fail) = NULL; 25void (^comm_teardown)(void) = NULL; 26 27static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) 28{ 29 fprintf(stderr, "Peer certificate verification callback fired, ctx->error %d, preverify_ok %d\n", ctx->error, preverify_ok); 30 return 0; 31} 32 33void comm_setup(id self) 34{ 35 NSBundle* myBundle = [NSBundle bundleWithIdentifier:@"com.apple.OpenSSL098-unit-tests"]; 36 37 char *cert_file = strdup([[myBundle pathForResource:@"server" ofType:@"pem"] UTF8String]); 38 char *priv_key_file = strdup([[myBundle pathForResource:@"server" ofType:@"pem"] UTF8String]); 39 client_pem = strdup([[myBundle pathForResource:@"client" ofType:@"pem"] UTF8String]); 40 signed_pem = strdup([[myBundle pathForResource:@"signed" ofType:@"pem"] UTF8String]); 41 42 STAssertTrue(NULL != cert_file, @"Can't setup test, missing cert_file"); 43 STAssertTrue(NULL != priv_key_file, @"Can't setup test, missing priv_key_file"); 44 STAssertTrue(NULL != client_pem, @"Can't setup test, missing client_pem"); 45 STAssertTrue(NULL != signed_pem, @"Can't setup test, missing signed_pem"); 46 47 SSL_library_init(); 48 SSL_load_error_strings(); 49 ERR_load_BIO_strings(); 50 ERR_load_SSL_strings(); 51 52 __block SSL_CTX *ctx = SSL_CTX_new(SSLv23_server_method()); 53 if (ctx == NULL) { 54 STFail(@"Failed to create SSL context"); 55 return; 56 } 57 if (!SSL_CTX_use_certificate_file(ctx, cert_file, SSL_FILETYPE_PEM)) { 58 STFail(@"Failed to load certificate file %s", cert_file); 59 // if it is important to get these errors into the "real" failure log, look at funopen 60 ERR_print_errors_fp(stderr); 61 SSL_CTX_free(ctx); 62 return; 63 } 64 if (!SSL_CTX_use_PrivateKey_file(ctx, priv_key_file, SSL_FILETYPE_PEM)) 65 { 66 STFail(@"Failed to load private key file %s", priv_key_file); 67 ERR_print_errors_fp(stdout); 68 SSL_CTX_free(ctx); 69 return; 70 } 71 // at this point we have loaded the certificate and private key 72 73 SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback); 74 75 __block BIO *bio = BIO_new_ssl(ctx, 0); 76 if (bio == NULL) 77 { 78 STFail(@"Failed to acquire I/O chain for SSL context"); 79 ERR_print_errors_fp(stdout); 80 SSL_CTX_free(ctx); 81 return; 82 } 83 84 char *portstr; 85 asprintf(&portstr, "%d", port); 86 asprintf(&url, "https://127.0.0.1:%d", port); 87 __block BIO *abio = BIO_new_accept(portstr); 88 BIO_set_bind_mode(abio, BIO_BIND_REUSEADDR); 89 free(portstr); 90 91 BIO_set_accept_bios(abio, bio); 92 if (BIO_do_accept(abio) <= 0) 93 { 94 STFail(@"Failed to listen on port %d", port); 95 ERR_print_errors_fp(stdout); 96 SSL_CTX_free(ctx); 97 BIO_free_all(bio); 98 BIO_free_all(abio); 99 return; 100 } 101 102 comm_test = ^(BOOL cert_fail, BOOL handshake_fail) { 103 if (BIO_do_accept(abio) <= 0) 104 { 105 STFail(@"Failed to accept a connection"); 106 ERR_print_errors_fp(stdout); 107 SSL_CTX_free(ctx); 108 BIO_free_all(bio); 109 BIO_free_all(abio); 110 return; 111 } 112 113 BIO *out = BIO_pop(abio); 114 115 int handshake = BIO_do_handshake(out); 116 117 SSL *ssl; 118 BIO_get_ssl(out, &ssl); 119 int result = SSL_get_verify_result(ssl); 120 if (result == X509_V_OK) { 121 STAssertFalse(cert_fail, @"Client certificate passed verification"); 122 } else { 123 STAssertTrue(cert_fail, @"Client certificate failed verification -- did you set it to trusted in your keychain (security add-certificate client.crt; security add-trusted-cert client.crt)?"); 124 } 125 126 if (handshake <= 0) 127 { 128 STAssertTrue(handshake_fail, @"Handshake failed, resetting connection"); 129 BIO_free_all(out); 130 return; 131 } else { 132 STAssertFalse(handshake_fail, @"Unexpected handshake success"); 133 } 134 135 char msg[] = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: 34\r\n\r\n[+] Secure connection established\n"; 136 BIO_puts(out, msg); 137 BIO_flush(out); 138 139 BIO_free_all(out); 140 }; 141 comm_test = Block_copy(comm_test); 142 143 comm_teardown = ^{ 144 BIO_free_all(abio); 145 SSL_CTX_free(ctx); 146 }; 147 comm_teardown = Block_copy(comm_teardown); 148}; 149 150+(void)tearDown 151{ 152 if (comm_teardown) { 153 comm_teardown(); 154 } 155} 156 157static void spawn_for_test(char *program, ...) 158{ 159 const int m_args = 48; 160 int a_count; 161 char *args[m_args]; 162 va_list ap; 163 164 va_start(ap, program); 165 for(a_count = 0; a_count < m_args; a_count++) { 166 args[a_count] = va_arg(ap, char*); 167 if (args[a_count] == NULL) { 168 break; 169 } 170 } 171 if (a_count >= m_args) { 172 errx(3, "Too many arguments (%d max; program='%s')", m_args, program); 173 } 174 va_end(ap); 175 176 pid_t pid; 177 int rc = posix_spawn(&pid, program, NULL, NULL, args, *_NSGetEnviron()); 178 if (rc != 0) { 179 err(3, "spawn failed for program '%s' ", program); 180 } 181 182 // Avoid zombies, but we don't really care about processes exit code 183 int ignored; 184 waitpid(pid, &ignored, 0); 185} 186 187-(void)testTrustedKeyExists 188{ 189 if (!comm_test) { 190 comm_setup(self); 191 } 192 193 if (comm_test) { 194 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 195 spawn_for_test("/usr/bin/curl", "curl", "-s", "-k", "-E", signed_pem, url, NULL); 196 }); 197 comm_test(NO, YES); 198 } else { 199 STFail(@"Couldn't set up test"); 200 } 201} 202 203-(void)testTrustedKeyDoesntOveride 204{ 205 if (!comm_test) { 206 comm_setup(self); 207 } 208 209 if (comm_test) { 210 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 211 spawn_for_test("/usr/bin/curl", "curl", "-s", "-k", "-E", client_pem, url, NULL); 212 }); 213 comm_test(YES, YES); 214 } else { 215 STFail(@"Couldn't set up test"); 216 } 217} 218 219@end 220