X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5dd5f9ec28f304ca377c42fd7f711d6cf12b90e1..5c19dc3ae3bd8e40a9c028b0deddd50ff337692c:/Security/libsecurity_smime/lib/tsaSupport.c?ds=sidebyside diff --git a/Security/libsecurity_smime/lib/tsaSupport.c b/Security/libsecurity_smime/lib/tsaSupport.c deleted file mode 100644 index 1d2db833..00000000 --- a/Security/libsecurity_smime/lib/tsaSupport.c +++ /dev/null @@ -1,1410 +0,0 @@ -/* - * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - * - * tsaSupport.c - ASN1 templates Time Stamping Authority requests and responses - */ - -/* -#include -#include -#include -#include -*/ - -#include -#include - -#include -#include -#include -#include -#include -#include "tsaTemplates.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tsaSupport.h" -#include "tsaSupportPriv.h" -#include "tsaTemplates.h" -#include "cmslocal.h" - -#include "secoid.h" -#include "secitem.h" -#include - -const CFStringRef kTSAContextKeyURL = CFSTR("ServerURL"); -const CFStringRef kTSAContextKeyNoCerts = CFSTR("NoCerts"); -const CFStringRef kTSADebugContextKeyBadReq = CFSTR("DebugBadReq"); -const CFStringRef kTSADebugContextKeyBadNonce = CFSTR("DebugBadNonce"); - -extern const SecAsn1Template kSecAsn1TSATSTInfoTemplate[]; - -extern OSStatus impExpImportCertCommon( - const CSSM_DATA *cdata, - SecKeychainRef importKeychain, // optional - CFMutableArrayRef outArray); // optional, append here - -#pragma mark ----- Debug Logs ----- - -#ifndef NDEBUG -#define TSA_USE_SYSLOG 1 -#endif - -#if TSA_USE_SYSLOG -#include - #include - #include - #define tsaDebug(fmt, ...) \ - do { if (true) { \ - char buf[64]; \ - struct timeval time_now; \ - gettimeofday(&time_now, NULL); \ - struct tm* time_info = localtime(&time_now.tv_sec); \ - strftime(buf, sizeof(buf), "[%Y-%m-%d %H:%M:%S]", time_info); \ - fprintf(stderr, "%s " fmt, buf, ## __VA_ARGS__); \ - syslog(LOG_ERR, " " fmt, ## __VA_ARGS__); \ - } } while (0) - #define tsa_secdebug(scope, format...) \ - { \ - syslog(LOG_NOTICE, format); \ - secdebug(scope, format); \ - printf(format); \ - } -#else - #define tsaDebug(args...) tsa_secdebug("tsa", ## args) -#define tsa_secdebug(scope, format...) \ - secdebug(scope, format) -#endif - -#ifndef NDEBUG -#define TSTINFO_DEBUG 1 //jch -#endif - -#if TSTINFO_DEBUG -#define dtprintf(args...) tsaDebug(args) -#else -#define dtprintf(args...) -#endif - -#define kHTTPResponseCodeContinue 100 -#define kHTTPResponseCodeOK 200 -#define kHTTPResponseCodeNoContent 204 -#define kHTTPResponseCodeBadRequest 400 -#define kHTTPResponseCodeUnauthorized 401 -#define kHTTPResponseCodeForbidden 403 -#define kHTTPResponseCodeNotFound 404 -#define kHTTPResponseCodeConflict 409 -#define kHTTPResponseCodeExpectationFailed 417 -#define kHTTPResponseCodeServFail 500 -#define kHTTPResponseCodeServiceUnavailable 503 -#define kHTTPResponseCodeInsufficientStorage 507 - -#pragma mark ----- Debug/Utilities ----- - -static OSStatus remapHTTPErrorCodes(OSStatus status) -{ - switch (status) - { - case kHTTPResponseCodeOK: - case kHTTPResponseCodeContinue: - return noErr; - case kHTTPResponseCodeBadRequest: - return errSecTimestampBadRequest; - case kHTTPResponseCodeNoContent: - case kHTTPResponseCodeUnauthorized: - case kHTTPResponseCodeForbidden: - case kHTTPResponseCodeNotFound: - case kHTTPResponseCodeConflict: - case kHTTPResponseCodeExpectationFailed: - case kHTTPResponseCodeServFail: - case kHTTPResponseCodeInsufficientStorage: - case kHTTPResponseCodeServiceUnavailable: - return errSecTimestampServiceNotAvailable; - default: - return status; - } - return status; - -} - -static void printDataAsHex(const char *title, const CSSM_DATA *d, unsigned maxToPrint) // 0 means print it all -{ -#ifndef NDEBUG - unsigned i; - bool more = false; - uint32 len = (uint32)d->Length; - uint8 *cp = d->Data; - char *buffer = NULL; - size_t bufferSize; - int offset, sz = 0; - const int wrapwid = 24; // large enough so SHA-1 hashes fit on one line... - - if ((maxToPrint != 0) && (len > maxToPrint)) - { - len = maxToPrint; - more = true; - } - - bufferSize = wrapwid+3*len; - buffer = (char *)malloc(bufferSize); - - offset = sprintf(buffer, "%s [len = %u]\n", title, len); - dtprintf("%s", buffer); - offset = 0; - - for (i=0; (i < len) && (offset+3 < bufferSize); i++, offset += sz) - { - sz = sprintf(buffer + offset, " %02x", (unsigned int)cp[i] & 0xff); - if ((i % wrapwid) == (wrapwid-1)) - { - dtprintf("%s", buffer); - offset = 0; - sz = 0; - } - } - - sz=sprintf(buffer + offset, more?" ...\n":"\n"); - offset += sz; - buffer[offset+1]=0; - -// fprintf(stderr, "%s", buffer); - dtprintf("%s", buffer); - - free(buffer); -#endif -} - -#ifndef NDEBUG -int tsaWriteFileX(const char *fileName, const unsigned char *bytes, size_t numBytes) -{ - int rtn; - int fd; - - fd = open(fileName, O_RDWR | O_CREAT | O_TRUNC, 0600); - if (fd <= 0) - return errno; - - rtn = (int)write(fd, bytes, numBytes); - if(rtn != (int)numBytes) - { - if (rtn >= 0) - fprintf(stderr, "writeFile: short write\n"); - rtn = EIO; - } - else - rtn = 0; - - close(fd); - return rtn; -} -#endif - -char *cfStringToChar(CFStringRef inStr) -{ - // Caller must free - char *result = NULL; - const char *str = NULL; - - if (!inStr) - return strdup(""); // return a null string - - // quick path first - if ((str = CFStringGetCStringPtr(inStr, kCFStringEncodingUTF8))) { - result = strdup(str); - } else { - // need to extract into buffer - CFIndex length = CFStringGetLength(inStr); // in 16-bit character units - CFIndex bytesToAllocate = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1; - result = malloc(bytesToAllocate); - if (!CFStringGetCString(inStr, result, bytesToAllocate, kCFStringEncodingUTF8)) - result[0] = 0; - } - - return result; -} - -/* Oids longer than this are considered invalid. */ -#define MAX_OID_SIZE 32 - -#ifndef NDEBUG -/* FIXME: There are other versions of this in SecCertifcate.c and SecCertificateP.c */ -static CFStringRef SecDERItemCopyOIDDecimalRepresentation(CFAllocatorRef allocator, const CSSM_OID *oid) -{ - if (oid->Length == 0) - return CFSTR(""); - - if (oid->Length > MAX_OID_SIZE) - return CFSTR("Oid too long"); - - CFMutableStringRef result = CFStringCreateMutable(allocator, 0); - - // The first two levels are encoded into one byte, since the root levelq - // has only 3 nodes (40*x + y). However if x = joint-iso-itu-t(2) then - // y may be > 39, so we have to add special-case handling for this. - uint32_t x = oid->Data[0] / 40; - uint32_t y = oid->Data[0] % 40; - if (x > 2) - { - // Handle special case for large y if x = 2 - y += (x - 2) * 40; - x = 2; - } - CFStringAppendFormat(result, NULL, CFSTR("%u.%u"), x, y); - - uint32_t value = 0; - for (x = 1; x < oid->Length; ++x) - { - value = (value << 7) | (oid->Data[x] & 0x7F); - /* @@@ value may not span more than 4 bytes. */ - /* A max number of 20 values is allowed. */ - if (!(oid->Data[x] & 0x80)) - { - CFStringAppendFormat(result, NULL, CFSTR(".%lu"), (unsigned long)value); - value = 0; - } - } - return result; -} -#endif - -static void debugSaveCertificates(CSSM_DATA **outCerts) -{ -#ifndef NDEBUG - if (outCerts) - { - CSSM_DATA_PTR *certp; - unsigned jx = 0; - const char *certNameBase = "/tmp/tsa-resp-cert-"; - char fname[PATH_MAX]; - unsigned certCount = SecCmsArrayCount((void **)outCerts); - dtprintf("Found %d certs\n",certCount); - - for (certp=outCerts;*certp;certp++, ++jx) - { - char numstr[32]; - strncpy(fname, certNameBase, strlen(certNameBase)+1); - sprintf(numstr,"%u", jx); - strcat(fname,numstr); - tsaWriteFileX(fname, (*certp)->Data, (*certp)->Length); - if (jx > 5) - break; //something wrong - } - } -#endif -} - -static void debugShowSignerInfo(SecCmsSignedDataRef signedData) -{ -#ifndef NDEBUG - int numberOfSigners = SecCmsSignedDataSignerInfoCount (signedData); - dtprintf("numberOfSigners : %d\n", numberOfSigners); - int ix; - for (ix=0;ix < numberOfSigners;ix++) - { - SecCmsSignerInfoRef sigi = SecCmsSignedDataGetSignerInfo(signedData,ix); - if (sigi) - { - CFStringRef commonName = SecCmsSignerInfoGetSignerCommonName(sigi); - const char *signerhdr = " signer : "; - if (commonName) - { - char *cn = cfStringToChar(commonName); - dtprintf("%s%s\n", signerhdr, cn); - if (cn) - free(cn); - CFReleaseNull(commonName); - } - else - dtprintf("%s\n", signerhdr); - } - } -#endif -} - -static void debugShowContentTypeOID(SecCmsContentInfoRef contentInfo) -{ -#ifndef NDEBUG - - CSSM_OID *typeOID = SecCmsContentInfoGetContentTypeOID(contentInfo); - if (typeOID) - { - CFStringRef oidCFStr = SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault, typeOID); - char *oidstr = cfStringToChar(oidCFStr); - printDataAsHex("oid:", typeOID, (unsigned int)typeOID->Length); - dtprintf("\toid: %s\n", oidstr); - if (oidCFStr) - CFRelease(oidCFStr); - if (oidstr) - free(oidstr); - } -#endif -} - -uint64_t tsaDER_ToInt(const CSSM_DATA *DER_Data) -{ - uint64_t rtn = 0; - unsigned i = 0; - - while(i < DER_Data->Length) { - rtn |= DER_Data->Data[i]; - if(++i == DER_Data->Length) { - break; - } - rtn <<= 8; - } - return rtn; -} - -void displayTSTInfo(SecAsn1TSATSTInfo *tstInfo) -{ -#ifndef NDEBUG - dtprintf("--- TSTInfo ---\n"); - if (!tstInfo) - return; - - if (tstInfo->version.Data) - { - uint64_t vers = tsaDER_ToInt(&tstInfo->version); - dtprintf("Version:\t\t%u\n", (int)vers); - } - - if (tstInfo->serialNumber.Data) - { - uint64_t sn = tsaDER_ToInt(&tstInfo->serialNumber); - dtprintf("SerialNumber:\t%llu\n", sn); - } - - if (tstInfo->ordering.Data) - { - uint64_t ord = tsaDER_ToInt(&tstInfo->ordering); - dtprintf("Ordering:\t\t%s\n", ord?"yes":"no"); - } - - if (tstInfo->nonce.Data) - { - uint64_t nonce = tsaDER_ToInt(&tstInfo->nonce); - dtprintf("Nonce:\t\t%llu\n", nonce); - } - else - dtprintf("Nonce:\t\tnot specified\n"); - - if (tstInfo->genTime.Data) - { - char buf[tstInfo->genTime.Length+1]; - memcpy(buf, (const char *)tstInfo->genTime.Data, tstInfo->genTime.Length); - buf[tstInfo->genTime.Length]=0; - dtprintf("GenTime:\t\t%s\n", buf); - } - - dtprintf("-- MessageImprint --\n"); - if (true) // SecAsn1TSAMessageImprint - { - printDataAsHex(" Algorithm:",&tstInfo->messageImprint.hashAlgorithm.algorithm, 0); - printDataAsHex(" Message :", &tstInfo->messageImprint.hashedMessage, 0);//tstInfo->messageImprint.hashedMessage.Length); - } -#endif -} - -#pragma mark ----- TimeStamp Response using XPC ----- - -#include - -static OSStatus checkForNonDERResponse(const unsigned char *resp, size_t respLen) -{ - /* - Good start is something like 30 82 0c 03 30 15 02 01 00 30 10 0c 0e 4f 70 65 - - URL: http://timestamp-int.corp.apple.com/signserver/process?TimeStampSigner - Resp: Http/1.1 Service Unavailable - - URL: http://timestamp-int.corp.apple.com/ts01 - Resp: blank - - URL: http://cutandtaste.com/404 (or other forced 404 site) - Resp: 404 - */ - - OSStatus status = noErr; - const char ader[2] = { 0x30, 0x82 }; - char *respStr = NULL; - size_t maxlen = 0; - size_t badResponseCount; - - const char *badResponses[] = - { - "", - "Http/1.1 Service Unavailable", - "blank" - }; - - require_action(resp && respLen, xit, status = errSecTimestampServiceNotAvailable); - - // This is usual case - if ((respLen > 1) && (memcmp(resp, ader, 2)==0)) // might be good; pass on to DER decoder - return noErr; - - badResponseCount = sizeof(badResponses)/sizeof(char *); - int ix; - for (ix = 0; ix < badResponseCount; ++ix) - if (strlen(badResponses[ix]) > maxlen) - maxlen = strlen(badResponses[ix]); - - // Prevent a large response from allocating a ton of memory - if (respLen > maxlen) - respLen = maxlen; - - respStr = (char *)malloc(respLen+1); - strlcpy(respStr, (const char *)resp, respLen); - - for (ix = 0; ix < badResponseCount; ++ix) - if (strcmp(respStr, badResponses[ix])==0) - return errSecTimestampServiceNotAvailable; - -xit: - if (respStr) - free((void *)respStr); - - return status; -} - -static OSStatus sendTSARequestWithXPC(const unsigned char *tsaReq, size_t tsaReqLength, const unsigned char *tsaURL, unsigned char **tsaResp, size_t *tsaRespLength) -{ - __block OSStatus result = noErr; - int timeoutInSeconds = 15; - extern xpc_object_t xpc_create_with_format(const char * format, ...); - - dispatch_queue_t xpc_queue = dispatch_queue_create("com.apple.security.XPCTimeStampingService", DISPATCH_QUEUE_SERIAL); - __block dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0); - dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, timeoutInSeconds * NSEC_PER_SEC); - - xpc_connection_t con = xpc_connection_create("com.apple.security.XPCTimeStampingService", xpc_queue); - - xpc_connection_set_event_handler(con, ^(xpc_object_t event) { - xpc_type_t xtype = xpc_get_type(event); - if (XPC_TYPE_ERROR == xtype) - { tsaDebug("default: connection error: %s\n", xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION)); } - else - { tsaDebug("default: unexpected connection event %p\n", event); } - }); - - xpc_connection_resume(con); - - xpc_object_t tsaReqData = xpc_data_create(tsaReq, tsaReqLength); - const char *urlstr = (tsaURL?(const char *)tsaURL:""); - xpc_object_t url_as_xpc_string = xpc_string_create(urlstr); - - xpc_object_t message = xpc_create_with_format("{operation: TimeStampRequest, ServerURL: %value, TimeStampRequest: %value}", url_as_xpc_string, tsaReqData); - - xpc_connection_send_message_with_reply(con, message, xpc_queue, ^(xpc_object_t reply) - { - tsaDebug("xpc_connection_send_message_with_reply handler called back\n"); - dispatch_retain(waitSemaphore); - - xpc_type_t xtype = xpc_get_type(reply); - if (XPC_TYPE_ERROR == xtype) - { tsaDebug("message error: %s\n", xpc_dictionary_get_string(reply, XPC_ERROR_KEY_DESCRIPTION)); } - else if (XPC_TYPE_CONNECTION == xtype) - { tsaDebug("received connection\n"); } - else if (XPC_TYPE_DICTIONARY == xtype) - { -#ifndef NDEBUG - /* - // This is useful for debugging. - char *debug = xpc_copy_description(reply); - tsaDebug("DEBUG %s\n", debug); - free(debug); - */ -#endif - - xpc_object_t xpcTimeStampReply = xpc_dictionary_get_value(reply, "TimeStampReply"); - size_t xpcTSRLength = xpc_data_get_length(xpcTimeStampReply); - tsaDebug("xpcTSRLength: %ld bytes of response\n", xpcTSRLength); - - xpc_object_t xpcTimeStampError = xpc_dictionary_get_value(reply, "TimeStampError"); - xpc_object_t xpcTimeStampStatus = xpc_dictionary_get_value(reply, "TimeStampStatus"); - - if (xpcTimeStampError || xpcTimeStampStatus) - { -#ifndef NDEBUG - if (xpcTimeStampError) - { - size_t len = xpc_string_get_length(xpcTimeStampError); - char *buf = (char *)malloc(len); - strlcpy(buf, xpc_string_get_string_ptr(xpcTimeStampError), len+1); - tsaDebug("xpcTimeStampError: %s\n", buf); - if (buf) - free(buf); - } -#endif - if (xpcTimeStampStatus) - { - result = (OSStatus)xpc_int64_get_value(xpcTimeStampStatus); - tsaDebug("xpcTimeStampStatus: %d\n", (int)result); - } - } - - result = remapHTTPErrorCodes(result); - - if ((result == noErr) && tsaResp && tsaRespLength) - { - *tsaRespLength = xpcTSRLength; - *tsaResp = (unsigned char *)malloc(xpcTSRLength); - - size_t bytesCopied = xpc_data_get_bytes(xpcTimeStampReply, *tsaResp, 0, xpcTSRLength); - if (bytesCopied != xpcTSRLength) - { tsaDebug("length mismatch: copied: %ld, xpc: %ld\n", bytesCopied, xpcTSRLength); } - else - if ((result = checkForNonDERResponse(*tsaResp,bytesCopied))) - { - tsaDebug("received non-DER response from timestamp server\n"); - } - else - { - result = noErr; - tsaDebug("copied: %ld bytes of response\n", bytesCopied); - } - } - tsaDebug("releasing connection\n"); - xpc_release(con); - } - else - { tsaDebug("unexpected message reply type %p\n", xtype); } - - dispatch_semaphore_signal(waitSemaphore); - dispatch_release(waitSemaphore); - }); - - { tsaDebug("waiting up to %d seconds for response from XPC\n", timeoutInSeconds); } - dispatch_semaphore_wait(waitSemaphore, finishTime); - - dispatch_release(waitSemaphore); - xpc_release(tsaReqData); - xpc_release(message); - - { tsaDebug("sendTSARequestWithXPC exit\n"); } - - return result; -} - -#pragma mark ----- TimeStamp request ----- - -#include "tsaTemplates.h" -#include -#include -#include -#include - -extern const SecAsn1Template kSecAsn1TSATimeStampReqTemplate; -extern const SecAsn1Template kSecAsn1TSATimeStampRespTemplateDER; - -CFMutableDictionaryRef SecCmsTSAGetDefaultContext(CFErrorRef *error) -{ - // Caller responsible for retain/release - // Update SecCmsTSAGetDefaultContext with actual URL for Apple Timestamp server - // URL will be in TimeStampingPrefs.plist - - CFBundleRef secFWbundle = NULL; - CFURLRef resourceURL = NULL; - CFDataRef resourceData = NULL; - CFPropertyListRef prefs = NULL; - CFMutableDictionaryRef contextDict = NULL; - SInt32 errorCode = 0; - CFOptionFlags options = 0; - CFPropertyListFormat format = 0; - OSStatus status = noErr; - - require_action(secFWbundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.security")), xit, status = errSecInternalError); - require_action(resourceURL = CFBundleCopyResourceURL(secFWbundle, CFSTR("TimeStampingPrefs"), CFSTR("plist"), NULL), - xit, status = errSecInvalidPrefsDomain); - - require(CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, resourceURL, &resourceData, - NULL, NULL, &errorCode), xit); - require_action(resourceData, xit, status = errSecDataNotAvailable); - - prefs = CFPropertyListCreateWithData(kCFAllocatorDefault, resourceData, options, &format, error); - require_action(prefs && (CFGetTypeID(prefs)==CFDictionaryGetTypeID()), xit, status = errSecInvalidPrefsDomain); - - contextDict = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, prefs); - - if (error) - *error = NULL; -xit: - if (errorCode) - status = errorCode; - if (error && status) - *error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainOSStatus, status, NULL); - if (secFWbundle) - CFRelease(secFWbundle); - if (resourceURL) - CFRelease(resourceURL); - if (resourceData) - CFRelease(resourceData); - if (prefs) - CFRelease(prefs); - - return contextDict; -} - -static CFDataRef _SecTSARequestCopyDEREncoding(SecAsn1TSAMessageImprint *messageImprint, bool noCerts, uint64_t nonce) -{ - // Returns DER encoded TimeStampReq - // Modeled on _SecOCSPRequestCopyDEREncoding - // The Timestamp Authority supports 64 bit nonces (or more possibly) - - SecAsn1CoderRef coder = NULL; - uint8_t version = 1; - SecAsn1Item vers = {1, &version}; - uint8_t creq = noCerts?0:1; - SecAsn1Item certReq = {1, &creq}; //jch - to request or not? - SecAsn1TSATimeStampReq tsreq = {}; - CFDataRef der = NULL; - uint64_t nonceVal = OSSwapHostToBigConstInt64(nonce); - SecAsn1Item nonceItem = {sizeof(uint64_t), (unsigned char *)&nonceVal}; - - uint8_t OID_FakePolicy_Data[] = { 0x2A, 0x03, 0x04, 0x05, 0x06}; - const CSSM_OID fakePolicyOID = {sizeof(OID_FakePolicy_Data),OID_FakePolicy_Data}; - - tsreq.version = vers; - - tsreq.messageImprint = *messageImprint; - tsreq.certReq = certReq; - - // skip reqPolicy, extensions for now - FAKES - jch - tsreq.reqPolicy = fakePolicyOID; //policyID; - - tsreq.nonce = nonceItem; - - // Encode the request - require_noerr(SecAsn1CoderCreate(&coder), errOut); - - SecAsn1Item encoded; - require_noerr(SecAsn1EncodeItem(coder, &tsreq, - &kSecAsn1TSATimeStampReqTemplate, &encoded), errOut); - der = CFDataCreate(kCFAllocatorDefault, encoded.Data, - encoded.Length); - -errOut: - if (coder) - SecAsn1CoderRelease(coder); - - return der; -} - -OSStatus SecTSAResponseCopyDEREncoding(SecAsn1CoderRef coder, const CSSM_DATA *tsaResponse, SecAsn1TimeStampRespDER *respDER) -{ - // Partially decode the response - OSStatus status = paramErr; - - require(tsaResponse && respDER, errOut); - require_noerr(SecAsn1DecodeData(coder, tsaResponse, - &kSecAsn1TSATimeStampRespTemplateDER, respDER), errOut); - status = noErr; - -errOut: - - return status; -} - -#pragma mark ----- TS Callback ----- - -OSStatus SecCmsTSADefaultCallback(CFTypeRef context, void *messageImprintV, uint64_t nonce, CSSM_DATA *signedDERBlob) -{ - OSStatus result = paramErr; - const unsigned char *tsaReq = NULL; - size_t tsaReqLength = 0; - CFDataRef cfreq = NULL; - unsigned char *tsaURL = NULL; - bool noCerts = false; - - if (!context || CFGetTypeID(context)!=CFDictionaryGetTypeID()) - return paramErr; - - SecAsn1TSAMessageImprint *messageImprint = (SecAsn1TSAMessageImprint *)messageImprintV; - if (!messageImprint || !signedDERBlob) - return paramErr; - - CFBooleanRef cfnocerts = (CFBooleanRef)CFDictionaryGetValue((CFDictionaryRef)context, kTSAContextKeyNoCerts); - if (cfnocerts) - { - tsaDebug("[TSA] Request noCerts\n"); - noCerts = CFBooleanGetValue(cfnocerts); - } - - // We must spoof the nonce here, before sending the request. - // If we tried to alter the reply, then the signature would break instead. - CFBooleanRef cfBadNonce = (CFBooleanRef)CFDictionaryGetValue((CFDictionaryRef)context, kTSADebugContextKeyBadNonce); - if (cfBadNonce && CFBooleanGetValue(cfBadNonce)) - { - tsaDebug("[TSA] Forcing bad TS Request by changing nonce\n"); - nonce++; - } - - printDataAsHex("[TSA] hashToTimeStamp:", &messageImprint->hashedMessage,128); - cfreq = _SecTSARequestCopyDEREncoding(messageImprint, noCerts, nonce); - if (cfreq) - { - tsaReq = CFDataGetBytePtr(cfreq); - tsaReqLength = CFDataGetLength(cfreq); - -#ifndef NDEBUG - CFShow(cfreq); - tsaWriteFileX("/tmp/tsareq.req", tsaReq, tsaReqLength); -#endif - } - - CFStringRef url = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)context, kTSAContextKeyURL); - if (!url) - { - tsaDebug("[TSA] missing URL for TSA (key: %s)\n", "kTSAContextKeyURL"); - goto xit; - } - - /* - If debugging, look at special values in the context to mess things up - */ - - CFBooleanRef cfBadReq = (CFBooleanRef)CFDictionaryGetValue((CFDictionaryRef)context, kTSADebugContextKeyBadReq); - if (cfBadReq && CFBooleanGetValue(cfBadReq)) - { - tsaDebug("[TSA] Forcing bad TS Request by truncating length from %ld to %ld\n", tsaReqLength, (tsaReqLength-4)); - tsaReqLength -= 4; - } - - // need to extract into buffer - CFIndex length = CFStringGetLength(url); // in 16-bit character units - tsaURL = malloc(6 * length + 1); // pessimistic - if (!CFStringGetCString(url, (char *)tsaURL, 6 * length + 1, kCFStringEncodingUTF8)) - goto xit; - - tsaDebug("[TSA] URL for timestamp server: %s\n", tsaURL); - - unsigned char *tsaResp = NULL; - size_t tsaRespLength = 0; - tsaDebug("calling sendTSARequestWithXPC with %ld bytes of request\n", tsaReqLength); - - require_noerr(result = sendTSARequestWithXPC(tsaReq, tsaReqLength, tsaURL, &tsaResp, &tsaRespLength), xit); - - tsaDebug("sendTSARequestWithXPC copied: %ld bytes of response\n", tsaRespLength); - - signedDERBlob->Data = tsaResp; - signedDERBlob->Length = tsaRespLength; - - result = noErr; - -xit: - if (tsaURL) - free((void *)tsaURL); - if (cfreq) - CFRelease(cfreq); - - return result; -} - -#pragma mark ----- TimeStamp Verification ----- - -static OSStatus convertGeneralizedTimeToCFAbsoluteTime(const char *timeStr, CFAbsoluteTime *ptime) -{ - /* - See http://userguide.icu-project.org/formatparse/datetime for date/time format. - The "Z" signal a GMT time, but CFDateFormatterGetAbsoluteTimeFromString returns - values based on local time. - */ - - OSStatus result = noErr; - CFDateFormatterRef formatter = NULL; - CFStringRef time_string = NULL; - CFTimeZoneRef gmt = NULL; - CFLocaleRef locale = NULL; - CFRange *rangep = NULL; - - require(timeStr && timeStr[0] && ptime, xit); - require(formatter = CFDateFormatterCreate(kCFAllocatorDefault, locale, kCFDateFormatterNoStyle, kCFDateFormatterNoStyle), xit); -// CFRetain(formatter); - CFDateFormatterSetFormat(formatter, CFSTR("yyyyMMddHHmmss'Z'")); // GeneralizedTime - gmt = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0); - CFDateFormatterSetProperty(formatter, kCFDateFormatterTimeZone, gmt); - - time_string = CFStringCreateWithCString(kCFAllocatorDefault, timeStr, kCFStringEncodingUTF8); - if (!time_string || !CFDateFormatterGetAbsoluteTimeFromString(formatter, time_string, rangep, ptime)) - { - dtprintf("%s is not a valid date\n", timeStr); - result = 1; - } - -xit: - if (formatter) - CFRelease(formatter); - if (time_string) - CFRelease(time_string); - if (gmt) - CFRelease(gmt); - - return result; -} - -static OSStatus SecTSAValidateTimestamp(const SecAsn1TSATSTInfo *tstInfo, CSSM_DATA **signingCerts, CFAbsoluteTime *timestampTime) -{ - // See Properly handle revocation information of timestamping certificate - OSStatus result = paramErr; - CFAbsoluteTime genTime = 0; - char timeStr[32] = {0,}; - SecCertificateRef signingCertificate = NULL; - - require(tstInfo && signingCerts && (tstInfo->genTime.Length < 16), xit); - - // Find the leaf signingCert - require_noerr(result = SecCertificateCreateFromData(*signingCerts, - CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, &signingCertificate), xit); - - memcpy(timeStr, tstInfo->genTime.Data, tstInfo->genTime.Length); - timeStr[tstInfo->genTime.Length] = 0; - require_noerr(convertGeneralizedTimeToCFAbsoluteTime(timeStr, &genTime), xit); - if (SecCertificateIsValidX(signingCertificate, genTime)) // iOS? - result = noErr; - else - result = errSecTimestampInvalid; - if (timestampTime) - *timestampTime = genTime; -xit: - if (signingCertificate) - CFReleaseNull(signingCertificate); - return result; -} - -static OSStatus verifyTSTInfo(const CSSM_DATA_PTR content, CSSM_DATA **signingCerts, SecAsn1TSATSTInfo *tstInfo, CFAbsoluteTime *timestampTime, uint64_t expectedNonce) -{ - OSStatus status = paramErr; - SecAsn1CoderRef coder = NULL; - - if (!tstInfo) - return SECFailure; - - require_noerr(SecAsn1CoderCreate(&coder), xit); - require_noerr(SecAsn1Decode(coder, content->Data, content->Length, - kSecAsn1TSATSTInfoTemplate, tstInfo), xit); - displayTSTInfo(tstInfo); - - // Check the nonce - if (tstInfo->nonce.Data && expectedNonce!=0) - { - uint64_t nonce = tsaDER_ToInt(&tstInfo->nonce); - // if (expectedNonce!=nonce) - dtprintf("verifyTSTInfo nonce: actual: %lld, expected: %lld\n", nonce, expectedNonce); - require_action(expectedNonce==nonce, xit, status = errSecTimestampRejection); - } - - status = SecTSAValidateTimestamp(tstInfo, signingCerts, timestampTime); - dtprintf("SecTSAValidateTimestamp result: %ld\n", (long)status); - -xit: - if (coder) - SecAsn1CoderRelease(coder); - return status; -} - -static void debugShowExtendedTrustResult(int index, CFDictionaryRef extendedResult) -{ -#ifndef NDEBUG - if (extendedResult) - { - CFStringRef xresStr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, - CFSTR("Extended trust result for signer #%d : %@"), index, extendedResult); - if (xresStr) - { - CFShow(xresStr); - CFRelease(xresStr); - } - } -#endif -} - -#ifndef NDEBUG -extern const char *cssmErrorString(CSSM_RETURN error); - -static void statusBitTest(CSSM_TP_APPLE_CERT_STATUS certStatus, uint32 bit, const char *str) -{ - if (certStatus & bit) - dtprintf("%s ", str); -} -#endif - -static void debugShowCertEvidenceInfo(uint16_t certCount, const CSSM_TP_APPLE_EVIDENCE_INFO *info) -{ -#ifndef NDEBUG - CSSM_TP_APPLE_CERT_STATUS cs; -// const CSSM_TP_APPLE_EVIDENCE_INFO *pinfo = info; - uint16_t ix; - for (ix=0; info && (ixStatusBits; - dtprintf(" cert %u:\n", ix); - dtprintf(" StatusBits : 0x%x", (unsigned)cs); - if (cs) - { - dtprintf(" ( "); - statusBitTest(cs, CSSM_CERT_STATUS_EXPIRED, "EXPIRED"); - statusBitTest(cs, CSSM_CERT_STATUS_NOT_VALID_YET, - "NOT_VALID_YET"); - statusBitTest(cs, CSSM_CERT_STATUS_IS_IN_INPUT_CERTS, - "IS_IN_INPUT_CERTS"); - statusBitTest(cs, CSSM_CERT_STATUS_IS_IN_ANCHORS, - "IS_IN_ANCHORS"); - statusBitTest(cs, CSSM_CERT_STATUS_IS_ROOT, "IS_ROOT"); - statusBitTest(cs, CSSM_CERT_STATUS_IS_FROM_NET, "IS_FROM_NET"); - dtprintf(")\n"); - } - else - dtprintf("\n"); - - dtprintf(" NumStatusCodes : %u ", info->NumStatusCodes); - CSSM_RETURN *pstatuscode = info->StatusCodes; - uint16_t jx; - for (jx=0; pstatuscode && (jxNumStatusCodes); jx++, ++pstatuscode) - dtprintf("%s ", cssmErrorString(*pstatuscode)); - - dtprintf("\n"); - dtprintf(" Index: %u\n", info->Index); - } - -#endif -} - -#ifndef NDEBUG -static const char *trustResultTypeString(SecTrustResultType trustResultType) -{ - switch (trustResultType) - { - case kSecTrustResultProceed: return "TrustResultProceed"; - case kSecTrustResultUnspecified: return "TrustResultUnspecified"; - case kSecTrustResultDeny: return "TrustResultDeny"; // user reject - case kSecTrustResultInvalid: return "TrustResultInvalid"; - case kSecTrustResultConfirm: return "TrustResultConfirm"; - case kSecTrustResultRecoverableTrustFailure: return "TrustResultRecoverableTrustFailure"; - case kSecTrustResultFatalTrustFailure: return "TrustResultUnspecified"; - case kSecTrustResultOtherError: return "TrustResultOtherError"; - default: return "TrustResultUnknown"; - } - return ""; -} -#endif - -static OSStatus verifySigners(SecCmsSignedDataRef signedData, int numberOfSigners, CFTypeRef timeStampPolicy) -{ - // See Bubble up SecTrustEvaluate of timestamp response to high level callers - // Also Properly handle revocation information of timestamping certificate - - SecTrustRef trustRef = NULL; - CFTypeRef policy = CFRetainSafe(timeStampPolicy); - int result=errSecInternalError; - int rx; - - if (!policy) { - require(policy = SecPolicyCreateWithOID(kSecPolicyAppleTimeStamping), xit); - } - - int jx; - for (jx = 0; jx < numberOfSigners; ++jx) - { - SecTrustResultType trustResultType; - CFDictionaryRef extendedResult = NULL; - CFArrayRef certChain = NULL; - uint16_t certCount = 0; - - CSSM_TP_APPLE_EVIDENCE_INFO *statusChain = NULL; - - // SecCmsSignedDataVerifySignerInfo returns trustRef, which we can call SecTrustEvaluate on - // usually (always?) if result is noErr, the SecTrust*Result calls will return errSecTrustNotAvailable - result = SecCmsSignedDataVerifySignerInfo (signedData, jx, NULL, policy, &trustRef); - dtprintf("[%s] SecCmsSignedDataVerifySignerInfo: result: %d, signer: %d\n", - __FUNCTION__, result, jx); - require_noerr(result, xit); - - result = SecTrustEvaluate (trustRef, &trustResultType); - dtprintf("[%s] SecTrustEvaluate: result: %d, trustResult: %s (%d)\n", - __FUNCTION__, result, trustResultTypeString(trustResultType), trustResultType); - if (result) - goto xit; - switch (trustResultType) - { - case kSecTrustResultProceed: - case kSecTrustResultUnspecified: - break; // success - case kSecTrustResultDeny: // user reject - result = errSecTimestampNotTrusted; // SecCmsVSTimestampNotTrusted ? - break; - case kSecTrustResultInvalid: - assert(false); // should never happen - result = errSecTimestampNotTrusted; // SecCmsVSTimestampNotTrusted ? - break; - case kSecTrustResultConfirm: - case kSecTrustResultRecoverableTrustFailure: - case kSecTrustResultFatalTrustFailure: - case kSecTrustResultOtherError: - default: - { - /* - There are two "errors" that need to be resolved externally: - CSSMERR_TP_CERT_EXPIRED can be OK if the timestamp was made - before the TSA chain expired; CSSMERR_TP_CERT_NOT_VALID_YET - can happen in the case where the user's clock was set to 0. - We don't want to prevent them using apps automatically, so - return noErr and let codesign or whover decide. - */ - OSStatus resultCode; - require_action(SecTrustGetCssmResultCode(trustRef, &resultCode)==noErr, xit, result = errSecTimestampNotTrusted); - result = (resultCode == CSSMERR_TP_CERT_EXPIRED || resultCode == CSSMERR_TP_CERT_NOT_VALID_YET)?noErr:errSecTimestampNotTrusted; - } - break; - } - - rx = SecTrustGetResult(trustRef, &trustResultType, &certChain, &statusChain); - dtprintf("[%s] SecTrustGetResult: result: %d, type: %d\n", __FUNCTION__,rx, trustResultType); - certCount = certChain?CFArrayGetCount(certChain):0; - debugShowCertEvidenceInfo(certCount, statusChain); - CFReleaseNull(certChain); - - rx = SecTrustCopyExtendedResult(trustRef, &extendedResult); - dtprintf("[%s] SecTrustCopyExtendedResult: result: %d\n", __FUNCTION__, rx); - if (extendedResult) - { - debugShowExtendedTrustResult(jx, extendedResult); - CFRelease(extendedResult); - } - - if (trustRef) - CFReleaseNull(trustRef); - } - -xit: - if (trustRef) - CFReleaseNull(trustRef); - if (policy) - CFRelease(policy); - return result; -} - -static OSStatus impExpImportCertUnCommon( - const CSSM_DATA *cdata, - SecKeychainRef importKeychain, // optional - CFMutableArrayRef outArray) // optional, append here -{ - // The only difference between this and impExpImportCertCommon is that we append to outArray - // before attempting to add to the keychain - OSStatus status = noErr; - SecCertificateRef certRef = NULL; - - require_action(cdata, xit, status = errSecUnsupportedFormat); - - /* Pass kCFAllocatorNull as bytesDeallocator to assure the bytes aren't freed */ - CFDataRef data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)cdata->Data, (CFIndex)cdata->Length, kCFAllocatorNull); - require_action(data, xit, status = errSecUnsupportedFormat); - - certRef = SecCertificateCreateWithData(kCFAllocatorDefault, data); - CFRelease(data); /* certRef has its own copy of the data now */ - if(!certRef) { - dtprintf("impExpHandleCert error\n"); - return errSecUnsupportedFormat; - } - - if (outArray) - CFArrayAppendValue(outArray, certRef); - - if (importKeychain) - { - status = SecCertificateAddToKeychain(certRef, importKeychain); - if (status!=noErr && status!=errSecDuplicateItem) - { dtprintf("SecCertificateAddToKeychain error: %ld\n", (long)status); } - } - -xit: - if (certRef) - CFRelease(certRef); - return status; -} - -static void saveTSACertificates(CSSM_DATA **signingCerts, CFMutableArrayRef outArray) -{ - SecKeychainRef defaultKeychain = NULL; - // Don't save certificates in keychain to avoid securityd issues -// if (SecKeychainCopyDefault(&defaultKeychain)) -// defaultKeychain = NULL; - - unsigned certCount = SecCmsArrayCount((void **)signingCerts); - unsigned dex; - for (dex=0; dextimestampCertList || (CFArrayGetCount(signerinfo->timestampCertList) == 0)) - return SecCmsVSSigningCertNotFound; - - SecCertificateRef tsaLeaf = (SecCertificateRef)CFArrayGetValueAtIndex(signerinfo->timestampCertList, 0); - require_action(tsaLeaf, xit, status = errSecCertificateCannotOperate); - - signerinfo->tsaLeafNotBefore = SecCertificateNotValidBefore(tsaLeaf); /* Start date for Timestamp Authority leaf */ - signerinfo->tsaLeafNotAfter = SecCertificateNotValidAfter(tsaLeaf); /* Expiration date for Timestamp Authority leaf */ - - const char *nbefore = cfabsoluteTimeToString(signerinfo->tsaLeafNotBefore); - const char *nafter = cfabsoluteTimeToString(signerinfo->tsaLeafNotAfter); - if (nbefore && nafter) - { - dtprintf("Timestamp Authority leaf valid from %s to %s\n", nbefore, nafter); - free((void *)nbefore);free((void *)nafter); - } - -/* - if(at < nb) - status = errSecCertificateNotValidYet; - else if (at > na) - status = errSecCertificateExpired; -*/ - -xit: - return status; -} - -/* - From RFC 3161: Time-Stamp Protocol (TSP),August 2001, APPENDIX B: - - B) The validity of the digital signature may then be verified in the - following way: - - 1) The time-stamp token itself MUST be verified and it MUST be - verified that it applies to the signature of the signer. - - 2) The date/time indicated by the TSA in the TimeStampToken - MUST be retrieved. - - 3) The certificate used by the signer MUST be identified and - retrieved. - - 4) The date/time indicated by the TSA MUST be within the - validity period of the signer's certificate. - - 5) The revocation information about that certificate, at the - date/time of the Time-Stamping operation, MUST be retrieved. - - 6) Should the certificate be revoked, then the date/time of - revocation shall be later than the date/time indicated by - the TSA. - - If all these conditions are successful, then the digital signature - shall be declared as valid. - -*/ - -OSStatus decodeTimeStampToken(SecCmsSignerInfoRef signerinfo, CSSM_DATA_PTR inData, CSSM_DATA_PTR encDigest, uint64_t expectedNonce) -{ - return decodeTimeStampTokenWithPolicy(signerinfo, NULL, inData, encDigest, expectedNonce); -} - -OSStatus decodeTimeStampTokenWithPolicy(SecCmsSignerInfoRef signerinfo, CFTypeRef timeStampPolicy, CSSM_DATA_PTR inData, CSSM_DATA_PTR encDigest, uint64_t expectedNonce) -{ - /* - We update signerinfo with timestamp and tsa certificate chain. - encDigest is the original signed blob, which we must hash and compare. - inData comes from the unAuthAttr section of the CMS message - - These are set in signerinfo as side effects: - timestampTime - - timestampCertList - */ - - SecCmsDecoderRef decoderContext = NULL; - SecCmsMessageRef cmsMessage = NULL; - SecCmsContentInfoRef contentInfo; - SecCmsSignedDataRef signedData; - SECOidTag contentTypeTag; - int contentLevelCount; - int ix; - OSStatus result = errSecUnknownFormat; - CSSM_DATA **signingCerts = NULL; - - dtprintf("decodeTimeStampToken top: PORT_GetError() %d -----\n", PORT_GetError()); - PORT_SetError(0); - - /* decode the message */ - require_noerr(result = SecCmsDecoderCreate (NULL, NULL, NULL, NULL, NULL, NULL, NULL, &decoderContext), xit); - result = SecCmsDecoderUpdate(decoderContext, inData->Data, inData->Length); - if (result) - { - result = errSecTimestampInvalid; - SecCmsDecoderDestroy(decoderContext); - goto xit; - } - - require_noerr(result = SecCmsDecoderFinish(decoderContext, &cmsMessage), xit); - - // process the results - contentLevelCount = SecCmsMessageContentLevelCount(cmsMessage); - - if (encDigest) - printDataAsHex("encDigest",encDigest, 0); - - for (ix = 0; ix < contentLevelCount; ++ix) - { - dtprintf("\n----- Content Level %d -----\n", ix); - // get content information - contentInfo = SecCmsMessageContentLevel (cmsMessage, ix); - contentTypeTag = SecCmsContentInfoGetContentTypeTag (contentInfo); - - // After 2nd round, contentInfo.content.data is the TSTInfo - - debugShowContentTypeOID(contentInfo); - - switch (contentTypeTag) - { - case SEC_OID_PKCS7_SIGNED_DATA: - { - require((signedData = (SecCmsSignedDataRef)SecCmsContentInfoGetContent(contentInfo)) != NULL, xit); - - debugShowSignerInfo(signedData); - - SECAlgorithmID **digestAlgorithms = SecCmsSignedDataGetDigestAlgs(signedData); - unsigned digestAlgCount = SecCmsArrayCount((void **)digestAlgorithms); - dtprintf("digestAlgCount: %d\n", digestAlgCount); - if (signedData->digests) - { - int jx; - char buffer[128]; - for (jx=0;jx < digestAlgCount;jx++) - { - sprintf(buffer, " digest[%u]", jx); - printDataAsHex(buffer,signedData->digests[jx], 0); - } - } - else - { - dtprintf("No digests\n"); - CSSM_DATA_PTR innerContent = SecCmsContentInfoGetInnerContent(contentInfo); - if (innerContent) - { - dtprintf("inner content length: %ld\n", innerContent->Length); - SecAsn1TSAMessageImprint fakeMessageImprint = {{{0}},}; - OSStatus status = createTSAMessageImprint(signedData, innerContent, &fakeMessageImprint); - if (status) - { dtprintf("createTSAMessageImprint status: %d\n", (int)status); } - printDataAsHex("inner content hash",&fakeMessageImprint.hashedMessage, 0); - CSSM_DATA_PTR digestdata = &fakeMessageImprint.hashedMessage; - CSSM_DATA_PTR digests[2] = {digestdata, NULL}; - SecCmsSignedDataSetDigests(signedData, digestAlgorithms, (CSSM_DATA_PTR *)&digests); - } - else - dtprintf("no inner content\n"); - } - - /* - Import the certificates. We leave this as a warning, since - there are configurations where the certificates are not returned. - */ - signingCerts = SecCmsSignedDataGetCertificateList(signedData); - if (signingCerts == NULL) - { dtprintf("SecCmsSignedDataGetCertificateList returned NULL\n"); } - else - { - if (!signerinfo->timestampCertList) - signerinfo->timestampCertList = CFArrayCreateMutable(kCFAllocatorDefault, 10, &kCFTypeArrayCallBacks); - saveTSACertificates(signingCerts, signerinfo->timestampCertList); - require_noerr(result = setTSALeafValidityDates(signerinfo), xit); - debugSaveCertificates(signingCerts); - } - - int numberOfSigners = SecCmsSignedDataSignerInfoCount (signedData); - - result = verifySigners(signedData, numberOfSigners, timeStampPolicy); - if (result) - dtprintf("verifySigners failed: %ld\n", (long)result); // warning - - - if (result) // remap to SecCmsVSTimestampNotTrusted ? - goto xit; - - break; - } - case SEC_OID_PKCS9_SIGNING_CERTIFICATE: - { - dtprintf("SEC_OID_PKCS9_SIGNING_CERTIFICATE seen\n"); - break; - } - - case SEC_OID_PKCS9_ID_CT_TSTInfo: - { - SecAsn1TSATSTInfo tstInfo = {{0},}; - result = verifyTSTInfo(contentInfo->rawContent, signingCerts, &tstInfo, &signerinfo->timestampTime, expectedNonce); - if (signerinfo->timestampTime) - { - const char *tstamp = cfabsoluteTimeToString(signerinfo->timestampTime); - if (tstamp) - { - dtprintf("Timestamp Authority timestamp: %s\n", tstamp); - free((void *)tstamp); - } - } - break; - } - case SEC_OID_OTHER: - { - dtprintf("otherContent : %p\n", (char *)SecCmsContentInfoGetContent (contentInfo)); - break; - } - default: - dtprintf("ContentTypeTag : %x\n", contentTypeTag); - break; - } - } -xit: - if (cmsMessage) - SecCmsMessageDestroy(cmsMessage); - - return result; -} - -