/*
- * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
+ * Copyright (c) 2012-2016 Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include "tsaTemplates.h"
#include <Security/SecAsn1Coder.h>
#include <AssertMacros.h>
+#include <Security/SecBasePriv.h>
#include <Security/SecPolicy.h>
#include <Security/SecTrustPriv.h>
#include <Security/SecImportExport.h>
#include <Security/SecCertificatePriv.h>
-#include <security_keychain/SecCertificateP.h>
-#include <security_keychain/SecCertificatePrivP.h>
#include <utilities/SecCFRelease.h>
+#include <utilities/SecDispatchRelease.h>
+#include <utilities/debugging.h>
#include "tsaSupport.h"
#include "tsaSupportPriv.h"
#include "tsaTemplates.h"
#include "cmslocal.h"
+#include "cert.h"
#include "secoid.h"
#include "secitem.h"
fprintf(stderr, "%s " fmt, buf, ## __VA_ARGS__); \
syslog(LOG_ERR, " " fmt, ## __VA_ARGS__); \
} } while (0)
- #define tsa_secdebug(scope, format...) \
+ #define tsa_secinfo(scope, format...) \
{ \
syslog(LOG_NOTICE, format); \
- secdebug(scope, format); \
+ secinfo(scope, format); \
printf(format); \
}
#else
- #define tsaDebug(args...) tsa_secdebug("tsa", ## args)
-#define tsa_secdebug(scope, format...) \
- secdebug(scope, format)
+ #define tsaDebug(args...) tsa_secinfo("tsa", ## args)
+#define tsa_secinfo(scope, format...) \
+ secinfo(scope, format)
#endif
#ifndef NDEBUG
int offset, sz = 0;
const int wrapwid = 24; // large enough so SHA-1 hashes fit on one line...
- if ((maxToPrint != 0) && (len > maxToPrint))
- {
+ if ((maxToPrint != 0) && (len > maxToPrint)) {
len = maxToPrint;
more = true;
}
dtprintf("%s", buffer);
offset = 0;
- for (i=0; (i < len) && (offset+3 < bufferSize); i++, offset += sz)
- {
+ 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);
+ if ((i % wrapwid) == (wrapwid-1)) {
+ dtprintf("%s\n", buffer);
offset = 0;
sz = 0;
}
}
sz=sprintf(buffer + offset, more?" ...\n":"\n");
- offset += sz;
+ offset += sz;
buffer[offset+1]=0;
-// fprintf(stderr, "%s", buffer);
dtprintf("%s", buffer);
free(buffer);
int fd;
fd = open(fileName, O_RDWR | O_CREAT | O_TRUNC, 0600);
- if (fd <= 0)
+ if (fd == -1) {
return errno;
+ }
rtn = (int)write(fd, bytes, numBytes);
if(rtn != (int)numBytes)
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);
+ dispatch_retain_safe(waitSemaphore);
- xpc_type_t xtype = xpc_get_type(reply);
+ 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)
}
else
{ tsaDebug("unexpected message reply type %p\n", xtype); }
-
- dispatch_semaphore_signal(waitSemaphore);
- dispatch_release(waitSemaphore);
+ if (waitSemaphore) { dispatch_semaphore_signal(waitSemaphore); }
+ dispatch_release_null(waitSemaphore);
});
-
{ tsaDebug("waiting up to %d seconds for response from XPC\n", timeoutInSeconds); }
- dispatch_semaphore_wait(waitSemaphore, finishTime);
+ if (waitSemaphore) { dispatch_semaphore_wait(waitSemaphore, finishTime); }
- dispatch_release(waitSemaphore);
+ dispatch_release_null(waitSemaphore);
xpc_release(tsaReqData);
xpc_release(message);
#endif
}
- CFStringRef url = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)context, kTSAContextKeyURL);
+ CFTypeRef url = CFDictionaryGetValue((CFDictionaryRef)context, kTSAContextKeyURL);
if (!url)
{
tsaDebug("[TSA] missing URL for TSA (key: %s)\n", "kTSAContextKeyURL");
goto xit;
}
+ CFStringRef urlStr = NULL;
+ if (CFURLGetTypeID() == CFGetTypeID(url)) {
+ urlStr = CFURLGetString(url);
+ } else {
+ require_quiet(CFStringGetTypeID() == CFGetTypeID(url), xit);
+ urlStr = url;
+ }
+ require_quiet(urlStr, xit);
+
/*
If debugging, look at special values in the context to mess things up
*/
}
// need to extract into buffer
- CFIndex length = CFStringGetLength(url); // in 16-bit character units
+ CFIndex length = CFStringGetLength(urlStr); // in 16-bit character units
tsaURL = malloc(6 * length + 1); // pessimistic
- if (!CFStringGetCString(url, (char *)tsaURL, 6 * length + 1, kCFStringEncodingUTF8))
+ if (!CFStringGetCString(urlStr, (char *)tsaURL, 6 * length + 1, kCFStringEncodingUTF8))
goto xit;
tsaDebug("[TSA] URL for timestamp server: %s\n", tsaURL);
return result;
}
-static OSStatus SecTSAValidateTimestamp(const SecAsn1TSATSTInfo *tstInfo, CSSM_DATA **signingCerts, CFAbsoluteTime *timestampTime)
+static OSStatus SecTSAValidateTimestamp(const SecAsn1TSATSTInfo *tstInfo, SecCertificateRef signerCert, CFAbsoluteTime *timestampTime)
{
// See <rdar://problem/11077708> 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);
+ require(tstInfo && signerCert && (tstInfo->genTime.Length < 16), 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?
+ if (SecCertificateIsValidX(signerCert, 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)
+static OSStatus verifyTSTInfo(const CSSM_DATA_PTR content, SecCmsSignerInfoRef signerinfo, SecAsn1TSATSTInfo *tstInfo, CFAbsoluteTime *timestampTime, uint64_t expectedNonce, CSSM_DATA_PTR encDigest)
{
OSStatus status = paramErr;
SecAsn1CoderRef coder = NULL;
if (!tstInfo)
return SECFailure;
+ SecCertificateRef signerCert = SecCmsSignerInfoGetTimestampSigningCert(signerinfo);
+ SecAsn1TSAMessageImprint expectedMessageImprint;
+
require_noerr(SecAsn1CoderCreate(&coder), xit);
require_noerr(SecAsn1Decode(coder, content->Data, content->Length,
kSecAsn1TSATSTInfoTemplate, tstInfo), xit);
require_action(expectedNonce==nonce, xit, status = errSecTimestampRejection);
}
- status = SecTSAValidateTimestamp(tstInfo, signingCerts, timestampTime);
+ // Check the times in the timestamp
+ require_noerr(status = SecTSAValidateTimestamp(tstInfo, signerCert, timestampTime), xit);
dtprintf("SecTSAValidateTimestamp result: %ld\n", (long)status);
+ // Check the message imprint against the encDigest from the signerInfo containing this timestamp
+ SECOidTag hashAlg = SECOID_GetAlgorithmTag(&tstInfo->messageImprint.hashAlgorithm);
+ require_action(hashAlg == SEC_OID_SHA256 || hashAlg == SEC_OID_SHA1, xit, status = errSecInvalidDigestAlgorithm);
+ require_noerr(status = createTSAMessageImprint(signerinfo, &tstInfo->messageImprint.hashAlgorithm,
+ encDigest, &expectedMessageImprint), xit);
+ require_action(CERT_CompareCssmData(&expectedMessageImprint.hashedMessage, &tstInfo->messageImprint.hashedMessage), xit,
+ status = errSecTimestampInvalid; secerror("Timestamp MessageImprint did not match the signature's hash"));
+
xit:
if (coder)
SecAsn1CoderRelease(coder);
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";
assert(false); // should never happen
result = errSecTimestampNotTrusted; // SecCmsVSTimestampNotTrusted ?
break;
- case kSecTrustResultConfirm:
case kSecTrustResultRecoverableTrustFailure:
case kSecTrustResultFatalTrustFailure:
case kSecTrustResultOtherError:
static OSStatus setTSALeafValidityDates(SecCmsSignerInfoRef signerinfo)
{
- OSStatus status = noErr;
+ SecCertificateRef tsaLeaf = SecCmsSignerInfoGetSigningCertificate(signerinfo, NULL);
- if (!signerinfo->timestampCertList || (CFArrayGetCount(signerinfo->timestampCertList) == 0))
+ if (!tsaLeaf)
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 */
free((void *)nbefore);free((void *)nafter);
}
-/*
- if(at < nb)
- status = errSecCertificateNotValidYet;
- else if (at > na)
- status = errSecCertificateExpired;
-*/
-
-xit:
- return status;
+ return errSecSuccess;
}
/*
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
+ 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
- */
+ These are set in signerinfo as side effects:
+ timestampTime
+ timestampCertList
+ timestampCert
+ */
SecCmsDecoderRef decoderContext = NULL;
SecCmsMessageRef cmsMessage = NULL;
/* 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)
- {
+ if (result) {
result = errSecTimestampInvalid;
SecCmsDecoderDestroy(decoderContext);
goto xit;
- }
+ }
require_noerr(result = SecCmsDecoderFinish(decoderContext, &cmsMessage), xit);
// process the results
contentLevelCount = SecCmsMessageContentLevelCount(cmsMessage);
- if (encDigest)
+ if (encDigest) {
printDataAsHex("encDigest",encDigest, 0);
+ }
- for (ix = 0; ix < contentLevelCount; ++ix)
- {
+ for (ix = 0; ix < contentLevelCount; ++ix) {
dtprintf("\n----- Content Level %d -----\n", ix);
// get content information
contentInfo = SecCmsMessageContentLevel (cmsMessage, ix);
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);
+ 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("digests not yet computed\n");
+ CSSM_DATA_PTR innerContent = SecCmsContentInfoGetInnerContent(contentInfo);
+ if (innerContent)
+ {
+ dtprintf("inner content length: %ld\n", innerContent->Length);
+ SecAsn1TSAMessageImprint fakeMessageImprint = {{{0}},};
+ SecCmsSignerInfoRef tsaSigner = SecCmsSignedDataGetSignerInfo(signedData, 0);
+ OSStatus status = createTSAMessageImprint(tsaSigner, &tsaSigner->digestAlg, innerContent, &fakeMessageImprint);
+ require_noerr_action(status, xit, dtprintf("createTSAMessageImprint status: %d\n", (int)status); result = status);
+ printDataAsHex("inner content hash",&fakeMessageImprint.hashedMessage, 0);
+ CSSM_DATA_PTR digestdata = &fakeMessageImprint.hashedMessage;
+ CSSM_DATA_PTR digests[2] = {digestdata, NULL};
+ status = SecCmsSignedDataSetDigests(signedData, digestAlgorithms, (CSSM_DATA_PTR *)&digests);
+ require_noerr_action(status, xit, dtprintf("createTSAMessageImprint status: %d\n", (int)status); result = status);
+ } else {
+ dtprintf("no inner content\n");
+ }
}
- 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
+ /*
+ 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);
- if (result) // remap to SecCmsVSTimestampNotTrusted ?
- goto xit;
+ if (numberOfSigners > 0) {
+ /* @@@ assume there's only one signer since SecCms can't handle multiple signers anyway */
+ signerinfo->timestampCert = CFRetainSafe(SecCmsSignerInfoGetSigningCertificate(signedData->signerInfos[0], NULL));
+ }
- break;
- }
- case SEC_OID_PKCS9_SIGNING_CERTIFICATE:
- {
- dtprintf("SEC_OID_PKCS9_SIGNING_CERTIFICATE seen\n");
- break;
- }
+ result = verifySigners(signedData, numberOfSigners, timeStampPolicy);
+ if (result) {
+ dtprintf("verifySigners failed: %ld\n", (long)result); // warning
+ goto xit; // remap to SecCmsVSTimestampNotTrusted ?
+ }
- 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_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, signerinfo, &tstInfo, &signerinfo->timestampTime, expectedNonce, encDigest);
+ if (signerinfo->timestampTime) {
+ const char *tstamp = cfabsoluteTimeToString(signerinfo->timestampTime);
+ if (tstamp) {
+ dtprintf("Timestamp Authority timestamp: %s\n", tstamp);
+ free((void *)tstamp);
+ }
}
+ break;
}
- break;
- }
- case SEC_OID_OTHER:
- {
- dtprintf("otherContent : %p\n", (char *)SecCmsContentInfoGetContent (contentInfo));
- break;
- }
- default:
- dtprintf("ContentTypeTag : %x\n", contentTypeTag);
- 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);
+ if (cmsMessage) {
+ SecCmsMessageDestroy(cmsMessage);
+ }
return result;
}