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