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