(ssl) wip code to handle certs on apple

This commit is contained in:
Benjamin Sergeant 2019-12-23 15:59:54 -08:00
parent d72e5e70f6
commit 84361c16a9
2 changed files with 159 additions and 0 deletions

View File

@ -130,6 +130,104 @@ namespace
return errMsg; return errMsg;
} }
#undef CURL_BUILD_IOS
OSStatus CopyIdentityFromPKCS12File(
const char *cPath,
const char *cPassword,
SecIdentityRef *out_cert_and_key)
{
OSStatus status = errSecItemNotFound;
CFURLRef pkcs_url = CFURLCreateFromFileSystemRepresentation(
NULL, (const UInt8 *)cPath, strlen(cPath), false);
CFStringRef password = cPassword ? CFStringCreateWithCString(NULL,
cPassword, kCFStringEncodingUTF8) : NULL;
CFDataRef pkcs_data = NULL;
/* We can import P12 files on iOS or OS X 10.7 or later: */
/* These constants are documented as having first appeared in 10.6 but they
raise linker errors when used on that cat for some reason. */
if (CFURLCreateDataAndPropertiesFromResource(
NULL, pkcs_url, &pkcs_data, NULL, NULL, &status)) {
CFArrayRef items = NULL;
/* On iOS SecPKCS12Import will never add the client certificate to the
* Keychain.
*
* It gives us back a SecIdentityRef that we can use directly. */
#if CURL_BUILD_IOS
const void *cKeys[] = {kSecImportExportPassphrase};
const void *cValues[] = {password};
CFDictionaryRef options = CFDictionaryCreate(NULL, cKeys, cValues,
password ? 1L : 0L, NULL, NULL);
if (options != NULL) {
status = SecPKCS12Import(pkcs_data, options, &items);
CFRelease(options);
}
/* On macOS SecPKCS12Import will always add the client certificate to
* the Keychain.
*
* As this doesn't match iOS, and apps may not want to see their client
* certificate saved in the the user's keychain, we use SecItemImport
* with a NULL keychain to avoid importing it.
*
* This returns a SecCertificateRef from which we can construct a
* SecIdentityRef.
*/
#else
SecItemImportExportKeyParameters keyParams;
SecExternalFormat inputFormat = kSecFormatPKCS12;
SecExternalItemType inputType = kSecItemTypeCertificate;
memset(&keyParams, 0x00, sizeof(keyParams));
keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
keyParams.passphrase = password;
status = SecItemImport(pkcs_data, NULL, &inputFormat, &inputType,
0, &keyParams, NULL, &items);
#endif
/* Extract the SecIdentityRef */
if (status == errSecSuccess && items && CFArrayGetCount(items))
{
CFIndex i, count;
count = CFArrayGetCount(items);
for (i = 0; i < count; i++)
{
CFTypeRef item = (CFTypeRef) CFArrayGetValueAtIndex(items, i);
CFTypeID itemID = CFGetTypeID(item);
if (itemID == CFDictionaryGetTypeID())
{
CFTypeRef identity = (CFTypeRef) CFDictionaryGetValue(
(CFDictionaryRef) item,
kSecImportItemIdentity);
CFRetain(identity);
*out_cert_and_key = (SecIdentityRef) identity;
break;
}
else if (itemID == SecCertificateGetTypeID())
{
status = SecIdentityCreateWithCertificate(NULL,
(SecCertificateRef) item,
out_cert_and_key);
break;
}
}
}
if (items) CFRelease(items);
CFRelease(pkcs_data);
}
if (password) CFRelease(password);
CFRelease(pkcs_url);
return status;
}
} // anonymous namespace } // anonymous namespace
namespace ix namespace ix
@ -153,6 +251,63 @@ namespace ix
return false; return false;
} }
bool SocketAppleSSL::handleTLSOptions(std::string& errMsg)
{
SecIdentityRef cert_and_key = NULL;
const char* ssl_cert = _tlsOptions.certFile.c_str();
OSStatus err = CopyIdentityFromPKCS12File(ssl_cert, "foobar", &cert_and_key);
if (err == noErr && cert_and_key)
{
SecCertificateRef cert = NULL;
CFTypeRef certs_c[1];
CFArrayRef certs;
err = SecIdentityCopyCertificate(cert_and_key, &cert);
certs_c[0] = cert_and_key;
certs = CFArrayCreate(NULL, (const void **)certs_c, 1L,
&kCFTypeArrayCallBacks);
err = SSLSetCertificate(_sslContext, certs);
if (err != noErr)
{
errMsg = "SSLSetCertificate failed";
return false;
}
}
else
{
switch(err) {
case errSecAuthFailed: case -25264: /* errSecPkcs12VerifyFailure */
errMsg = "SSL: Incorrect password for the certificate \"%s\" "
"and its private key."; // , ssl_cert);
break;
case -26275: /* errSecDecode */ case -25257: /* errSecUnknownFormat */
errMsg = "SSL: Couldn't make sense of the data in the "
"certificate \"%s\" and its private key.";
; // ssl_cert);
break;
case -25260: /* errSecPassphraseRequired */
errMsg = "SSL The certificate \"%s\" requires a password.";
// ssl_cert);
break;
case errSecItemNotFound:
errMsg = "SSL: Can't find the certificate \"%s\" and its private "
"key in the Keychain."; // , ssl_cert);
break;
default:
errMsg = "SSL: Can't load the certificate \"%s\" and its private "
"key: OSStatus %d" ; // , ssl_cert, err);
break;
}
return false;
}
return true;
}
// No wait support // No wait support
bool SocketAppleSSL::connect(const std::string& host, bool SocketAppleSSL::connect(const std::string& host,
int port, int port,
@ -173,6 +328,8 @@ namespace ix
SSLSetProtocolVersionMin(_sslContext, kTLSProtocol12); SSLSetProtocolVersionMin(_sslContext, kTLSProtocol12);
SSLSetPeerDomainName(_sslContext, host.c_str(), host.size()); SSLSetPeerDomainName(_sslContext, host.c_str(), host.size());
if (!handleTLSOptions(errMsg)) return false; // FIXME not calling close()
if (_tlsOptions.isPeerVerifyDisabled()) if (_tlsOptions.isPeerVerifyDisabled())
{ {
Boolean option(1); Boolean option(1);

View File

@ -34,6 +34,8 @@ namespace ix
virtual ssize_t recv(void* buffer, size_t length) final; virtual ssize_t recv(void* buffer, size_t length) final;
private: private:
bool handleTLSOptions(std::string& errMsg);
SSLContextRef _sslContext; SSLContextRef _sslContext;
mutable std::mutex _mutex; // AppleSSL routines are not thread-safe mutable std::mutex _mutex; // AppleSSL routines are not thread-safe