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