X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/d87e115847b84cc1c3a1ef198a20181cd85b5309..refs/heads/master:/libsecurity_smime/lib/crypto-embedded.c diff --git a/libsecurity_smime/lib/crypto-embedded.c b/libsecurity_smime/lib/crypto-embedded.c index a6c4197a..8e9a4b9b 100644 --- a/libsecurity_smime/lib/crypto-embedded.c +++ b/libsecurity_smime/lib/crypto-embedded.c @@ -1,10 +1,24 @@ /* - * crypto-embedded.c - * libsecurity_smime + * Copyright (c) 2008-2011,2013,2015 Apple Inc. All Rights Reserved. * - * Created by Conrad Sauerwald on 2/7/08. - * Copyright (c) 2008-2011,2013 Apple Inc. All Rights Reserved. + * @APPLE_LICENSE_HEADER_START@ * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ */ #include @@ -28,16 +42,18 @@ #include #include #include +#include #include #include #include +#include #include #include SECStatus CERT_VerifyCert(SecKeychainRef keychainOrArray __unused, CFArrayRef certs, - CFTypeRef policies, CFAbsoluteTime stime, SecTrustRef *trustRef) + CFTypeRef policies, CFAbsoluteTime stime, SecTrustRef *trustRef) { SecTrustRef trust = NULL; OSStatus rv; @@ -50,44 +66,76 @@ CERT_VerifyCert(SecKeychainRef keychainOrArray __unused, CFArrayRef certs, rv = SecTrustSetVerifyDate(trust, verifyDate); CFRelease(verifyDate); if (rv) - goto loser; + goto loser; if (trustRef) { - *trustRef = trust; + *trustRef = trust; } else { - SecTrustResultType result; - /* The caller doesn't want a SecTrust object, so let's evaluate it for them. */ - rv = SecTrustEvaluate(trust, &result); - if (rv) - goto loser; - - switch (result) - { - case kSecTrustResultProceed: - case kSecTrustResultUnspecified: - /* TP Verification succeeded and there was either a UserTurst entry - telling us to procceed, or no user trust setting was specified. */ - CFRelease(trust); - break; - default: - PORT_SetError(SEC_ERROR_UNTRUSTED_CERT); - rv = SECFailure; - goto loser; - break; - } + SecTrustResultType result; + /* The caller doesn't want a SecTrust object, so let's evaluate it for them. */ + rv = SecTrustEvaluate(trust, &result); + if (rv) + goto loser; + + switch (result) + { + case kSecTrustResultProceed: + case kSecTrustResultUnspecified: + /* TP Verification succeeded and there was either a UserTurst entry + telling us to procceed, or no user trust setting was specified. */ + CFRelease(trust); + break; + default: + PORT_SetError(SEC_ERROR_UNTRUSTED_CERT); + rv = SECFailure; + goto loser; + } } return SECSuccess; loser: if (trust) - CFRelease(trust); + CFRelease(trust); return rv; } +static CF_RETURNS_RETAINED CFTypeRef CERT_FindItemInAllAvailableKeychains(CFDictionaryRef query) { + CFTypeRef item = NULL; + CFMutableDictionaryRef q = NULL; + CFDictionaryRef whoAmI = NULL; + CFErrorRef error = NULL; + CFDataRef musr = NULL; + const uint8_t activeUserUuid[16] = "\xA7\x5A\x3A\x35\xA5\x57\x4B\x10\xBE\x2E\x83\x94\x7E\x4A\x34\x72"; + + /* Do the standard keychain query */ + require_quiet(errSecItemNotFound == SecItemCopyMatching(query, &item), out); + + /* No item found. Can caller use the system keychain? */ + whoAmI = _SecSecuritydCopyWhoAmI(&error); + require_quiet(NULL == error && whoAmI && CFDictionaryGetValue(whoAmI, CFSTR("status")), out); + musr = CFDictionaryGetValue(whoAmI, CFSTR("musr")); + /* Caller has system-keychain entitlement, is in multi-user mode, and is an active user. */ + if (CFDictionaryGetValue(whoAmI, CFSTR("system-keychain")) && musr && + (16 == CFDataGetLength(musr)) && (0 == memcmp(activeUserUuid,CFDataGetBytePtr(musr),12))) { + q = CFDictionaryCreateMutableCopy(NULL, CFDictionaryGetCount(query) + 1, query); + CFDictionaryAddValue(q, kSecUseSystemKeychain, kCFBooleanTrue); + SecItemCopyMatching(q, &item); + } + +out: + if (q) + CFRelease(q); + if (whoAmI) + CFRelease(whoAmI); + if (error) + CFRelease(error); + + return item; +} SecCertificateRef CERT_FindUserCertByUsage(SecKeychainRef keychainOrArray, char *nickname,SECCertUsage usage,Boolean validOnly,void *proto_win) @@ -97,13 +145,13 @@ SecCertificateRef CERT_FindUserCertByUsage(SecKeychainRef keychainOrArray, const void *values[] = { kSecClassCertificate, nickname_cfstr }; CFDictionaryRef query = CFDictionaryCreate(kCFAllocatorDefault, keys, values, sizeof(keys)/sizeof(*keys), NULL, NULL); CFTypeRef result = NULL; - SecItemCopyMatching(query, &result); + result = CERT_FindItemInAllAvailableKeychains(query); CFRelease(query); CFRelease(nickname_cfstr); return (SecCertificateRef)result; } -CF_RETURNS_RETAINED CFArrayRef CERT_CertChainFromCert(SecCertificateRef cert, SECCertUsage usage, Boolean includeRoot) +CF_RETURNS_RETAINED CFArrayRef CERT_CertChainFromCert(SecCertificateRef cert, SECCertUsage usage, Boolean includeRoot, Boolean mustIncludeRoot) { CFMutableArrayRef certs = NULL; SecPolicyRef policy = NULL; @@ -111,30 +159,55 @@ CF_RETURNS_RETAINED CFArrayRef CERT_CertChainFromCert(SecCertificateRef cert, SE CFArrayRef wrappedCert = NULL; policy = SecPolicyCreateBasicX509(); - if (!policy) + if (!policy) { goto out; + } wrappedCert = CERT_CertListFromCert(cert); - if (SecTrustCreateWithCertificates(wrappedCert, policy, &trust)) + if (SecTrustCreateWithCertificates(wrappedCert, policy, &trust)) { goto out; + } SecTrustResultType result; - if (SecTrustEvaluate(trust, &result)) + if (SecTrustEvaluate(trust, &result)) { goto out; + } CFIndex idx, count = SecTrustGetCertificateCount(trust); + + /* If we weren't able to build a chain to a self-signed cert, warn. */ + Boolean isSelfSigned = false; + SecCertificateRef lastCert = SecTrustGetCertificateAtIndex(trust, count - 1); + if (lastCert && (0 == SecCertificateIsSelfSigned(lastCert, &isSelfSigned)) && !isSelfSigned) { + CFStringRef commonName = NULL; + (void)SecCertificateCopyCommonName(cert, &commonName); + fprintf(stderr, "Warning: unable to build chain to self-signed root for signer \"%s\"\n", + commonName ? CFStringGetCStringPtr(commonName, kCFStringEncodingUTF8) : ""); + if (commonName) { CFRelease(commonName); } + + // we don't have a root, so if the caller required one, fail + if (mustIncludeRoot) { + goto out; + } + } + + /* We don't drop the root if there is only 1 certificate in the chain. */ + if (isSelfSigned && !includeRoot && count > 1) { + count--; + } certs = CFArrayCreateMutable(kCFAllocatorDefault, count, &kCFTypeArrayCallBacks); - for(idx = 0; idx < count; idx++) + for(idx = 0; idx < count; idx++) { CFArrayAppendValue(certs, SecTrustGetCertificateAtIndex(trust, idx)); - + } + out: - if (trust) CFRelease(trust); - if (policy) CFRelease(policy); - if (wrappedCert) CFRelease(wrappedCert); + if (trust) { CFRelease(trust); } + if (policy) { CFRelease(policy); } + if (wrappedCert) { CFRelease(wrappedCert); } return certs; } -CFArrayRef CERT_CertListFromCert(SecCertificateRef cert) +CF_RETURNS_RETAINED CFArrayRef CERT_CertListFromCert(SecCertificateRef cert) { const void *value = cert; return cert ? CFArrayCreate(NULL, &value, 1, &kCFTypeArrayCallBacks) : NULL; @@ -149,7 +222,7 @@ CFArrayRef CERT_DupCertList(CFArrayRef oldList) // Extract a public key object from a SubjectPublicKeyInfo SecPublicKeyRef CERT_ExtractPublicKey(SecCertificateRef cert) { - return SecCertificateCopyPublicKey(cert); + return SecCertificateCopyKey(cert); } // Extract the issuer and serial number from a certificate @@ -159,35 +232,40 @@ SecCmsIssuerAndSN *CERT_GetCertIssuerAndSN(PRArenaPool *pl, SecCertificateRef ce void *mark; mark = PORT_ArenaMark(pl); - CFDataRef serial_data = NULL; CFDataRef issuer_data = SecCertificateCopyIssuerSequence(cert); - if (!issuer_data) - goto loser; - serial_data = SecCertificateCopySerialNumber(cert); - if (!serial_data) + CFDataRef serial_data = SecCertificateCopySerialNumberData(cert, NULL); + if (!issuer_data || !serial_data) { goto loser; - - SecAsn1Item serialNumber = { CFDataGetLength(serial_data), - (uint8_t *)CFDataGetBytePtr(serial_data) }; - SecAsn1Item issuer = { CFDataGetLength(issuer_data), - (uint8_t *)CFDataGetBytePtr(issuer_data) }; - - /* Allocate the SecCmsIssuerAndSN struct. */ + } + + SecAsn1Item serialNumber = { + .Length = CFDataGetLength(serial_data), + .Data = (uint8_t *)CFDataGetBytePtr(serial_data) + }; + SecAsn1Item issuer = { + .Length = CFDataGetLength(issuer_data), + .Data = (uint8_t *)CFDataGetBytePtr(issuer_data) + }; + + /* Allocate the SecCmsIssuerAndSN struct. */ certIssuerAndSN = (SecCmsIssuerAndSN *)PORT_ArenaZAlloc (pl, sizeof(SecCmsIssuerAndSN)); - if (certIssuerAndSN == NULL) - goto loser; + if (certIssuerAndSN == NULL) { + goto loser; + } /* Copy the issuer. */ certIssuerAndSN->derIssuer.Data = (uint8_t *) PORT_ArenaAlloc(pl, issuer.Length); - if (!certIssuerAndSN->derIssuer.Data) - goto loser; + if (!certIssuerAndSN->derIssuer.Data) { + goto loser; + } PORT_Memcpy(certIssuerAndSN->derIssuer.Data, issuer.Data, issuer.Length); certIssuerAndSN->derIssuer.Length = issuer.Length; /* Copy the serialNumber. */ certIssuerAndSN->serialNumber.Data = (uint8_t *) PORT_ArenaAlloc(pl, serialNumber.Length); - if (!certIssuerAndSN->serialNumber.Data) - goto loser; + if (!certIssuerAndSN->serialNumber.Data) { + goto loser; + } PORT_Memcpy(certIssuerAndSN->serialNumber.Data, serialNumber.Data, serialNumber.Length); certIssuerAndSN->serialNumber.Length = serialNumber.Length; @@ -195,14 +273,12 @@ SecCmsIssuerAndSN *CERT_GetCertIssuerAndSN(PRArenaPool *pl, SecCertificateRef ce CFRelease(issuer_data); PORT_ArenaUnmark(pl, mark); - + return certIssuerAndSN; loser: - if (serial_data) - CFRelease(serial_data); - if (issuer_data) - CFRelease(issuer_data); + CFReleaseNull(serial_data); + CFReleaseNull(issuer_data); PORT_ArenaRelease(pl, mark); PORT_SetError(SEC_INTERNAL_ONLY); @@ -217,7 +293,7 @@ SecAsn1Item *CERT_FindSMimeProfile(SecCertificateRef cert) // Generate a certificate key from the issuer and serialnumber, then look it up in the database. // Return the cert if found. "issuerAndSN" is the issuer and serial number to look for -static CFTypeRef CERT_FindByIssuerAndSN (CFTypeRef keychainOrArray, CFTypeRef class, const SecCmsIssuerAndSN *issuerAndSN) +static CF_RETURNS_RETAINED CFTypeRef CERT_FindByIssuerAndSN (CFTypeRef keychainOrArray, CFTypeRef class, const SecCmsIssuerAndSN *issuerAndSN) { CFTypeRef ident = NULL; CFDictionaryRef query = NULL; @@ -240,24 +316,24 @@ static CFTypeRef CERT_FindByIssuerAndSN (CFTypeRef keychainOrArray, CFTypeRef cl SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex((CFArrayRef)keychainOrArray, c); CFDataRef nic = (cert) ? SecCertificateGetNormalizedIssuerContent(cert) : NULL; if (nic && CFEqual(nic, issuer)) { - CFDataRef cert_serial = SecCertificateCopySerialNumber(cert); + CFDataRef cert_serial = SecCertificateCopySerialNumberData(cert, NULL); if (cert_serial) { - bool found = CFEqual(cert_serial, serial); - CFRelease(cert_serial); - if (found) { - CFRetain(cert); - ident = cert; - goto out; - } - } - } - } + bool found = CFEqual(cert_serial, serial); + CFRelease(cert_serial); + if (found) { + CFRetain(cert); + ident = cert; + goto out; + } + } + } + } } const void *keys[] = { kSecClass, kSecAttrIssuer, kSecAttrSerialNumber, kSecReturnRef }; const void *values[] = { class, issuer, serial, kCFBooleanTrue }; query = CFDictionaryCreate(kCFAllocatorDefault, keys, values, sizeof(keys)/sizeof(*keys), NULL, NULL); - require_noerr_quiet(SecItemCopyMatching(query, (CFTypeRef*)&ident), out); + ident = CERT_FindItemInAllAvailableKeychains(query); out: if (query) @@ -280,16 +356,36 @@ SecCertificateRef CERT_FindCertificateByIssuerAndSN (CFTypeRef keychainOrArray, return (SecCertificateRef)CERT_FindByIssuerAndSN(keychainOrArray, kSecClassCertificate, issuerAndSN); } -SecIdentityRef CERT_FindIdentityBySubjectKeyID (CFTypeRef keychainOrArray __unused, const SecAsn1Item *subjKeyID) +// Generate a certificate key from the Subject Key ID, then look it up in the database. +// Return the cert if found. "subjKeyID" is the Subject Key ID to look for +static CF_RETURNS_RETAINED CFTypeRef CERT_FindBySubjectKeyID (CFTypeRef keychainOrArray, CFTypeRef class, const SecAsn1Item *subjKeyID) { - SecIdentityRef ident = NULL; - CFDictionaryRef query = NULL; + CFTypeRef ident = NULL; + CFDictionaryRef query = NULL; + + if (!subjKeyID || !subjKeyID->Data || !subjKeyID->Length) { + return NULL; + } + CFDataRef subjectkeyid = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, subjKeyID->Data, subjKeyID->Length, kCFAllocatorNull); + if (keychainOrArray && (CFGetTypeID(keychainOrArray) == CFArrayGetTypeID()) && CFEqual(class, kSecClassCertificate)) + { + CFIndex c, count = CFArrayGetCount((CFArrayRef)keychainOrArray); + for (c = 0; c < count; c++) { + SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex((CFArrayRef)keychainOrArray, c); + CFDataRef skid = (cert) ? SecCertificateGetSubjectKeyID(cert) : NULL; + if (skid && CFEqual(skid, subjectkeyid)) { + CFRetain(cert); + ident = cert; + goto out; + } + } + } - const void *keys[] = { kSecClass, kSecAttrSubjectKeyID, kSecReturnRef }; - const void *values[] = { kSecClassIdentity, subjectkeyid, kCFBooleanTrue }; - query = CFDictionaryCreate(kCFAllocatorDefault, keys, values, sizeof(keys)/sizeof(*keys), NULL, NULL); - require_noerr_quiet(SecItemCopyMatching(query, (CFTypeRef*)&ident), out); + const void *keys[] = { kSecClass, kSecAttrSubjectKeyID, kSecReturnRef }; + const void *values[] = { class, subjectkeyid, kCFBooleanTrue }; + query = CFDictionaryCreate(kCFAllocatorDefault, keys, values, sizeof(keys)/sizeof(*keys), NULL, NULL); + ident = CERT_FindItemInAllAvailableKeychains(query); out: if (query) @@ -300,6 +396,16 @@ out: return ident; } +SecIdentityRef CERT_FindIdentityBySubjectKeyID (CFTypeRef keychainOrArray, const SecAsn1Item *subjKeyID) +{ + return (SecIdentityRef)CERT_FindBySubjectKeyID(keychainOrArray, kSecClassIdentity, subjKeyID); +} + +SecCertificateRef CERT_FindCertificateBySubjectKeyID(CFTypeRef keychainOrArray, const SecAsn1Item *subjKeyID) +{ + return (SecCertificateRef)CERT_FindBySubjectKeyID(keychainOrArray, kSecClassCertificate, subjKeyID); +} + SecPublicKeyRef SECKEY_CopyPublicKey(SecPublicKeyRef pubKey) @@ -308,7 +414,7 @@ SecPublicKeyRef SECKEY_CopyPublicKey(SecPublicKeyRef pubKey) return pubKey; } -void SECKEY_DestroyPublicKey(SecPublicKeyRef pubKey) +void SECKEY_DestroyPublicKey(SecPublicKeyRef CF_CONSUMED pubKey) { CFRelease(pubKey); } @@ -345,16 +451,24 @@ WRAP_PubWrapSymKey(SecPublicKeyRef publickey, encKey->Data, &encKey->Length); } +#define MAX_KEY_SIZE 8192/8 SecSymmetricKeyRef WRAP_PubUnwrapSymKey(SecPrivateKeyRef privkey, const SecAsn1Item *encKey, SECOidTag bulkalgtag) { size_t bulkkey_size = encKey->Length; - uint8_t bulkkey_buffer[bulkkey_size]; - if (SecKeyDecrypt(privkey, kSecPaddingPKCS1, - encKey->Data, encKey->Length, bulkkey_buffer, &bulkkey_size)) - return NULL; + if (bulkkey_size > MAX_KEY_SIZE) { + return NULL; + } + + uint8_t *bulkkey_buffer = (uint8_t *)malloc(bulkkey_size); + if (!bulkkey_buffer) { + return NULL; + } + if (SecKeyDecrypt(privkey, kSecPaddingPKCS1, encKey->Data, encKey->Length, bulkkey_buffer, &bulkkey_size)) { + return NULL; + } - CFDataRef bulkkey = CFDataCreate(kCFAllocatorDefault, bulkkey_buffer, bulkkey_size); + CFDataRef bulkkey = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, bulkkey_buffer, bulkkey_size, kCFAllocatorMalloc); return (SecSymmetricKeyRef)bulkkey; } @@ -372,7 +486,7 @@ CERT_CheckIssuerAndSerial(SecCertificateRef cert, SecAsn1Item *issuer, SecAsn1It break; } CFRelease(cert_issuer); - CFDataRef cert_serial = SecCertificateCopySerialNumber(cert); + CFDataRef cert_serial = SecCertificateCopySerialNumberData(cert, NULL); if (!cert_serial) break; if ((serial->Length != (size_t)CFDataGetLength(cert_serial)) ||