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