/*
* Copyright (c) 2011-2014 Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
#include "testpolicy.h"
#include <TargetConditionals.h>
#include <Foundation/Foundation.h>
#include <CoreFoundation/CoreFoundation.h>
#include <utilities/SecCFWrappers.h>
#include <Security/SecCertificate.h>
#include <Security/SecCertificatePriv.h>
#include <Security/SecPolicyPriv.h>
#include <Security/SecTrust.h>
#include <Security/SecTrustPriv.h>
#include <stdlib.h>
#include <unistd.h>
#include "testmore.h"
/* Those tests were originally written around that date. */
CFGiblisGetSingleton(CFDateRef, GetFrozenTime, frozenTime, ^{
*frozenTime = CFDateCreateForGregorianZuluDay(NULL, 2011, 9, 1);
});
static void runOneLeafTest(SecPolicyRef policy,
NSArray* anchors,
NSArray* intermediates,
NSString* path,
bool expectedResult,
NSObject* expectations,
CFDateRef date)
{
NSString* fileName = [path lastPathComponent];
const char *reason = NULL;
SecTrustRef trustRef = NULL;
CFStringRef failReason = NULL;
NSMutableArray* certArray = NULL;
SecCertificateRef certRef = NULL;
if (expectations) {
if ([expectations isKindOfClass: [NSString class]]) {
reason = [(NSString *)expectations UTF8String];
} else if ([expectations isKindOfClass: [NSDictionary class]]) {
NSDictionary *dict = (NSDictionary *)expectations;
NSObject *value = [dict valueForKey:@"valid"];
if (value) {
if ([value isKindOfClass: [NSNumber class]]) {
expectedResult = [(NSNumber *)value boolValue];
} else {
NSLog(@"Unexpected valid value }
}
value = [dict valueForKey:@"reason"];
if (value) {
if ([value isKindOfClass: [NSString class]]) {
reason = [(NSString *)value UTF8String];
} else {
NSLog(@"Unexpected reason value }
}
} else if ([expectations isKindOfClass: [NSNumber class]]) {
expectedResult = [(NSNumber *)expectations boolValue];
} else {
NSLog(@"Unexpected class }
}
certRef = SecCertificateCreateWithData(NULL, (CFDataRef)[NSData dataWithContentsOfFile:path]);
if (!certRef) {
if (reason) {
todo(reason);
fail(" } else {
fail("PARSE }
goto exit;
}
certArray = [NSMutableArray arrayWithArray:intermediates];
[certArray insertObject:(__bridge id)certRef atIndex:0]; //The certificate to be verified must be the first in the array.
OSStatus err;
err = SecTrustCreateWithCertificates((__bridge CFTypeRef _Nonnull)(certArray), policy, &trustRef);
if (err) {
ok_status(err, "SecTrustCreateWithCertificates");
goto exit;
}
if ([anchors count])
SecTrustSetAnchorCertificates(trustRef, (CFArrayRef)anchors);
SecTrustSetVerifyDate(trustRef, date ? date : GetFrozenTime());
SecTrustResultType evalRes = 0;
//NSLog(@"Evaluating: err = SecTrustEvaluate(trustRef, &evalRes);
if (err) {
ok_status(err, "SecTrustEvaluate");
goto exit;
}
BOOL isValid = (evalRes == kSecTrustResultProceed || evalRes == kSecTrustResultUnspecified);
if (!isValid && expectedResult) {
failReason = SecTrustCopyFailureDescription(trustRef);
}
if (reason) {
todo(reason);
ok(isValid == expectedResult, " fileName,
(expectedResult
? (failReason ? failReason : CFSTR(""))
: CFSTR(" valid")));
} else {
ok(isValid == expectedResult, " expectedResult ? "REGRESSION" : "SECURITY", fileName,
failReason ? failReason : CFSTR(""));
}
exit:
CFReleaseSafe(failReason);
CFReleaseSafe(trustRef);
CFReleaseSafe(certRef);
}
// TODO: Export this interface in a better way.
static void runCertificateTestFor(SecPolicyRef policy,
NSArray* anchors,
NSArray* intermediates,
NSMutableArray* leafPaths,
NSDictionary* expect,
CFDateRef date)
{
/* Sort the tests by name. */
[leafPaths sortUsingSelector:@selector(compare:)];
for (NSString* path in leafPaths) {
NSString* fileName = [path lastPathComponent];
runOneLeafTest(policy, anchors, intermediates, path, ![fileName hasPrefix:@"Invalid"], [expect objectForKey:fileName], date);
}
}
void runCertificateTestForDirectory(SecPolicyRef policy, CFStringRef resourceSubDirectory, CFDateRef date)
{
NSMutableArray* allRoots = [NSMutableArray array];
NSMutableArray* allCAs = [NSMutableArray array];
NSMutableArray* certTests = [NSMutableArray array];
NSDictionary* expect = NULL;
/* Iterate though the nist-certs resources dir. */
NSURL* filesDirectory = [[[NSBundle mainBundle] resourceURL] URLByAppendingPathComponent:(__bridge NSString*)resourceSubDirectory];
for (NSURL* fileURL in [[NSFileManager defaultManager] contentsOfDirectoryAtURL:filesDirectory includingPropertiesForKeys:[NSArray array] options:NSDirectoryEnumerationSkipsSubdirectoryDescendants error:nil]) {
NSString* path = [fileURL path];
if ([path hasSuffix:@"Cert.crt"]) {
SecCertificateRef certRef = SecCertificateCreateWithData(NULL, (CFDataRef)[NSData dataWithContentsOfFile:path]);
[allCAs addObject:(__bridge id)certRef];
CFReleaseNull(certRef);
} else if ([path hasSuffix:@"RootCertificate.crt"]) {
SecCertificateRef certRef = SecCertificateCreateWithData(NULL, (CFDataRef)[NSData dataWithContentsOfFile:path]);
[allRoots addObject:(__bridge id)certRef];
CFReleaseNull(certRef);
} else if ([path hasSuffix:@".crt"]) {
[certTests addObject:path];
} else if ([path hasSuffix:@".plist"]) {
if (expect) {
fail("Multiple .plist files found in } else {
expect = [NSDictionary dictionaryWithContentsOfFile:path];
}
}
}
runCertificateTestFor(policy, allRoots, allCAs, certTests, expect, date);
}