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 <utilities/debugging.h> 44#include <Security/SecCertificateInternal.h> 45#include <securityd/asynchttp.h> 46#include <stdlib.h> 47 48/* CA Issuer lookup code. */ 49 50typedef struct SecCAIssuerRequest *SecCAIssuerRequestRef; 51struct 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 60static void SecCAIssuerRequestRelease(SecCAIssuerRequestRef request) { 61 CFRelease(request->certificate); 62 asynchttp_free(&request->http); 63 free(request); 64} 65 66static 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 CFRelease(scheme); 81 return done; 82 } 83 } 84 secdebug("caissuer", "failed to get %@", issuer); 85 } else { 86 secdebug("caissuer", "skipping unsupported uri %@", issuer); 87 } 88 CFRelease(scheme); 89 } 90 } 91 92 /* No more issuers left to try, we're done. */ 93 secdebug("caissuer", "no request issued"); 94 request->callback(request->context, NULL); 95 SecCAIssuerRequestRelease(request); 96 return true; 97} 98 99/* Releases parent unconditionally, and return a CFArrayRef containing 100 parent if the normalized subject of parent matches the normalized issuer 101 of certificate. */ 102static CFArrayRef SecCAIssuerConvertToParents(SecCertificateRef certificate, 103 SecCertificateRef parent) { 104 CFDataRef nic = SecCertificateGetNormalizedIssuerContent(certificate); 105 CFArrayRef parents = NULL; 106 if (parent) { 107 if (CFEqual(nic, SecCertificateGetNormalizedSubjectContent(parent))) { 108 const void *ventry = parent; 109 parents = CFArrayCreate(NULL, &ventry, 1, &kCFTypeArrayCallBacks); 110 } 111 CFRelease(parent); 112 } 113 return parents; 114} 115 116#define SECONDS_PER_DAY (86400.0) 117static 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); 123 if (data) { 124 SecCertificateRef parent = SecCertificateCreateWithData(NULL, data); 125 CFRelease(data); 126 if (parent) { 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); 137 if (parents) { 138 secdebug("caissuer", "response: %@ good", http->response); 139 request->callback(request->context, parents); 140 CFRelease(parents); 141 SecCAIssuerRequestRelease(request); 142 return; 143 } 144 } 145 } 146 147 secdebug("caissuer", "response: %@ not parent, trying next caissuer", 148 http->response); 149 SecCAIssuerRequestIssue(request); 150} 151 152static 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); 158 if (scheme) { 159 if (CFEqual(CFSTR("http"), scheme)) { 160 CFArrayRef parents = SecCAIssuerConvertToParents(cert, 161 SecCAIssuerCacheCopyMatching(issuer)); 162 if (parents) { 163 secdebug("caissuer", "cache hit, for %@ no request issued", issuer); 164 CFRelease(scheme); 165 return parents; 166 } 167 } 168 CFRelease(scheme); 169 } 170 } 171 return NULL; 172} 173 174bool SecCAIssuerCopyParents(SecCertificateRef certificate, dispatch_queue_t queue, 175 void *context, void (*callback)(void *, CFArrayRef)) { 176 CFArrayRef issuers = SecCertificateGetCAIssuers(certificate); 177 if (!issuers) { 178 /* certificate has no caissuer urls, we're done. */ 179 callback(context, NULL); 180 return true; 181 } 182 183 CFArrayRef parents = SecCAIssuerRequestCacheCopyParents(certificate, issuers); 184 if (parents) { 185 callback(context, parents); 186 CFReleaseSafe(parents); 187 return true; 188 } 189 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; 201 202 return SecCAIssuerRequestIssue(request); 203} 204 205