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