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