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