]> git.saurik.com Git - apple/security.git/blob - OSX/include/security_smime/tsaSupport.c
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / include / security_smime / 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 CFRetain(secFWbundle);
644
645 require_action(resourceURL = CFBundleCopyResourceURL(secFWbundle, CFSTR("TimeStampingPrefs"), CFSTR("plist"), NULL),
646 xit, status = errSecInvalidPrefsDomain);
647
648 require(CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, resourceURL, &resourceData,
649 NULL, NULL, &errorCode), xit);
650 require_action(resourceData, xit, status = errSecDataNotAvailable);
651
652 prefs = CFPropertyListCreateWithData(kCFAllocatorDefault, resourceData, options, &format, error);
653 require_action(prefs && (CFGetTypeID(prefs)==CFDictionaryGetTypeID()), xit, status = errSecInvalidPrefsDomain);
654
655 contextDict = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, prefs);
656
657 if (error)
658 *error = NULL;
659 xit:
660 if (errorCode)
661 status = errorCode;
662 if (error && status)
663 *error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainOSStatus, status, NULL);
664 if (secFWbundle)
665 CFRelease(secFWbundle);
666 if (resourceURL)
667 CFRelease(resourceURL);
668 if (resourceData)
669 CFRelease(resourceData);
670 if (prefs)
671 CFRelease(prefs);
672
673 return contextDict;
674 }
675
676 static CFDataRef _SecTSARequestCopyDEREncoding(SecAsn1TSAMessageImprint *messageImprint, bool noCerts, uint64_t nonce)
677 {
678 // Returns DER encoded TimeStampReq
679 // Modeled on _SecOCSPRequestCopyDEREncoding
680 // The Timestamp Authority supports 64 bit nonces (or more possibly)
681
682 SecAsn1CoderRef coder = NULL;
683 uint8_t version = 1;
684 SecAsn1Item vers = {1, &version};
685 uint8_t creq = noCerts?0:1;
686 SecAsn1Item certReq = {1, &creq}; //jch - to request or not?
687 SecAsn1TSATimeStampReq tsreq = {};
688 CFDataRef der = NULL;
689 uint64_t nonceVal = OSSwapHostToBigConstInt64(nonce);
690 SecAsn1Item nonceItem = {sizeof(uint64_t), (unsigned char *)&nonceVal};
691
692 uint8_t OID_FakePolicy_Data[] = { 0x2A, 0x03, 0x04, 0x05, 0x06};
693 const CSSM_OID fakePolicyOID = {sizeof(OID_FakePolicy_Data),OID_FakePolicy_Data};
694
695 tsreq.version = vers;
696
697 tsreq.messageImprint = *messageImprint;
698 tsreq.certReq = certReq;
699
700 // skip reqPolicy, extensions for now - FAKES - jch
701 tsreq.reqPolicy = fakePolicyOID; //policyID;
702
703 tsreq.nonce = nonceItem;
704
705 // Encode the request
706 require_noerr(SecAsn1CoderCreate(&coder), errOut);
707
708 SecAsn1Item encoded;
709 require_noerr(SecAsn1EncodeItem(coder, &tsreq,
710 &kSecAsn1TSATimeStampReqTemplate, &encoded), errOut);
711 der = CFDataCreate(kCFAllocatorDefault, encoded.Data,
712 encoded.Length);
713
714 errOut:
715 if (coder)
716 SecAsn1CoderRelease(coder);
717
718 return der;
719 }
720
721 OSStatus SecTSAResponseCopyDEREncoding(SecAsn1CoderRef coder, const CSSM_DATA *tsaResponse, SecAsn1TimeStampRespDER *respDER)
722 {
723 // Partially decode the response
724 OSStatus status = paramErr;
725
726 require(tsaResponse && respDER, errOut);
727 require_noerr(SecAsn1DecodeData(coder, tsaResponse,
728 &kSecAsn1TSATimeStampRespTemplateDER, respDER), errOut);
729 status = noErr;
730
731 errOut:
732
733 return status;
734 }
735
736 #pragma mark ----- TS Callback -----
737
738 OSStatus SecCmsTSADefaultCallback(CFTypeRef context, void *messageImprintV, uint64_t nonce, CSSM_DATA *signedDERBlob)
739 {
740 OSStatus result = paramErr;
741 const unsigned char *tsaReq = NULL;
742 size_t tsaReqLength = 0;
743 CFDataRef cfreq = NULL;
744 unsigned char *tsaURL = NULL;
745 bool noCerts = false;
746
747 if (!context || CFGetTypeID(context)!=CFDictionaryGetTypeID())
748 return paramErr;
749
750 SecAsn1TSAMessageImprint *messageImprint = (SecAsn1TSAMessageImprint *)messageImprintV;
751 if (!messageImprint || !signedDERBlob)
752 return paramErr;
753
754 CFBooleanRef cfnocerts = (CFBooleanRef)CFDictionaryGetValue((CFDictionaryRef)context, kTSAContextKeyNoCerts);
755 if (cfnocerts)
756 {
757 tsaDebug("[TSA] Request noCerts\n");
758 noCerts = CFBooleanGetValue(cfnocerts);
759 }
760
761 // We must spoof the nonce here, before sending the request.
762 // If we tried to alter the reply, then the signature would break instead.
763 CFBooleanRef cfBadNonce = (CFBooleanRef)CFDictionaryGetValue((CFDictionaryRef)context, kTSADebugContextKeyBadNonce);
764 if (cfBadNonce && CFBooleanGetValue(cfBadNonce))
765 {
766 tsaDebug("[TSA] Forcing bad TS Request by changing nonce\n");
767 nonce++;
768 }
769
770 printDataAsHex("[TSA] hashToTimeStamp:", &messageImprint->hashedMessage,128);
771 cfreq = _SecTSARequestCopyDEREncoding(messageImprint, noCerts, nonce);
772 if (cfreq)
773 {
774 tsaReq = CFDataGetBytePtr(cfreq);
775 tsaReqLength = CFDataGetLength(cfreq);
776
777 #ifndef NDEBUG
778 CFShow(cfreq);
779 tsaWriteFileX("/tmp/tsareq.req", tsaReq, tsaReqLength);
780 #endif
781 }
782
783 CFStringRef url = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)context, kTSAContextKeyURL);
784 if (!url)
785 {
786 tsaDebug("[TSA] missing URL for TSA (key: %s)\n", "kTSAContextKeyURL");
787 goto xit;
788 }
789
790 /*
791 If debugging, look at special values in the context to mess things up
792 */
793
794 CFBooleanRef cfBadReq = (CFBooleanRef)CFDictionaryGetValue((CFDictionaryRef)context, kTSADebugContextKeyBadReq);
795 if (cfBadReq && CFBooleanGetValue(cfBadReq))
796 {
797 tsaDebug("[TSA] Forcing bad TS Request by truncating length from %ld to %ld\n", tsaReqLength, (tsaReqLength-4));
798 tsaReqLength -= 4;
799 }
800
801 // need to extract into buffer
802 CFIndex length = CFStringGetLength(url); // in 16-bit character units
803 tsaURL = malloc(6 * length + 1); // pessimistic
804 if (!CFStringGetCString(url, (char *)tsaURL, 6 * length + 1, kCFStringEncodingUTF8))
805 goto xit;
806
807 tsaDebug("[TSA] URL for timestamp server: %s\n", tsaURL);
808
809 unsigned char *tsaResp = NULL;
810 size_t tsaRespLength = 0;
811 tsaDebug("calling sendTSARequestWithXPC with %ld bytes of request\n", tsaReqLength);
812
813 require_noerr(result = sendTSARequestWithXPC(tsaReq, tsaReqLength, tsaURL, &tsaResp, &tsaRespLength), xit);
814
815 tsaDebug("sendTSARequestWithXPC copied: %ld bytes of response\n", tsaRespLength);
816
817 signedDERBlob->Data = tsaResp;
818 signedDERBlob->Length = tsaRespLength;
819
820 result = noErr;
821
822 xit:
823 if (tsaURL)
824 free((void *)tsaURL);
825 if (cfreq)
826 CFRelease(cfreq);
827
828 return result;
829 }
830
831 #pragma mark ----- TimeStamp Verification -----
832
833 static OSStatus convertGeneralizedTimeToCFAbsoluteTime(const char *timeStr, CFAbsoluteTime *ptime)
834 {
835 /*
836 See http://userguide.icu-project.org/formatparse/datetime for date/time format.
837 The "Z" signal a GMT time, but CFDateFormatterGetAbsoluteTimeFromString returns
838 values based on local time.
839 */
840
841 OSStatus result = noErr;
842 CFDateFormatterRef formatter = NULL;
843 CFStringRef time_string = NULL;
844 CFTimeZoneRef gmt = NULL;
845 CFLocaleRef locale = NULL;
846 CFRange *rangep = NULL;
847
848 require(timeStr && timeStr[0] && ptime, xit);
849 require(formatter = CFDateFormatterCreate(kCFAllocatorDefault, locale, kCFDateFormatterNoStyle, kCFDateFormatterNoStyle), xit);
850 // CFRetain(formatter);
851 CFDateFormatterSetFormat(formatter, CFSTR("yyyyMMddHHmmss'Z'")); // GeneralizedTime
852 gmt = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0);
853 CFDateFormatterSetProperty(formatter, kCFDateFormatterTimeZone, gmt);
854
855 time_string = CFStringCreateWithCString(kCFAllocatorDefault, timeStr, kCFStringEncodingUTF8);
856 if (!time_string || !CFDateFormatterGetAbsoluteTimeFromString(formatter, time_string, rangep, ptime))
857 {
858 dtprintf("%s is not a valid date\n", timeStr);
859 result = 1;
860 }
861
862 xit:
863 if (formatter)
864 CFRelease(formatter);
865 if (time_string)
866 CFRelease(time_string);
867 if (gmt)
868 CFRelease(gmt);
869
870 return result;
871 }
872
873 static OSStatus SecTSAValidateTimestamp(const SecAsn1TSATSTInfo *tstInfo, CSSM_DATA **signingCerts, CFAbsoluteTime *timestampTime)
874 {
875 // See <rdar://problem/11077708> Properly handle revocation information of timestamping certificate
876 OSStatus result = paramErr;
877 CFAbsoluteTime genTime = 0;
878 char timeStr[32] = {0,};
879 SecCertificateRef signingCertificate = NULL;
880
881 require(tstInfo && signingCerts && (tstInfo->genTime.Length < 16), xit);
882
883 // Find the leaf signingCert
884 require_noerr(result = SecCertificateCreateFromData(*signingCerts,
885 CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, &signingCertificate), xit);
886
887 memcpy(timeStr, tstInfo->genTime.Data, tstInfo->genTime.Length);
888 timeStr[tstInfo->genTime.Length] = 0;
889 require_noerr(convertGeneralizedTimeToCFAbsoluteTime(timeStr, &genTime), xit);
890 if (SecCertificateIsValidX(signingCertificate, genTime)) // iOS?
891 result = noErr;
892 else
893 result = errSecTimestampInvalid;
894 if (timestampTime)
895 *timestampTime = genTime;
896 xit:
897 if (signingCertificate)
898 CFReleaseNull(signingCertificate);
899 return result;
900 }
901
902 static OSStatus verifyTSTInfo(const CSSM_DATA_PTR content, CSSM_DATA **signingCerts, SecAsn1TSATSTInfo *tstInfo, CFAbsoluteTime *timestampTime, uint64_t expectedNonce)
903 {
904 OSStatus status = paramErr;
905 SecAsn1CoderRef coder = NULL;
906
907 if (!tstInfo)
908 return SECFailure;
909
910 require_noerr(SecAsn1CoderCreate(&coder), xit);
911 require_noerr(SecAsn1Decode(coder, content->Data, content->Length,
912 kSecAsn1TSATSTInfoTemplate, tstInfo), xit);
913 displayTSTInfo(tstInfo);
914
915 // Check the nonce
916 if (tstInfo->nonce.Data && expectedNonce!=0)
917 {
918 uint64_t nonce = tsaDER_ToInt(&tstInfo->nonce);
919 // if (expectedNonce!=nonce)
920 dtprintf("verifyTSTInfo nonce: actual: %lld, expected: %lld\n", nonce, expectedNonce);
921 require_action(expectedNonce==nonce, xit, status = errSecTimestampRejection);
922 }
923
924 status = SecTSAValidateTimestamp(tstInfo, signingCerts, timestampTime);
925 dtprintf("SecTSAValidateTimestamp result: %ld\n", (long)status);
926
927 xit:
928 if (coder)
929 SecAsn1CoderRelease(coder);
930 return status;
931 }
932
933 static void debugShowExtendedTrustResult(int index, CFDictionaryRef extendedResult)
934 {
935 #ifndef NDEBUG
936 if (extendedResult)
937 {
938 CFStringRef xresStr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
939 CFSTR("Extended trust result for signer #%d : %@"), index, extendedResult);
940 if (xresStr)
941 {
942 CFShow(xresStr);
943 CFRelease(xresStr);
944 }
945 }
946 #endif
947 }
948
949 #ifndef NDEBUG
950 extern const char *cssmErrorString(CSSM_RETURN error);
951
952 static void statusBitTest(CSSM_TP_APPLE_CERT_STATUS certStatus, uint32 bit, const char *str)
953 {
954 if (certStatus & bit)
955 dtprintf("%s ", str);
956 }
957 #endif
958
959 static void debugShowCertEvidenceInfo(uint16_t certCount, const CSSM_TP_APPLE_EVIDENCE_INFO *info)
960 {
961 #ifndef NDEBUG
962 CSSM_TP_APPLE_CERT_STATUS cs;
963 // const CSSM_TP_APPLE_EVIDENCE_INFO *pinfo = info;
964 uint16_t ix;
965 for (ix=0; info && (ix<certCount); ix++, ++info)
966 {
967 cs = info->StatusBits;
968 dtprintf(" cert %u:\n", ix);
969 dtprintf(" StatusBits : 0x%x", (unsigned)cs);
970 if (cs)
971 {
972 dtprintf(" ( ");
973 statusBitTest(cs, CSSM_CERT_STATUS_EXPIRED, "EXPIRED");
974 statusBitTest(cs, CSSM_CERT_STATUS_NOT_VALID_YET,
975 "NOT_VALID_YET");
976 statusBitTest(cs, CSSM_CERT_STATUS_IS_IN_INPUT_CERTS,
977 "IS_IN_INPUT_CERTS");
978 statusBitTest(cs, CSSM_CERT_STATUS_IS_IN_ANCHORS,
979 "IS_IN_ANCHORS");
980 statusBitTest(cs, CSSM_CERT_STATUS_IS_ROOT, "IS_ROOT");
981 statusBitTest(cs, CSSM_CERT_STATUS_IS_FROM_NET, "IS_FROM_NET");
982 dtprintf(")\n");
983 }
984 else
985 dtprintf("\n");
986
987 dtprintf(" NumStatusCodes : %u ", info->NumStatusCodes);
988 CSSM_RETURN *pstatuscode = info->StatusCodes;
989 uint16_t jx;
990 for (jx=0; pstatuscode && (jx<info->NumStatusCodes); jx++, ++pstatuscode)
991 dtprintf("%s ", cssmErrorString(*pstatuscode));
992
993 dtprintf("\n");
994 dtprintf(" Index: %u\n", info->Index);
995 }
996
997 #endif
998 }
999
1000 #ifndef NDEBUG
1001 static const char *trustResultTypeString(SecTrustResultType trustResultType)
1002 {
1003 switch (trustResultType)
1004 {
1005 case kSecTrustResultProceed: return "TrustResultProceed";
1006 case kSecTrustResultUnspecified: return "TrustResultUnspecified";
1007 case kSecTrustResultDeny: return "TrustResultDeny"; // user reject
1008 case kSecTrustResultInvalid: return "TrustResultInvalid";
1009 case kSecTrustResultConfirm: return "TrustResultConfirm";
1010 case kSecTrustResultRecoverableTrustFailure: return "TrustResultRecoverableTrustFailure";
1011 case kSecTrustResultFatalTrustFailure: return "TrustResultUnspecified";
1012 case kSecTrustResultOtherError: return "TrustResultOtherError";
1013 default: return "TrustResultUnknown";
1014 }
1015 return "";
1016 }
1017 #endif
1018
1019 static OSStatus verifySigners(SecCmsSignedDataRef signedData, int numberOfSigners, CFTypeRef timeStampPolicy)
1020 {
1021 // See <rdar://problem/11077588> Bubble up SecTrustEvaluate of timestamp response to high level callers
1022 // Also <rdar://problem/11077708> Properly handle revocation information of timestamping certificate
1023
1024 SecTrustRef trustRef = NULL;
1025 CFTypeRef policy = CFRetainSafe(timeStampPolicy);
1026 int result=errSecInternalError;
1027 int rx;
1028
1029 if (!policy) {
1030 require(policy = SecPolicyCreateWithOID(kSecPolicyAppleTimeStamping), xit);
1031 }
1032
1033 int jx;
1034 for (jx = 0; jx < numberOfSigners; ++jx)
1035 {
1036 SecTrustResultType trustResultType;
1037 CFDictionaryRef extendedResult = NULL;
1038 CFArrayRef certChain = NULL;
1039 uint16_t certCount = 0;
1040
1041 CSSM_TP_APPLE_EVIDENCE_INFO *statusChain = NULL;
1042
1043 // SecCmsSignedDataVerifySignerInfo returns trustRef, which we can call SecTrustEvaluate on
1044 // usually (always?) if result is noErr, the SecTrust*Result calls will return errSecTrustNotAvailable
1045 result = SecCmsSignedDataVerifySignerInfo (signedData, jx, NULL, policy, &trustRef);
1046 dtprintf("[%s] SecCmsSignedDataVerifySignerInfo: result: %d, signer: %d\n",
1047 __FUNCTION__, result, jx);
1048 require_noerr(result, xit);
1049
1050 result = SecTrustEvaluate (trustRef, &trustResultType);
1051 dtprintf("[%s] SecTrustEvaluate: result: %d, trustResult: %s (%d)\n",
1052 __FUNCTION__, result, trustResultTypeString(trustResultType), trustResultType);
1053 if (result)
1054 goto xit;
1055 switch (trustResultType)
1056 {
1057 case kSecTrustResultProceed:
1058 case kSecTrustResultUnspecified:
1059 break; // success
1060 case kSecTrustResultDeny: // user reject
1061 result = errSecTimestampNotTrusted; // SecCmsVSTimestampNotTrusted ?
1062 break;
1063 case kSecTrustResultInvalid:
1064 assert(false); // should never happen
1065 result = errSecTimestampNotTrusted; // SecCmsVSTimestampNotTrusted ?
1066 break;
1067 case kSecTrustResultConfirm:
1068 case kSecTrustResultRecoverableTrustFailure:
1069 case kSecTrustResultFatalTrustFailure:
1070 case kSecTrustResultOtherError:
1071 default:
1072 {
1073 /*
1074 There are two "errors" that need to be resolved externally:
1075 CSSMERR_TP_CERT_EXPIRED can be OK if the timestamp was made
1076 before the TSA chain expired; CSSMERR_TP_CERT_NOT_VALID_YET
1077 can happen in the case where the user's clock was set to 0.
1078 We don't want to prevent them using apps automatically, so
1079 return noErr and let codesign or whover decide.
1080 */
1081 OSStatus resultCode;
1082 require_action(SecTrustGetCssmResultCode(trustRef, &resultCode)==noErr, xit, result = errSecTimestampNotTrusted);
1083 result = (resultCode == CSSMERR_TP_CERT_EXPIRED || resultCode == CSSMERR_TP_CERT_NOT_VALID_YET)?noErr:errSecTimestampNotTrusted;
1084 }
1085 break;
1086 }
1087
1088 rx = SecTrustGetResult(trustRef, &trustResultType, &certChain, &statusChain);
1089 dtprintf("[%s] SecTrustGetResult: result: %d, type: %d\n", __FUNCTION__,rx, trustResultType);
1090 certCount = certChain?CFArrayGetCount(certChain):0;
1091 debugShowCertEvidenceInfo(certCount, statusChain);
1092 CFReleaseNull(certChain);
1093
1094 rx = SecTrustCopyExtendedResult(trustRef, &extendedResult);
1095 dtprintf("[%s] SecTrustCopyExtendedResult: result: %d\n", __FUNCTION__, rx);
1096 if (extendedResult)
1097 {
1098 debugShowExtendedTrustResult(jx, extendedResult);
1099 CFRelease(extendedResult);
1100 }
1101
1102 if (trustRef)
1103 CFReleaseNull(trustRef);
1104 }
1105
1106 xit:
1107 if (trustRef)
1108 CFReleaseNull(trustRef);
1109 if (policy)
1110 CFRelease(policy);
1111 return result;
1112 }
1113
1114 static OSStatus impExpImportCertUnCommon(
1115 const CSSM_DATA *cdata,
1116 SecKeychainRef importKeychain, // optional
1117 CFMutableArrayRef outArray) // optional, append here
1118 {
1119 // The only difference between this and impExpImportCertCommon is that we append to outArray
1120 // before attempting to add to the keychain
1121 OSStatus status = noErr;
1122 SecCertificateRef certRef = NULL;
1123
1124 require_action(cdata, xit, status = errSecUnsupportedFormat);
1125
1126 /* Pass kCFAllocatorNull as bytesDeallocator to assure the bytes aren't freed */
1127 CFDataRef data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)cdata->Data, (CFIndex)cdata->Length, kCFAllocatorNull);
1128 require_action(data, xit, status = errSecUnsupportedFormat);
1129
1130 certRef = SecCertificateCreateWithData(kCFAllocatorDefault, data);
1131 CFRelease(data); /* certRef has its own copy of the data now */
1132 if(!certRef) {
1133 dtprintf("impExpHandleCert error\n");
1134 return errSecUnsupportedFormat;
1135 }
1136
1137 if (outArray)
1138 CFArrayAppendValue(outArray, certRef);
1139
1140 if (importKeychain)
1141 {
1142 status = SecCertificateAddToKeychain(certRef, importKeychain);
1143 if (status!=noErr && status!=errSecDuplicateItem)
1144 { dtprintf("SecCertificateAddToKeychain error: %ld\n", (long)status); }
1145 }
1146
1147 xit:
1148 if (certRef)
1149 CFRelease(certRef);
1150 return status;
1151 }
1152
1153 static void saveTSACertificates(CSSM_DATA **signingCerts, CFMutableArrayRef outArray)
1154 {
1155 SecKeychainRef defaultKeychain = NULL;
1156 // Don't save certificates in keychain to avoid securityd issues
1157 // if (SecKeychainCopyDefault(&defaultKeychain))
1158 // defaultKeychain = NULL;
1159
1160 unsigned certCount = SecCmsArrayCount((void **)signingCerts);
1161 unsigned dex;
1162 for (dex=0; dex<certCount; dex++)
1163 {
1164 OSStatus rx = impExpImportCertUnCommon(signingCerts[dex], defaultKeychain, outArray);
1165 if (rx!=noErr && rx!=errSecDuplicateItem)
1166 dtprintf("impExpImportCertCommon failed: %ld\n", (long)rx);
1167 }
1168 if (defaultKeychain)
1169 CFRelease(defaultKeychain);
1170 }
1171
1172 static const char *cfabsoluteTimeToString(CFAbsoluteTime abstime)
1173 {
1174 CFGregorianDate greg = CFAbsoluteTimeGetGregorianDate(abstime, NULL);
1175 char str[20];
1176 if (19 != snprintf(str, 20, "%4.4d-%2.2d-%2.2d_%2.2d:%2.2d:%2.2d",
1177 (int)greg.year, greg.month, greg.day, greg.hour, greg.minute, (int)greg.second))
1178 str[0]=0;
1179 char *data = (char *)malloc(20);
1180 strncpy(data, str, 20);
1181 return data;
1182 }
1183
1184 static OSStatus setTSALeafValidityDates(SecCmsSignerInfoRef signerinfo)
1185 {
1186 OSStatus status = noErr;
1187
1188 if (!signerinfo->timestampCertList || (CFArrayGetCount(signerinfo->timestampCertList) == 0))
1189 return SecCmsVSSigningCertNotFound;
1190
1191 SecCertificateRef tsaLeaf = (SecCertificateRef)CFArrayGetValueAtIndex(signerinfo->timestampCertList, 0);
1192 require_action(tsaLeaf, xit, status = errSecCertificateCannotOperate);
1193
1194 signerinfo->tsaLeafNotBefore = SecCertificateNotValidBefore(tsaLeaf); /* Start date for Timestamp Authority leaf */
1195 signerinfo->tsaLeafNotAfter = SecCertificateNotValidAfter(tsaLeaf); /* Expiration date for Timestamp Authority leaf */
1196
1197 const char *nbefore = cfabsoluteTimeToString(signerinfo->tsaLeafNotBefore);
1198 const char *nafter = cfabsoluteTimeToString(signerinfo->tsaLeafNotAfter);
1199 if (nbefore && nafter)
1200 {
1201 dtprintf("Timestamp Authority leaf valid from %s to %s\n", nbefore, nafter);
1202 free((void *)nbefore);free((void *)nafter);
1203 }
1204
1205 /*
1206 if(at < nb)
1207 status = errSecCertificateNotValidYet;
1208 else if (at > na)
1209 status = errSecCertificateExpired;
1210 */
1211
1212 xit:
1213 return status;
1214 }
1215
1216 /*
1217 From RFC 3161: Time-Stamp Protocol (TSP),August 2001, APPENDIX B:
1218
1219 B) The validity of the digital signature may then be verified in the
1220 following way:
1221
1222 1) The time-stamp token itself MUST be verified and it MUST be
1223 verified that it applies to the signature of the signer.
1224
1225 2) The date/time indicated by the TSA in the TimeStampToken
1226 MUST be retrieved.
1227
1228 3) The certificate used by the signer MUST be identified and
1229 retrieved.
1230
1231 4) The date/time indicated by the TSA MUST be within the
1232 validity period of the signer's certificate.
1233
1234 5) The revocation information about that certificate, at the
1235 date/time of the Time-Stamping operation, MUST be retrieved.
1236
1237 6) Should the certificate be revoked, then the date/time of
1238 revocation shall be later than the date/time indicated by
1239 the TSA.
1240
1241 If all these conditions are successful, then the digital signature
1242 shall be declared as valid.
1243
1244 */
1245
1246 OSStatus decodeTimeStampToken(SecCmsSignerInfoRef signerinfo, CSSM_DATA_PTR inData, CSSM_DATA_PTR encDigest, uint64_t expectedNonce)
1247 {
1248 return decodeTimeStampTokenWithPolicy(signerinfo, NULL, inData, encDigest, expectedNonce);
1249 }
1250
1251 OSStatus decodeTimeStampTokenWithPolicy(SecCmsSignerInfoRef signerinfo, CFTypeRef timeStampPolicy, CSSM_DATA_PTR inData, CSSM_DATA_PTR encDigest, uint64_t expectedNonce)
1252 {
1253 /*
1254 We update signerinfo with timestamp and tsa certificate chain.
1255 encDigest is the original signed blob, which we must hash and compare.
1256 inData comes from the unAuthAttr section of the CMS message
1257
1258 These are set in signerinfo as side effects:
1259 timestampTime -
1260 timestampCertList
1261 */
1262
1263 SecCmsDecoderRef decoderContext = NULL;
1264 SecCmsMessageRef cmsMessage = NULL;
1265 SecCmsContentInfoRef contentInfo;
1266 SecCmsSignedDataRef signedData;
1267 SECOidTag contentTypeTag;
1268 int contentLevelCount;
1269 int ix;
1270 OSStatus result = errSecUnknownFormat;
1271 CSSM_DATA **signingCerts = NULL;
1272
1273 dtprintf("decodeTimeStampToken top: PORT_GetError() %d -----\n", PORT_GetError());
1274 PORT_SetError(0);
1275
1276 /* decode the message */
1277 require_noerr(result = SecCmsDecoderCreate (NULL, NULL, NULL, NULL, NULL, NULL, NULL, &decoderContext), xit);
1278 result = SecCmsDecoderUpdate(decoderContext, inData->Data, inData->Length);
1279 if (result)
1280 {
1281 result = errSecTimestampInvalid;
1282 SecCmsDecoderDestroy(decoderContext);
1283 goto xit;
1284 }
1285
1286 require_noerr(result = SecCmsDecoderFinish(decoderContext, &cmsMessage), xit);
1287
1288 // process the results
1289 contentLevelCount = SecCmsMessageContentLevelCount(cmsMessage);
1290
1291 if (encDigest)
1292 printDataAsHex("encDigest",encDigest, 0);
1293
1294 for (ix = 0; ix < contentLevelCount; ++ix)
1295 {
1296 dtprintf("\n----- Content Level %d -----\n", ix);
1297 // get content information
1298 contentInfo = SecCmsMessageContentLevel (cmsMessage, ix);
1299 contentTypeTag = SecCmsContentInfoGetContentTypeTag (contentInfo);
1300
1301 // After 2nd round, contentInfo.content.data is the TSTInfo
1302
1303 debugShowContentTypeOID(contentInfo);
1304
1305 switch (contentTypeTag)
1306 {
1307 case SEC_OID_PKCS7_SIGNED_DATA:
1308 {
1309 require((signedData = (SecCmsSignedDataRef)SecCmsContentInfoGetContent(contentInfo)) != NULL, xit);
1310
1311 debugShowSignerInfo(signedData);
1312
1313 SECAlgorithmID **digestAlgorithms = SecCmsSignedDataGetDigestAlgs(signedData);
1314 unsigned digestAlgCount = SecCmsArrayCount((void **)digestAlgorithms);
1315 dtprintf("digestAlgCount: %d\n", digestAlgCount);
1316 if (signedData->digests)
1317 {
1318 int jx;
1319 char buffer[128];
1320 for (jx=0;jx < digestAlgCount;jx++)
1321 {
1322 sprintf(buffer, " digest[%u]", jx);
1323 printDataAsHex(buffer,signedData->digests[jx], 0);
1324 }
1325 }
1326 else
1327 {
1328 dtprintf("No digests\n");
1329 CSSM_DATA_PTR innerContent = SecCmsContentInfoGetInnerContent(contentInfo);
1330 if (innerContent)
1331 {
1332 dtprintf("inner content length: %ld\n", innerContent->Length);
1333 SecAsn1TSAMessageImprint fakeMessageImprint = {{{0}},};
1334 OSStatus status = createTSAMessageImprint(signedData, innerContent, &fakeMessageImprint);
1335 if (status)
1336 { dtprintf("createTSAMessageImprint status: %d\n", (int)status); }
1337 printDataAsHex("inner content hash",&fakeMessageImprint.hashedMessage, 0);
1338 CSSM_DATA_PTR digestdata = &fakeMessageImprint.hashedMessage;
1339 CSSM_DATA_PTR digests[2] = {digestdata, NULL};
1340 SecCmsSignedDataSetDigests(signedData, digestAlgorithms, (CSSM_DATA_PTR *)&digests);
1341 }
1342 else
1343 dtprintf("no inner content\n");
1344 }
1345
1346 /*
1347 Import the certificates. We leave this as a warning, since
1348 there are configurations where the certificates are not returned.
1349 */
1350 signingCerts = SecCmsSignedDataGetCertificateList(signedData);
1351 if (signingCerts == NULL)
1352 { dtprintf("SecCmsSignedDataGetCertificateList returned NULL\n"); }
1353 else
1354 {
1355 if (!signerinfo->timestampCertList)
1356 signerinfo->timestampCertList = CFArrayCreateMutable(kCFAllocatorDefault, 10, &kCFTypeArrayCallBacks);
1357 saveTSACertificates(signingCerts, signerinfo->timestampCertList);
1358 require_noerr(result = setTSALeafValidityDates(signerinfo), xit);
1359 debugSaveCertificates(signingCerts);
1360 }
1361
1362 int numberOfSigners = SecCmsSignedDataSignerInfoCount (signedData);
1363
1364 result = verifySigners(signedData, numberOfSigners, timeStampPolicy);
1365 if (result)
1366 dtprintf("verifySigners failed: %ld\n", (long)result); // warning
1367
1368
1369 if (result) // remap to SecCmsVSTimestampNotTrusted ?
1370 goto xit;
1371
1372 break;
1373 }
1374 case SEC_OID_PKCS9_SIGNING_CERTIFICATE:
1375 {
1376 dtprintf("SEC_OID_PKCS9_SIGNING_CERTIFICATE seen\n");
1377 break;
1378 }
1379
1380 case SEC_OID_PKCS9_ID_CT_TSTInfo:
1381 {
1382 SecAsn1TSATSTInfo tstInfo = {{0},};
1383 result = verifyTSTInfo(contentInfo->rawContent, signingCerts, &tstInfo, &signerinfo->timestampTime, expectedNonce);
1384 if (signerinfo->timestampTime)
1385 {
1386 const char *tstamp = cfabsoluteTimeToString(signerinfo->timestampTime);
1387 if (tstamp)
1388 {
1389 dtprintf("Timestamp Authority timestamp: %s\n", tstamp);
1390 free((void *)tstamp);
1391 }
1392 }
1393 break;
1394 }
1395 case SEC_OID_OTHER:
1396 {
1397 dtprintf("otherContent : %p\n", (char *)SecCmsContentInfoGetContent (contentInfo));
1398 break;
1399 }
1400 default:
1401 dtprintf("ContentTypeTag : %x\n", contentTypeTag);
1402 break;
1403 }
1404 }
1405 xit:
1406 if (cmsMessage)
1407 SecCmsMessageDestroy(cmsMessage);
1408
1409 return result;
1410 }
1411
1412