5 * Copyright (c) 2009-2014 Apple Inc.. All Rights Reserved.
9 * Copyright (c) 2009-2014 Apple Inc. All Rights Reserved.
11 * @APPLE_LICENSE_HEADER_START@
13 * This file contains Original Code and/or Modifications of Original Code
14 * as defined in and that are subject to the Apple Public Source License
15 * Version 2.0 (the 'License'). You may not use this file except in
16 * compliance with the License. Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
28 * @APPLE_LICENSE_HEADER_END@
32 * SecCAIssuerRequest.c - asynchronous CAIssuer request fetching engine.
36 #include "SecCAIssuerRequest.h"
37 #include "SecCAIssuerCache.h"
39 #include <Security/SecInternal.h>
40 #include <CoreFoundation/CFURL.h>
41 #include <CFNetwork/CFHTTPMessage.h>
42 #include <utilities/debugging.h>
43 #include <Security/SecCertificateInternal.h>
44 #include <securityd/asynchttp.h>
47 /* CA Issuer lookup code. */
49 typedef struct SecCAIssuerRequest
*SecCAIssuerRequestRef
;
50 struct SecCAIssuerRequest
{
51 asynchttp_t http
; /* Must be first field. */
52 SecCertificateRef certificate
;
53 CFArrayRef issuers
; /* NONRETAINED */
56 void (*callback
)(void *, CFArrayRef
);
59 static void SecCAIssuerRequestRelease(SecCAIssuerRequestRef request
) {
60 CFRelease(request
->certificate
);
61 asynchttp_free(&request
->http
);
65 static bool SecCAIssuerRequestIssue(SecCAIssuerRequestRef request
) {
66 while (request
->issuerIX
< CFArrayGetCount(request
->issuers
)) {
67 CFURLRef issuer
= CFArrayGetValueAtIndex(request
->issuers
,
69 CFStringRef scheme
= CFURLCopyScheme(issuer
);
71 if (CFEqual(CFSTR("http"), scheme
)) {
72 CFHTTPMessageRef msg
= CFHTTPMessageCreateRequest(kCFAllocatorDefault
,
73 CFSTR("GET"), issuer
, kCFHTTPVersion1_1
);
75 secdebug("caissuer", "%@", msg
);
76 bool done
= asynchttp_request(msg
, &request
->http
);
83 secdebug("caissuer", "failed to get %@", issuer
);
85 secdebug("caissuer", "skipping unsupported uri %@", issuer
);
91 /* No more issuers left to try, we're done. */
92 secdebug("caissuer", "no request issued");
93 request
->callback(request
->context
, NULL
);
94 SecCAIssuerRequestRelease(request
);
98 /* Releases parent unconditionally, and return a CFArrayRef containing
99 parent if the normalized subject of parent matches the normalized issuer
101 static CFArrayRef
SecCAIssuerConvertToParents(SecCertificateRef certificate
,
102 SecCertificateRef parent
) {
103 CFDataRef nic
= SecCertificateGetNormalizedIssuerContent(certificate
);
104 CFArrayRef parents
= NULL
;
106 CFDataRef parent_nic
= SecCertificateGetNormalizedSubjectContent(parent
);
107 if (parent_nic
&& CFEqual(nic
, parent_nic
)) {
108 const void *ventry
= parent
;
109 parents
= CFArrayCreate(NULL
, &ventry
, 1, &kCFTypeArrayCallBacks
);
116 #define SECONDS_PER_DAY (86400.0)
117 static void SecCAIssuerRequestCompleted(asynchttp_t
*http
,
118 CFTimeInterval maxAge
) {
119 /* Cast depends on http being first field in struct SecCAIssuerRequest. */
120 SecCAIssuerRequestRef request
= (SecCAIssuerRequestRef
)http
;
121 CFDataRef data
= (request
->http
.response
?
122 CFHTTPMessageCopyBody(request
->http
.response
) : NULL
);
124 SecCertificateRef parent
= SecCertificateCreateWithData(NULL
, data
);
127 /* We keep responses in the cache for at least 7 days, or longer
128 if the http response tells us to keep it around for more. */
129 if (maxAge
< SECONDS_PER_DAY
* 7)
130 maxAge
= SECONDS_PER_DAY
* 7;
131 CFAbsoluteTime expires
= CFAbsoluteTimeGetCurrent() + maxAge
;
132 CFURLRef issuer
= CFArrayGetValueAtIndex(request
->issuers
,
133 request
->issuerIX
- 1);
134 SecCAIssuerCacheAddCertificate(parent
, issuer
, expires
);
135 CFArrayRef parents
= SecCAIssuerConvertToParents(
136 request
->certificate
, parent
);
138 secdebug("caissuer", "response: %@ good", http
->response
);
139 request
->callback(request
->context
, parents
);
141 SecCAIssuerRequestRelease(request
);
147 secdebug("caissuer", "response: %@ not parent, trying next caissuer",
149 SecCAIssuerRequestIssue(request
);
152 static CFArrayRef
SecCAIssuerRequestCacheCopyParents(SecCertificateRef cert
,
153 CFArrayRef issuers
) {
154 CFIndex ix
= 0, ex
= CFArrayGetCount(issuers
);
155 for (;ix
< ex
; ++ix
) {
156 CFURLRef issuer
= CFArrayGetValueAtIndex(issuers
, ix
);
157 CFStringRef scheme
= CFURLCopyScheme(issuer
);
159 if (CFEqual(CFSTR("http"), scheme
)) {
160 CFArrayRef parents
= SecCAIssuerConvertToParents(cert
,
161 SecCAIssuerCacheCopyMatching(issuer
));
163 secdebug("caissuer", "cache hit, for %@ no request issued", issuer
);
174 bool SecCAIssuerCopyParents(SecCertificateRef certificate
, dispatch_queue_t queue
,
175 void *context
, void (*callback
)(void *, CFArrayRef
)) {
176 CFArrayRef issuers
= SecCertificateGetCAIssuers(certificate
);
178 /* certificate has no caissuer urls, we're done. */
179 callback(context
, NULL
);
183 CFArrayRef parents
= SecCAIssuerRequestCacheCopyParents(certificate
, issuers
);
185 callback(context
, parents
);
186 CFReleaseSafe(parents
);
190 /* Cache miss, let's issue a network request. */
191 SecCAIssuerRequestRef request
=
192 (SecCAIssuerRequestRef
)calloc(1, sizeof(*request
));
193 request
->http
.queue
= queue
;
194 request
->http
.completed
= SecCAIssuerRequestCompleted
;
195 CFRetain(certificate
);
196 request
->certificate
= certificate
;
197 request
->issuers
= issuers
;
198 request
->issuerIX
= 0;
199 request
->context
= context
;
200 request
->callback
= callback
;
202 return SecCAIssuerRequestIssue(request
);