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