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