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