Ticket #42718: patch-handle-expired-certs-v0

File patch-handle-expired-certs-v0, 5.3 KB (added by landonf (Landon Fuller), 11 years ago)

*Actual* proposed fix.

Line 
1Index: Portfile
2===================================================================
3--- Portfile    (revision 117820)
4+++ Portfile    (working copy)
5@@ -3,8 +3,7 @@
6 PortSystem             1.0
7 
8 name                   certsync
9-version                        1.0.6
10-revision               3
11+version                        1.0.7
12 categories             security
13 conflicts              curl-ca-bundle
14 maintainers            landonf openmaintainer
15Index: files/certsync.m
16===================================================================
17--- files/certsync.m    (revision 117820)
18+++ files/certsync.m    (working copy)
19@@ -96,6 +96,60 @@
20 }
21 
22 /**
23+ * Verify that the root certificate trusts itself; this filters out certificates that
24+ * are still marked as trusted by the OS, but are expired or otherwise unusable.
25+ */
26+static BOOL ValidateSelfTrust (SecCertificateRef cert) {
27+    OSStatus err;
28+
29+    /* Create a new trust evaluation instance */
30+    SecTrustRef trust;
31+    {
32+        SecPolicyRef policy = SecPolicyCreateBasicX509();
33+        if ((err = SecTrustCreateWithCertificates((CFTypeRef)cert, policy, &trust)) != errSecSuccess) {
34+            /* Shouldn't happen */
35+            nsfprintf(stderr, @"Failed to create SecTrustRef: %d\n", err);
36+            CFRelease(policy);
37+            return NO;
38+        }
39+        CFRelease(policy);
40+    }
41+   
42+    /* Set this certificate as the only (self-)anchor */
43+    {
44+        CFArrayRef certs = CFArrayCreate(NULL, (const void **) &cert, 1, &kCFTypeArrayCallBacks);
45+        if ((err = SecTrustSetAnchorCertificates(trust, certs)) != errSecSuccess) {
46+            nsfprintf(stderr, @"Failed to set anchor certificates on our SecTrustRef: %d\n", err);
47+            CFRelease(certs);
48+            CFRelease(trust);
49+            return NO;
50+        }
51+        CFRelease(certs);
52+    }
53+   
54+    /* Evaluate the certificate trust */
55+    SecTrustResultType rt;
56+    if ((err = SecTrustEvaluate(trust, &rt)) != errSecSuccess) {
57+        nsfprintf(stderr, @"SecTrustEvaluate() failed: %d\n", err);
58+        CFRelease(trust);
59+    }
60+   
61+    CFRelease(trust);
62+   
63+    /* Check the result */
64+    switch (rt) {
65+        case kSecTrustResultUnspecified:
66+        case kSecTrustResultProceed:
67+            /* Trusted */
68+            return YES;
69+           
70+        default:
71+            /* Untrusted */
72+            return NO;
73+    }
74+}
75+
76+/**
77  * Fetch all trusted roots for the given @a domain.
78  *
79  * @param domain The trust domain to query.
80@@ -105,7 +159,7 @@
81  */
82 static NSArray *certificatesForTrustDomain (SecTrustSettingsDomain domain, NSError **outError) {
83     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
84-    NSArray *trusted = nil;
85+    NSMutableArray *trusted = nil;
86     CFArrayRef certs = nil;
87     OSStatus err;
88     
89@@ -131,8 +185,7 @@
90         }
91     
92         /* Extract trusted roots */
93-        NSMutableArray *results = [NSMutableArray arrayWithCapacity: CFArrayGetCount(certs)];
94-        trusted = results;
95+        trusted = [NSMutableArray arrayWithCapacity: CFArrayGetCount(certs)];
96         
97         NSEnumerator *resultEnumerator = [(NSArray *)certs objectEnumerator];
98         id certObj;
99@@ -152,7 +205,7 @@
100         
101             /* If empty, trust for everything (as per the Security Framework documentation) */
102             if (CFArrayGetCount(trustSettings) == 0) {
103-                [results addObject: certObj];
104+                [trusted addObject: certObj];
105             } else {
106                 /* Otherwise, walk the properties and evaluate the trust settings result */
107                 NSEnumerator *trustEnumerator = [(NSArray *)trustSettings objectEnumerator];
108@@ -166,7 +219,7 @@
109                 
110                     /* If a root, add to the result set */
111                     if (settingsResult == kSecTrustSettingsResultTrustRoot || settingsResult == kSecTrustSettingsResultTrustAsRoot) {
112-                        [results addObject: certObj];
113+                        [trusted addObject: certObj];
114                         break;
115                     }
116                 }
117@@ -192,9 +245,31 @@
118         }
119 
120         /* All certs are trusted */
121-        trusted = (NSArray *) certs;
122+        trusted = [[(NSArray *) certs mutableCopy] autorelease];
123+        CFRelease(certs);
124     }
125     
126+    /*
127+     * Filter out any trusted certificates that can not actually be used in verification; eg, they are expired.
128+     *
129+     * There are cases where CAs have issued new certificates using identical public keys, and the expired
130+     * and current CA certificates are both included in the list of trusted certificates. In such a case,
131+     * OpenSSL will simply use whichever certificate is listed first; if that happens to be the expired
132+     * certificate, validation will fail.
133+     *
134+     * This step ensures that we exclude any expired or known-unusable certificates.
135+     *
136+     * We enumerate a copy of the array so that we can safely modify the original during enumeration.
137+     */
138+    NSEnumerator *trustedEnumerator = [[[trusted copy] autorelease] objectEnumerator];
139+    id certObj;
140+    while ((certObj = [trustedEnumerator nextObject]) != nil) {
141+        /* If self-trust validation fails, the certificate is expired or otherwise not useable */
142+        if (!ValidateSelfTrust((SecCertificateRef) certObj)) {
143+            [trusted removeObject: certObj];
144+        }
145+    }
146+   
147     [trusted retain];
148     [pool release];
149     return [trusted autorelease];