1/* 2 * SecCAIssuerRequest.c 3 * Security 4 * 5 * Copyright (c) 2009-2014 Apple Inc.. All Rights Reserved. 6 * 7 */ 8/* 9 * Copyright (c) 2009-2014 Apple Inc. All Rights Reserved. 10 * 11 * @APPLE_LICENSE_HEADER_START@ 12 * 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 18 * file. 19 * 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. 27 * 28 * @APPLE_LICENSE_HEADER_END@ 29 */ 30 31/* 32 * SecCAIssuerRequest.c - asynchronous CAIssuer request fetching engine. 33 */ 34 35 36#include "SecCAIssuerRequest.h" 37#include "SecCAIssuerCache.h" 38 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> 45#include <stdlib.h> 46 47/* CA Issuer lookup code. */ 48 49typedef struct SecCAIssuerRequest *SecCAIssuerRequestRef; 50struct SecCAIssuerRequest { 51 asynchttp_t http; /* Must be first field. */ 52 SecCertificateRef certificate; 53 CFArrayRef issuers; /* NONRETAINED */ 54 CFIndex issuerIX; 55 void *context; 56 void (*callback)(void *, CFArrayRef); 57}; 58 59static void SecCAIssuerRequestRelease(SecCAIssuerRequestRef request) { 60 CFRelease(request->certificate); 61 asynchttp_free(&request->http); 62 free(request); 63} 64 65static bool SecCAIssuerRequestIssue(SecCAIssuerRequestRef request) { 66 while (request->issuerIX < CFArrayGetCount(request->issuers)) { 67 CFURLRef issuer = CFArrayGetValueAtIndex(request->issuers, 68 request->issuerIX++); 69 CFStringRef scheme = CFURLCopyScheme(issuer); 70 if (scheme) { 71 if (CFEqual(CFSTR("http"), scheme)) { 72 CFHTTPMessageRef msg = CFHTTPMessageCreateRequest(kCFAllocatorDefault, 73 CFSTR("GET"), issuer, kCFHTTPVersion1_1); 74 if (msg) { 75 secdebug("caissuer", "%@", msg); 76 bool done = asynchttp_request(msg, &request->http); 77 CFRelease(msg); 78 if (done == false) { 79 CFRelease(scheme); 80 return done; 81 } 82 } 83 secdebug("caissuer", "failed to get %@", issuer); 84 } else { 85 secdebug("caissuer", "skipping unsupported uri %@", issuer); 86 } 87 CFRelease(scheme); 88 } 89 } 90 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); 95 return true; 96} 97 98/* Releases parent unconditionally, and return a CFArrayRef containing 99 parent if the normalized subject of parent matches the normalized issuer 100 of certificate. */ 101static CFArrayRef SecCAIssuerConvertToParents(SecCertificateRef certificate, 102 SecCertificateRef parent) { 103 CFDataRef nic = SecCertificateGetNormalizedIssuerContent(certificate); 104 CFArrayRef parents = NULL; 105 if (parent) { 106 if (CFEqual(nic, SecCertificateGetNormalizedSubjectContent(parent))) { 107 const void *ventry = parent; 108 parents = CFArrayCreate(NULL, &ventry, 1, &kCFTypeArrayCallBacks); 109 } 110 CFRelease(parent); 111 } 112 return parents; 113} 114 115#define SECONDS_PER_DAY (86400.0) 116static void SecCAIssuerRequestCompleted(asynchttp_t *http, 117 CFTimeInterval maxAge) { 118 /* Cast depends on http being first field in struct SecCAIssuerRequest. */ 119 SecCAIssuerRequestRef request = (SecCAIssuerRequestRef)http; 120 CFDataRef data = (request->http.response ? 121 CFHTTPMessageCopyBody(request->http.response) : NULL); 122 if (data) { 123 SecCertificateRef parent = SecCertificateCreateWithData(NULL, data); 124 CFRelease(data); 125 if (parent) { 126 /* We keep responses in the cache for at least 7 days, or longer 127 if the http response tells us to keep it around for more. */ 128 if (maxAge < SECONDS_PER_DAY * 7) 129 maxAge = SECONDS_PER_DAY * 7; 130 CFAbsoluteTime expires = CFAbsoluteTimeGetCurrent() + maxAge; 131 CFURLRef issuer = CFArrayGetValueAtIndex(request->issuers, 132 request->issuerIX - 1); 133 SecCAIssuerCacheAddCertificate(parent, issuer, expires); 134 CFArrayRef parents = SecCAIssuerConvertToParents( 135 request->certificate, parent); 136 if (parents) { 137 secdebug("caissuer", "response: %@ good", http->response); 138 request->callback(request->context, parents); 139 CFRelease(parents); 140 SecCAIssuerRequestRelease(request); 141 return; 142 } 143 } 144 } 145 146 secdebug("caissuer", "response: %@ not parent, trying next caissuer", 147 http->response); 148 SecCAIssuerRequestIssue(request); 149} 150 151static CFArrayRef SecCAIssuerRequestCacheCopyParents(SecCertificateRef cert, 152 CFArrayRef issuers) { 153 CFIndex ix = 0, ex = CFArrayGetCount(issuers); 154 for (;ix < ex; ++ix) { 155 CFURLRef issuer = CFArrayGetValueAtIndex(issuers, ix); 156 CFStringRef scheme = CFURLCopyScheme(issuer); 157 if (scheme) { 158 if (CFEqual(CFSTR("http"), scheme)) { 159 CFArrayRef parents = SecCAIssuerConvertToParents(cert, 160 SecCAIssuerCacheCopyMatching(issuer)); 161 if (parents) { 162 secdebug("caissuer", "cache hit, for %@ no request issued", issuer); 163 CFRelease(scheme); 164 return parents; 165 } 166 } 167 CFRelease(scheme); 168 } 169 } 170 return NULL; 171} 172 173bool SecCAIssuerCopyParents(SecCertificateRef certificate, dispatch_queue_t queue, 174 void *context, void (*callback)(void *, CFArrayRef)) { 175 CFArrayRef issuers = SecCertificateGetCAIssuers(certificate); 176 if (!issuers) { 177 /* certificate has no caissuer urls, we're done. */ 178 callback(context, NULL); 179 return true; 180 } 181 182 CFArrayRef parents = SecCAIssuerRequestCacheCopyParents(certificate, issuers); 183 if (parents) { 184 callback(context, parents); 185 CFReleaseSafe(parents); 186 return true; 187 } 188 189 /* Cache miss, let's issue a network request. */ 190 SecCAIssuerRequestRef request = 191 (SecCAIssuerRequestRef)calloc(1, sizeof(*request)); 192 request->http.queue = queue; 193 request->http.completed = SecCAIssuerRequestCompleted; 194 CFRetain(certificate); 195 request->certificate = certificate; 196 request->issuers = issuers; 197 request->issuerIX = 0; 198 request->context = context; 199 request->callback = callback; 200 201 return SecCAIssuerRequestIssue(request); 202} 203 204