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