]>
Commit | Line | Data |
---|---|---|
866f8763 A |
1 | /* |
2 | * Copyright (c) 2006-2016 Apple Inc. All Rights Reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
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@ | |
22 | */ | |
23 | ||
24 | /* | |
25 | * CMSDecoder.c - Interface for decoding CMS messages. | |
26 | */ | |
27 | ||
28 | #include "CMSDecoder.h" | |
29 | #include "CMSUtils.h" | |
30 | ||
31 | #include <Security/SecCmsDecoder.h> | |
32 | #include <Security/SecCmsEnvelopedData.h> | |
33 | #include <Security/SecCmsMessage.h> | |
34 | #include <Security/SecCmsSignedData.h> | |
35 | #include <Security/SecCmsSignerInfo.h> | |
36 | #include <Security/SecCmsContentInfo.h> | |
37 | #include <Security/SecCmsDigestContext.h> | |
38 | #include <Security/SecCertificate.h> | |
39 | #include <Security/SecCertificatePriv.h> | |
40 | #include <Security/SecSMIME.h> | |
41 | #include <Security/oidsattr.h> | |
42 | #include <Security/SecTrustPriv.h> | |
43 | #include <utilities/SecAppleAnchorPriv.h> | |
44 | #include <CoreFoundation/CFRuntime.h> | |
45 | #include <pthread.h> | |
46 | #include <syslog.h> | |
47 | #include <AssertMacros.h> | |
48 | ||
49 | #pragma mark --- Private types and definitions --- | |
50 | ||
51 | /* | |
52 | * Decoder state. | |
53 | */ | |
54 | typedef enum { | |
55 | DS_Init, /* between CMSDecoderCreate and CMSDecoderUpdateMessage */ | |
56 | DS_Updating, /* between first CMSDecoderUpdateMessage and CMSDecoderFinalizeMessage */ | |
57 | DS_Final /* CMSDecoderFinalizeMessage has been called */ | |
58 | } CMSDecoderState; | |
59 | ||
60 | /* | |
61 | * Caller's CMSDecoderRef points to one of these. | |
62 | */ | |
63 | struct _CMSDecoder { | |
64 | CFRuntimeBase base; | |
65 | CMSDecoderState decState; | |
66 | SecCmsDecoderRef decoder; | |
67 | CFDataRef detachedContent; | |
68 | ||
69 | /* | |
70 | * The following are valid (and quiescent) after CMSDecoderFinalizeMessage(). | |
71 | */ | |
72 | SecCmsMessageRef cmsMsg; | |
73 | Boolean wasEncrypted; /* valid after CMSDecoderFinalizeMessage() */ | |
74 | SecCmsSignedDataRef signedData; /* if there is one... */ | |
75 | /* only non-NULL if we found a signedData */ | |
76 | size_t numSigners; | |
77 | SecAsn1Oid *eContentType; | |
78 | /* etc. */ | |
79 | }; | |
80 | ||
81 | static void cmsDecoderInit(CFTypeRef dec); | |
82 | static void cmsDecoderFinalize(CFTypeRef dec); | |
83 | ||
84 | static CFRuntimeClass cmsDecoderRuntimeClass = | |
85 | { | |
86 | 0, /* version */ | |
87 | "CMSDecoder", | |
88 | cmsDecoderInit, | |
89 | NULL, /* copy */ | |
90 | cmsDecoderFinalize, | |
91 | NULL, /* equal - just use pointer equality */ | |
92 | NULL, /* hash, ditto */ | |
93 | NULL, /* copyFormattingDesc */ | |
94 | NULL /* copyDebugDesc */ | |
95 | }; | |
96 | ||
97 | #pragma mark --- Private Routines --- | |
98 | ||
99 | static CFTypeID cmsDecoderTypeID = _kCFRuntimeNotATypeID; | |
100 | ||
101 | /* one time only class init, called via pthread_once() in CMSDecoderGetTypeID() */ | |
102 | static void cmsDecoderClassInitialize(void) | |
103 | { | |
104 | cmsDecoderTypeID = | |
105 | _CFRuntimeRegisterClass((const CFRuntimeClass * const)&cmsDecoderRuntimeClass); | |
106 | } | |
107 | ||
108 | /* init called out from _CFRuntimeCreateInstance() */ | |
109 | static void cmsDecoderInit(CFTypeRef dec) | |
110 | { | |
111 | char *start = ((char *)dec) + sizeof(CFRuntimeBase); | |
112 | memset(start, 0, sizeof(struct _CMSDecoder) - sizeof(CFRuntimeBase)); | |
113 | } | |
114 | ||
115 | /* | |
116 | * Dispose of a CMSDecoder. Called out from CFRelease(). | |
117 | */ | |
118 | static void cmsDecoderFinalize( | |
119 | CFTypeRef dec) | |
120 | { | |
121 | CMSDecoderRef cmsDecoder = (CMSDecoderRef)dec; | |
122 | if(cmsDecoder == NULL) { | |
123 | return; | |
124 | } | |
125 | if(cmsDecoder->decoder != NULL) { | |
126 | /* | |
127 | * Normally this gets freed in SecCmsDecoderFinish - this is | |
128 | * an error case. Unlike Finish, this calls SecCmsMessageDestroy. | |
129 | */ | |
130 | SecCmsDecoderDestroy(cmsDecoder->decoder); | |
131 | cmsDecoder->cmsMsg = NULL; | |
132 | } | |
133 | CFRELEASE(cmsDecoder->detachedContent); | |
134 | if(cmsDecoder->cmsMsg != NULL) { | |
135 | SecCmsMessageDestroy(cmsDecoder->cmsMsg); | |
136 | cmsDecoder->cmsMsg = NULL; | |
137 | } | |
138 | } | |
139 | ||
140 | ||
141 | /* | |
142 | * Given detached content and a valid (decoded) SignedData, digest the detached | |
143 | * content. This occurs at the later of {CMSDecoderFinalizeMessage() finding a | |
144 | * SignedData when already have detachedContent, or CMSDecoderSetDetachedContent() | |
145 | * when we already have a SignedData). | |
146 | */ | |
147 | static OSStatus cmsDigestDetachedContent( | |
148 | CMSDecoderRef cmsDecoder) | |
149 | { | |
150 | ASSERT((cmsDecoder->signedData != NULL) && (cmsDecoder->detachedContent != NULL)); | |
151 | ||
152 | SECAlgorithmID **digestAlgorithms = SecCmsSignedDataGetDigestAlgs(cmsDecoder->signedData); | |
153 | if(digestAlgorithms == NULL) { | |
154 | return errSecUnknownFormat; | |
155 | } | |
156 | SecCmsDigestContextRef digcx = SecCmsDigestContextStartMultiple(digestAlgorithms); | |
157 | if(digcx == NULL) { | |
158 | return errSecAllocate; | |
159 | } | |
160 | ||
161 | SecCmsDigestContextUpdate(digcx, CFDataGetBytePtr(cmsDecoder->detachedContent), | |
162 | CFDataGetLength(cmsDecoder->detachedContent)); | |
163 | OSStatus ortn = SecCmsSignedDataSetDigestContext(cmsDecoder->signedData, digcx); | |
164 | SecCmsDigestContextDestroy(digcx); | |
165 | ||
166 | if(ortn) { | |
167 | ortn = cmsRtnToOSStatus(ortn); | |
168 | CSSM_PERROR("SecCmsSignedDataSetDigestContext", ortn); | |
169 | return ortn; | |
170 | } | |
171 | ||
172 | return ortn; | |
173 | } | |
174 | ||
175 | #pragma mark --- Start of Public API --- | |
176 | ||
177 | CFTypeID CMSDecoderGetTypeID(void) | |
178 | { | |
179 | static pthread_once_t once = PTHREAD_ONCE_INIT; | |
180 | ||
181 | if(cmsDecoderTypeID == _kCFRuntimeNotATypeID) { | |
182 | pthread_once(&once, &cmsDecoderClassInitialize); | |
183 | } | |
184 | return cmsDecoderTypeID; | |
185 | } | |
186 | ||
187 | /* | |
188 | * Create a CMSDecoder. Result must eventually be freed via CFRelease(). | |
189 | */ | |
190 | OSStatus CMSDecoderCreate( | |
191 | CMSDecoderRef *cmsDecoderOut) /* RETURNED */ | |
192 | { | |
193 | CMSDecoderRef cmsDecoder = NULL; | |
194 | ||
195 | uint32_t extra = sizeof(*cmsDecoder) - sizeof(cmsDecoder->base); | |
196 | cmsDecoder = (CMSDecoderRef)_CFRuntimeCreateInstance(NULL, CMSDecoderGetTypeID(), | |
197 | extra, NULL); | |
198 | if(cmsDecoder == NULL) { | |
199 | return errSecAllocate; | |
200 | } | |
201 | cmsDecoder->decState = DS_Init; | |
202 | *cmsDecoderOut = cmsDecoder; | |
203 | return errSecSuccess; | |
204 | } | |
205 | ||
206 | /* | |
207 | * Feed raw bytes of the message to be decoded into the decoder. Can be called | |
208 | * multiple times. | |
209 | */ | |
210 | OSStatus CMSDecoderUpdateMessage( | |
211 | CMSDecoderRef cmsDecoder, | |
212 | const void *msgBytes, | |
213 | size_t msgBytesLen) | |
214 | { | |
215 | if(cmsDecoder == NULL) { | |
216 | return errSecParam; | |
217 | } | |
218 | ||
219 | OSStatus ortn; | |
220 | switch(cmsDecoder->decState) { | |
221 | case DS_Init: | |
222 | /* First time through; set up */ | |
223 | ASSERT(cmsDecoder->decoder == NULL); | |
224 | ASSERT(cmsDecoder->arena == NULL); | |
225 | ortn = SecCmsDecoderCreate(NULL, NULL, NULL, NULL, NULL, NULL, &cmsDecoder->decoder); | |
226 | if(ortn) { | |
227 | ortn = cmsRtnToOSStatus(ortn); | |
228 | CSSM_PERROR("SecCmsDecoderCreate", ortn); | |
229 | return ortn; | |
230 | } | |
231 | cmsDecoder->decState = DS_Updating; | |
232 | break; | |
233 | ||
234 | case DS_Updating: | |
235 | ASSERT(cmsDecoder->decoder != NULL); | |
236 | break; | |
237 | ||
238 | case DS_Final: | |
239 | /* Too late for another update */ | |
240 | return errSecParam; | |
241 | ||
242 | default: | |
243 | dprintf("CMSDecoderUpdateMessage: bad decState\n"); | |
244 | return errSecInternalComponent; | |
245 | } | |
246 | ||
247 | /* FIXME - CFIndex same size as size_t on 64bit? */ | |
248 | ortn = SecCmsDecoderUpdate(cmsDecoder->decoder, msgBytes, (CFIndex)msgBytesLen); | |
249 | if(ortn) { | |
250 | ortn = cmsRtnToOSStatusDefault(ortn, errSecUnknownFormat); | |
251 | CSSM_PERROR("SecCmsDecoderUpdate", ortn); | |
252 | } | |
253 | return ortn; | |
254 | } | |
255 | ||
256 | /* | |
257 | * Indicate that no more CMSDecoderUpdateMessage() calls are forthcoming; | |
258 | * finish decoding the message. We parse the message as best we can, up to | |
259 | * but not including verifying individual signerInfos. | |
260 | */ | |
261 | OSStatus CMSDecoderFinalizeMessage( | |
262 | CMSDecoderRef cmsDecoder) | |
263 | { | |
264 | if(cmsDecoder == NULL) { | |
265 | return errSecParam; | |
266 | } | |
267 | if(cmsDecoder->decState != DS_Updating) { | |
268 | return errSecParam; | |
269 | } | |
270 | ASSERT(cmsDecoder->decoder != NULL); | |
271 | OSStatus ortn = SecCmsDecoderFinish(cmsDecoder->decoder, &cmsDecoder->cmsMsg); | |
272 | cmsDecoder->decState = DS_Final; | |
273 | ||
274 | /* SecCmsDecoderFinish destroyed the decoder even on failure */ | |
275 | cmsDecoder->decoder = NULL; | |
276 | ||
277 | if(ortn) { | |
278 | ortn = cmsRtnToOSStatusDefault(ortn, errSecUnknownFormat); | |
279 | CSSM_PERROR("SecCmsDecoderFinish", ortn); | |
280 | return ortn; | |
281 | } | |
282 | ||
283 | ASSERT(cmsDecoder->cmsMsg != NULL); | |
284 | cmsDecoder->wasEncrypted = SecCmsMessageIsEncrypted(cmsDecoder->cmsMsg); | |
285 | ||
286 | /* Look for a SignedData */ | |
287 | int numContentInfos = SecCmsMessageContentLevelCount(cmsDecoder->cmsMsg); | |
288 | int dex; | |
289 | for(dex=0; dex<numContentInfos; dex++) { | |
290 | SecCmsContentInfoRef ci = SecCmsMessageContentLevel(cmsDecoder->cmsMsg, dex); | |
291 | SECOidTag tag = SecCmsContentInfoGetContentTypeTag(ci); | |
292 | switch(tag) { | |
293 | case SEC_OID_PKCS7_SIGNED_DATA: | |
294 | cmsDecoder->signedData = | |
295 | (SecCmsSignedDataRef)SecCmsContentInfoGetContent(ci); | |
296 | /* dig down one more layer for eContentType */ | |
297 | ci = SecCmsSignedDataGetContentInfo(cmsDecoder->signedData); | |
298 | cmsDecoder->eContentType = SecCmsContentInfoGetContentTypeOID(ci); | |
299 | break; | |
300 | default: | |
301 | break; | |
302 | } | |
303 | if(cmsDecoder->signedData != NULL) { | |
304 | break; | |
305 | } | |
306 | ||
307 | } | |
308 | ||
309 | /* minimal processing of optional signedData... */ | |
310 | if(cmsDecoder->signedData != NULL) { | |
311 | cmsDecoder->numSigners = (size_t) | |
312 | SecCmsSignedDataSignerInfoCount(cmsDecoder->signedData); | |
313 | if(cmsDecoder->detachedContent != NULL) { | |
314 | /* time to calculate digests from detached content */ | |
315 | ortn = cmsDigestDetachedContent(cmsDecoder); | |
316 | } | |
317 | } | |
318 | return ortn; | |
319 | } | |
320 | ||
321 | /* | |
322 | * A signed CMS message optionally includes the data which was signed. If the | |
323 | * message does not include the signed data, caller specifies the signed data | |
324 | * (the "detached content") here. | |
325 | * | |
326 | * This can be called either before or after the actual decoding of the message | |
327 | * (via CMSDecoderUpdateMessage() and CMSDecoderFinalizeMessage()); the only | |
328 | * restriction is that, if detached content is required, this function must | |
329 | * be called befoere successfully ascertaining the signature status via | |
330 | * CMSDecoderCopySignerStatus(). | |
331 | */ | |
332 | OSStatus CMSDecoderSetDetachedContent( | |
333 | CMSDecoderRef cmsDecoder, | |
334 | CFDataRef detachedContent) | |
335 | { | |
336 | if((cmsDecoder == NULL) || (detachedContent == NULL)) { | |
337 | return errSecParam; | |
338 | } | |
339 | cmsDecoder->detachedContent = detachedContent; | |
340 | CFRetain(detachedContent); | |
341 | ||
342 | if(cmsDecoder->signedData != NULL) { | |
343 | /* time to calculate digests from detached content */ | |
344 | ASSERT(cmsDecoder->decState == DS_Final); | |
345 | return cmsDigestDetachedContent(cmsDecoder); | |
346 | } | |
347 | return errSecSuccess; | |
348 | } | |
349 | ||
350 | /* | |
351 | * Obtain the detached content specified in CMSDecoderSetDetachedContent(). | |
352 | * Returns a NULL detachedContent if no detached content has been specified. | |
353 | * Caller must CFRelease() the result. | |
354 | */ | |
355 | OSStatus CMSDecoderCopyDetachedContent( | |
356 | CMSDecoderRef cmsDecoder, | |
357 | CFDataRef *detachedContent) /* RETURNED */ | |
358 | { | |
359 | if((cmsDecoder == NULL) || (detachedContent == NULL)) { | |
360 | return errSecParam; | |
361 | } | |
362 | if(cmsDecoder->detachedContent != NULL) { | |
363 | CFRetain(cmsDecoder->detachedContent); | |
364 | } | |
365 | *detachedContent = cmsDecoder->detachedContent; | |
366 | return errSecSuccess; | |
367 | } | |
368 | ||
369 | /* | |
370 | * Obtain the number of signers of a message. A result of zero indicates that | |
371 | * the message was not signed. | |
372 | */ | |
373 | OSStatus CMSDecoderGetNumSigners( | |
374 | CMSDecoderRef cmsDecoder, | |
375 | size_t *numSigners) /* RETURNED */ | |
376 | { | |
377 | if((cmsDecoder == NULL) || (numSigners == NULL)) { | |
378 | return errSecParam; | |
379 | } | |
380 | if(cmsDecoder->decState != DS_Final) { | |
381 | return errSecParam; | |
382 | } | |
383 | *numSigners = cmsDecoder->numSigners; | |
384 | return errSecSuccess; | |
385 | } | |
386 | ||
387 | /* | |
388 | * Obtain the status of a CMS message's signature. A CMS message can | |
389 | * be signed my multiple signers; this function returns the status | |
390 | * associated with signer 'n' as indicated by the signerIndex parameter. | |
391 | */ | |
392 | OSStatus CMSDecoderCopySignerStatus( | |
393 | CMSDecoderRef cmsDecoder, | |
394 | size_t signerIndex, | |
395 | CFTypeRef policyOrArray, | |
396 | Boolean evaluateSecTrust, | |
397 | CMSSignerStatus *signerStatus, /* optional; RETURNED */ | |
398 | SecTrustRef *secTrust, /* optional; RETURNED */ | |
399 | OSStatus *certVerifyResultCode) /* optional; RETURNED */ | |
400 | { | |
401 | if((cmsDecoder == NULL) || (cmsDecoder->decState != DS_Final) || (!policyOrArray)) { | |
402 | return errSecParam; | |
403 | } | |
404 | ||
405 | /* initialize return values */ | |
406 | if(signerStatus) { | |
407 | *signerStatus = kCMSSignerUnsigned; | |
408 | } | |
409 | if(secTrust) { | |
410 | *secTrust = NULL; | |
411 | } | |
412 | if(certVerifyResultCode) { | |
413 | *certVerifyResultCode = 0; | |
414 | } | |
415 | ||
416 | if(cmsDecoder->signedData == NULL) { | |
417 | *signerStatus = kCMSSignerUnsigned; /* redundant, I know, but explicit */ | |
418 | return errSecSuccess; | |
419 | } | |
420 | ASSERT(cmsDecoder->numSigners > 0); | |
421 | if(signerIndex >= cmsDecoder->numSigners) { | |
422 | *signerStatus = kCMSSignerInvalidIndex; | |
423 | return errSecSuccess; | |
424 | } | |
425 | if(!SecCmsSignedDataHasDigests(cmsDecoder->signedData)) { | |
426 | *signerStatus = kCMSSignerNeedsDetachedContent; | |
427 | return errSecSuccess; | |
428 | } | |
429 | ||
430 | /* | |
431 | * OK, we should be able to verify this signerInfo. | |
432 | * I think we have to do the SecCmsSignedDataVerifySignerInfo first | |
433 | * in order get all the cert pieces into place before returning them | |
434 | * to the caller. | |
435 | */ | |
436 | SecTrustRef theTrust = NULL; | |
437 | OSStatus vfyRtn = SecCmsSignedDataVerifySignerInfo(cmsDecoder->signedData, | |
438 | (int)signerIndex, | |
439 | NULL, | |
440 | policyOrArray, | |
441 | &theTrust); | |
442 | ||
443 | #if SECTRUST_VERBOSE_DEBUG | |
444 | syslog(LOG_ERR, "CMSDecoderCopySignerStatus: SecCmsSignedDataVerifySignerInfo returned %d", (int)vfyRtn); | |
445 | if (policyOrArray) CFShow(policyOrArray); | |
446 | if (theTrust) CFShow(theTrust); | |
447 | #endif | |
448 | ||
449 | /* Subsequent errors to errOut: */ | |
450 | ||
451 | /* | |
452 | * NOTE the smime lib did NOT evaluate that SecTrust - it only does | |
453 | * SecTrustEvaluate() if we don't ask for a copy. | |
454 | * | |
455 | * FIXME deal with multitudes of status returns here...for now, proceed with | |
456 | * obtaining components the caller wants and assume that a nonzero vfyRtn | |
457 | * means "bad signature". | |
458 | */ | |
459 | OSStatus ortn = errSecSuccess; | |
460 | SecTrustResultType secTrustResult; | |
461 | OSStatus evalRtn, verifyStatus = errSecSuccess; | |
462 | ||
463 | if(secTrust != NULL) { | |
464 | *secTrust = theTrust; | |
465 | /* we'll release our reference at the end */ | |
466 | if (theTrust) | |
467 | CFRetain(theTrust); | |
468 | } | |
469 | SecCmsSignerInfoRef signerInfo = | |
470 | SecCmsSignedDataGetSignerInfo(cmsDecoder->signedData, (int)signerIndex); | |
471 | if(signerInfo == NULL) { | |
472 | /* should never happen */ | |
473 | ASSERT(0); | |
474 | dprintf("CMSDecoderCopySignerStatus: no signerInfo\n"); | |
475 | ortn = errSecInternalComponent; | |
476 | goto errOut; | |
477 | } | |
478 | ||
479 | /* now do the actual cert verify */ | |
480 | if(evaluateSecTrust) { | |
481 | evalRtn = SecTrustEvaluate(theTrust, &secTrustResult); | |
482 | if(evalRtn) { | |
483 | /* should never happen */ | |
484 | CSSM_PERROR("SecTrustEvaluate", evalRtn); | |
485 | dprintf("CMSDecoderCopySignerStatus: SecTrustEvaluate error\n"); | |
486 | ortn = errSecInternalComponent; | |
487 | goto errOut; | |
488 | } | |
489 | switch(secTrustResult) { | |
490 | case kSecTrustResultUnspecified: | |
491 | /* cert chain valid, no special UserTrust assignments */ | |
492 | case kSecTrustResultProceed: | |
493 | /* cert chain valid AND user explicitly trusts this */ | |
494 | break; | |
495 | case kSecTrustResultDeny: | |
496 | verifyStatus = errSecTrustSettingDeny; | |
497 | break; | |
498 | default: | |
499 | { | |
500 | verifyStatus = errSecNotTrusted; | |
501 | break; | |
502 | } | |
503 | } /* switch(secTrustResult) */ | |
504 | } /* evaluateSecTrust true */ | |
505 | if(certVerifyResultCode != NULL) { | |
506 | *certVerifyResultCode = verifyStatus; | |
507 | } | |
508 | ||
509 | /* cook up global status based on vfyRtn and tpVfyStatus */ | |
510 | if(signerStatus != NULL) { | |
511 | if((vfyRtn == errSecSuccess) && (verifyStatus == errSecSuccess)) { | |
512 | *signerStatus = kCMSSignerValid; | |
513 | } | |
514 | else if(vfyRtn != errSecSuccess) { | |
515 | /* this could mean other things, but for now... */ | |
516 | *signerStatus = kCMSSignerInvalidSignature; | |
517 | } | |
518 | else { | |
519 | *signerStatus = kCMSSignerInvalidCert; | |
520 | } | |
521 | } | |
522 | errOut: | |
523 | CFRELEASE(theTrust); | |
524 | return ortn; | |
525 | } | |
526 | ||
527 | /* | |
528 | * Obtain the email address of signer 'signerIndex' of a CMS message, if | |
529 | * present. | |
530 | * | |
531 | * This cannot be called until after CMSDecoderFinalizeMessage() is called. | |
532 | */ | |
533 | OSStatus CMSDecoderCopySignerEmailAddress( | |
534 | CMSDecoderRef cmsDecoder, | |
535 | size_t signerIndex, | |
536 | CFStringRef *signerEmailAddress) /* RETURNED */ | |
537 | { | |
538 | if((cmsDecoder == NULL) || | |
539 | (signerEmailAddress == NULL) || | |
540 | (cmsDecoder->signedData == NULL) || /* not signed */ | |
541 | (signerIndex >= cmsDecoder->numSigners) || /* index out of range */ | |
542 | (cmsDecoder->decState != DS_Final)) { | |
543 | return errSecParam; | |
544 | } | |
545 | ||
546 | SecCmsSignerInfoRef signerInfo = | |
547 | SecCmsSignedDataGetSignerInfo(cmsDecoder->signedData, (int)signerIndex); | |
548 | if(signerInfo == NULL) { | |
549 | /* should never happen */ | |
550 | ASSERT(0); | |
551 | dprintf("CMSDecoderCopySignerEmailAddress: no signerInfo\n"); | |
552 | return errSecInternalComponent; | |
553 | } | |
554 | ||
555 | /* | |
556 | * This is leaking memory in libsecurityKeychain per Radar 4412699. | |
557 | */ | |
558 | *signerEmailAddress = SecCmsSignerInfoGetSignerEmailAddress(signerInfo); | |
559 | return errSecSuccess; | |
560 | } | |
561 | ||
562 | /* | |
563 | * Obtain the certificate of signer 'signerIndex' of a CMS message, if | |
564 | * present. | |
565 | * | |
566 | * This cannot be called until after CMSDecoderFinalizeMessage() is called. | |
567 | */ | |
568 | OSStatus CMSDecoderCopySignerCert( | |
569 | CMSDecoderRef cmsDecoder, | |
570 | size_t signerIndex, | |
571 | SecCertificateRef *signerCert) /* RETURNED */ | |
572 | { | |
573 | if((cmsDecoder == NULL) || | |
574 | (signerCert == NULL) || | |
575 | (cmsDecoder->signedData == NULL) || /* not signed */ | |
576 | (signerIndex >= cmsDecoder->numSigners) || /* index out of range */ | |
577 | (cmsDecoder->decState != DS_Final)) { | |
578 | return errSecParam; | |
579 | } | |
580 | ||
581 | SecCmsSignerInfoRef signerInfo = | |
582 | SecCmsSignedDataGetSignerInfo(cmsDecoder->signedData, (int)signerIndex); | |
583 | if(signerInfo == NULL) { | |
584 | /* should never happen */ | |
585 | ASSERT(0); | |
586 | dprintf("CMSDecoderCopySignerCertificate: no signerInfo\n"); | |
587 | return errSecInternalComponent; | |
588 | } | |
589 | *signerCert = SecCmsSignerInfoGetSigningCertificate(signerInfo, NULL); | |
590 | /* libsecurity_smime does NOT retain that */ | |
591 | if(*signerCert == NULL) { | |
592 | /* should never happen */ | |
593 | ASSERT(0); | |
594 | dprintf("CMSDecoderCopySignerCertificate: no signerCert\n"); | |
595 | return errSecInternalComponent; | |
596 | } | |
597 | CFRetain(*signerCert); | |
598 | return errSecSuccess; | |
599 | } | |
600 | ||
601 | /* | |
602 | * Determine whether a CMS message was encrypted, and if so, whether we were | |
603 | * able to decrypt it. | |
604 | */ | |
605 | OSStatus CMSDecoderIsContentEncrypted( | |
606 | CMSDecoderRef cmsDecoder, | |
607 | Boolean *wasEncrypted) | |
608 | { | |
609 | if((cmsDecoder == NULL) || (wasEncrypted == NULL)) { | |
610 | return errSecParam; | |
611 | } | |
612 | if(cmsDecoder->decState != DS_Final) { | |
613 | return errSecParam; | |
614 | } | |
615 | *wasEncrypted = cmsDecoder->wasEncrypted; | |
616 | return errSecSuccess; | |
617 | } | |
618 | ||
619 | /* | |
620 | * Obtain the eContentType OID for a SignedData's EncapsulatedContentType, if | |
621 | * present. | |
622 | */ | |
623 | OSStatus CMSDecoderCopyEncapsulatedContentType( | |
624 | CMSDecoderRef cmsDecoder, | |
625 | CFDataRef *eContentType) /* RETURNED */ | |
626 | { | |
627 | if((cmsDecoder == NULL) || (eContentType == NULL)) { | |
628 | return errSecParam; | |
629 | } | |
630 | if(cmsDecoder->decState != DS_Final) { | |
631 | return errSecParam; | |
632 | } | |
633 | if(cmsDecoder->signedData == NULL) { | |
634 | *eContentType = NULL; | |
635 | } | |
636 | else { | |
637 | SecAsn1Oid *ecOid = cmsDecoder->eContentType; | |
638 | *eContentType = CFDataCreate(NULL, ecOid->Data, ecOid->Length); | |
639 | } | |
640 | return errSecSuccess; | |
641 | } | |
642 | ||
643 | /* | |
644 | * Obtain an array of all of the certificates in a message. Elements of the | |
645 | * returned array are SecCertificateRefs. The caller must CFRelease the returned | |
646 | * array. | |
647 | * This cannot be called until after CMSDecoderFinalizeMessage() is called. | |
648 | */ | |
649 | OSStatus CMSDecoderCopyAllCerts( | |
650 | CMSDecoderRef cmsDecoder, | |
651 | CFArrayRef *certs) /* RETURNED */ | |
652 | { | |
653 | if((cmsDecoder == NULL) || (certs == NULL)) { | |
654 | return errSecParam; | |
655 | } | |
656 | if(cmsDecoder->decState != DS_Final) { | |
657 | return errSecParam; | |
658 | } | |
659 | if(cmsDecoder->signedData == NULL) { | |
660 | /* message wasn't signed */ | |
661 | *certs = NULL; | |
662 | return errSecSuccess; | |
663 | } | |
664 | ||
665 | /* NULL_terminated array of CSSM_DATA ptrs */ | |
666 | SecAsn1Item **cssmCerts = SecCmsSignedDataGetCertificateList(cmsDecoder->signedData); | |
667 | if((cssmCerts == NULL) || (*cssmCerts == NULL)) { | |
668 | *certs = NULL; | |
669 | return errSecSuccess; | |
670 | } | |
671 | ||
672 | CFMutableArrayRef allCerts = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
673 | SecAsn1Item **cssmCert; | |
674 | for(cssmCert=cssmCerts; *cssmCert!=NULL; cssmCert++) { | |
675 | SecCertificateRef cfCert = SecCertificateCreateWithBytes(NULL, (*cssmCert)->Data, (*cssmCert)->Length); | |
676 | if(!cfCert) { | |
677 | CFRelease(allCerts); | |
678 | return errSecDecode; | |
679 | } | |
680 | CFArrayAppendValue(allCerts, cfCert); | |
681 | /* the array holds the only needed refcount */ | |
682 | CFRelease(cfCert); | |
683 | } | |
684 | *certs = allCerts; | |
685 | return errSecSuccess; | |
686 | } | |
687 | ||
688 | /* | |
689 | * Obtain the actual message content (payload), if any. If the message was | |
690 | * signed with detached content this will return NULL. | |
691 | * Caller must CFRelease the result. | |
692 | */ | |
693 | OSStatus CMSDecoderCopyContent( | |
694 | CMSDecoderRef cmsDecoder, | |
695 | CFDataRef *content) /* RETURNED */ | |
696 | { | |
697 | if((cmsDecoder == NULL) || (content == NULL)) { | |
698 | return errSecParam; | |
699 | } | |
700 | if(cmsDecoder->decState != DS_Final) { | |
701 | return errSecParam; | |
702 | } | |
703 | if(cmsDecoder->cmsMsg == NULL) { | |
704 | /* Hmmm....looks like the finalize call failed */ | |
705 | return errSecParam; | |
706 | } | |
707 | const SecAsn1Item *odata = SecCmsMessageGetContent(cmsDecoder->cmsMsg); | |
708 | if((odata == NULL) || (odata->Length == 0)) { | |
709 | /* i.e., detached content */ | |
710 | *content = NULL; | |
711 | return errSecSuccess; | |
712 | } | |
713 | *content = CFDataCreate(NULL, (const UInt8 *)odata->Data, odata->Length); | |
714 | return errSecSuccess; | |
715 | } | |
716 | ||
717 | #pragma mark --- SPI declared in CMSPrivate.h --- | |
718 | ||
719 | /* | |
720 | * Obtain the SecCmsMessageRef associated with a CMSDecoderRef. Intended | |
721 | * to be called after decoding the message (i.e., after | |
722 | * CMSDecoderFinalizeMessage() to gain finer access to the contents of the | |
723 | * SecCmsMessageRef than is otherwise available via the CMSDecoder interface. | |
724 | * Returns a NULL SecCmsMessageRef if CMSDecoderFinalizeMessage() has not been | |
725 | * called. | |
726 | * | |
727 | * The CMSDecoder retains ownership of the returned SecCmsMessageRef. | |
728 | */ | |
729 | OSStatus CMSDecoderGetCmsMessage( | |
730 | CMSDecoderRef cmsDecoder, | |
731 | SecCmsMessageRef *cmsMessage) /* RETURNED */ | |
732 | { | |
733 | if((cmsDecoder == NULL) || (cmsMessage == NULL)) { | |
734 | return errSecParam; | |
735 | } | |
736 | /* any state, whether we have a msg or not is OK */ | |
737 | *cmsMessage = cmsDecoder->cmsMsg; | |
738 | return errSecSuccess; | |
739 | } | |
740 | ||
741 | /* | |
742 | * Optionally specify a SecCmsDecoderRef to use with a CMSDecoderRef. | |
743 | * If this is called, it must be called before the first call to | |
744 | * CMSDecoderUpdateMessage(). The CMSDecoderRef takes ownership of the | |
745 | * incoming SecCmsDecoderRef. | |
746 | */ | |
747 | OSStatus CMSDecoderSetDecoder( | |
748 | CMSDecoderRef cmsDecoder, | |
749 | SecCmsDecoderRef decoder) | |
750 | { | |
751 | if((cmsDecoder == NULL) || (decoder == NULL)) { | |
752 | return errSecParam; | |
753 | } | |
754 | switch(cmsDecoder->decState) { | |
755 | case DS_Init: | |
756 | ASSERT(cmsDecoder->decoder == NULL); | |
757 | cmsDecoder->decoder = decoder; | |
758 | cmsDecoder->decState = DS_Updating; | |
759 | return errSecSuccess; | |
760 | case DS_Updating: | |
761 | case DS_Final: | |
762 | return errSecParam; | |
763 | } | |
764 | return errSecSuccess; | |
765 | } | |
766 | ||
767 | /* | |
768 | * Obtain the SecCmsDecoderRef associated with a CMSDecoderRef. | |
769 | * Returns a NULL SecCmsDecoderRef if neither CMSDecoderSetDecoder() nor | |
770 | * CMSDecoderUpdateMessage() has been called. | |
771 | * The CMSDecoderRef retains ownership of the SecCmsDecoderRef. | |
772 | */ | |
773 | OSStatus CMSDecoderGetDecoder( | |
774 | CMSDecoderRef cmsDecoder, | |
775 | SecCmsDecoderRef *decoder) /* RETURNED */ | |
776 | { | |
777 | if((cmsDecoder == NULL) || (decoder == NULL)) { | |
778 | return errSecParam; | |
779 | } | |
780 | /* any state, whether we have a decoder or not is OK */ | |
781 | *decoder = cmsDecoder->decoder; | |
782 | return errSecSuccess; | |
783 | } | |
784 | ||
785 | /* | |
786 | * Obtain the signing time of signer 'signerIndex' of a CMS message, if | |
787 | * present. This is an unauthenticate time, although it is part of the | |
788 | * signed attributes of the message. | |
789 | * | |
790 | * Returns errSecParam if the CMS message was not signed or if signerIndex | |
791 | * is greater than the number of signers of the message minus one. | |
792 | * | |
793 | * This cannot be called until after CMSDecoderFinalizeMessage() is called. | |
794 | */ | |
795 | OSStatus CMSDecoderCopySignerSigningTime( | |
796 | CMSDecoderRef cmsDecoder, | |
797 | size_t signerIndex, /* usually 0 */ | |
798 | CFAbsoluteTime *signingTime) /* RETURNED */ | |
799 | { | |
800 | OSStatus status = errSecParam; | |
801 | SecCmsMessageRef cmsg; | |
802 | SecCmsSignedDataRef signedData = NULL; | |
803 | int numContentInfos = 0; | |
804 | ||
805 | require(cmsDecoder && signingTime, xit); | |
806 | require_noerr(CMSDecoderGetCmsMessage(cmsDecoder, &cmsg), xit); | |
807 | numContentInfos = SecCmsMessageContentLevelCount(cmsg); | |
808 | for (int dex = 0; !signedData && dex < numContentInfos; dex++) | |
809 | { | |
810 | SecCmsContentInfoRef ci = SecCmsMessageContentLevel(cmsg, dex); | |
811 | SECOidTag tag = SecCmsContentInfoGetContentTypeTag(ci); | |
812 | if (tag == SEC_OID_PKCS7_SIGNED_DATA) | |
813 | if ((signedData = (SecCmsSignedDataRef)SecCmsContentInfoGetContent(ci))) { | |
814 | SecCmsSignerInfoRef signerInfo = SecCmsSignedDataGetSignerInfo(signedData, (int)signerIndex); | |
815 | if (signerInfo) | |
816 | { | |
817 | status = SecCmsSignerInfoGetSigningTime(signerInfo, signingTime); | |
818 | break; | |
819 | } | |
820 | } | |
821 | } | |
822 | xit: | |
823 | return status; | |
824 | } | |
825 | ||
826 | #if TIMESTAMPING_SUPPORTED | |
827 | /* | |
828 | * Obtain the timestamp of signer 'signerIndex' of a CMS message, if | |
829 | * present. This timestamp is an authenticated timestamp provided by | |
830 | * a timestamping authority. | |
831 | * | |
832 | * Returns errSecParam if the CMS message was not signed or if signerIndex | |
833 | * is greater than the number of signers of the message minus one. | |
834 | * | |
835 | * This cannot be called until after CMSDecoderFinalizeMessage() is called. | |
836 | */ | |
837 | ||
838 | OSStatus CMSDecoderCopySignerTimestamp( | |
839 | CMSDecoderRef cmsDecoder, | |
840 | size_t signerIndex, /* usually 0 */ | |
841 | CFAbsoluteTime *timestamp) /* RETURNED */ | |
842 | { | |
843 | return CMSDecoderCopySignerTimestampWithPolicy(cmsDecoder, NULL, signerIndex, timestamp); | |
844 | } | |
845 | ||
846 | OSStatus CMSDecoderCopySignerTimestampWithPolicy( | |
847 | CMSDecoderRef cmsDecoder, | |
848 | CFTypeRef timeStampPolicy, | |
849 | size_t signerIndex, /* usually 0 */ | |
850 | CFAbsoluteTime *timestamp) /* RETURNED */ | |
851 | { | |
852 | OSStatus status = errSecParam; | |
853 | SecCmsMessageRef cmsg; | |
854 | SecCmsSignedDataRef signedData = NULL; | |
855 | int numContentInfos = 0; | |
856 | ||
857 | require(cmsDecoder && timestamp, xit); | |
858 | require_noerr(CMSDecoderGetCmsMessage(cmsDecoder, &cmsg), xit); | |
859 | numContentInfos = SecCmsMessageContentLevelCount(cmsg); | |
860 | for (int dex = 0; !signedData && dex < numContentInfos; dex++) | |
861 | { | |
862 | SecCmsContentInfoRef ci = SecCmsMessageContentLevel(cmsg, dex); | |
863 | SECOidTag tag = SecCmsContentInfoGetContentTypeTag(ci); | |
864 | if (tag == SEC_OID_PKCS7_SIGNED_DATA) | |
865 | if ((signedData = (SecCmsSignedDataRef)SecCmsContentInfoGetContent(ci))) { | |
866 | SecCmsSignerInfoRef signerInfo = SecCmsSignedDataGetSignerInfo(signedData, (int)signerIndex); | |
867 | if (signerInfo) | |
868 | { | |
869 | status = SecCmsSignerInfoGetTimestampTimeWithPolicy(signerInfo, timeStampPolicy, timestamp); | |
870 | break; | |
871 | } | |
872 | } | |
873 | } | |
874 | ||
875 | xit: | |
876 | return status; | |
877 | } | |
878 | ||
879 | /* | |
880 | * Obtain an array of the certificates in a timestamp response. Elements of the | |
881 | * returned array are SecCertificateRefs. The caller must CFRelease the returned | |
882 | * array. This timestamp is an authenticated timestamp provided by | |
883 | * a timestamping authority. | |
884 | * | |
885 | * Returns errSecParam if the CMS message was not signed or if signerIndex | |
886 | * is greater than the number of signers of the message minus one. It returns | |
887 | * errSecItemNotFound if no certificates were found. | |
888 | * | |
889 | * This cannot be called until after CMSDecoderFinalizeMessage() is called. | |
890 | */ | |
891 | OSStatus CMSDecoderCopySignerTimestampCertificates( | |
892 | CMSDecoderRef cmsDecoder, | |
893 | size_t signerIndex, /* usually 0 */ | |
894 | CFArrayRef *certificateRefs) /* RETURNED */ | |
895 | { | |
896 | OSStatus status = errSecParam; | |
897 | SecCmsMessageRef cmsg = NULL; | |
898 | SecCmsSignedDataRef signedData = NULL; | |
899 | int numContentInfos = 0; | |
900 | CFIndex tsn = 0; | |
901 | bool good = false; | |
902 | ||
903 | require(cmsDecoder && certificateRefs, xit); | |
904 | require_noerr(CMSDecoderGetCmsMessage(cmsDecoder, &cmsg), xit); | |
905 | numContentInfos = SecCmsMessageContentLevelCount(cmsg); | |
906 | for (int dex = 0; !signedData && dex < numContentInfos; dex++) | |
907 | { | |
908 | SecCmsContentInfoRef ci = SecCmsMessageContentLevel(cmsg, dex); | |
909 | SECOidTag tag = SecCmsContentInfoGetContentTypeTag(ci); | |
910 | if (tag == SEC_OID_PKCS7_SIGNED_DATA) | |
911 | if ((signedData = (SecCmsSignedDataRef)SecCmsContentInfoGetContent(ci))) { | |
912 | SecCmsSignerInfoRef signerInfo = SecCmsSignedDataGetSignerInfo(signedData, (int)signerIndex); | |
913 | if (signerInfo) | |
914 | { | |
915 | CFArrayRef certList = SecCmsSignerInfoGetTimestampCertList(signerInfo); | |
916 | require_action(certList, xit, status = errSecItemNotFound); | |
917 | CFMutableArrayRef certs = CFArrayCreateMutableCopy(kCFAllocatorDefault, CFArrayGetCount(certList), certList); | |
918 | ||
919 | if(certs){ | |
920 | //reorder certificates: | |
921 | tsn = CFArrayGetCount(certs); | |
922 | good = tsn > 0 && SecIsAppleTrustAnchor((SecCertificateRef)CFArrayGetValueAtIndex(certs, tsn-1), 0); | |
923 | ||
924 | if ( good == false ) | |
925 | { | |
926 | //change TS certificate ordering. | |
927 | for (CFIndex n = 0; n < tsn; n++) | |
928 | { | |
929 | SecCertificateRef tsRoot = (SecCertificateRef)CFArrayGetValueAtIndex(certs, n); | |
930 | if (tsRoot) | |
931 | if ((good = SecIsAppleTrustAnchor(tsRoot, 0))) { | |
932 | CFArrayExchangeValuesAtIndices(certs, n, tsn-1); | |
933 | break; | |
934 | } | |
935 | } | |
936 | } | |
937 | ||
938 | *certificateRefs = CFArrayCreateCopy(kCFAllocatorDefault, certs); | |
939 | CFRelease(certs); | |
940 | status = errSecSuccess; | |
941 | } | |
942 | break; | |
943 | } | |
944 | } | |
945 | } | |
946 | ||
947 | ||
948 | xit: | |
949 | return status; | |
950 | } | |
951 | #endif | |
952 | ||
953 | /* | |
954 | * Obtain the Hash Agility attribute value of signer 'signerIndex' | |
955 | * of a CMS message, if present. | |
956 | * | |
957 | * Returns errSecParam if the CMS message was not signed or if signerIndex | |
958 | * is greater than the number of signers of the message minus one. | |
959 | * | |
960 | * This cannot be called until after CMSDecoderFinalizeMessage() is called. | |
961 | */ | |
962 | OSStatus CMSDecoderCopySignerAppleCodesigningHashAgility( | |
963 | CMSDecoderRef cmsDecoder, | |
964 | size_t signerIndex, /* usually 0 */ | |
965 | CFDataRef CF_RETURNS_RETAINED *hashAgilityAttrValue) /* RETURNED */ | |
966 | { | |
967 | OSStatus status = errSecParam; | |
968 | SecCmsMessageRef cmsg; | |
969 | SecCmsSignedDataRef signedData = NULL; | |
970 | int numContentInfos = 0; | |
971 | CFDataRef returnedValue = NULL; | |
972 | ||
ecaf5866 A |
973 | require(cmsDecoder && hashAgilityAttrValue, exit); |
974 | require_noerr(CMSDecoderGetCmsMessage(cmsDecoder, &cmsg), exit); | |
866f8763 A |
975 | numContentInfos = SecCmsMessageContentLevelCount(cmsg); |
976 | for (int dex = 0; !signedData && dex < numContentInfos; dex++) | |
977 | { | |
978 | SecCmsContentInfoRef ci = SecCmsMessageContentLevel(cmsg, dex); | |
979 | SECOidTag tag = SecCmsContentInfoGetContentTypeTag(ci); | |
980 | if (tag == SEC_OID_PKCS7_SIGNED_DATA) | |
981 | if ((signedData = (SecCmsSignedDataRef)SecCmsContentInfoGetContent(ci))) { | |
982 | SecCmsSignerInfoRef signerInfo = SecCmsSignedDataGetSignerInfo(signedData, (int)signerIndex); | |
983 | if (signerInfo) | |
984 | { | |
985 | status = SecCmsSignerInfoGetAppleCodesigningHashAgility(signerInfo, &returnedValue); | |
986 | break; | |
987 | } | |
988 | } | |
989 | } | |
ecaf5866 | 990 | exit: |
866f8763 A |
991 | if (status == errSecSuccess && returnedValue) { |
992 | *hashAgilityAttrValue = (CFDataRef) CFRetain(returnedValue); | |
993 | } else { | |
994 | *hashAgilityAttrValue = NULL; | |
995 | } | |
996 | return status; | |
997 | } | |
ecaf5866 A |
998 | |
999 | /* | |
1000 | * Obtain the Hash Agility V2 attribute value of signer 'signerIndex' | |
1001 | * of a CMS message, if present. | |
1002 | * | |
1003 | * Returns errSecParam if the CMS message was not signed or if signerIndex | |
1004 | * is greater than the number of signers of the message minus one. | |
1005 | * | |
1006 | * This cannot be called until after CMSDecoderFinalizeMessage() is called. | |
1007 | */ | |
1008 | OSStatus CMSDecoderCopySignerAppleCodesigningHashAgilityV2( | |
1009 | CMSDecoderRef cmsDecoder, | |
1010 | size_t signerIndex, /* usually 0 */ | |
1011 | CFDictionaryRef CF_RETURNS_RETAINED *hashAgilityV2AttrValues) /* RETURNED */ | |
1012 | { | |
1013 | OSStatus status = errSecParam; | |
1014 | SecCmsMessageRef cmsg; | |
1015 | SecCmsSignedDataRef signedData = NULL; | |
1016 | int numContentInfos = 0; | |
1017 | CFDictionaryRef returnedValue = NULL; | |
1018 | ||
1019 | require(cmsDecoder && hashAgilityV2AttrValues, exit); | |
1020 | require_noerr(CMSDecoderGetCmsMessage(cmsDecoder, &cmsg), exit); | |
1021 | numContentInfos = SecCmsMessageContentLevelCount(cmsg); | |
1022 | for (int dex = 0; !signedData && dex < numContentInfos; dex++) | |
1023 | { | |
1024 | SecCmsContentInfoRef ci = SecCmsMessageContentLevel(cmsg, dex); | |
1025 | SECOidTag tag = SecCmsContentInfoGetContentTypeTag(ci); | |
1026 | if (tag == SEC_OID_PKCS7_SIGNED_DATA) | |
1027 | if ((signedData = (SecCmsSignedDataRef)SecCmsContentInfoGetContent(ci))) { | |
1028 | SecCmsSignerInfoRef signerInfo = SecCmsSignedDataGetSignerInfo(signedData, (int)signerIndex); | |
1029 | if (signerInfo) | |
1030 | { | |
1031 | status = SecCmsSignerInfoGetAppleCodesigningHashAgilityV2(signerInfo, &returnedValue); | |
1032 | break; | |
1033 | } | |
1034 | } | |
1035 | } | |
1036 | exit: | |
1037 | if (status == errSecSuccess && returnedValue) { | |
1038 | *hashAgilityV2AttrValues = (CFDictionaryRef) CFRetain(returnedValue); | |
1039 | } else { | |
1040 | *hashAgilityV2AttrValues = NULL; | |
1041 | } | |
1042 | return status; | |
1043 | } |