]> git.saurik.com Git - apple/security.git/blame - OSX/libsecurity_smime/lib/tsaSupport.c
Security-59754.80.3.tar.gz
[apple/security.git] / OSX / libsecurity_smime / lib / tsaSupport.c
CommitLineData
b1ab9ed8 1/*
8a50f688 2 * Copyright (c) 2012-2016 Apple Inc. All Rights Reserved.
427c49bc 3 *
b1ab9ed8 4 * @APPLE_LICENSE_HEADER_START@
d8f41ccd 5 *
b1ab9ed8
A
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.
d8f41ccd 12 *
b1ab9ed8
A
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.
d8f41ccd 20 *
b1ab9ed8
A
21 * @APPLE_LICENSE_HEADER_END@
22 *
23 * tsaSupport.c - ASN1 templates Time Stamping Authority requests and responses
24 */
25
26/*
27#include <Security/SecCmsDigestContext.h>
28#include <Security/SecCmsMessage.h>
29#include <security_asn1/secasn1.h>
30#include <security_asn1/secerr.h>
31*/
32
33#include <security_utilities/debugging.h>
34#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
35
36#include <Security/SecCmsDecoder.h>
37#include <Security/SecCmsMessage.h>
38#include <Security/SecCmsContentInfo.h>
39#include <Security/SecCmsSignedData.h>
40#include <Security/SecCmsSignerInfo.h>
41#include "tsaTemplates.h"
42#include <Security/SecAsn1Coder.h>
43#include <AssertMacros.h>
8a50f688 44#include <Security/SecBasePriv.h>
b1ab9ed8
A
45#include <Security/SecPolicy.h>
46#include <Security/SecTrustPriv.h>
47#include <Security/SecImportExport.h>
48#include <Security/SecCertificatePriv.h>
d8f41ccd 49#include <utilities/SecCFRelease.h>
fa7225c8 50#include <utilities/SecDispatchRelease.h>
79b9da22 51#include <utilities/debugging.h>
b1ab9ed8
A
52
53#include "tsaSupport.h"
54#include "tsaSupportPriv.h"
55#include "tsaTemplates.h"
56#include "cmslocal.h"
79b9da22 57#include "cert.h"
b1ab9ed8
A
58
59#include "secoid.h"
60#include "secitem.h"
61#include <fcntl.h>
62
63const CFStringRef kTSAContextKeyURL = CFSTR("ServerURL");
64const CFStringRef kTSAContextKeyNoCerts = CFSTR("NoCerts");
65const CFStringRef kTSADebugContextKeyBadReq = CFSTR("DebugBadReq");
66const CFStringRef kTSADebugContextKeyBadNonce = CFSTR("DebugBadNonce");
67
68extern const SecAsn1Template kSecAsn1TSATSTInfoTemplate[];
69
70extern OSStatus impExpImportCertCommon(
71 const CSSM_DATA *cdata,
72 SecKeychainRef importKeychain, // optional
427c49bc 73 CFMutableArrayRef outArray); // optional, append here
b1ab9ed8
A
74
75#pragma mark ----- Debug Logs -----
76
77#ifndef NDEBUG
78#define TSA_USE_SYSLOG 1
79#endif
80
81#if TSA_USE_SYSLOG
82#include <syslog.h>
83 #include <time.h>
84 #include <sys/time.h>
85 #define tsaDebug(fmt, ...) \
86 do { if (true) { \
87 char buf[64]; \
88 struct timeval time_now; \
89 gettimeofday(&time_now, NULL); \
90 struct tm* time_info = localtime(&time_now.tv_sec); \
91 strftime(buf, sizeof(buf), "[%Y-%m-%d %H:%M:%S]", time_info); \
92 fprintf(stderr, "%s " fmt, buf, ## __VA_ARGS__); \
93 syslog(LOG_ERR, " " fmt, ## __VA_ARGS__); \
94 } } while (0)
fa7225c8 95 #define tsa_secinfo(scope, format...) \
b1ab9ed8
A
96 { \
97 syslog(LOG_NOTICE, format); \
fa7225c8 98 secinfo(scope, format); \
b1ab9ed8
A
99 printf(format); \
100 }
101#else
fa7225c8
A
102 #define tsaDebug(args...) tsa_secinfo("tsa", ## args)
103#define tsa_secinfo(scope, format...) \
104 secinfo(scope, format)
b1ab9ed8
A
105#endif
106
107#ifndef NDEBUG
108#define TSTINFO_DEBUG 1 //jch
109#endif
110
111#if TSTINFO_DEBUG
112#define dtprintf(args...) tsaDebug(args)
113#else
114#define dtprintf(args...)
115#endif
116
117#define kHTTPResponseCodeContinue 100
118#define kHTTPResponseCodeOK 200
119#define kHTTPResponseCodeNoContent 204
120#define kHTTPResponseCodeBadRequest 400
121#define kHTTPResponseCodeUnauthorized 401
122#define kHTTPResponseCodeForbidden 403
123#define kHTTPResponseCodeNotFound 404
124#define kHTTPResponseCodeConflict 409
125#define kHTTPResponseCodeExpectationFailed 417
126#define kHTTPResponseCodeServFail 500
127#define kHTTPResponseCodeServiceUnavailable 503
128#define kHTTPResponseCodeInsufficientStorage 507
129
130#pragma mark ----- Debug/Utilities -----
131
132static OSStatus remapHTTPErrorCodes(OSStatus status)
133{
134 switch (status)
135 {
136 case kHTTPResponseCodeOK:
137 case kHTTPResponseCodeContinue:
138 return noErr;
139 case kHTTPResponseCodeBadRequest:
140 return errSecTimestampBadRequest;
141 case kHTTPResponseCodeNoContent:
142 case kHTTPResponseCodeUnauthorized:
143 case kHTTPResponseCodeForbidden:
144 case kHTTPResponseCodeNotFound:
145 case kHTTPResponseCodeConflict:
146 case kHTTPResponseCodeExpectationFailed:
147 case kHTTPResponseCodeServFail:
148 case kHTTPResponseCodeInsufficientStorage:
149 case kHTTPResponseCodeServiceUnavailable:
150 return errSecTimestampServiceNotAvailable;
151 default:
152 return status;
153 }
154 return status;
427c49bc 155
b1ab9ed8
A
156}
157
158static void printDataAsHex(const char *title, const CSSM_DATA *d, unsigned maxToPrint) // 0 means print it all
159{
160#ifndef NDEBUG
161 unsigned i;
162 bool more = false;
163 uint32 len = (uint32)d->Length;
164 uint8 *cp = d->Data;
165 char *buffer = NULL;
166 size_t bufferSize;
167 int offset, sz = 0;
168 const int wrapwid = 24; // large enough so SHA-1 hashes fit on one line...
427c49bc 169
ecaf5866 170 if ((maxToPrint != 0) && (len > maxToPrint)) {
b1ab9ed8
A
171 len = maxToPrint;
172 more = true;
173 }
174
175 bufferSize = wrapwid+3*len;
176 buffer = (char *)malloc(bufferSize);
427c49bc 177
b1ab9ed8
A
178 offset = sprintf(buffer, "%s [len = %u]\n", title, len);
179 dtprintf("%s", buffer);
180 offset = 0;
181
ecaf5866 182 for (i=0; (i < len) && (offset+3 < bufferSize); i++, offset += sz) {
b1ab9ed8 183 sz = sprintf(buffer + offset, " %02x", (unsigned int)cp[i] & 0xff);
ecaf5866
A
184 if ((i % wrapwid) == (wrapwid-1)) {
185 dtprintf("%s\n", buffer);
b1ab9ed8
A
186 offset = 0;
187 sz = 0;
188 }
189 }
427c49bc 190
b1ab9ed8 191 sz=sprintf(buffer + offset, more?" ...\n":"\n");
ecaf5866 192 offset += sz;
b1ab9ed8 193 buffer[offset+1]=0;
427c49bc 194
b1ab9ed8 195 dtprintf("%s", buffer);
d87e1158
A
196
197 free(buffer);
b1ab9ed8
A
198#endif
199}
200
201#ifndef NDEBUG
202int tsaWriteFileX(const char *fileName, const unsigned char *bytes, size_t numBytes)
203{
204 int rtn;
205 int fd;
206
207 fd = open(fileName, O_RDWR | O_CREAT | O_TRUNC, 0600);
866f8763 208 if (fd == -1) {
b1ab9ed8 209 return errno;
866f8763 210 }
b1ab9ed8
A
211
212 rtn = (int)write(fd, bytes, numBytes);
213 if(rtn != (int)numBytes)
214 {
215 if (rtn >= 0)
216 fprintf(stderr, "writeFile: short write\n");
217 rtn = EIO;
218 }
219 else
220 rtn = 0;
221
222 close(fd);
223 return rtn;
224}
225#endif
226
227char *cfStringToChar(CFStringRef inStr)
228{
229 // Caller must free
230 char *result = NULL;
231 const char *str = NULL;
b1ab9ed8 232
427c49bc 233 if (!inStr)
c2a06e24 234 return strdup(""); // return a null string
b1ab9ed8 235
427c49bc
A
236 // quick path first
237 if ((str = CFStringGetCStringPtr(inStr, kCFStringEncodingUTF8))) {
c2a06e24 238 result = strdup(str);
427c49bc 239 } else {
c2a06e24
A
240 // need to extract into buffer
241 CFIndex length = CFStringGetLength(inStr); // in 16-bit character units
242 CFIndex bytesToAllocate = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
243 result = malloc(bytesToAllocate);
244 if (!CFStringGetCString(inStr, result, bytesToAllocate, kCFStringEncodingUTF8))
245 result[0] = 0;
246 }
427c49bc
A
247
248 return result;
b1ab9ed8
A
249}
250
251/* Oids longer than this are considered invalid. */
252#define MAX_OID_SIZE 32
253
427c49bc
A
254#ifndef NDEBUG
255/* FIXME: There are other versions of this in SecCertifcate.c and SecCertificateP.c */
b1ab9ed8 256static CFStringRef SecDERItemCopyOIDDecimalRepresentation(CFAllocatorRef allocator, const CSSM_OID *oid)
427c49bc 257{
b1ab9ed8
A
258 if (oid->Length == 0)
259 return CFSTR("<NULL>");
260
261 if (oid->Length > MAX_OID_SIZE)
262 return CFSTR("Oid too long");
263
264 CFMutableStringRef result = CFStringCreateMutable(allocator, 0);
265
266 // The first two levels are encoded into one byte, since the root levelq
267 // has only 3 nodes (40*x + y). However if x = joint-iso-itu-t(2) then
268 // y may be > 39, so we have to add special-case handling for this.
269 uint32_t x = oid->Data[0] / 40;
270 uint32_t y = oid->Data[0] % 40;
271 if (x > 2)
272 {
273 // Handle special case for large y if x = 2
274 y += (x - 2) * 40;
275 x = 2;
276 }
277 CFStringAppendFormat(result, NULL, CFSTR("%u.%u"), x, y);
278
279 uint32_t value = 0;
280 for (x = 1; x < oid->Length; ++x)
281 {
282 value = (value << 7) | (oid->Data[x] & 0x7F);
283 /* @@@ value may not span more than 4 bytes. */
284 /* A max number of 20 values is allowed. */
285 if (!(oid->Data[x] & 0x80))
286 {
427c49bc 287 CFStringAppendFormat(result, NULL, CFSTR(".%lu"), (unsigned long)value);
b1ab9ed8
A
288 value = 0;
289 }
290 }
291 return result;
292}
427c49bc 293#endif
b1ab9ed8
A
294
295static void debugSaveCertificates(CSSM_DATA **outCerts)
296{
297#ifndef NDEBUG
298 if (outCerts)
299 {
300 CSSM_DATA_PTR *certp;
301 unsigned jx = 0;
302 const char *certNameBase = "/tmp/tsa-resp-cert-";
303 char fname[PATH_MAX];
304 unsigned certCount = SecCmsArrayCount((void **)outCerts);
305 dtprintf("Found %d certs\n",certCount);
306
307 for (certp=outCerts;*certp;certp++, ++jx)
308 {
309 char numstr[32];
310 strncpy(fname, certNameBase, strlen(certNameBase)+1);
311 sprintf(numstr,"%u", jx);
312 strcat(fname,numstr);
313 tsaWriteFileX(fname, (*certp)->Data, (*certp)->Length);
314 if (jx > 5)
315 break; //something wrong
316 }
317 }
318#endif
319}
320
321static void debugShowSignerInfo(SecCmsSignedDataRef signedData)
322{
323#ifndef NDEBUG
324 int numberOfSigners = SecCmsSignedDataSignerInfoCount (signedData);
325 dtprintf("numberOfSigners : %d\n", numberOfSigners);
326 int ix;
327 for (ix=0;ix < numberOfSigners;ix++)
328 {
329 SecCmsSignerInfoRef sigi = SecCmsSignedDataGetSignerInfo(signedData,ix);
330 if (sigi)
331 {
332 CFStringRef commonName = SecCmsSignerInfoGetSignerCommonName(sigi);
333 const char *signerhdr = " signer : ";
334 if (commonName)
335 {
336 char *cn = cfStringToChar(commonName);
337 dtprintf("%s%s\n", signerhdr, cn);
338 if (cn)
339 free(cn);
d87e1158 340 CFReleaseNull(commonName);
b1ab9ed8
A
341 }
342 else
343 dtprintf("%s<NULL>\n", signerhdr);
344 }
345 }
346#endif
347}
348
349static void debugShowContentTypeOID(SecCmsContentInfoRef contentInfo)
350{
351#ifndef NDEBUG
352
353 CSSM_OID *typeOID = SecCmsContentInfoGetContentTypeOID(contentInfo);
354 if (typeOID)
355 {
356 CFStringRef oidCFStr = SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault, typeOID);
357 char *oidstr = cfStringToChar(oidCFStr);
358 printDataAsHex("oid:", typeOID, (unsigned int)typeOID->Length);
359 dtprintf("\toid: %s\n", oidstr);
360 if (oidCFStr)
361 CFRelease(oidCFStr);
362 if (oidstr)
363 free(oidstr);
364 }
365#endif
366}
367
368uint64_t tsaDER_ToInt(const CSSM_DATA *DER_Data)
369{
370 uint64_t rtn = 0;
371 unsigned i = 0;
372
373 while(i < DER_Data->Length) {
374 rtn |= DER_Data->Data[i];
375 if(++i == DER_Data->Length) {
376 break;
377 }
378 rtn <<= 8;
379 }
380 return rtn;
381}
382
383void displayTSTInfo(SecAsn1TSATSTInfo *tstInfo)
384{
385#ifndef NDEBUG
386 dtprintf("--- TSTInfo ---\n");
387 if (!tstInfo)
388 return;
389
390 if (tstInfo->version.Data)
391 {
392 uint64_t vers = tsaDER_ToInt(&tstInfo->version);
393 dtprintf("Version:\t\t%u\n", (int)vers);
394 }
395
396 if (tstInfo->serialNumber.Data)
397 {
398 uint64_t sn = tsaDER_ToInt(&tstInfo->serialNumber);
399 dtprintf("SerialNumber:\t%llu\n", sn);
400 }
401
402 if (tstInfo->ordering.Data)
403 {
404 uint64_t ord = tsaDER_ToInt(&tstInfo->ordering);
405 dtprintf("Ordering:\t\t%s\n", ord?"yes":"no");
406 }
407
408 if (tstInfo->nonce.Data)
409 {
410 uint64_t nonce = tsaDER_ToInt(&tstInfo->nonce);
411 dtprintf("Nonce:\t\t%llu\n", nonce);
412 }
413 else
414 dtprintf("Nonce:\t\tnot specified\n");
415
416 if (tstInfo->genTime.Data)
417 {
418 char buf[tstInfo->genTime.Length+1];
419 memcpy(buf, (const char *)tstInfo->genTime.Data, tstInfo->genTime.Length);
420 buf[tstInfo->genTime.Length]=0;
421 dtprintf("GenTime:\t\t%s\n", buf);
422 }
423
424 dtprintf("-- MessageImprint --\n");
425 if (true) // SecAsn1TSAMessageImprint
426 {
427 printDataAsHex(" Algorithm:",&tstInfo->messageImprint.hashAlgorithm.algorithm, 0);
428 printDataAsHex(" Message :", &tstInfo->messageImprint.hashedMessage, 0);//tstInfo->messageImprint.hashedMessage.Length);
429 }
430#endif
431}
432
433#pragma mark ----- TimeStamp Response using XPC -----
434
435#include <xpc/private.h>
436
437static OSStatus checkForNonDERResponse(const unsigned char *resp, size_t respLen)
438{
439 /*
440 Good start is something like 30 82 0c 03 30 15 02 01 00 30 10 0c 0e 4f 70 65
427c49bc 441
b1ab9ed8
A
442 URL: http://timestamp-int.corp.apple.com/signserver/process?TimeStampSigner
443 Resp: Http/1.1 Service Unavailable
427c49bc 444
b1ab9ed8
A
445 URL: http://timestamp-int.corp.apple.com/ts01
446 Resp: blank
427c49bc 447
b1ab9ed8
A
448 URL: http://cutandtaste.com/404 (or other forced 404 site)
449 Resp: 404
450 */
427c49bc 451
b1ab9ed8
A
452 OSStatus status = noErr;
453 const char ader[2] = { 0x30, 0x82 };
454 char *respStr = NULL;
455 size_t maxlen = 0;
456 size_t badResponseCount;
427c49bc 457
b1ab9ed8
A
458 const char *badResponses[] =
459 {
460 "<!DOCTYPE html>",
461 "Http/1.1 Service Unavailable",
462 "blank"
463 };
427c49bc 464
b1ab9ed8 465 require_action(resp && respLen, xit, status = errSecTimestampServiceNotAvailable);
427c49bc 466
b1ab9ed8
A
467 // This is usual case
468 if ((respLen > 1) && (memcmp(resp, ader, 2)==0)) // might be good; pass on to DER decoder
469 return noErr;
470
471 badResponseCount = sizeof(badResponses)/sizeof(char *);
472 int ix;
473 for (ix = 0; ix < badResponseCount; ++ix)
474 if (strlen(badResponses[ix]) > maxlen)
475 maxlen = strlen(badResponses[ix]);
476
477 // Prevent a large response from allocating a ton of memory
478 if (respLen > maxlen)
479 respLen = maxlen;
427c49bc 480
b1ab9ed8
A
481 respStr = (char *)malloc(respLen+1);
482 strlcpy(respStr, (const char *)resp, respLen);
427c49bc 483
b1ab9ed8 484 for (ix = 0; ix < badResponseCount; ++ix)
b54c578e
A
485 if (strcmp(respStr, badResponses[ix])==0) {
486 if(respStr) {
487 free(respStr);
488 }
b1ab9ed8 489 return errSecTimestampServiceNotAvailable;
b54c578e 490 }
427c49bc 491
b1ab9ed8
A
492xit:
493 if (respStr)
494 free((void *)respStr);
495
496 return status;
497}
498
499static OSStatus sendTSARequestWithXPC(const unsigned char *tsaReq, size_t tsaReqLength, const unsigned char *tsaURL, unsigned char **tsaResp, size_t *tsaRespLength)
427c49bc 500{
b1ab9ed8
A
501 __block OSStatus result = noErr;
502 int timeoutInSeconds = 15;
503 extern xpc_object_t xpc_create_with_format(const char * format, ...);
504
505 dispatch_queue_t xpc_queue = dispatch_queue_create("com.apple.security.XPCTimeStampingService", DISPATCH_QUEUE_SERIAL);
506 __block dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
507 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, timeoutInSeconds * NSEC_PER_SEC);
508
509 xpc_connection_t con = xpc_connection_create("com.apple.security.XPCTimeStampingService", xpc_queue);
510
511 xpc_connection_set_event_handler(con, ^(xpc_object_t event) {
512 xpc_type_t xtype = xpc_get_type(event);
513 if (XPC_TYPE_ERROR == xtype)
514 { tsaDebug("default: connection error: %s\n", xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION)); }
515 else
427c49bc 516 { tsaDebug("default: unexpected connection event %p\n", event); }
b1ab9ed8
A
517 });
518
519 xpc_connection_resume(con);
520
521 xpc_object_t tsaReqData = xpc_data_create(tsaReq, tsaReqLength);
522 const char *urlstr = (tsaURL?(const char *)tsaURL:"");
523 xpc_object_t url_as_xpc_string = xpc_string_create(urlstr);
524
525 xpc_object_t message = xpc_create_with_format("{operation: TimeStampRequest, ServerURL: %value, TimeStampRequest: %value}", url_as_xpc_string, tsaReqData);
526
527 xpc_connection_send_message_with_reply(con, message, xpc_queue, ^(xpc_object_t reply)
528 {
529 tsaDebug("xpc_connection_send_message_with_reply handler called back\n");
fa7225c8 530 dispatch_retain_safe(waitSemaphore);
b1ab9ed8 531
fa7225c8 532 xpc_type_t xtype = xpc_get_type(reply);
b1ab9ed8
A
533 if (XPC_TYPE_ERROR == xtype)
534 { tsaDebug("message error: %s\n", xpc_dictionary_get_string(reply, XPC_ERROR_KEY_DESCRIPTION)); }
535 else if (XPC_TYPE_CONNECTION == xtype)
536 { tsaDebug("received connection\n"); }
537 else if (XPC_TYPE_DICTIONARY == xtype)
538 {
539#ifndef NDEBUG
540 /*
541 // This is useful for debugging.
542 char *debug = xpc_copy_description(reply);
543 tsaDebug("DEBUG %s\n", debug);
544 free(debug);
545 */
546#endif
427c49bc 547
b1ab9ed8
A
548 xpc_object_t xpcTimeStampReply = xpc_dictionary_get_value(reply, "TimeStampReply");
549 size_t xpcTSRLength = xpc_data_get_length(xpcTimeStampReply);
550 tsaDebug("xpcTSRLength: %ld bytes of response\n", xpcTSRLength);
551
552 xpc_object_t xpcTimeStampError = xpc_dictionary_get_value(reply, "TimeStampError");
553 xpc_object_t xpcTimeStampStatus = xpc_dictionary_get_value(reply, "TimeStampStatus");
427c49bc 554
b1ab9ed8
A
555 if (xpcTimeStampError || xpcTimeStampStatus)
556 {
557#ifndef NDEBUG
558 if (xpcTimeStampError)
559 {
560 size_t len = xpc_string_get_length(xpcTimeStampError);
561 char *buf = (char *)malloc(len);
562 strlcpy(buf, xpc_string_get_string_ptr(xpcTimeStampError), len+1);
563 tsaDebug("xpcTimeStampError: %s\n", buf);
564 if (buf)
565 free(buf);
566 }
567#endif
568 if (xpcTimeStampStatus)
569 {
570 result = (OSStatus)xpc_int64_get_value(xpcTimeStampStatus);
571 tsaDebug("xpcTimeStampStatus: %d\n", (int)result);
572 }
573 }
427c49bc 574
b1ab9ed8
A
575 result = remapHTTPErrorCodes(result);
576
577 if ((result == noErr) && tsaResp && tsaRespLength)
578 {
579 *tsaRespLength = xpcTSRLength;
580 *tsaResp = (unsigned char *)malloc(xpcTSRLength);
581
582 size_t bytesCopied = xpc_data_get_bytes(xpcTimeStampReply, *tsaResp, 0, xpcTSRLength);
583 if (bytesCopied != xpcTSRLength)
584 { tsaDebug("length mismatch: copied: %ld, xpc: %ld\n", bytesCopied, xpcTSRLength); }
585 else
586 if ((result = checkForNonDERResponse(*tsaResp,bytesCopied)))
587 {
588 tsaDebug("received non-DER response from timestamp server\n");
589 }
590 else
591 {
592 result = noErr;
593 tsaDebug("copied: %ld bytes of response\n", bytesCopied);
594 }
595 }
596 tsaDebug("releasing connection\n");
597 xpc_release(con);
598 }
599 else
600 { tsaDebug("unexpected message reply type %p\n", xtype); }
fa7225c8
A
601 if (waitSemaphore) { dispatch_semaphore_signal(waitSemaphore); }
602 dispatch_release_null(waitSemaphore);
b1ab9ed8 603 });
b1ab9ed8 604 { tsaDebug("waiting up to %d seconds for response from XPC\n", timeoutInSeconds); }
fa7225c8 605 if (waitSemaphore) { dispatch_semaphore_wait(waitSemaphore, finishTime); }
b1ab9ed8 606
fa7225c8 607 dispatch_release_null(waitSemaphore);
b1ab9ed8
A
608 xpc_release(tsaReqData);
609 xpc_release(message);
427c49bc 610
b1ab9ed8
A
611 { tsaDebug("sendTSARequestWithXPC exit\n"); }
612
613 return result;
614}
615
616#pragma mark ----- TimeStamp request -----
617
618#include "tsaTemplates.h"
619#include <security_asn1/SecAsn1Coder.h>
620#include <Security/oidsalg.h>
621#include <AssertMacros.h>
622#include <libkern/OSByteOrder.h>
623
624extern const SecAsn1Template kSecAsn1TSATimeStampReqTemplate;
625extern const SecAsn1Template kSecAsn1TSATimeStampRespTemplateDER;
626
627CFMutableDictionaryRef SecCmsTSAGetDefaultContext(CFErrorRef *error)
628{
629 // Caller responsible for retain/release
630 // <rdar://problem/11077440> Update SecCmsTSAGetDefaultContext with actual URL for Apple Timestamp server
631 // URL will be in TimeStampingPrefs.plist
427c49bc 632
b1ab9ed8
A
633 CFBundleRef secFWbundle = NULL;
634 CFURLRef resourceURL = NULL;
635 CFDataRef resourceData = NULL;
636 CFPropertyListRef prefs = NULL;
637 CFMutableDictionaryRef contextDict = NULL;
638 SInt32 errorCode = 0;
639 CFOptionFlags options = 0;
640 CFPropertyListFormat format = 0;
641 OSStatus status = noErr;
427c49bc 642
b1ab9ed8 643 require_action(secFWbundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.security")), xit, status = errSecInternalError);
5c19dc3a
A
644 CFRetain(secFWbundle);
645
b1ab9ed8
A
646 require_action(resourceURL = CFBundleCopyResourceURL(secFWbundle, CFSTR("TimeStampingPrefs"), CFSTR("plist"), NULL),
647 xit, status = errSecInvalidPrefsDomain);
648
649 require(CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, resourceURL, &resourceData,
650 NULL, NULL, &errorCode), xit);
651 require_action(resourceData, xit, status = errSecDataNotAvailable);
652
653 prefs = CFPropertyListCreateWithData(kCFAllocatorDefault, resourceData, options, &format, error);
654 require_action(prefs && (CFGetTypeID(prefs)==CFDictionaryGetTypeID()), xit, status = errSecInvalidPrefsDomain);
427c49bc 655
b1ab9ed8
A
656 contextDict = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, prefs);
657
658 if (error)
659 *error = NULL;
660xit:
661 if (errorCode)
662 status = errorCode;
663 if (error && status)
664 *error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainOSStatus, status, NULL);
665 if (secFWbundle)
666 CFRelease(secFWbundle);
667 if (resourceURL)
668 CFRelease(resourceURL);
669 if (resourceData)
670 CFRelease(resourceData);
671 if (prefs)
672 CFRelease(prefs);
673
674 return contextDict;
675}
676
677static CFDataRef _SecTSARequestCopyDEREncoding(SecAsn1TSAMessageImprint *messageImprint, bool noCerts, uint64_t nonce)
678{
679 // Returns DER encoded TimeStampReq
680 // Modeled on _SecOCSPRequestCopyDEREncoding
681 // The Timestamp Authority supports 64 bit nonces (or more possibly)
427c49bc 682
b1ab9ed8
A
683 SecAsn1CoderRef coder = NULL;
684 uint8_t version = 1;
685 SecAsn1Item vers = {1, &version};
686 uint8_t creq = noCerts?0:1;
687 SecAsn1Item certReq = {1, &creq}; //jch - to request or not?
688 SecAsn1TSATimeStampReq tsreq = {};
689 CFDataRef der = NULL;
690 uint64_t nonceVal = OSSwapHostToBigConstInt64(nonce);
691 SecAsn1Item nonceItem = {sizeof(uint64_t), (unsigned char *)&nonceVal};
692
427c49bc 693 uint8_t OID_FakePolicy_Data[] = { 0x2A, 0x03, 0x04, 0x05, 0x06};
b1ab9ed8
A
694 const CSSM_OID fakePolicyOID = {sizeof(OID_FakePolicy_Data),OID_FakePolicy_Data};
695
696 tsreq.version = vers;
697
698 tsreq.messageImprint = *messageImprint;
699 tsreq.certReq = certReq;
427c49bc 700
b1ab9ed8
A
701 // skip reqPolicy, extensions for now - FAKES - jch
702 tsreq.reqPolicy = fakePolicyOID; //policyID;
703
704 tsreq.nonce = nonceItem;
705
706 // Encode the request
707 require_noerr(SecAsn1CoderCreate(&coder), errOut);
427c49bc 708
b1ab9ed8
A
709 SecAsn1Item encoded;
710 require_noerr(SecAsn1EncodeItem(coder, &tsreq,
711 &kSecAsn1TSATimeStampReqTemplate, &encoded), errOut);
712 der = CFDataCreate(kCFAllocatorDefault, encoded.Data,
713 encoded.Length);
714
715errOut:
716 if (coder)
717 SecAsn1CoderRelease(coder);
718
719 return der;
720}
721
722OSStatus SecTSAResponseCopyDEREncoding(SecAsn1CoderRef coder, const CSSM_DATA *tsaResponse, SecAsn1TimeStampRespDER *respDER)
723{
724 // Partially decode the response
725 OSStatus status = paramErr;
427c49bc 726
b1ab9ed8
A
727 require(tsaResponse && respDER, errOut);
728 require_noerr(SecAsn1DecodeData(coder, tsaResponse,
729 &kSecAsn1TSATimeStampRespTemplateDER, respDER), errOut);
730 status = noErr;
731
732errOut:
733
734 return status;
735}
736
737#pragma mark ----- TS Callback -----
738
739OSStatus SecCmsTSADefaultCallback(CFTypeRef context, void *messageImprintV, uint64_t nonce, CSSM_DATA *signedDERBlob)
740{
741 OSStatus result = paramErr;
742 const unsigned char *tsaReq = NULL;
743 size_t tsaReqLength = 0;
744 CFDataRef cfreq = NULL;
745 unsigned char *tsaURL = NULL;
746 bool noCerts = false;
427c49bc 747
b1ab9ed8
A
748 if (!context || CFGetTypeID(context)!=CFDictionaryGetTypeID())
749 return paramErr;
427c49bc 750
b1ab9ed8
A
751 SecAsn1TSAMessageImprint *messageImprint = (SecAsn1TSAMessageImprint *)messageImprintV;
752 if (!messageImprint || !signedDERBlob)
753 return paramErr;
754
755 CFBooleanRef cfnocerts = (CFBooleanRef)CFDictionaryGetValue((CFDictionaryRef)context, kTSAContextKeyNoCerts);
b54c578e 756 if (cfnocerts != NULL)
b1ab9ed8
A
757 {
758 tsaDebug("[TSA] Request noCerts\n");
759 noCerts = CFBooleanGetValue(cfnocerts);
760 }
761
762 // We must spoof the nonce here, before sending the request.
763 // If we tried to alter the reply, then the signature would break instead.
764 CFBooleanRef cfBadNonce = (CFBooleanRef)CFDictionaryGetValue((CFDictionaryRef)context, kTSADebugContextKeyBadNonce);
765 if (cfBadNonce && CFBooleanGetValue(cfBadNonce))
766 {
767 tsaDebug("[TSA] Forcing bad TS Request by changing nonce\n");
768 nonce++;
769 }
770
771 printDataAsHex("[TSA] hashToTimeStamp:", &messageImprint->hashedMessage,128);
772 cfreq = _SecTSARequestCopyDEREncoding(messageImprint, noCerts, nonce);
773 if (cfreq)
774 {
775 tsaReq = CFDataGetBytePtr(cfreq);
776 tsaReqLength = CFDataGetLength(cfreq);
427c49bc 777
b1ab9ed8
A
778#ifndef NDEBUG
779 CFShow(cfreq);
780 tsaWriteFileX("/tmp/tsareq.req", tsaReq, tsaReqLength);
781#endif
782 }
427c49bc 783
fa7225c8 784 CFTypeRef url = CFDictionaryGetValue((CFDictionaryRef)context, kTSAContextKeyURL);
b1ab9ed8
A
785 if (!url)
786 {
787 tsaDebug("[TSA] missing URL for TSA (key: %s)\n", "kTSAContextKeyURL");
788 goto xit;
789 }
427c49bc 790
fa7225c8
A
791 CFStringRef urlStr = NULL;
792 if (CFURLGetTypeID() == CFGetTypeID(url)) {
793 urlStr = CFURLGetString(url);
794 } else {
795 require_quiet(CFStringGetTypeID() == CFGetTypeID(url), xit);
796 urlStr = url;
797 }
798 require_quiet(urlStr, xit);
799
b1ab9ed8
A
800 /*
801 If debugging, look at special values in the context to mess things up
802 */
427c49bc 803
b1ab9ed8
A
804 CFBooleanRef cfBadReq = (CFBooleanRef)CFDictionaryGetValue((CFDictionaryRef)context, kTSADebugContextKeyBadReq);
805 if (cfBadReq && CFBooleanGetValue(cfBadReq))
806 {
807 tsaDebug("[TSA] Forcing bad TS Request by truncating length from %ld to %ld\n", tsaReqLength, (tsaReqLength-4));
808 tsaReqLength -= 4;
809 }
427c49bc 810
b1ab9ed8 811 // need to extract into buffer
fa7225c8 812 CFIndex length = CFStringGetLength(urlStr); // in 16-bit character units
b1ab9ed8 813 tsaURL = malloc(6 * length + 1); // pessimistic
fa7225c8 814 if (!CFStringGetCString(urlStr, (char *)tsaURL, 6 * length + 1, kCFStringEncodingUTF8))
b1ab9ed8
A
815 goto xit;
816
817 tsaDebug("[TSA] URL for timestamp server: %s\n", tsaURL);
818
819 unsigned char *tsaResp = NULL;
820 size_t tsaRespLength = 0;
821 tsaDebug("calling sendTSARequestWithXPC with %ld bytes of request\n", tsaReqLength);
822
823 require_noerr(result = sendTSARequestWithXPC(tsaReq, tsaReqLength, tsaURL, &tsaResp, &tsaRespLength), xit);
824
825 tsaDebug("sendTSARequestWithXPC copied: %ld bytes of response\n", tsaRespLength);
826
827 signedDERBlob->Data = tsaResp;
828 signedDERBlob->Length = tsaRespLength;
427c49bc 829
b1ab9ed8 830 result = noErr;
427c49bc 831
b1ab9ed8
A
832xit:
833 if (tsaURL)
834 free((void *)tsaURL);
835 if (cfreq)
836 CFRelease(cfreq);
837
838 return result;
839}
840
841#pragma mark ----- TimeStamp Verification -----
842
843static OSStatus convertGeneralizedTimeToCFAbsoluteTime(const char *timeStr, CFAbsoluteTime *ptime)
844{
845 /*
846 See http://userguide.icu-project.org/formatparse/datetime for date/time format.
847 The "Z" signal a GMT time, but CFDateFormatterGetAbsoluteTimeFromString returns
427c49bc 848 values based on local time.
b1ab9ed8
A
849 */
850
851 OSStatus result = noErr;
852 CFDateFormatterRef formatter = NULL;
853 CFStringRef time_string = NULL;
854 CFTimeZoneRef gmt = NULL;
855 CFLocaleRef locale = NULL;
856 CFRange *rangep = NULL;
857
858 require(timeStr && timeStr[0] && ptime, xit);
859 require(formatter = CFDateFormatterCreate(kCFAllocatorDefault, locale, kCFDateFormatterNoStyle, kCFDateFormatterNoStyle), xit);
860// CFRetain(formatter);
861 CFDateFormatterSetFormat(formatter, CFSTR("yyyyMMddHHmmss'Z'")); // GeneralizedTime
862 gmt = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0);
863 CFDateFormatterSetProperty(formatter, kCFDateFormatterTimeZone, gmt);
864
865 time_string = CFStringCreateWithCString(kCFAllocatorDefault, timeStr, kCFStringEncodingUTF8);
866 if (!time_string || !CFDateFormatterGetAbsoluteTimeFromString(formatter, time_string, rangep, ptime))
867 {
868 dtprintf("%s is not a valid date\n", timeStr);
869 result = 1;
870 }
871
872xit:
427c49bc 873 if (formatter)
b1ab9ed8 874 CFRelease(formatter);
427c49bc 875 if (time_string)
b1ab9ed8 876 CFRelease(time_string);
427c49bc 877 if (gmt)
b1ab9ed8 878 CFRelease(gmt);
427c49bc 879
b1ab9ed8
A
880 return result;
881}
882
8a50f688 883static OSStatus SecTSAValidateTimestamp(const SecAsn1TSATSTInfo *tstInfo, SecCertificateRef signerCert, CFAbsoluteTime *timestampTime)
b1ab9ed8
A
884{
885 // See <rdar://problem/11077708> Properly handle revocation information of timestamping certificate
886 OSStatus result = paramErr;
887 CFAbsoluteTime genTime = 0;
888 char timeStr[32] = {0,};
b1ab9ed8 889
8a50f688 890 require(tstInfo && signerCert && (tstInfo->genTime.Length < 16), xit);;
427c49bc 891
b1ab9ed8
A
892 memcpy(timeStr, tstInfo->genTime.Data, tstInfo->genTime.Length);
893 timeStr[tstInfo->genTime.Length] = 0;
894 require_noerr(convertGeneralizedTimeToCFAbsoluteTime(timeStr, &genTime), xit);
8a50f688 895 if (SecCertificateIsValidX(signerCert, genTime)) // iOS?
b1ab9ed8
A
896 result = noErr;
897 else
898 result = errSecTimestampInvalid;
899 if (timestampTime)
900 *timestampTime = genTime;
901xit:
902 return result;
903}
904
79b9da22 905static OSStatus verifyTSTInfo(const CSSM_DATA_PTR content, SecCmsSignerInfoRef signerinfo, SecAsn1TSATSTInfo *tstInfo, CFAbsoluteTime *timestampTime, uint64_t expectedNonce, CSSM_DATA_PTR encDigest)
b1ab9ed8
A
906{
907 OSStatus status = paramErr;
908 SecAsn1CoderRef coder = NULL;
909
910 if (!tstInfo)
911 return SECFailure;
427c49bc 912
79b9da22
A
913 SecCertificateRef signerCert = SecCmsSignerInfoGetTimestampSigningCert(signerinfo);
914 SecAsn1TSAMessageImprint expectedMessageImprint;
915
b1ab9ed8 916 require_noerr(SecAsn1CoderCreate(&coder), xit);
427c49bc 917 require_noerr(SecAsn1Decode(coder, content->Data, content->Length,
b1ab9ed8
A
918 kSecAsn1TSATSTInfoTemplate, tstInfo), xit);
919 displayTSTInfo(tstInfo);
427c49bc 920
b1ab9ed8
A
921 // Check the nonce
922 if (tstInfo->nonce.Data && expectedNonce!=0)
923 {
924 uint64_t nonce = tsaDER_ToInt(&tstInfo->nonce);
925 // if (expectedNonce!=nonce)
926 dtprintf("verifyTSTInfo nonce: actual: %lld, expected: %lld\n", nonce, expectedNonce);
927 require_action(expectedNonce==nonce, xit, status = errSecTimestampRejection);
928 }
427c49bc 929
79b9da22
A
930 // Check the times in the timestamp
931 require_noerr(status = SecTSAValidateTimestamp(tstInfo, signerCert, timestampTime), xit);
b1ab9ed8
A
932 dtprintf("SecTSAValidateTimestamp result: %ld\n", (long)status);
933
79b9da22
A
934 // Check the message imprint against the encDigest from the signerInfo containing this timestamp
935 SECOidTag hashAlg = SECOID_GetAlgorithmTag(&tstInfo->messageImprint.hashAlgorithm);
936 require_action(hashAlg == SEC_OID_SHA256 || hashAlg == SEC_OID_SHA1, xit, status = errSecInvalidDigestAlgorithm);
937 require_noerr(status = createTSAMessageImprint(signerinfo, &tstInfo->messageImprint.hashAlgorithm,
938 encDigest, &expectedMessageImprint), xit);
939 require_action(CERT_CompareCssmData(&expectedMessageImprint.hashedMessage, &tstInfo->messageImprint.hashedMessage), xit,
940 status = errSecTimestampInvalid; secerror("Timestamp MessageImprint did not match the signature's hash"));
941
b1ab9ed8
A
942xit:
943 if (coder)
944 SecAsn1CoderRelease(coder);
945 return status;
946}
947
948static void debugShowExtendedTrustResult(int index, CFDictionaryRef extendedResult)
949{
950#ifndef NDEBUG
951 if (extendedResult)
952 {
953 CFStringRef xresStr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
954 CFSTR("Extended trust result for signer #%d : %@"), index, extendedResult);
955 if (xresStr)
956 {
957 CFShow(xresStr);
958 CFRelease(xresStr);
959 }
960 }
961#endif
962}
963
964#ifndef NDEBUG
965extern const char *cssmErrorString(CSSM_RETURN error);
966
967static void statusBitTest(CSSM_TP_APPLE_CERT_STATUS certStatus, uint32 bit, const char *str)
968{
969 if (certStatus & bit)
970 dtprintf("%s ", str);
971}
972#endif
973
974static void debugShowCertEvidenceInfo(uint16_t certCount, const CSSM_TP_APPLE_EVIDENCE_INFO *info)
975{
976#ifndef NDEBUG
977 CSSM_TP_APPLE_CERT_STATUS cs;
978// const CSSM_TP_APPLE_EVIDENCE_INFO *pinfo = info;
979 uint16_t ix;
980 for (ix=0; info && (ix<certCount); ix++, ++info)
981 {
982 cs = info->StatusBits;
983 dtprintf(" cert %u:\n", ix);
984 dtprintf(" StatusBits : 0x%x", (unsigned)cs);
985 if (cs)
986 {
987 dtprintf(" ( ");
988 statusBitTest(cs, CSSM_CERT_STATUS_EXPIRED, "EXPIRED");
427c49bc 989 statusBitTest(cs, CSSM_CERT_STATUS_NOT_VALID_YET,
b1ab9ed8 990 "NOT_VALID_YET");
427c49bc 991 statusBitTest(cs, CSSM_CERT_STATUS_IS_IN_INPUT_CERTS,
b1ab9ed8 992 "IS_IN_INPUT_CERTS");
427c49bc 993 statusBitTest(cs, CSSM_CERT_STATUS_IS_IN_ANCHORS,
b1ab9ed8
A
994 "IS_IN_ANCHORS");
995 statusBitTest(cs, CSSM_CERT_STATUS_IS_ROOT, "IS_ROOT");
996 statusBitTest(cs, CSSM_CERT_STATUS_IS_FROM_NET, "IS_FROM_NET");
997 dtprintf(")\n");
998 }
999 else
1000 dtprintf("\n");
1001
1002 dtprintf(" NumStatusCodes : %u ", info->NumStatusCodes);
1003 CSSM_RETURN *pstatuscode = info->StatusCodes;
1004 uint16_t jx;
1005 for (jx=0; pstatuscode && (jx<info->NumStatusCodes); jx++, ++pstatuscode)
1006 dtprintf("%s ", cssmErrorString(*pstatuscode));
427c49bc 1007
b1ab9ed8
A
1008 dtprintf("\n");
1009 dtprintf(" Index: %u\n", info->Index);
1010 }
1011
1012#endif
1013}
1014
427c49bc 1015#ifndef NDEBUG
b1ab9ed8
A
1016static const char *trustResultTypeString(SecTrustResultType trustResultType)
1017{
b1ab9ed8
A
1018 switch (trustResultType)
1019 {
1020 case kSecTrustResultProceed: return "TrustResultProceed";
1021 case kSecTrustResultUnspecified: return "TrustResultUnspecified";
1022 case kSecTrustResultDeny: return "TrustResultDeny"; // user reject
1023 case kSecTrustResultInvalid: return "TrustResultInvalid";
b1ab9ed8
A
1024 case kSecTrustResultRecoverableTrustFailure: return "TrustResultRecoverableTrustFailure";
1025 case kSecTrustResultFatalTrustFailure: return "TrustResultUnspecified";
1026 case kSecTrustResultOtherError: return "TrustResultOtherError";
1027 default: return "TrustResultUnknown";
1028 }
b1ab9ed8
A
1029 return "";
1030}
427c49bc 1031#endif
b1ab9ed8 1032
d8f41ccd 1033static OSStatus verifySigners(SecCmsSignedDataRef signedData, int numberOfSigners, CFTypeRef timeStampPolicy)
b1ab9ed8
A
1034{
1035 // See <rdar://problem/11077588> Bubble up SecTrustEvaluate of timestamp response to high level callers
1036 // Also <rdar://problem/11077708> Properly handle revocation information of timestamping certificate
1037
d87e1158 1038 SecTrustRef trustRef = NULL;
d8f41ccd 1039 CFTypeRef policy = CFRetainSafe(timeStampPolicy);
427c49bc
A
1040 int result=errSecInternalError;
1041 int rx;
1042
d8f41ccd
A
1043 if (!policy) {
1044 require(policy = SecPolicyCreateWithOID(kSecPolicyAppleTimeStamping), xit);
1045 }
1046
b1ab9ed8
A
1047 int jx;
1048 for (jx = 0; jx < numberOfSigners; ++jx)
1049 {
1050 SecTrustResultType trustResultType;
b1ab9ed8
A
1051 CFDictionaryRef extendedResult = NULL;
1052 CFArrayRef certChain = NULL;
1053 uint16_t certCount = 0;
427c49bc 1054
b1ab9ed8
A
1055 CSSM_TP_APPLE_EVIDENCE_INFO *statusChain = NULL;
1056
1057 // SecCmsSignedDataVerifySignerInfo returns trustRef, which we can call SecTrustEvaluate on
1058 // usually (always?) if result is noErr, the SecTrust*Result calls will return errSecTrustNotAvailable
1059 result = SecCmsSignedDataVerifySignerInfo (signedData, jx, NULL, policy, &trustRef);
1060 dtprintf("[%s] SecCmsSignedDataVerifySignerInfo: result: %d, signer: %d\n",
1061 __FUNCTION__, result, jx);
1062 require_noerr(result, xit);
8e292c99 1063
b1ab9ed8
A
1064 result = SecTrustEvaluate (trustRef, &trustResultType);
1065 dtprintf("[%s] SecTrustEvaluate: result: %d, trustResult: %s (%d)\n",
1066 __FUNCTION__, result, trustResultTypeString(trustResultType), trustResultType);
1067 if (result)
1068 goto xit;
1069 switch (trustResultType)
1070 {
1071 case kSecTrustResultProceed:
1072 case kSecTrustResultUnspecified:
1073 break; // success
1074 case kSecTrustResultDeny: // user reject
1075 result = errSecTimestampNotTrusted; // SecCmsVSTimestampNotTrusted ?
1076 break;
1077 case kSecTrustResultInvalid:
1078 assert(false); // should never happen
1079 result = errSecTimestampNotTrusted; // SecCmsVSTimestampNotTrusted ?
1080 break;
b1ab9ed8
A
1081 case kSecTrustResultRecoverableTrustFailure:
1082 case kSecTrustResultFatalTrustFailure:
1083 case kSecTrustResultOtherError:
1084 default:
1085 {
1086 /*
1087 There are two "errors" that need to be resolved externally:
1088 CSSMERR_TP_CERT_EXPIRED can be OK if the timestamp was made
1089 before the TSA chain expired; CSSMERR_TP_CERT_NOT_VALID_YET
1090 can happen in the case where the user's clock was set to 0.
1091 We don't want to prevent them using apps automatically, so
1092 return noErr and let codesign or whover decide.
1093 */
1094 OSStatus resultCode;
1095 require_action(SecTrustGetCssmResultCode(trustRef, &resultCode)==noErr, xit, result = errSecTimestampNotTrusted);
1096 result = (resultCode == CSSMERR_TP_CERT_EXPIRED || resultCode == CSSMERR_TP_CERT_NOT_VALID_YET)?noErr:errSecTimestampNotTrusted;
1097 }
1098 break;
1099 }
427c49bc 1100
b1ab9ed8
A
1101 rx = SecTrustGetResult(trustRef, &trustResultType, &certChain, &statusChain);
1102 dtprintf("[%s] SecTrustGetResult: result: %d, type: %d\n", __FUNCTION__,rx, trustResultType);
1103 certCount = certChain?CFArrayGetCount(certChain):0;
1104 debugShowCertEvidenceInfo(certCount, statusChain);
d87e1158 1105 CFReleaseNull(certChain);
427c49bc 1106
b1ab9ed8
A
1107 rx = SecTrustCopyExtendedResult(trustRef, &extendedResult);
1108 dtprintf("[%s] SecTrustCopyExtendedResult: result: %d\n", __FUNCTION__, rx);
1109 if (extendedResult)
1110 {
1111 debugShowExtendedTrustResult(jx, extendedResult);
1112 CFRelease(extendedResult);
1113 }
427c49bc 1114
b1ab9ed8 1115 if (trustRef)
d87e1158 1116 CFReleaseNull(trustRef);
b1ab9ed8
A
1117 }
1118
1119xit:
d87e1158
A
1120 if (trustRef)
1121 CFReleaseNull(trustRef);
b1ab9ed8 1122 if (policy)
d87e1158 1123 CFRelease(policy);
b1ab9ed8
A
1124 return result;
1125}
1126
1127static OSStatus impExpImportCertUnCommon(
1128 const CSSM_DATA *cdata,
1129 SecKeychainRef importKeychain, // optional
427c49bc 1130 CFMutableArrayRef outArray) // optional, append here
b1ab9ed8
A
1131{
1132 // The only difference between this and impExpImportCertCommon is that we append to outArray
1133 // before attempting to add to the keychain
1134 OSStatus status = noErr;
1135 SecCertificateRef certRef = NULL;
427c49bc 1136
b1ab9ed8
A
1137 require_action(cdata, xit, status = errSecUnsupportedFormat);
1138
1139 /* Pass kCFAllocatorNull as bytesDeallocator to assure the bytes aren't freed */
1140 CFDataRef data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)cdata->Data, (CFIndex)cdata->Length, kCFAllocatorNull);
1141 require_action(data, xit, status = errSecUnsupportedFormat);
1142
1143 certRef = SecCertificateCreateWithData(kCFAllocatorDefault, data);
1144 CFRelease(data); /* certRef has its own copy of the data now */
1145 if(!certRef) {
1146 dtprintf("impExpHandleCert error\n");
1147 return errSecUnsupportedFormat;
1148 }
1149
1150 if (outArray)
1151 CFArrayAppendValue(outArray, certRef);
1152
1153 if (importKeychain)
1154 {
1155 status = SecCertificateAddToKeychain(certRef, importKeychain);
1156 if (status!=noErr && status!=errSecDuplicateItem)
1157 { dtprintf("SecCertificateAddToKeychain error: %ld\n", (long)status); }
1158 }
1159
1160xit:
d64be36e 1161 if (certRef) {
b1ab9ed8 1162 CFRelease(certRef);
d64be36e
A
1163 }
1164 return status;
b1ab9ed8
A
1165}
1166
1167static void saveTSACertificates(CSSM_DATA **signingCerts, CFMutableArrayRef outArray)
1168{
1169 SecKeychainRef defaultKeychain = NULL;
8e292c99
A
1170 // Don't save certificates in keychain to avoid securityd issues
1171// if (SecKeychainCopyDefault(&defaultKeychain))
1172// defaultKeychain = NULL;
b1ab9ed8
A
1173
1174 unsigned certCount = SecCmsArrayCount((void **)signingCerts);
1175 unsigned dex;
1176 for (dex=0; dex<certCount; dex++)
1177 {
1178 OSStatus rx = impExpImportCertUnCommon(signingCerts[dex], defaultKeychain, outArray);
1179 if (rx!=noErr && rx!=errSecDuplicateItem)
1180 dtprintf("impExpImportCertCommon failed: %ld\n", (long)rx);
1181 }
1182 if (defaultKeychain)
1183 CFRelease(defaultKeychain);
1184}
1185
1186static const char *cfabsoluteTimeToString(CFAbsoluteTime abstime)
1187{
1188 CFGregorianDate greg = CFAbsoluteTimeGetGregorianDate(abstime, NULL);
1189 char str[20];
1190 if (19 != snprintf(str, 20, "%4.4d-%2.2d-%2.2d_%2.2d:%2.2d:%2.2d",
1191 (int)greg.year, greg.month, greg.day, greg.hour, greg.minute, (int)greg.second))
1192 str[0]=0;
1193 char *data = (char *)malloc(20);
1194 strncpy(data, str, 20);
1195 return data;
1196}
1197
1198static OSStatus setTSALeafValidityDates(SecCmsSignerInfoRef signerinfo)
1199{
8a50f688 1200 SecCertificateRef tsaLeaf = SecCmsSignerInfoGetSigningCertificate(signerinfo, NULL);
427c49bc 1201
8a50f688 1202 if (!tsaLeaf)
b1ab9ed8 1203 return SecCmsVSSigningCertNotFound;
427c49bc 1204
427c49bc
A
1205 signerinfo->tsaLeafNotBefore = SecCertificateNotValidBefore(tsaLeaf); /* Start date for Timestamp Authority leaf */
1206 signerinfo->tsaLeafNotAfter = SecCertificateNotValidAfter(tsaLeaf); /* Expiration date for Timestamp Authority leaf */
b1ab9ed8
A
1207
1208 const char *nbefore = cfabsoluteTimeToString(signerinfo->tsaLeafNotBefore);
1209 const char *nafter = cfabsoluteTimeToString(signerinfo->tsaLeafNotAfter);
1210 if (nbefore && nafter)
1211 {
1212 dtprintf("Timestamp Authority leaf valid from %s to %s\n", nbefore, nafter);
1213 free((void *)nbefore);free((void *)nafter);
1214 }
1215
8a50f688 1216 return errSecSuccess;
b1ab9ed8
A
1217}
1218
1219/*
1220 From RFC 3161: Time-Stamp Protocol (TSP),August 2001, APPENDIX B:
427c49bc 1221
b1ab9ed8
A
1222 B) The validity of the digital signature may then be verified in the
1223 following way:
1224
1225 1) The time-stamp token itself MUST be verified and it MUST be
1226 verified that it applies to the signature of the signer.
1227
1228 2) The date/time indicated by the TSA in the TimeStampToken
1229 MUST be retrieved.
1230
1231 3) The certificate used by the signer MUST be identified and
1232 retrieved.
1233
1234 4) The date/time indicated by the TSA MUST be within the
1235 validity period of the signer's certificate.
1236
1237 5) The revocation information about that certificate, at the
1238 date/time of the Time-Stamping operation, MUST be retrieved.
1239
1240 6) Should the certificate be revoked, then the date/time of
1241 revocation shall be later than the date/time indicated by
1242 the TSA.
1243
1244 If all these conditions are successful, then the digital signature
1245 shall be declared as valid.
1246
1247*/
1248
1249OSStatus decodeTimeStampToken(SecCmsSignerInfoRef signerinfo, CSSM_DATA_PTR inData, CSSM_DATA_PTR encDigest, uint64_t expectedNonce)
d8f41ccd
A
1250{
1251 return decodeTimeStampTokenWithPolicy(signerinfo, NULL, inData, encDigest, expectedNonce);
1252}
1253
1254OSStatus decodeTimeStampTokenWithPolicy(SecCmsSignerInfoRef signerinfo, CFTypeRef timeStampPolicy, CSSM_DATA_PTR inData, CSSM_DATA_PTR encDigest, uint64_t expectedNonce)
b1ab9ed8
A
1255{
1256 /*
79b9da22
A
1257 We update signerinfo with timestamp and tsa certificate chain.
1258 encDigest is the original signed blob, which we must hash and compare.
1259 inData comes from the unAuthAttr section of the CMS message
1260
1261 These are set in signerinfo as side effects:
1262 timestampTime
1263 timestampCertList
1264 timestampCert
1265 */
b1ab9ed8
A
1266
1267 SecCmsDecoderRef decoderContext = NULL;
1268 SecCmsMessageRef cmsMessage = NULL;
1269 SecCmsContentInfoRef contentInfo;
1270 SecCmsSignedDataRef signedData;
1271 SECOidTag contentTypeTag;
1272 int contentLevelCount;
1273 int ix;
1274 OSStatus result = errSecUnknownFormat;
1275 CSSM_DATA **signingCerts = NULL;
1276
427c49bc 1277 dtprintf("decodeTimeStampToken top: PORT_GetError() %d -----\n", PORT_GetError());
8e292c99 1278 PORT_SetError(0);
427c49bc 1279
b1ab9ed8
A
1280 /* decode the message */
1281 require_noerr(result = SecCmsDecoderCreate (NULL, NULL, NULL, NULL, NULL, NULL, NULL, &decoderContext), xit);
1282 result = SecCmsDecoderUpdate(decoderContext, inData->Data, inData->Length);
79b9da22 1283 if (result) {
b1ab9ed8
A
1284 result = errSecTimestampInvalid;
1285 SecCmsDecoderDestroy(decoderContext);
1286 goto xit;
79b9da22 1287 }
427c49bc 1288
b1ab9ed8
A
1289 require_noerr(result = SecCmsDecoderFinish(decoderContext, &cmsMessage), xit);
1290
1291 // process the results
1292 contentLevelCount = SecCmsMessageContentLevelCount(cmsMessage);
427c49bc 1293
79b9da22 1294 if (encDigest) {
b1ab9ed8 1295 printDataAsHex("encDigest",encDigest, 0);
79b9da22 1296 }
b1ab9ed8 1297
79b9da22 1298 for (ix = 0; ix < contentLevelCount; ++ix) {
b1ab9ed8
A
1299 dtprintf("\n----- Content Level %d -----\n", ix);
1300 // get content information
1301 contentInfo = SecCmsMessageContentLevel (cmsMessage, ix);
1302 contentTypeTag = SecCmsContentInfoGetContentTypeTag (contentInfo);
427c49bc 1303
b1ab9ed8
A
1304 // After 2nd round, contentInfo.content.data is the TSTInfo
1305
1306 debugShowContentTypeOID(contentInfo);
427c49bc 1307
79b9da22
A
1308 switch (contentTypeTag) {
1309 case SEC_OID_PKCS7_SIGNED_DATA: {
1310 require((signedData = (SecCmsSignedDataRef)SecCmsContentInfoGetContent(contentInfo)) != NULL, xit);
1311
1312 debugShowSignerInfo(signedData);
1313
1314 SECAlgorithmID **digestAlgorithms = SecCmsSignedDataGetDigestAlgs(signedData);
1315 unsigned digestAlgCount = SecCmsArrayCount((void **)digestAlgorithms);
1316 dtprintf("digestAlgCount: %d\n", digestAlgCount);
1317 if (signedData->digests) {
1318 int jx;
1319 char buffer[128];
1320 for (jx=0;jx < digestAlgCount;jx++) {
1321 sprintf(buffer, " digest[%u]", jx);
1322 printDataAsHex(buffer,signedData->digests[jx], 0);
1323 }
1324 } else {
1325 dtprintf("digests not yet computed\n");
1326 CSSM_DATA_PTR innerContent = SecCmsContentInfoGetInnerContent(contentInfo);
1327 if (innerContent)
1328 {
1329 dtprintf("inner content length: %ld\n", innerContent->Length);
1330 SecAsn1TSAMessageImprint fakeMessageImprint = {{{0}},};
1331 SecCmsSignerInfoRef tsaSigner = SecCmsSignedDataGetSignerInfo(signedData, 0);
1332 OSStatus status = createTSAMessageImprint(tsaSigner, &tsaSigner->digestAlg, innerContent, &fakeMessageImprint);
1333 require_noerr_action(status, xit, dtprintf("createTSAMessageImprint status: %d\n", (int)status); result = status);
1334 printDataAsHex("inner content hash",&fakeMessageImprint.hashedMessage, 0);
1335 CSSM_DATA_PTR digestdata = &fakeMessageImprint.hashedMessage;
1336 CSSM_DATA_PTR digests[2] = {digestdata, NULL};
1337 status = SecCmsSignedDataSetDigests(signedData, digestAlgorithms, (CSSM_DATA_PTR *)&digests);
1338 require_noerr_action(status, xit, dtprintf("createTSAMessageImprint status: %d\n", (int)status); result = status);
1339 } else {
1340 dtprintf("no inner content\n");
1341 }
1342 }
427c49bc 1343
79b9da22
A
1344 /*
1345 Import the certificates. We leave this as a warning, since
1346 there are configurations where the certificates are not returned.
1347 */
1348 signingCerts = SecCmsSignedDataGetCertificateList(signedData);
1349 if (signingCerts == NULL) {
1350 dtprintf("SecCmsSignedDataGetCertificateList returned NULL\n");
1351 } else {
1352 if (!signerinfo->timestampCertList) {
1353 signerinfo->timestampCertList = CFArrayCreateMutable(kCFAllocatorDefault, 10, &kCFTypeArrayCallBacks);
1354 }
1355 saveTSACertificates(signingCerts, signerinfo->timestampCertList);
1356 require_noerr(result = setTSALeafValidityDates(signerinfo), xit);
1357 debugSaveCertificates(signingCerts);
1358 }
b1ab9ed8 1359
79b9da22
A
1360 int numberOfSigners = SecCmsSignedDataSignerInfoCount (signedData);
1361
1362 if (numberOfSigners > 0) {
1363 /* @@@ assume there's only one signer since SecCms can't handle multiple signers anyway */
1364 signerinfo->timestampCert = CFRetainSafe(SecCmsSignerInfoGetSigningCertificate(signedData->signerInfos[0], NULL));
b1ab9ed8 1365 }
79b9da22
A
1366
1367 result = verifySigners(signedData, numberOfSigners, timeStampPolicy);
1368 if (result) {
1369 dtprintf("verifySigners failed: %ld\n", (long)result); // warning
1370 goto xit; // remap to SecCmsVSTimestampNotTrusted ?
b1ab9ed8 1371 }
b1ab9ed8 1372
79b9da22 1373 break;
b1ab9ed8 1374 }
79b9da22
A
1375 case SEC_OID_PKCS9_SIGNING_CERTIFICATE: {
1376 dtprintf("SEC_OID_PKCS9_SIGNING_CERTIFICATE seen\n");
1377 break;
ecaf5866 1378 }
79b9da22
A
1379 case SEC_OID_PKCS9_ID_CT_TSTInfo: {
1380 SecAsn1TSATSTInfo tstInfo = {{0},};
1381 result = verifyTSTInfo(contentInfo->rawContent, signerinfo, &tstInfo, &signerinfo->timestampTime, expectedNonce, encDigest);
1382 if (signerinfo->timestampTime) {
1383 const char *tstamp = cfabsoluteTimeToString(signerinfo->timestampTime);
1384 if (tstamp) {
1385 dtprintf("Timestamp Authority timestamp: %s\n", tstamp);
1386 free((void *)tstamp);
1387 }
b1ab9ed8 1388 }
79b9da22 1389 break;
b1ab9ed8 1390 }
79b9da22
A
1391 case SEC_OID_OTHER: {
1392 dtprintf("otherContent : %p\n", (char *)SecCmsContentInfoGetContent (contentInfo));
1393 break;
1394 }
1395 default:
1396 dtprintf("ContentTypeTag : %x\n", contentTypeTag);
1397 break;
b1ab9ed8
A
1398 }
1399 }
1400xit:
79b9da22
A
1401 if (cmsMessage) {
1402 SecCmsMessageDestroy(cmsMessage);
1403 }
b1ab9ed8
A
1404
1405 return result;
1406}
1407
1408