diff --git a/include/aws/io/private/pki_utils.h b/include/aws/io/private/pki_utils.h index af0465560..f57fbadf8 100644 --- a/include/aws/io/private/pki_utils.h +++ b/include/aws/io/private/pki_utils.h @@ -15,6 +15,7 @@ #ifdef AWS_OS_APPLE /* It's ok to include external headers because this is a PRIVATE header file */ # include +# include #endif /* AWS_OS_APPLE */ struct aws_string; @@ -30,6 +31,21 @@ AWS_IO_API const char *aws_determine_default_pki_ca_file(void); #ifdef AWS_OS_APPLE # if !defined(AWS_OS_IOS) +/** + * Imports a PEM armored PKCS#7 public/private key pair + * into identity for use with SecurityFramework. + * + * WIP SecItem + */ +int aws_import_public_and_private_keys_to_keychain( + struct aws_allocator *alloc, + CFAllocatorRef cf_alloc, + const struct aws_byte_cursor *public_cert_chain, + const struct aws_byte_cursor *private_key, + SecCertificateRef *sec_certificate_ref, + CFStringRef *cert_label, + CFStringRef *key_label); + /** * Imports a PEM armored PKCS#7 public/private key pair * into identity for use with SecurityFramework. diff --git a/include/aws/io/tls_channel_handler.h b/include/aws/io/tls_channel_handler.h index f44335b12..a541fa34c 100644 --- a/include/aws/io/tls_channel_handler.h +++ b/include/aws/io/tls_channel_handler.h @@ -214,6 +214,11 @@ struct aws_tls_ctx_options { */ struct aws_byte_buf pkcs12_password; + // TODO STEVE + // We probably want to set whatever is needed for SecItem support in this struct somewhere. + const char *cert_label; + const char *key_label; + # if !defined(AWS_OS_IOS) /** * On Apple OS you can also use a custom keychain instead of diff --git a/source/darwin/darwin_pki_utils.c b/source/darwin/darwin_pki_utils.c index 20fcb82e0..0989794df 100644 --- a/source/darwin/darwin_pki_utils.c +++ b/source/darwin/darwin_pki_utils.c @@ -89,6 +89,173 @@ int aws_import_ecc_key_into_keychain( return result; } +int aws_import_public_and_private_keys_to_keychain( + struct aws_allocator *alloc, + CFAllocatorRef cf_alloc, + const struct aws_byte_cursor *public_cert_chain, + const struct aws_byte_cursor *private_key, + SecCertificateRef *sec_certificate_ref, + CFStringRef *cert_label, + CFStringRef *key_label) { + + AWS_PRECONDITION(public_cert_chain != NULL); + AWS_PRECONDITION(private_key != NULL); + + int result = AWS_OP_ERR; + + CFDataRef cert_data_ref = NULL; + CFDataRef key_data_ref = NULL; + + CFDictionaryRef cert_dict = NULL; + CFDictionaryRef key_dict = NULL; + + CFDictionaryRef update_query_cert_dict = NULL; + CFDictionaryRef update_cert_dict = NULL; + + CFDictionaryRef update_key_dict = NULL; + + cert_data_ref = CFDataCreate(cf_alloc, public_cert_chain->ptr, public_cert_chain->len); + if (!cert_data_ref) { + AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: failed creating public cert chain data."); + result = aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE); + goto done; + } + + key_data_ref = CFDataCreate(cf_alloc, private_key->ptr, private_key->len); + if (!key_data_ref) { + AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: failed creating private key data."); + result = aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE); + goto done; + } + + // Create dictionary for certificate + const void *cert_keys[] = { kSecClass, kSecAttrLabel, kSecValueData }; + const void *cert_values[] = { kSecClassCertificate, cert_label, cert_data_ref }; + cert_dict = CFDictionaryCreate( + cf_alloc, + cert_keys, + cert_values, + 3, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + // Create dictionary for private key + const void *key_keys[] = { kSecClass, kSecAttrLabel, kSecValueData }; + const void *key_values[] = { kSecClassKey, key_label, key_data_ref }; + key_dict = CFDictionaryCreate( + cf_alloc, + key_keys, + key_values, + 3, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + aws_mutex_lock(&s_sec_mutex); + + OSStatus cert_status = SecItemAdd(cert_dict, NULL); + AWS_LOGF_ERROR(AWS_LS_IO_PKI, "DEBUG: after SecItemAdd OSStatus %d", (int)cert_status); + if (cert_status == errSecDuplicateItem) { + AWS_LOGF_INFO( + AWS_LS_IO_PKI, + "static: certificate has an existing label-value pair that was previously imported into the Keychain. " + "Updating value in the keychain to the one provided."); + const void *update_cert_query_keys[] = { kSecClass, kSecAttrLabel }; + const void *update_cert_query_values[] = { kSecClassCertificate, cert_label }; + update_query_cert_dict = CFDictionaryCreate( + cf_alloc, + update_cert_query_keys, + update_cert_query_values, + 2, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + // Create update dictionary with the new value + const void *update_cert_keys[] = { kSecValueData }; + const void *update_cert_values[] = { cert_data_ref }; + update_cert_dict = CFDictionaryCreate( + cf_alloc, + update_cert_keys, + update_cert_values, + 1, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + cert_status = SecItemUpdate(update_query_cert_dict, update_cert_dict); + AWS_LOGF_ERROR(AWS_LS_IO_PKI, "DEBUG: after SecItemUpdate OSStatus %d", (int)cert_status); + } + + if (cert_status != errSecSuccess) { + AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: error importing certificate with OSStatus %d", (int)cert_status); + result = aws_raise_error(AWS_IO_FILE_VALIDATION_FAILURE); + goto done; + } + + OSStatus key_status = SecItemAdd(key_dict, NULL); + if (key_status == errSecDuplicateItem) { + AWS_LOGF_INFO( + AWS_LS_IO_PKI, + "static: private key has an existing key-value pair that was previously imported into the Keychain. " + "Updating value in the keychain to the one provided."); + const void *update_key_keys[] = { kSecValueData }; + const void *update_key_values[] = { key_data_ref }; + update_key_dict = CFDictionaryCreate( + cf_alloc, + update_key_keys, + update_key_values, + 1, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + key_status = SecItemUpdate(key_dict, update_key_dict); + } + + if (key_status != errSecSuccess) { + AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: error importing private key with OSStatus %d", (int)key_status); + result = aws_raise_error(AWS_IO_FILE_VALIDATION_FAILURE); + goto done; + } + + OSStatus copy_status = SecItemCopyMatching(cert_dict, (CFTypeRef *)sec_certificate_ref); + + if(copy_status != errSecSuccess){ + AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: failed retrieiving stored certificate."); + result = aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE); + goto done; + } + + result = AWS_OP_SUCCESS; + +done: + aws_mutex_unlock(&s_sec_mutex); + + if (cert_data_ref) { + CFRelease(cert_data_ref); + } + if (key_data_ref) { + CFRelease(key_data_ref); + } + + if (cert_dict) { + CFRelease(cert_dict); + } + + if (key_dict) { + CFRelease(key_dict); + } + + if (update_query_cert_dict) { + CFRelease(update_query_cert_dict); + } + if (update_cert_dict) { + CFRelease(update_cert_dict); + } + + if (update_key_dict) { + CFRelease(update_key_dict); + } + + return result; +} + int aws_import_public_and_private_keys_to_identity( struct aws_allocator *alloc, CFAllocatorRef cf_alloc, diff --git a/source/darwin/secure_transport_tls_channel_handler.c b/source/darwin/secure_transport_tls_channel_handler.c index 85fe5ea57..af8007018 100644 --- a/source/darwin/secure_transport_tls_channel_handler.c +++ b/source/darwin/secure_transport_tls_channel_handler.c @@ -784,6 +784,9 @@ struct secure_transport_ctx { CFAllocatorRef wrapped_allocator; CFArrayRef certs; CFArrayRef ca_cert; + SecCertificateRef sec_certificate_ref; + CFStringRef cert_label; + CFStringRef key_label; enum aws_tls_versions minimum_version; struct aws_string *alpn_list; bool veriify_peer; @@ -799,9 +802,6 @@ static struct aws_channel_handler *s_tls_handler_new( struct secure_transport_handler *secure_transport_handler = (struct secure_transport_handler *)aws_mem_calloc(allocator, 1, sizeof(struct secure_transport_handler)); - if (!secure_transport_handler) { - return NULL; - } secure_transport_handler->handler.alloc = allocator; secure_transport_handler->handler.impl = secure_transport_handler; @@ -885,9 +885,13 @@ static struct aws_channel_handler *s_tls_handler_new( SSLSetSessionOption(secure_transport_handler->ctx, kSSLSessionOptionBreakOnServerAuth, true); } + // STEVE DEBUG Setup for use if (secure_transport_ctx->certs) { status = SSLSetCertificate(secure_transport_handler->ctx, secure_transport_ctx->certs); } + // if (secure_transport_ctx->sec_certificate_ref) { + // status = SSLSetCertificate(secure_transport_handler->ctx, secure_transport_ctx->sec_certificate_ref); + // } secure_transport_handler->ca_certs = NULL; if (secure_transport_ctx->ca_cert) { @@ -968,6 +972,18 @@ static void s_aws_secure_transport_ctx_destroy(struct secure_transport_ctx *secu aws_release_certificates(secure_transport_ctx->ca_cert); } + if (secure_transport_ctx->sec_certificate_ref) { + CFRelease(secure_transport_ctx->sec_certificate_ref); + } + + if (secure_transport_ctx->cert_label) { + CFRelease(secure_transport_ctx->cert_label); + } + + if (secure_transport_ctx->key_label) { + CFRelease(secure_transport_ctx->key_label); + } + if (secure_transport_ctx->alpn_list) { aws_string_destroy(secure_transport_ctx->alpn_list); } @@ -1006,6 +1022,9 @@ static struct aws_tls_ctx *s_tls_ctx_new(struct aws_allocator *alloc, const stru secure_transport_ctx->veriify_peer = options->verify_peer; secure_transport_ctx->ca_cert = NULL; secure_transport_ctx->certs = NULL; + secure_transport_ctx->sec_certificate_ref = NULL; + secure_transport_ctx->cert_label = NULL; + secure_transport_ctx->key_label = NULL; secure_transport_ctx->ctx.alloc = alloc; secure_transport_ctx->ctx.impl = secure_transport_ctx; aws_ref_count_init( @@ -1031,6 +1050,34 @@ static struct aws_tls_ctx *s_tls_ctx_new(struct aws_allocator *alloc, const stru struct aws_byte_cursor cert_chain_cur = aws_byte_cursor_from_buf(&options->certificate); struct aws_byte_cursor private_key_cur = aws_byte_cursor_from_buf(&options->private_key); + + // Steve TODO convert key and cert labels and store them in secure_transport_ctx to use for + // storage and recovery. + if (options->cert_label) { + secure_transport_ctx->cert_label = CFStringCreateWithCString( + secure_transport_ctx->wrapped_allocator, + options->cert_label, + kCFStringEncodingUTF8); + } else { + // apply a default label to certificate + secure_transport_ctx->cert_label = CFStringCreateWithCString( + secure_transport_ctx->wrapped_allocator, + "default_cert_label", + kCFStringEncodingUTF8); + } + if (options->key_label) { + secure_transport_ctx->key_label = CFStringCreateWithCString( + secure_transport_ctx->wrapped_allocator, + options->key_label, + kCFStringEncodingUTF8); + } else { + // apply a default label to certificate + secure_transport_ctx->key_label = CFStringCreateWithCString( + secure_transport_ctx->wrapped_allocator, + "default_key_label", + kCFStringEncodingUTF8); + } + // STEVE DEBUG if (aws_import_public_and_private_keys_to_identity( alloc, secure_transport_ctx->wrapped_allocator, @@ -1042,6 +1089,21 @@ static struct aws_tls_ctx *s_tls_ctx_new(struct aws_allocator *alloc, const stru AWS_LS_IO_TLS, "static: failed to import certificate and private key with error %d.", aws_last_error()); goto cleanup_wrapped_allocator; } + + // if (aws_import_public_and_private_keys_to_keychain( + // alloc, + // secure_transport_ctx->wrapped_allocator, + // &cert_chain_cur, + // &private_key_cur, + // &secure_transport_ctx->sec_certificate_ref, + // secure_transport_ctx->cert_label, + // secure_transport_ctx->key_label)){ + // AWS_LOGF_ERROR( + // AWS_LS_IO_TLS, + // "static: failed to import certificate and private key with error %d.", + // aws_last_error()); + // goto cleanup_wrapped_allocator; + // } #endif } else if (aws_tls_options_buf_is_set(&options->pkcs12)) { AWS_LOGF_DEBUG(AWS_LS_IO_TLS, "static: a pkcs$12 certificate and key has been set, setting it up now.");