1/*
2 * Copyright (c) 2011-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
24
25#include "testpolicy.h"
26
27#include <TargetConditionals.h>
28
29#if TARGET_OS_IPHONE
30
31#include <Foundation/Foundation.h>
32#include <CoreFoundation/CoreFoundation.h>
33#include <utilities/SecCFWrappers.h>
34#include <Security/SecCertificate.h>
35#include <Security/SecCertificatePriv.h>
36#include <Security/SecInternal.h>
37#include <Security/SecPolicyPriv.h>
38#include <Security/SecTrust.h>
39#include <Security/SecTrustPriv.h>
40#include <stdlib.h>
41#include <unistd.h>
42
43#include "testmore.h"
44
45/*
46 * Copyright (c) 2011-2014 Apple Inc. All Rights Reserved.
47 */
48
49#include <Foundation/Foundation.h>
50#include <CoreFoundation/CoreFoundation.h>
51#include <Security/SecCertificate.h>
52#include <Security/SecCertificatePriv.h>
53#include <Security/SecInternal.h>
54#include <Security/SecPolicyPriv.h>
55#include <Security/SecTrust.h>
56#include <Security/SecTrustPriv.h>
57#include <stdlib.h>
58#include <unistd.h>
59
60#include "testmore.h"
61
62/* Those tests were originally written around that date. */
63CFGiblisGetSingleton(CFDateRef, GetFrozenTime, frozenTime, ^{
64    *frozenTime = CFDateCreateForGregorianZuluDay(NULL, 2011, 9, 1);
65});
66
67static void runOneLeafTest(SecPolicyRef policy,
68                           NSArray* anchors,
69                           NSArray* intermediates,
70                           NSString* path,
71                           bool expectedResult,
72                           NSObject* expectations,
73                           CFDateRef date)
74{
75    NSString* fileName = [path lastPathComponent];
76    const char *reason = NULL;
77    SecTrustRef trustRef = NULL;
78    CFStringRef failReason = NULL;
79    NSMutableArray* certArray = NULL;
80    SecCertificateRef certRef = NULL;
81
82    if (expectations) {
83        if ([expectations isKindOfClass: [NSString class]]) {
84            reason = [(NSString *)expectations UTF8String];
85        } else if ([expectations isKindOfClass: [NSDictionary class]]) {
86            NSDictionary *dict = (NSDictionary *)expectations;
87            NSObject *value = [dict valueForKey:@"valid"];
88            if (value) {
89                if ([value isKindOfClass: [NSNumber class]]) {
90                    expectedResult = [(NSNumber *)value boolValue];
91                } else {
92                    NSLog(@"Unexpected valid value %@ in dict for key %@", value, fileName);
93                }
94            }
95            value = [dict valueForKey:@"reason"];
96            if (value) {
97                if ([value isKindOfClass: [NSString class]]) {
98                    reason = [(NSString *)value UTF8String];
99                } else {
100                    NSLog(@"Unexpected reason value %@ in dict for key %@", value, fileName);
101                }
102            }
103        } else if ([expectations isKindOfClass: [NSNumber class]]) {
104            expectedResult = [(NSNumber *)expectations boolValue];
105        } else {
106            NSLog(@"Unexpected class %@ value %@ for key %@", [expectations class], expectations, fileName);
107        }
108    }
109
110    certRef = SecCertificateCreateWithData(NULL, (CFDataRef)[NSData dataWithContentsOfFile:path]);
111    if (!certRef) {
112        if (reason) {
113            todo(reason);
114            fail("%@ unable to create certificate", fileName);
115        } else {
116            fail("PARSE %@ unable to create certificate", fileName);
117        }
118        goto exit;
119    }
120
121    certArray = [NSMutableArray arrayWithArray:intermediates];
122    [certArray insertObject:(id)certRef atIndex:0]; //The certificate to be verified must be the first in the array.
123
124    OSStatus err;
125    err = SecTrustCreateWithCertificates(certArray, policy, &trustRef);
126    if (err) {
127        ok_status(err, "SecTrustCreateWithCertificates");
128        goto exit;
129    }
130    if ([anchors count])
131        SecTrustSetAnchorCertificates(trustRef, (CFArrayRef)anchors);
132
133    SecTrustSetVerifyDate(trustRef, date ? date : GetFrozenTime());
134
135    SecTrustResultType evalRes = 0;
136    //NSLog(@"Evaluating: %@",certRef);
137    err = SecTrustEvaluate(trustRef, &evalRes);
138    if (err) {
139        ok_status(err, "SecTrustCreateWithCertificates");
140        goto exit;
141    }
142    BOOL isValid = (evalRes == kSecTrustResultProceed || evalRes == kSecTrustResultUnspecified);
143    if (!isValid && expectedResult) {
144        failReason = SecTrustCopyFailureDescription(trustRef);
145    }
146    if (reason) {
147        todo(reason);
148        ok(isValid == expectedResult, "%@%@",
149           fileName,
150           (expectedResult
151            ? (failReason ? failReason : CFSTR(""))
152            : CFSTR(" valid")));
153    } else {
154        ok(isValid == expectedResult, "%s %@%@",
155           expectedResult ? "REGRESSION" : "SECURITY", fileName,
156           failReason ? failReason : CFSTR(""));
157    }
158
159exit:
160    CFReleaseSafe(failReason);
161    CFReleaseSafe(trustRef);
162    CFReleaseSafe(certRef);
163}
164
165// TODO: Export this interface in a better way.
166static void runCertificateTestFor(SecPolicyRef policy,
167                           NSArray* anchors,
168                           NSArray* intermediates,
169                           NSMutableArray* leafPaths,
170                           NSDictionary* expect,
171                           CFDateRef date)
172{
173    /* Sort the tests by name. */
174    [leafPaths sortUsingSelector:@selector(compare:)];
175
176	for (NSString* path in leafPaths) {
177        NSString* fileName = [path lastPathComponent];
178        runOneLeafTest(policy, anchors, intermediates, path, ![fileName hasPrefix:@"Invalid"], [expect objectForKey:fileName], date);
179	}
180}
181
182void runCertificateTestForDirectory(SecPolicyRef policy, CFStringRef resourceSubDirectory, CFDateRef date)
183{
184    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
185	NSMutableArray* allRoots = [NSMutableArray array];
186	NSMutableArray* allCAs = [NSMutableArray array];
187	NSMutableArray* certTests = [NSMutableArray array];
188    NSDictionary* expect = NULL;
189
190    /* Iterate though the nist-certs resources dir. */
191    NSURL* filesDirectory = [[[NSBundle mainBundle] resourceURL] URLByAppendingPathComponent:(NSString*)resourceSubDirectory];
192    for (NSURL* fileURL in [[NSFileManager defaultManager] contentsOfDirectoryAtURL:filesDirectory includingPropertiesForKeys:[NSArray array] options:NSDirectoryEnumerationSkipsSubdirectoryDescendants error:nil]) {
193        NSString* path = [fileURL path];
194		if ([path hasSuffix:@"Cert.crt"]) {
195            SecCertificateRef certRef = SecCertificateCreateWithData(NULL, (CFDataRef)[NSData dataWithContentsOfFile:path]);
196            [allCAs addObject:(id)certRef];
197        } else if ([path hasSuffix:@"RootCertificate.crt"]) {
198            SecCertificateRef certRef = SecCertificateCreateWithData(NULL, (CFDataRef)[NSData dataWithContentsOfFile:path]);
199            [allRoots addObject:(id)certRef];
200        } else if ([path hasSuffix:@".crt"]) {
201                [certTests addObject:path];
202        } else if ([path hasSuffix:@".plist"]) {
203            if (expect) {
204                fail("Multiple .plist files found in %@", filesDirectory);
205            } else {
206                expect = [NSDictionary dictionaryWithContentsOfFile:path];
207            }
208        }
209	}
210
211    runCertificateTestFor(policy, allRoots, allCAs, certTests, expect, date);
212
213    [pool release];
214}
215
216#endif /* TARGET_OS_IPHONE */
217