]>
Commit | Line | Data |
---|---|---|
d8f41ccd | 1 | /* |
e3d460c9 | 2 | * Copyright (c) 2008-2011,2013,2015 Apple Inc. All Rights Reserved. |
d8f41ccd | 3 | * |
e3d460c9 | 4 | * @APPLE_LICENSE_HEADER_START@ |
d8f41ccd | 5 | * |
e3d460c9 A |
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 | |
11 | * file. | |
12 | * | |
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. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
d8f41ccd A |
22 | */ |
23 | ||
24 | #include <stdio.h> | |
25 | ||
26 | #include "cert.h" | |
27 | #include "cryptohi.h" | |
28 | ||
29 | #include "cmstpriv.h" | |
30 | #include "secoid.h" | |
31 | #include "cmspriv.h" | |
32 | ||
33 | #include <libDER/DER_Decode.h> | |
34 | #include <security_asn1/secerr.h> | |
35 | #include <security_asn1/secport.h> | |
36 | ||
37 | #include <Security/SecBase.h> | |
38 | ||
39 | #include <CoreFoundation/CFNumber.h> | |
40 | #include <CoreFoundation/CFString.h> | |
41 | ||
42 | #include <Security/oidsalg.h> | |
43 | #include <Security/SecPolicy.h> | |
44 | #include <Security/SecItem.h> | |
e3d460c9 | 45 | #include <Security/SecItemPriv.h> |
d8f41ccd A |
46 | #include <Security/SecIdentity.h> |
47 | #include <Security/SecCertificateInternal.h> | |
48 | #include <Security/SecKeyPriv.h> | |
49 | ||
50 | #include <CommonCrypto/CommonDigest.h> | |
51 | #include <AssertMacros.h> | |
52 | ||
53 | SECStatus | |
54 | CERT_VerifyCert(SecKeychainRef keychainOrArray __unused, CFArrayRef certs, | |
866f8763 | 55 | CFTypeRef policies, CFAbsoluteTime stime, SecTrustRef *trustRef) |
d8f41ccd A |
56 | { |
57 | SecTrustRef trust = NULL; | |
58 | OSStatus rv; | |
59 | ||
60 | rv = SecTrustCreateWithCertificates(certs, policies, &trust); | |
61 | if (rv) | |
62 | goto loser; | |
63 | ||
64 | CFDateRef verifyDate = CFDateCreate(NULL, stime); | |
65 | rv = SecTrustSetVerifyDate(trust, verifyDate); | |
66 | CFRelease(verifyDate); | |
67 | if (rv) | |
866f8763 | 68 | goto loser; |
d8f41ccd A |
69 | |
70 | if (trustRef) | |
71 | { | |
866f8763 | 72 | *trustRef = trust; |
d8f41ccd A |
73 | } |
74 | else | |
75 | { | |
866f8763 A |
76 | SecTrustResultType result; |
77 | /* The caller doesn't want a SecTrust object, so let's evaluate it for them. */ | |
78 | rv = SecTrustEvaluate(trust, &result); | |
79 | if (rv) | |
80 | goto loser; | |
81 | ||
82 | switch (result) | |
83 | { | |
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. */ | |
88 | CFRelease(trust); | |
89 | break; | |
90 | default: | |
91 | PORT_SetError(SEC_ERROR_UNTRUSTED_CERT); | |
92 | rv = SECFailure; | |
93 | goto loser; | |
94 | break; | |
95 | } | |
d8f41ccd A |
96 | } |
97 | ||
98 | return SECSuccess; | |
99 | loser: | |
100 | if (trust) | |
e3d460c9 | 101 | CFRelease(trust); |
d8f41ccd A |
102 | |
103 | return rv; | |
104 | } | |
105 | ||
e3d460c9 A |
106 | static CFTypeRef CERT_FindItemInAllAvailableKeychains(CFDictionaryRef query) { |
107 | CFTypeRef item = NULL; | |
108 | CFMutableDictionaryRef q = NULL; | |
109 | CFDictionaryRef whoAmI = NULL; | |
110 | CFErrorRef error = NULL; | |
111 | CFDataRef musr = NULL; | |
112 | const uint8_t activeUserUuid[16] = "\xA7\x5A\x3A\x35\xA5\x57\x4B\x10\xBE\x2E\x83\x94\x7E\x4A\x34\x72"; | |
113 | ||
114 | /* Do the standard keychain query */ | |
115 | require_quiet(errSecItemNotFound == SecItemCopyMatching(query, &item), out); | |
116 | ||
117 | /* No item found. Can caller use the system keychain? */ | |
118 | whoAmI = _SecSecuritydCopyWhoAmI(&error); | |
119 | require_quiet(NULL == error && whoAmI && CFDictionaryGetValue(whoAmI, CFSTR("status")), out); | |
120 | musr = CFDictionaryGetValue(whoAmI, CFSTR("musr")); | |
121 | /* Caller has system-keychain entitlement, is in multi-user mode, and is an active user. */ | |
122 | if (CFDictionaryGetValue(whoAmI, CFSTR("system-keychain")) && musr && | |
123 | (16 == CFDataGetLength(musr)) && (0 == memcmp(activeUserUuid,CFDataGetBytePtr(musr),12))) { | |
124 | q = CFDictionaryCreateMutableCopy(NULL, CFDictionaryGetCount(query) + 1, query); | |
125 | CFDictionaryAddValue(q, kSecUseSystemKeychain, kCFBooleanTrue); | |
126 | SecItemCopyMatching(q, &item); | |
127 | } | |
128 | ||
129 | out: | |
130 | if (q) | |
131 | CFRelease(q); | |
132 | if (whoAmI) | |
133 | CFRelease(whoAmI); | |
134 | if (error) | |
135 | CFRelease(error); | |
136 | ||
137 | return item; | |
138 | } | |
d8f41ccd A |
139 | |
140 | SecCertificateRef CERT_FindUserCertByUsage(SecKeychainRef keychainOrArray, | |
141 | char *nickname,SECCertUsage usage,Boolean validOnly,void *proto_win) | |
142 | { | |
143 | CFStringRef nickname_cfstr = CFStringCreateWithCString(kCFAllocatorDefault, nickname, kCFStringEncodingUTF8); | |
144 | const void *keys[] = { kSecClass, kSecAttrLabel }; | |
145 | const void *values[] = { kSecClassCertificate, nickname_cfstr }; | |
146 | CFDictionaryRef query = CFDictionaryCreate(kCFAllocatorDefault, keys, values, sizeof(keys)/sizeof(*keys), NULL, NULL); | |
147 | CFTypeRef result = NULL; | |
e3d460c9 | 148 | result = CERT_FindItemInAllAvailableKeychains(query); |
d8f41ccd A |
149 | CFRelease(query); |
150 | CFRelease(nickname_cfstr); | |
151 | return (SecCertificateRef)result; | |
152 | } | |
153 | ||
154 | CF_RETURNS_RETAINED CFArrayRef CERT_CertChainFromCert(SecCertificateRef cert, SECCertUsage usage, Boolean includeRoot) | |
155 | { | |
156 | CFMutableArrayRef certs = NULL; | |
157 | SecPolicyRef policy = NULL; | |
158 | SecTrustRef trust = NULL; | |
159 | CFArrayRef wrappedCert = NULL; | |
160 | ||
161 | policy = SecPolicyCreateBasicX509(); | |
162 | if (!policy) | |
163 | goto out; | |
164 | ||
165 | wrappedCert = CERT_CertListFromCert(cert); | |
166 | if (SecTrustCreateWithCertificates(wrappedCert, policy, &trust)) | |
167 | goto out; | |
168 | ||
169 | SecTrustResultType result; | |
170 | if (SecTrustEvaluate(trust, &result)) | |
171 | goto out; | |
172 | CFIndex idx, count = SecTrustGetCertificateCount(trust); | |
866f8763 A |
173 | |
174 | /* If we weren't able to build a chain to a self-signed cert, warn. */ | |
175 | Boolean isSelfSigned = false; | |
176 | SecCertificateRef lastCert = SecTrustGetCertificateAtIndex(trust, count - 1); | |
177 | if (lastCert && (0 == SecCertificateIsSelfSigned(lastCert, &isSelfSigned)) && !isSelfSigned) { | |
178 | CFStringRef commonName = NULL; | |
179 | (void)SecCertificateCopyCommonName(cert, &commonName); | |
180 | fprintf(stderr, "Warning: unable to build chain to self-signed root for signer \"%s\"", | |
181 | commonName ? CFStringGetCStringPtr(commonName, kCFStringEncodingUTF8) : ""); | |
182 | if (commonName) { CFRelease(commonName); } | |
183 | } | |
184 | ||
185 | /* We don't drop the root if there is only 1 certificate in the chain. */ | |
186 | if (!includeRoot && count > 1) { count--; } | |
d8f41ccd A |
187 | certs = CFArrayCreateMutable(kCFAllocatorDefault, count, &kCFTypeArrayCallBacks); |
188 | for(idx = 0; idx < count; idx++) | |
189 | CFArrayAppendValue(certs, SecTrustGetCertificateAtIndex(trust, idx)); | |
866f8763 | 190 | |
d8f41ccd A |
191 | out: |
192 | if (trust) CFRelease(trust); | |
193 | if (policy) CFRelease(policy); | |
194 | if (wrappedCert) CFRelease(wrappedCert); | |
195 | ||
196 | return certs; | |
197 | } | |
198 | ||
199 | CFArrayRef CERT_CertListFromCert(SecCertificateRef cert) | |
200 | { | |
201 | const void *value = cert; | |
202 | return cert ? CFArrayCreate(NULL, &value, 1, &kCFTypeArrayCallBacks) : NULL; | |
203 | } | |
204 | ||
205 | CFArrayRef CERT_DupCertList(CFArrayRef oldList) | |
206 | { | |
207 | CFRetain(oldList); | |
208 | return oldList; | |
209 | } | |
210 | ||
211 | // Extract a public key object from a SubjectPublicKeyInfo | |
212 | SecPublicKeyRef CERT_ExtractPublicKey(SecCertificateRef cert) | |
213 | { | |
214 | return SecCertificateCopyPublicKey(cert); | |
215 | } | |
216 | ||
217 | // Extract the issuer and serial number from a certificate | |
218 | SecCmsIssuerAndSN *CERT_GetCertIssuerAndSN(PRArenaPool *pl, SecCertificateRef cert) | |
219 | { | |
220 | SecCmsIssuerAndSN *certIssuerAndSN; | |
221 | ||
222 | void *mark; | |
223 | mark = PORT_ArenaMark(pl); | |
224 | CFDataRef serial_data = NULL; | |
225 | CFDataRef issuer_data = SecCertificateCopyIssuerSequence(cert); | |
226 | if (!issuer_data) | |
227 | goto loser; | |
228 | serial_data = SecCertificateCopySerialNumber(cert); | |
229 | if (!serial_data) | |
230 | goto loser; | |
231 | ||
232 | SecAsn1Item serialNumber = { CFDataGetLength(serial_data), | |
233 | (uint8_t *)CFDataGetBytePtr(serial_data) }; | |
234 | SecAsn1Item issuer = { CFDataGetLength(issuer_data), | |
235 | (uint8_t *)CFDataGetBytePtr(issuer_data) }; | |
236 | ||
237 | /* Allocate the SecCmsIssuerAndSN struct. */ | |
238 | certIssuerAndSN = (SecCmsIssuerAndSN *)PORT_ArenaZAlloc (pl, sizeof(SecCmsIssuerAndSN)); | |
239 | if (certIssuerAndSN == NULL) | |
240 | goto loser; | |
241 | ||
242 | /* Copy the issuer. */ | |
243 | certIssuerAndSN->derIssuer.Data = (uint8_t *) PORT_ArenaAlloc(pl, issuer.Length); | |
244 | if (!certIssuerAndSN->derIssuer.Data) | |
245 | goto loser; | |
246 | PORT_Memcpy(certIssuerAndSN->derIssuer.Data, issuer.Data, issuer.Length); | |
247 | certIssuerAndSN->derIssuer.Length = issuer.Length; | |
248 | ||
249 | /* Copy the serialNumber. */ | |
250 | certIssuerAndSN->serialNumber.Data = (uint8_t *) PORT_ArenaAlloc(pl, serialNumber.Length); | |
251 | if (!certIssuerAndSN->serialNumber.Data) | |
252 | goto loser; | |
253 | PORT_Memcpy(certIssuerAndSN->serialNumber.Data, serialNumber.Data, serialNumber.Length); | |
254 | certIssuerAndSN->serialNumber.Length = serialNumber.Length; | |
255 | ||
256 | CFRelease(serial_data); | |
257 | CFRelease(issuer_data); | |
258 | ||
259 | PORT_ArenaUnmark(pl, mark); | |
260 | ||
261 | return certIssuerAndSN; | |
262 | ||
263 | loser: | |
264 | if (serial_data) | |
265 | CFRelease(serial_data); | |
266 | if (issuer_data) | |
267 | CFRelease(issuer_data); | |
268 | PORT_ArenaRelease(pl, mark); | |
269 | PORT_SetError(SEC_INTERNAL_ONLY); | |
270 | ||
271 | return NULL; | |
272 | } | |
273 | ||
274 | // find the smime symmetric capabilities profile for a given cert | |
275 | SecAsn1Item *CERT_FindSMimeProfile(SecCertificateRef cert) | |
276 | { | |
277 | return NULL; | |
278 | } | |
279 | ||
280 | // Generate a certificate key from the issuer and serialnumber, then look it up in the database. | |
281 | // Return the cert if found. "issuerAndSN" is the issuer and serial number to look for | |
282 | static CFTypeRef CERT_FindByIssuerAndSN (CFTypeRef keychainOrArray, CFTypeRef class, const SecCmsIssuerAndSN *issuerAndSN) | |
283 | { | |
284 | CFTypeRef ident = NULL; | |
285 | CFDictionaryRef query = NULL; | |
286 | CFDataRef issuer = NULL; | |
287 | CFDataRef serial = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, | |
288 | issuerAndSN->serialNumber.Data, issuerAndSN->serialNumber.Length, | |
289 | kCFAllocatorNull); | |
290 | ||
291 | DERItem der_issuer = { issuerAndSN->derIssuer.Data, | |
292 | issuerAndSN->derIssuer.Length }; | |
293 | DERDecodedInfo content; | |
294 | require_noerr_quiet(DERDecodeItem(&der_issuer, &content), out); | |
295 | require_quiet(issuer = createNormalizedX501Name(kCFAllocatorDefault, | |
296 | &content.content), out); | |
297 | ||
298 | if (keychainOrArray && (CFGetTypeID(keychainOrArray) == CFArrayGetTypeID()) && CFEqual(class, kSecClassCertificate)) | |
299 | { | |
300 | CFIndex c, count = CFArrayGetCount((CFArrayRef)keychainOrArray); | |
301 | for (c = 0; c < count; c++) { | |
302 | SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex((CFArrayRef)keychainOrArray, c); | |
d87e1158 A |
303 | CFDataRef nic = (cert) ? SecCertificateGetNormalizedIssuerContent(cert) : NULL; |
304 | if (nic && CFEqual(nic, issuer)) { | |
866f8763 | 305 | CFDataRef cert_serial = SecCertificateCopySerialNumberData(cert, NULL); |
d87e1158 | 306 | if (cert_serial) { |
5c19dc3a A |
307 | bool found = CFEqual(cert_serial, serial); |
308 | CFRelease(cert_serial); | |
309 | if (found) { | |
310 | CFRetain(cert); | |
311 | ident = cert; | |
312 | goto out; | |
313 | } | |
314 | } | |
315 | } | |
316 | } | |
d8f41ccd A |
317 | } |
318 | ||
319 | const void *keys[] = { kSecClass, kSecAttrIssuer, kSecAttrSerialNumber, kSecReturnRef }; | |
320 | const void *values[] = { class, issuer, serial, kCFBooleanTrue }; | |
321 | query = CFDictionaryCreate(kCFAllocatorDefault, keys, values, sizeof(keys)/sizeof(*keys), NULL, NULL); | |
e3d460c9 | 322 | ident = CERT_FindItemInAllAvailableKeychains(query); |
d8f41ccd A |
323 | |
324 | out: | |
325 | if (query) | |
326 | CFRelease(query); | |
327 | if (issuer) | |
328 | CFRelease(issuer); | |
329 | if (serial) | |
330 | CFRelease(serial); | |
331 | ||
332 | return ident; | |
333 | } | |
334 | ||
335 | SecIdentityRef CERT_FindIdentityByIssuerAndSN (CFTypeRef keychainOrArray, const SecCmsIssuerAndSN *issuerAndSN) | |
336 | { | |
337 | return (SecIdentityRef)CERT_FindByIssuerAndSN(keychainOrArray, kSecClassIdentity, issuerAndSN); | |
338 | } | |
339 | ||
340 | SecCertificateRef CERT_FindCertificateByIssuerAndSN (CFTypeRef keychainOrArray, const SecCmsIssuerAndSN *issuerAndSN) | |
341 | { | |
342 | return (SecCertificateRef)CERT_FindByIssuerAndSN(keychainOrArray, kSecClassCertificate, issuerAndSN); | |
343 | } | |
344 | ||
866f8763 A |
345 | // Generate a certificate key from the Subject Key ID, then look it up in the database. |
346 | // Return the cert if found. "subjKeyID" is the Subject Key ID to look for | |
347 | static CFTypeRef CERT_FindBySubjectKeyID (CFTypeRef keychainOrArray, CFTypeRef class, const SecAsn1Item *subjKeyID) | |
d8f41ccd | 348 | { |
866f8763 A |
349 | CFTypeRef ident = NULL; |
350 | CFDictionaryRef query = NULL; | |
d8f41ccd A |
351 | CFDataRef subjectkeyid = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, subjKeyID->Data, subjKeyID->Length, kCFAllocatorNull); |
352 | ||
866f8763 A |
353 | if (keychainOrArray && (CFGetTypeID(keychainOrArray) == CFArrayGetTypeID()) && CFEqual(class, kSecClassCertificate)) |
354 | { | |
355 | CFIndex c, count = CFArrayGetCount((CFArrayRef)keychainOrArray); | |
356 | for (c = 0; c < count; c++) { | |
357 | SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex((CFArrayRef)keychainOrArray, c); | |
358 | CFDataRef skid = (cert) ? SecCertificateGetSubjectKeyID(cert) : NULL; | |
359 | if (skid && CFEqual(skid, subjectkeyid)) { | |
360 | CFRetain(cert); | |
361 | ident = cert; | |
362 | goto out; | |
363 | } | |
364 | } | |
365 | } | |
366 | ||
367 | const void *keys[] = { kSecClass, kSecAttrSubjectKeyID, kSecReturnRef }; | |
368 | const void *values[] = { class, subjectkeyid, kCFBooleanTrue }; | |
369 | query = CFDictionaryCreate(kCFAllocatorDefault, keys, values, sizeof(keys)/sizeof(*keys), NULL, NULL); | |
370 | ident = CERT_FindItemInAllAvailableKeychains(query); | |
d8f41ccd | 371 | |
866f8763 | 372 | out: |
d8f41ccd A |
373 | if (query) |
374 | CFRelease(query); | |
375 | if (subjectkeyid) | |
376 | CFRelease(subjectkeyid); | |
377 | ||
378 | return ident; | |
379 | } | |
380 | ||
866f8763 A |
381 | SecIdentityRef CERT_FindIdentityBySubjectKeyID (CFTypeRef keychainOrArray, const SecAsn1Item *subjKeyID) |
382 | { | |
383 | return (SecIdentityRef)CERT_FindBySubjectKeyID(keychainOrArray, kSecClassIdentity, subjKeyID); | |
384 | } | |
385 | ||
386 | SecCertificateRef CERT_FindCertificateBySubjectKeyID(CFTypeRef keychainOrArray, const SecAsn1Item *subjKeyID) | |
387 | { | |
388 | return (SecCertificateRef)CERT_FindBySubjectKeyID(keychainOrArray, kSecClassCertificate, subjKeyID); | |
389 | } | |
390 | ||
d8f41ccd A |
391 | |
392 | ||
393 | SecPublicKeyRef SECKEY_CopyPublicKey(SecPublicKeyRef pubKey) | |
394 | { | |
395 | CFRetain(pubKey); | |
396 | return pubKey; | |
397 | } | |
398 | ||
399 | void SECKEY_DestroyPublicKey(SecPublicKeyRef pubKey) | |
400 | { | |
401 | CFRelease(pubKey); | |
402 | } | |
403 | ||
404 | SecPublicKeyRef SECKEY_CopyPrivateKey(SecPublicKeyRef privKey) | |
405 | { | |
406 | CFRetain(privKey); | |
407 | return privKey; | |
408 | } | |
409 | ||
410 | void SECKEY_DestroyPrivateKey(SecPublicKeyRef privKey) | |
411 | { | |
412 | CFRelease(privKey); | |
413 | } | |
414 | ||
415 | void CERT_DestroyCertificate(SecCertificateRef cert) | |
416 | { | |
417 | CFRelease(cert); | |
418 | } | |
419 | ||
420 | SecCertificateRef CERT_DupCertificate(SecCertificateRef cert) | |
421 | { | |
422 | CFRetain(cert); | |
423 | return cert; | |
424 | } | |
425 | ||
426 | SECStatus | |
427 | WRAP_PubWrapSymKey(SecPublicKeyRef publickey, | |
428 | SecSymmetricKeyRef bulkkey, | |
429 | SecAsn1Item * encKey) | |
430 | { | |
431 | return SecKeyEncrypt(publickey, kSecPaddingPKCS1, | |
432 | CFDataGetBytePtr(bulkkey), CFDataGetLength(bulkkey), | |
433 | encKey->Data, &encKey->Length); | |
434 | } | |
435 | ||
436 | SecSymmetricKeyRef | |
437 | WRAP_PubUnwrapSymKey(SecPrivateKeyRef privkey, const SecAsn1Item *encKey, SECOidTag bulkalgtag) | |
438 | { | |
439 | size_t bulkkey_size = encKey->Length; | |
6b200bc3 A |
440 | if (bulkkey_size > 16384) { |
441 | return NULL; | |
442 | } | |
443 | ||
d8f41ccd A |
444 | uint8_t bulkkey_buffer[bulkkey_size]; |
445 | if (SecKeyDecrypt(privkey, kSecPaddingPKCS1, | |
446 | encKey->Data, encKey->Length, bulkkey_buffer, &bulkkey_size)) | |
447 | return NULL; | |
448 | ||
449 | CFDataRef bulkkey = CFDataCreate(kCFAllocatorDefault, bulkkey_buffer, bulkkey_size); | |
450 | return (SecSymmetricKeyRef)bulkkey; | |
451 | } | |
452 | ||
453 | ||
454 | bool | |
455 | CERT_CheckIssuerAndSerial(SecCertificateRef cert, SecAsn1Item *issuer, SecAsn1Item *serial) | |
456 | { | |
457 | do { | |
458 | CFDataRef cert_issuer = SecCertificateCopyIssuerSequence(cert); | |
459 | if (!cert_issuer) | |
460 | break; | |
461 | if ((issuer->Length != (size_t)CFDataGetLength(cert_issuer)) || | |
462 | memcmp(issuer->Data, CFDataGetBytePtr(cert_issuer), issuer->Length)) { | |
463 | CFRelease(cert_issuer); | |
464 | break; | |
465 | } | |
466 | CFRelease(cert_issuer); | |
467 | CFDataRef cert_serial = SecCertificateCopySerialNumber(cert); | |
468 | if (!cert_serial) | |
469 | break; | |
470 | if ((serial->Length != (size_t)CFDataGetLength(cert_serial)) || | |
471 | memcmp(serial->Data, CFDataGetBytePtr(cert_serial), serial->Length)) { | |
472 | CFRelease(cert_serial); | |
473 | break; | |
474 | } | |
475 | CFRelease(cert_serial); | |
476 | return true; | |
477 | } while(0); | |
478 | return false; | |
479 | } |