237251283ae40c83dfb69eb85fbb739a3cac7007
[apple/security.git] / sec / securityd / SecCAIssuerRequest.c
1 /*
2 * SecCAIssuerRequest.c
3 * Security
4 *
5 * Created by Michael Brouwer on 9/17/09.
6 * Copyright (c) 2009-2011 Apple Inc.. All Rights Reserved.
7 *
8 */
9 /*
10 * Copyright (c) 2009-2011 Apple Inc. All Rights Reserved.
11 *
12 * @APPLE_LICENSE_HEADER_START@
13 *
14 * This file contains Original Code and/or Modifications of Original Code
15 * as defined in and that are subject to the Apple Public Source License
16 * Version 2.0 (the 'License'). You may not use this file except in
17 * compliance with the License. Please obtain a copy of the License at
18 * http://www.opensource.apple.com/apsl/ and read it before using this
19 * file.
20 *
21 * The Original Code and all software distributed under the License are
22 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
23 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
24 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
25 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
26 * Please see the License for the specific language governing rights and
27 * limitations under the License.
28 *
29 * @APPLE_LICENSE_HEADER_END@
30 */
31
32 /*
33 * SecCAIssuerRequest.c - asynchronous CAIssuer request fetching engine.
34 */
35
36
37 #include "SecCAIssuerRequest.h"
38 #include "SecCAIssuerCache.h"
39
40 #include <Security/SecInternal.h>
41 #include <CoreFoundation/CFURL.h>
42 #include <CFNetwork/CFHTTPMessage.h>
43 #include <security_utilities/debugging.h>
44 #include <Security/SecCertificateInternal.h>
45 #include <securityd/asynchttp.h>
46 #include <stdlib.h>
47
48 /* CA Issuer lookup code. */
49
50 typedef struct SecCAIssuerRequest *SecCAIssuerRequestRef;
51 struct SecCAIssuerRequest {
52 asynchttp_t http; /* Must be first field. */
53 SecCertificateRef certificate;
54 CFArrayRef issuers; /* NONRETAINED */
55 CFIndex issuerIX;
56 void *context;
57 void (*callback)(void *, CFArrayRef);
58 };
59
60 static void SecCAIssuerRequestRelease(SecCAIssuerRequestRef request) {
61 CFRelease(request->certificate);
62 asynchttp_free(&request->http);
63 free(request);
64 }
65
66 static bool SecCAIssuerRequestIssue(SecCAIssuerRequestRef request) {
67 while (request->issuerIX < CFArrayGetCount(request->issuers)) {
68 CFURLRef issuer = CFArrayGetValueAtIndex(request->issuers,
69 request->issuerIX++);
70 CFStringRef scheme = CFURLCopyScheme(issuer);
71 if (scheme) {
72 if (CFEqual(CFSTR("http"), scheme)) {
73 CFHTTPMessageRef msg = CFHTTPMessageCreateRequest(kCFAllocatorDefault,
74 CFSTR("GET"), issuer, kCFHTTPVersion1_1);
75 if (msg) {
76 secdebug("caissuer", "%@", msg);
77 bool done = asynchttp_request(msg, &request->http);
78 CFRelease(msg);
79 if (done == false)
80 return done;
81 }
82 secdebug("caissuer", "failed to get %@", issuer);
83 } else {
84 secdebug("caissuer", "skipping unsupported uri %@", issuer);
85 }
86 CFRelease(scheme);
87 }
88 }
89
90 /* No more issuers left to try, we're done. */
91 secdebug("caissuer", "no request issued");
92 request->callback(request->context, NULL);
93 SecCAIssuerRequestRelease(request);
94 return true;
95 }
96
97 /* Releases parent unconditionally, and return a CFArrayRef containing
98 parent if the normalized subject of parent matches the normalized issuer
99 of certificate. */
100 static CFArrayRef SecCAIssuerConvertToParents(SecCertificateRef certificate,
101 SecCertificateRef parent) {
102 CFDataRef nic = SecCertificateGetNormalizedIssuerContent(certificate);
103 CFArrayRef parents = NULL;
104 if (parent) {
105 if (CFEqual(nic, SecCertificateGetNormalizedSubjectContent(parent))) {
106 const void *ventry = parent;
107 parents = CFArrayCreate(NULL, &ventry, 1, &kCFTypeArrayCallBacks);
108 }
109 CFRelease(parent);
110 }
111 return parents;
112 }
113
114 #define SECONDS_PER_DAY (86400.0)
115 static void SecCAIssuerRequestCompleted(asynchttp_t *http,
116 CFTimeInterval maxAge) {
117 /* Cast depends on http being first field in struct SecCAIssuerRequest. */
118 SecCAIssuerRequestRef request = (SecCAIssuerRequestRef)http;
119 CFDataRef data = (request->http.response ?
120 CFHTTPMessageCopyBody(request->http.response) : NULL);
121 if (data) {
122 SecCertificateRef parent = SecCertificateCreateWithData(NULL, data);
123 CFRelease(data);
124 if (parent) {
125 /* We keep responses in the cache for at least 7 days, or longer
126 if the http response tells us to keep it around for more. */
127 if (maxAge < SECONDS_PER_DAY * 7)
128 maxAge = SECONDS_PER_DAY * 7;
129 CFAbsoluteTime expires = CFAbsoluteTimeGetCurrent() + maxAge;
130 CFURLRef issuer = CFArrayGetValueAtIndex(request->issuers,
131 request->issuerIX - 1);
132 SecCAIssuerCacheAddCertificate(parent, issuer, expires);
133 CFArrayRef parents = SecCAIssuerConvertToParents(
134 request->certificate, parent);
135 if (parents) {
136 secdebug("caissuer", "response: %@ good", http->response);
137 request->callback(request->context, parents);
138 CFRelease(parents);
139 SecCAIssuerRequestRelease(request);
140 return;
141 }
142 }
143 }
144
145 secdebug("caissuer", "response: %@ not parent, trying next caissuer",
146 http->response);
147 SecCAIssuerRequestIssue(request);
148 }
149
150 static CFArrayRef SecCAIssuerRequestCacheCopyParents(SecCertificateRef cert,
151 CFArrayRef issuers) {
152 CFIndex ix = 0, ex = CFArrayGetCount(issuers);
153 for (;ix < ex; ++ix) {
154 CFURLRef issuer = CFArrayGetValueAtIndex(issuers, ix);
155 CFStringRef scheme = CFURLCopyScheme(issuer);
156 if (scheme) {
157 if (CFEqual(CFSTR("http"), scheme)) {
158 CFArrayRef parents = SecCAIssuerConvertToParents(cert,
159 SecCAIssuerCacheCopyMatching(issuer));
160 if (parents) {
161 secdebug("caissuer", "cache hit, for %@ no request issued", issuer);
162 return parents;
163 }
164 }
165 CFRelease(scheme);
166 }
167 }
168 return NULL;
169 }
170
171 bool SecCAIssuerCopyParents(SecCertificateRef certificate,
172 void *context, void (*callback)(void *, CFArrayRef)) {
173 CFArrayRef issuers = SecCertificateGetCAIssuers(certificate);
174 if (!issuers) {
175 /* certificate has no caissuer urls, we're done. */
176 callback(context, NULL);
177 return true;
178 }
179
180 CFArrayRef parents = SecCAIssuerRequestCacheCopyParents(certificate, issuers);
181 if (parents) {
182 callback(context, parents);
183 CFReleaseSafe(parents);
184 return true;
185 }
186
187 /* Cache miss, let's issue a network request. */
188 SecCAIssuerRequestRef request =
189 (SecCAIssuerRequestRef)calloc(1, sizeof(*request));
190 request->http.completed = SecCAIssuerRequestCompleted;
191 CFRetain(certificate);
192 request->certificate = certificate;
193 request->issuers = issuers;
194 request->issuerIX = 0;
195 request->context = context;
196 request->callback = callback;
197
198 return SecCAIssuerRequestIssue(request);
199 }
200