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