2 * Copyright (c) 2008-2011,2013,2015 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
33 #include <libDER/DER_Decode.h>
34 #include <security_asn1/secerr.h>
35 #include <security_asn1/secport.h>
37 #include <Security/SecBase.h>
39 #include <CoreFoundation/CFNumber.h>
40 #include <CoreFoundation/CFString.h>
42 #include <Security/oidsalg.h>
43 #include <Security/SecPolicy.h>
44 #include <Security/SecItem.h>
45 #include <Security/SecItemPriv.h>
46 #include <Security/SecIdentity.h>
47 #include <Security/SecCertificateInternal.h>
48 #include <Security/SecKeyPriv.h>
50 #include <CommonCrypto/CommonDigest.h>
51 #include <AssertMacros.h>
54 CERT_VerifyCert(SecKeychainRef keychainOrArray __unused
, CFArrayRef certs
,
55 CFTypeRef policies
, CFAbsoluteTime stime
, SecTrustRef
*trustRef
)
57 SecTrustRef trust
= NULL
;
60 rv
= SecTrustCreateWithCertificates(certs
, policies
, &trust
);
64 CFDateRef verifyDate
= CFDateCreate(NULL
, stime
);
65 rv
= SecTrustSetVerifyDate(trust
, verifyDate
);
66 CFRelease(verifyDate
);
76 SecTrustResultType result
;
77 /* The caller doesn't want a SecTrust object, so let's evaluate it for them. */
78 rv
= SecTrustEvaluate(trust
, &result
);
84 case kSecTrustResultProceed
:
85 case kSecTrustResultUnspecified
:
86 /* TP Verification succeeded and there was either a UserTurst entry
87 telling us to procceed, or no user trust setting was specified. */
91 PORT_SetError(SEC_ERROR_UNTRUSTED_CERT
);
105 static CF_RETURNS_RETAINED CFTypeRef
CERT_FindItemInAllAvailableKeychains(CFDictionaryRef query
) {
106 CFTypeRef item
= NULL
;
107 CFMutableDictionaryRef q
= NULL
;
108 CFDictionaryRef whoAmI
= NULL
;
109 CFErrorRef error
= NULL
;
110 CFDataRef musr
= NULL
;
111 const uint8_t activeUserUuid
[16] = "\xA7\x5A\x3A\x35\xA5\x57\x4B\x10\xBE\x2E\x83\x94\x7E\x4A\x34\x72";
113 /* Do the standard keychain query */
114 require_quiet(errSecItemNotFound
== SecItemCopyMatching(query
, &item
), out
);
116 /* No item found. Can caller use the system keychain? */
117 whoAmI
= _SecSecuritydCopyWhoAmI(&error
);
118 require_quiet(NULL
== error
&& whoAmI
&& CFDictionaryGetValue(whoAmI
, CFSTR("status")), out
);
119 musr
= CFDictionaryGetValue(whoAmI
, CFSTR("musr"));
120 /* Caller has system-keychain entitlement, is in multi-user mode, and is an active user. */
121 if (CFDictionaryGetValue(whoAmI
, CFSTR("system-keychain")) && musr
&&
122 (16 == CFDataGetLength(musr
)) && (0 == memcmp(activeUserUuid
,CFDataGetBytePtr(musr
),12))) {
123 q
= CFDictionaryCreateMutableCopy(NULL
, CFDictionaryGetCount(query
) + 1, query
);
124 CFDictionaryAddValue(q
, kSecUseSystemKeychain
, kCFBooleanTrue
);
125 SecItemCopyMatching(q
, &item
);
139 SecCertificateRef
CERT_FindUserCertByUsage(SecKeychainRef keychainOrArray
,
140 char *nickname
,SECCertUsage usage
,Boolean validOnly
,void *proto_win
)
142 CFStringRef nickname_cfstr
= CFStringCreateWithCString(kCFAllocatorDefault
, nickname
, kCFStringEncodingUTF8
);
143 const void *keys
[] = { kSecClass
, kSecAttrLabel
};
144 const void *values
[] = { kSecClassCertificate
, nickname_cfstr
};
145 CFDictionaryRef query
= CFDictionaryCreate(kCFAllocatorDefault
, keys
, values
, sizeof(keys
)/sizeof(*keys
), NULL
, NULL
);
146 CFTypeRef result
= NULL
;
147 result
= CERT_FindItemInAllAvailableKeychains(query
);
149 CFRelease(nickname_cfstr
);
150 return (SecCertificateRef
)result
;
153 CF_RETURNS_RETAINED CFArrayRef
CERT_CertChainFromCert(SecCertificateRef cert
, SECCertUsage usage
, Boolean includeRoot
, Boolean mustIncludeRoot
)
155 CFMutableArrayRef certs
= NULL
;
156 SecPolicyRef policy
= NULL
;
157 SecTrustRef trust
= NULL
;
158 CFArrayRef wrappedCert
= NULL
;
160 policy
= SecPolicyCreateBasicX509();
165 wrappedCert
= CERT_CertListFromCert(cert
);
166 if (SecTrustCreateWithCertificates(wrappedCert
, policy
, &trust
)) {
170 SecTrustResultType result
;
171 if (SecTrustEvaluate(trust
, &result
)) {
174 CFIndex idx
, count
= SecTrustGetCertificateCount(trust
);
176 /* If we weren't able to build a chain to a self-signed cert, warn. */
177 Boolean isSelfSigned
= false;
178 SecCertificateRef lastCert
= SecTrustGetCertificateAtIndex(trust
, count
- 1);
179 if (lastCert
&& (0 == SecCertificateIsSelfSigned(lastCert
, &isSelfSigned
)) && !isSelfSigned
) {
180 CFStringRef commonName
= NULL
;
181 (void)SecCertificateCopyCommonName(cert
, &commonName
);
182 fprintf(stderr
, "Warning: unable to build chain to self-signed root for signer \"%s\"\n",
183 commonName
? CFStringGetCStringPtr(commonName
, kCFStringEncodingUTF8
) : "");
184 if (commonName
) { CFRelease(commonName
); }
186 // we don't have a root, so if the caller required one, fail
187 if (mustIncludeRoot
) {
192 /* We don't drop the root if there is only 1 certificate in the chain. */
193 if (isSelfSigned
&& !includeRoot
&& count
> 1) {
196 certs
= CFArrayCreateMutable(kCFAllocatorDefault
, count
, &kCFTypeArrayCallBacks
);
197 for(idx
= 0; idx
< count
; idx
++) {
198 CFArrayAppendValue(certs
, SecTrustGetCertificateAtIndex(trust
, idx
));
202 if (trust
) { CFRelease(trust
); }
203 if (policy
) { CFRelease(policy
); }
204 if (wrappedCert
) { CFRelease(wrappedCert
); }
209 CF_RETURNS_RETAINED CFArrayRef
CERT_CertListFromCert(SecCertificateRef cert
)
211 const void *value
= cert
;
212 return cert
? CFArrayCreate(NULL
, &value
, 1, &kCFTypeArrayCallBacks
) : NULL
;
215 CFArrayRef
CERT_DupCertList(CFArrayRef oldList
)
221 // Extract a public key object from a SubjectPublicKeyInfo
222 SecPublicKeyRef
CERT_ExtractPublicKey(SecCertificateRef cert
)
224 return SecCertificateCopyKey(cert
);
227 // Extract the issuer and serial number from a certificate
228 SecCmsIssuerAndSN
*CERT_GetCertIssuerAndSN(PRArenaPool
*pl
, SecCertificateRef cert
)
230 SecCmsIssuerAndSN
*certIssuerAndSN
;
233 mark
= PORT_ArenaMark(pl
);
234 CFDataRef serial_data
= NULL
;
235 CFDataRef issuer_data
= SecCertificateCopyIssuerSequence(cert
);
238 serial_data
= SecCertificateCopySerialNumberData(cert
, NULL
);
242 SecAsn1Item serialNumber
= { CFDataGetLength(serial_data
),
243 (uint8_t *)CFDataGetBytePtr(serial_data
) };
244 SecAsn1Item issuer
= { CFDataGetLength(issuer_data
),
245 (uint8_t *)CFDataGetBytePtr(issuer_data
) };
247 /* Allocate the SecCmsIssuerAndSN struct. */
248 certIssuerAndSN
= (SecCmsIssuerAndSN
*)PORT_ArenaZAlloc (pl
, sizeof(SecCmsIssuerAndSN
));
249 if (certIssuerAndSN
== NULL
)
252 /* Copy the issuer. */
253 certIssuerAndSN
->derIssuer
.Data
= (uint8_t *) PORT_ArenaAlloc(pl
, issuer
.Length
);
254 if (!certIssuerAndSN
->derIssuer
.Data
)
256 PORT_Memcpy(certIssuerAndSN
->derIssuer
.Data
, issuer
.Data
, issuer
.Length
);
257 certIssuerAndSN
->derIssuer
.Length
= issuer
.Length
;
259 /* Copy the serialNumber. */
260 certIssuerAndSN
->serialNumber
.Data
= (uint8_t *) PORT_ArenaAlloc(pl
, serialNumber
.Length
);
261 if (!certIssuerAndSN
->serialNumber
.Data
)
263 PORT_Memcpy(certIssuerAndSN
->serialNumber
.Data
, serialNumber
.Data
, serialNumber
.Length
);
264 certIssuerAndSN
->serialNumber
.Length
= serialNumber
.Length
;
266 CFRelease(serial_data
);
267 CFRelease(issuer_data
);
269 PORT_ArenaUnmark(pl
, mark
);
271 return certIssuerAndSN
;
275 CFRelease(serial_data
);
277 CFRelease(issuer_data
);
278 PORT_ArenaRelease(pl
, mark
);
279 PORT_SetError(SEC_INTERNAL_ONLY
);
284 // find the smime symmetric capabilities profile for a given cert
285 SecAsn1Item
*CERT_FindSMimeProfile(SecCertificateRef cert
)
290 // Generate a certificate key from the issuer and serialnumber, then look it up in the database.
291 // Return the cert if found. "issuerAndSN" is the issuer and serial number to look for
292 static CF_RETURNS_RETAINED CFTypeRef
CERT_FindByIssuerAndSN (CFTypeRef keychainOrArray
, CFTypeRef
class, const SecCmsIssuerAndSN
*issuerAndSN
)
294 CFTypeRef ident
= NULL
;
295 CFDictionaryRef query
= NULL
;
296 CFDataRef issuer
= NULL
;
297 CFDataRef serial
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
,
298 issuerAndSN
->serialNumber
.Data
, issuerAndSN
->serialNumber
.Length
,
301 DERItem der_issuer
= { issuerAndSN
->derIssuer
.Data
,
302 issuerAndSN
->derIssuer
.Length
};
303 DERDecodedInfo content
;
304 require_noerr_quiet(DERDecodeItem(&der_issuer
, &content
), out
);
305 require_quiet(issuer
= createNormalizedX501Name(kCFAllocatorDefault
,
306 &content
.content
), out
);
308 if (keychainOrArray
&& (CFGetTypeID(keychainOrArray
) == CFArrayGetTypeID()) && CFEqual(class, kSecClassCertificate
))
310 CFIndex c
, count
= CFArrayGetCount((CFArrayRef
)keychainOrArray
);
311 for (c
= 0; c
< count
; c
++) {
312 SecCertificateRef cert
= (SecCertificateRef
)CFArrayGetValueAtIndex((CFArrayRef
)keychainOrArray
, c
);
313 CFDataRef nic
= (cert
) ? SecCertificateGetNormalizedIssuerContent(cert
) : NULL
;
314 if (nic
&& CFEqual(nic
, issuer
)) {
315 CFDataRef cert_serial
= SecCertificateCopySerialNumberData(cert
, NULL
);
317 bool found
= CFEqual(cert_serial
, serial
);
318 CFRelease(cert_serial
);
329 const void *keys
[] = { kSecClass
, kSecAttrIssuer
, kSecAttrSerialNumber
, kSecReturnRef
};
330 const void *values
[] = { class, issuer
, serial
, kCFBooleanTrue
};
331 query
= CFDictionaryCreate(kCFAllocatorDefault
, keys
, values
, sizeof(keys
)/sizeof(*keys
), NULL
, NULL
);
332 ident
= CERT_FindItemInAllAvailableKeychains(query
);
345 SecIdentityRef
CERT_FindIdentityByIssuerAndSN (CFTypeRef keychainOrArray
, const SecCmsIssuerAndSN
*issuerAndSN
)
347 return (SecIdentityRef
)CERT_FindByIssuerAndSN(keychainOrArray
, kSecClassIdentity
, issuerAndSN
);
350 SecCertificateRef
CERT_FindCertificateByIssuerAndSN (CFTypeRef keychainOrArray
, const SecCmsIssuerAndSN
*issuerAndSN
)
352 return (SecCertificateRef
)CERT_FindByIssuerAndSN(keychainOrArray
, kSecClassCertificate
, issuerAndSN
);
355 // Generate a certificate key from the Subject Key ID, then look it up in the database.
356 // Return the cert if found. "subjKeyID" is the Subject Key ID to look for
357 static CF_RETURNS_RETAINED CFTypeRef
CERT_FindBySubjectKeyID (CFTypeRef keychainOrArray
, CFTypeRef
class, const SecAsn1Item
*subjKeyID
)
359 CFTypeRef ident
= NULL
;
360 CFDictionaryRef query
= NULL
;
362 if (!subjKeyID
|| !subjKeyID
->Data
|| !subjKeyID
->Length
) {
366 CFDataRef subjectkeyid
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, subjKeyID
->Data
, subjKeyID
->Length
, kCFAllocatorNull
);
367 if (keychainOrArray
&& (CFGetTypeID(keychainOrArray
) == CFArrayGetTypeID()) && CFEqual(class, kSecClassCertificate
))
369 CFIndex c
, count
= CFArrayGetCount((CFArrayRef
)keychainOrArray
);
370 for (c
= 0; c
< count
; c
++) {
371 SecCertificateRef cert
= (SecCertificateRef
)CFArrayGetValueAtIndex((CFArrayRef
)keychainOrArray
, c
);
372 CFDataRef skid
= (cert
) ? SecCertificateGetSubjectKeyID(cert
) : NULL
;
373 if (skid
&& CFEqual(skid
, subjectkeyid
)) {
381 const void *keys
[] = { kSecClass
, kSecAttrSubjectKeyID
, kSecReturnRef
};
382 const void *values
[] = { class, subjectkeyid
, kCFBooleanTrue
};
383 query
= CFDictionaryCreate(kCFAllocatorDefault
, keys
, values
, sizeof(keys
)/sizeof(*keys
), NULL
, NULL
);
384 ident
= CERT_FindItemInAllAvailableKeychains(query
);
390 CFRelease(subjectkeyid
);
395 SecIdentityRef
CERT_FindIdentityBySubjectKeyID (CFTypeRef keychainOrArray
, const SecAsn1Item
*subjKeyID
)
397 return (SecIdentityRef
)CERT_FindBySubjectKeyID(keychainOrArray
, kSecClassIdentity
, subjKeyID
);
400 SecCertificateRef
CERT_FindCertificateBySubjectKeyID(CFTypeRef keychainOrArray
, const SecAsn1Item
*subjKeyID
)
402 return (SecCertificateRef
)CERT_FindBySubjectKeyID(keychainOrArray
, kSecClassCertificate
, subjKeyID
);
407 SecPublicKeyRef
SECKEY_CopyPublicKey(SecPublicKeyRef pubKey
)
413 void SECKEY_DestroyPublicKey(SecPublicKeyRef CF_CONSUMED pubKey
)
418 SecPublicKeyRef
SECKEY_CopyPrivateKey(SecPublicKeyRef privKey
)
424 void SECKEY_DestroyPrivateKey(SecPublicKeyRef privKey
)
429 void CERT_DestroyCertificate(SecCertificateRef cert
)
434 SecCertificateRef
CERT_DupCertificate(SecCertificateRef cert
)
441 WRAP_PubWrapSymKey(SecPublicKeyRef publickey
,
442 SecSymmetricKeyRef bulkkey
,
443 SecAsn1Item
* encKey
)
445 return SecKeyEncrypt(publickey
, kSecPaddingPKCS1
,
446 CFDataGetBytePtr(bulkkey
), CFDataGetLength(bulkkey
),
447 encKey
->Data
, &encKey
->Length
);
450 #define MAX_KEY_SIZE 8192/8
452 WRAP_PubUnwrapSymKey(SecPrivateKeyRef privkey
, const SecAsn1Item
*encKey
, SECOidTag bulkalgtag
)
454 size_t bulkkey_size
= encKey
->Length
;
455 if (bulkkey_size
> MAX_KEY_SIZE
) {
459 uint8_t *bulkkey_buffer
= (uint8_t *)malloc(bulkkey_size
);
460 if (!bulkkey_buffer
) {
463 if (SecKeyDecrypt(privkey
, kSecPaddingPKCS1
, encKey
->Data
, encKey
->Length
, bulkkey_buffer
, &bulkkey_size
)) {
467 CFDataRef bulkkey
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, bulkkey_buffer
, bulkkey_size
, kCFAllocatorMalloc
);
468 return (SecSymmetricKeyRef
)bulkkey
;
473 CERT_CheckIssuerAndSerial(SecCertificateRef cert
, SecAsn1Item
*issuer
, SecAsn1Item
*serial
)
476 CFDataRef cert_issuer
= SecCertificateCopyIssuerSequence(cert
);
479 if ((issuer
->Length
!= (size_t)CFDataGetLength(cert_issuer
)) ||
480 memcmp(issuer
->Data
, CFDataGetBytePtr(cert_issuer
), issuer
->Length
)) {
481 CFRelease(cert_issuer
);
484 CFRelease(cert_issuer
);
485 CFDataRef cert_serial
= SecCertificateCopySerialNumberData(cert
, NULL
);
488 if ((serial
->Length
!= (size_t)CFDataGetLength(cert_serial
)) ||
489 memcmp(serial
->Data
, CFDataGetBytePtr(cert_serial
), serial
->Length
)) {
490 CFRelease(cert_serial
);
493 CFRelease(cert_serial
);