]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_apple_x509_tp/lib/TPNetwork.cpp
Security-59306.11.20.tar.gz
[apple/security.git] / OSX / libsecurity_apple_x509_tp / lib / TPNetwork.cpp
1 /*
2 * Copyright (c) 2002-2012 Apple Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
8 * using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
16 */
17
18
19 /*
20 * TPNetwork.h - LDAP, HTTP and (eventually) other network tools
21 */
22
23 #include "TPNetwork.h"
24 #include "tpdebugging.h"
25 #include "tpTime.h"
26 #include "cuEnc64.h"
27 #include <Security/cssmtype.h>
28 #include <Security/cssmapple.h>
29 #include <Security/oidscert.h>
30 #include <security_utilities/logging.h>
31 #include <security_ocspd/ocspdClient.h>
32
33 #define CA_ISSUERS_OID OID_PKIX, 0x30, 0x02
34 #define CA_ISSUERS_OID_LEN OID_PKIX_LENGTH + 2
35
36 static const uint8 OID_CA_ISSUERS[] = {CA_ISSUERS_OID};
37 const CSSM_OID CSSMOID_CA_ISSUERS = {CA_ISSUERS_OID_LEN, (uint8 *)OID_CA_ISSUERS};
38
39 typedef enum {
40 LT_Crl = 1,
41 LT_Cert
42 } LF_Type;
43
44 static CSSM_RETURN tpDecodeCert(
45 Allocator &alloc,
46 CSSM_DATA &rtnBlob) // will be reallocated if needed
47 {
48 const unsigned char *inbuf = (const unsigned char *)rtnBlob.Data;
49 unsigned inlen = (unsigned)rtnBlob.Length;
50 unsigned char *outbuf = NULL;
51 unsigned outlen = 0;
52 CSSM_RETURN ortn = cuConvertPem(inbuf, inlen, &outbuf, &outlen);
53
54 if(ortn == 0 && outbuf != NULL) {
55 /* Decoded result needs to be malloc'd via input allocator */
56 unsigned char *rtnP = (unsigned char *) alloc.malloc(outlen);
57 if(rtnP != NULL) {
58 memcpy(rtnP, outbuf, outlen);
59 rtnBlob.Data = rtnP;
60 rtnBlob.Length = outlen;
61 }
62 free(outbuf);
63 alloc.free((void *)inbuf);
64 }
65 return ortn;
66 }
67
68 static CSSM_RETURN tpFetchViaNet(
69 const CSSM_DATA &url,
70 const CSSM_DATA *issuer, // optional
71 LF_Type lfType,
72 CSSM_TIMESTRING verifyTime, // CRL only
73 Allocator &alloc,
74 CSSM_DATA &rtnBlob) // mallocd and RETURNED
75 {
76 if(lfType == LT_Crl) {
77 return ocspdCRLFetch(alloc, url, issuer,
78 true, true, // cache r/w both enable
79 verifyTime, rtnBlob);
80 }
81 else {
82 CSSM_RETURN result = ocspdCertFetch(alloc, url, rtnBlob);
83 if(result == CSSM_OK) {
84 /* The data might be in PEM format; if so, convert it here */
85 (void)tpDecodeCert(alloc, rtnBlob);
86 }
87 return result;
88 }
89 }
90
91 static CSSM_RETURN tpCrlViaNet(
92 const CSSM_DATA &url,
93 const CSSM_DATA *issuer, // optional, only if cert and CRL have same issuer
94 TPVerifyContext &vfyCtx,
95 TPCertInfo &forCert, // for verifyWithContext
96 TPCrlInfo *&rtnCrl)
97 {
98 TPCrlInfo *crl = NULL;
99 CSSM_DATA crlData;
100 CSSM_RETURN crtn;
101 Allocator &alloc = Allocator::standard();
102 char cssmTime[CSSM_TIME_STRLEN+1];
103
104 rtnCrl = NULL;
105
106 /* verifyTime: we want a CRL that's valid right now. */
107 {
108 StLock<Mutex> _(tpTimeLock());
109 timeAtNowPlus(0, TP_TIME_CSSM, cssmTime);
110 }
111
112 crtn = tpFetchViaNet(url, issuer, LT_Crl, cssmTime, alloc, crlData);
113 if(crtn) {
114 return crtn;
115 }
116 try {
117 crl = new TPCrlInfo(vfyCtx.clHand,
118 vfyCtx.cspHand,
119 &crlData,
120 TIC_CopyData,
121 NULL); // verifyTime = Now
122 }
123 catch(...) {
124 alloc.free(crlData.Data);
125
126 /*
127 * There is a slight possibility of recovering from this error. In case
128 * the CRL came from disk cache, flush the cache and try to get the CRL
129 * from the net.
130 */
131 tpDebug(" bad CRL; flushing from cache and retrying");
132 ocspdCRLFlush(url);
133 crtn = tpFetchViaNet(url, issuer, LT_Crl, cssmTime, alloc, crlData);
134 if(crtn == CSSM_OK) {
135 try {
136 crl = new TPCrlInfo(vfyCtx.clHand,
137 vfyCtx.cspHand,
138 &crlData,
139 TIC_CopyData,
140 NULL);
141 tpDebug(" RECOVERY: good CRL obtained from net");
142 }
143 catch(...) {
144 alloc.free(crlData.Data);
145 tpDebug(" bad CRL; recovery FAILED (1)");
146 return CSSMERR_APPLETP_CRL_NOT_FOUND;
147 }
148 }
149 else {
150 /* it was in cache but we can't find it on the net */
151 tpDebug(" bad CRL; recovery FAILED (2)");
152 return CSSMERR_APPLETP_CRL_NOT_FOUND;
153 }
154 }
155 alloc.free(crlData.Data);
156
157 /*
158 * Full CRL verify.
159 * The verify time in the TPVerifyContext is the time at which various
160 * entities (CRL and its own cert chain) are to be verified; that's
161 * NULL for "right now". The current vfyCtx.verifyTime is the time at
162 * which the cert's revocation status to be determined; this call to
163 * verifyWithContextNow() doesn't do that.
164 */
165 crtn = crl->verifyWithContextNow(vfyCtx, &forCert);
166 if(crtn == CSSM_OK) {
167 crl->uri(url);
168 }
169 else {
170 delete crl;
171 crl = NULL;
172 }
173 rtnCrl = crl;
174 return crtn;
175 }
176
177 static CSSM_RETURN tpIssuerCertViaNet(
178 const CSSM_DATA &url,
179 CSSM_CL_HANDLE clHand,
180 CSSM_CSP_HANDLE cspHand,
181 const char *verifyTime,
182 TPCertInfo &subject,
183 TPCertInfo *&rtnCert)
184 {
185 TPCertInfo *issuer = NULL;
186 CSSM_DATA certData;
187 CSSM_RETURN crtn;
188 Allocator &alloc = Allocator::standard();
189
190 crtn = tpFetchViaNet(url, NULL, LT_Cert, NULL, alloc, certData);
191 if(crtn) {
192 tpErrorLog("tpIssuerCertViaNet: net fetch failed\n");
193 return CSSMERR_APPLETP_CERT_NOT_FOUND_FROM_ISSUER;
194 }
195 try {
196 issuer = new TPCertInfo(clHand,
197 cspHand,
198 &certData,
199 TIC_CopyData,
200 verifyTime);
201 }
202 catch(...) {
203 tpErrorLog("tpIssuerCertViaNet: bad cert via net fetch\n");
204 alloc.free(certData.Data);
205 rtnCert = NULL;
206 return CSSMERR_APPLETP_BAD_CERT_FROM_ISSUER;
207 }
208 alloc.free(certData.Data);
209
210 /* subject/issuer match? */
211 if(!issuer->isIssuerOf(subject)) {
212 tpErrorLog("tpIssuerCertViaNet: wrong issuer cert via net fetch\n");
213 crtn = CSSMERR_APPLETP_BAD_CERT_FROM_ISSUER;
214 }
215 else {
216 /* yep, do a sig verify */
217 crtn = subject.verifyWithIssuer(issuer);
218 if(crtn) {
219 tpErrorLog("tpIssuerCertViaNet: sig verify fail for cert via net "
220 "fetch\n");
221 crtn = CSSMERR_APPLETP_BAD_CERT_FROM_ISSUER;
222 }
223 }
224 if(crtn) {
225 assert(issuer != NULL);
226 delete issuer;
227 issuer = NULL;
228 }
229 rtnCert = issuer;
230 return crtn;
231 }
232
233 /*
234 * Fetch a CRL or a cert via a GeneralNames.
235 * Shared by cert and CRL code to avoid duplicating GeneralNames traversal
236 * code, despite the awkward interface for this function.
237 */
238 static CSSM_RETURN tpFetchViaGeneralNames(
239 const CE_GeneralNames *names,
240 TPCertInfo &forCert,
241 const CSSM_DATA *issuer, // optional, and only for CRLs
242 TPVerifyContext *verifyContext, // only for CRLs
243 CSSM_CL_HANDLE clHand, // only for certs
244 CSSM_CSP_HANDLE cspHand, // only for certs
245 const char *verifyTime, // optional
246 /* exactly one must be non-NULL, that one is returned */
247 TPCertInfo **certInfo,
248 TPCrlInfo **crlInfo)
249 {
250 assert(certInfo || crlInfo);
251 assert(!certInfo || !crlInfo);
252 CSSM_RETURN crtn;
253
254 for(unsigned nameDex=0; nameDex<names->numNames; nameDex++) {
255 CE_GeneralName *name = &names->generalName[nameDex];
256 switch(name->nameType) {
257 case GNT_URI:
258 if(name->name.Length < 5) {
259 continue;
260 }
261 if(strncmp((char *)name->name.Data, "ldap:", 5) &&
262 strncmp((char *)name->name.Data, "http:", 5) &&
263 strncmp((char *)name->name.Data, "https:", 6)) {
264 /* eventually handle other schemes here */
265 continue;
266 }
267 if(certInfo) {
268 tpDebug(" fetching cert via net");
269 crtn = tpIssuerCertViaNet(name->name,
270 clHand,
271 cspHand,
272 verifyTime,
273 forCert,
274 *certInfo);
275 }
276 else {
277 tpDebug(" fetching CRL via net");
278 assert(verifyContext != NULL);
279 crtn = tpCrlViaNet(name->name,
280 issuer,
281 *verifyContext,
282 forCert,
283 *crlInfo);
284 }
285 switch(crtn) {
286 case CSSM_OK:
287 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE: // caller handles
288 return crtn;
289 default:
290 break;
291 }
292 /* not found/no good; try again */
293 break;
294 default:
295 tpCrlDebug(" tpFetchCrlFromNet: unknown"
296 "nameType (%u)", (unsigned)name->nameType);
297 break;
298 } /* switch nameType */
299 } /* for each name */
300 if(certInfo) {
301 return CSSMERR_TP_CERTGROUP_INCOMPLETE;
302 }
303 else {
304 return CSSMERR_APPLETP_CRL_NOT_FOUND;
305 }
306 }
307
308 /*
309 * Fetch CRL(s) from specified cert if the cert has a cRlDistributionPoint
310 * extension.
311 *
312 * Return values:
313 * CSSM_OK - found and returned fully verified CRL
314 * CSSMERR_APPLETP_CRL_NOT_FOUND - no CRL in cRlDistributionPoint
315 * Anything else - gross error, typically from last LDAP/HTTP attempt
316 *
317 * FIXME - this whole mechanism sort of falls apart if verifyContext.verifyTime
318 * is non-NULL. How are we supposed to get the CRL which was valid at
319 * a specified time in the past?
320 */
321 CSSM_RETURN tpFetchCrlFromNet(
322 TPCertInfo &cert,
323 TPVerifyContext &vfyCtx,
324 TPCrlInfo *&crl) // RETURNED
325 {
326 /* does the cert have a cRlDistributionPoint? */
327 CSSM_DATA_PTR fieldValue; // mallocd by CL
328
329 CSSM_RETURN crtn = cert.fetchField(&CSSMOID_CrlDistributionPoints,
330 &fieldValue);
331 switch(crtn) {
332 case CSSM_OK:
333 break;
334 case CSSMERR_CL_NO_FIELD_VALUES:
335 /* field not present */
336 return CSSMERR_APPLETP_CRL_NOT_FOUND;
337 default:
338 /* gross error */
339 return crtn;
340 }
341 if(fieldValue->Length != sizeof(CSSM_X509_EXTENSION)) {
342 tpErrorLog("tpFetchCrlFromNet: malformed CSSM_FIELD");
343 return CSSMERR_TP_UNKNOWN_FORMAT;
344 }
345 CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)fieldValue->Data;
346 CE_CRLDistPointsSyntax *dps =
347 (CE_CRLDistPointsSyntax *)cssmExt->value.parsedValue;
348 TPCrlInfo *rtnCrl = NULL;
349
350 /* default return if we don't find anything */
351 crtn = CSSMERR_APPLETP_CRL_NOT_FOUND;
352 for(unsigned dex=0; dex<dps->numDistPoints; dex++) {
353 CE_CRLDistributionPoint *dp = &dps->distPoints[dex];
354 if(dp->distPointName == NULL) {
355 continue;
356 }
357 /*
358 * FIXME if this uses an indirect CRL, we need to follow the
359 * crlIssuer field... TBD.
360 */
361 switch(dp->distPointName->nameType) {
362 case CE_CDNT_NameRelativeToCrlIssuer:
363 /* not yet */
364 tpErrorLog("tpFetchCrlFromNet: "
365 "CE_CDNT_NameRelativeToCrlIssuer not implemented\n");
366 break;
367
368 case CE_CDNT_FullName:
369 {
370 /*
371 * Since we don't support indirect CRLs (yet), we always pass
372 * the cert-to-be-verified's issuer as the CRL issuer for
373 * cache lookup.
374 */
375 CE_GeneralNames *names = dp->distPointName->dpn.fullName;
376 crtn = tpFetchViaGeneralNames(names,
377 cert,
378 cert.issuerName(),
379 &vfyCtx,
380 0, // clHand, use the one in vfyCtx
381 0, // cspHand, ditto
382 vfyCtx.verifyTime,
383 NULL,
384 &rtnCrl);
385 break;
386 } /* CE_CDNT_FullName */
387
388 default:
389 /* not yet */
390 tpErrorLog("tpFetchCrlFromNet: "
391 "unknown distPointName->nameType (%u)\n",
392 (unsigned)dp->distPointName->nameType);
393 break;
394 } /* switch distPointName->nameType */
395 if(crtn == CSSM_OK) {
396 /* i.e., tpFetchViaGeneralNames SUCCEEDED */
397 break;
398 }
399 } /* for each distPoints */
400
401 cert.freeField(&CSSMOID_CrlDistributionPoints, fieldValue);
402 if(crtn == CSSM_OK) {
403 assert(rtnCrl != NULL);
404 crl = rtnCrl;
405 }
406 return crtn;
407 }
408
409 /*
410 * Fetch issuer cert of specified cert if the cert has an issuerAltName
411 * with a URI. If non-NULL cert is returned, it has passed subject/issuer
412 * name comparison and signature verification with target cert.
413 *
414 * Return values:
415 * CSSM_OK - found and returned issuer cert
416 * CSSMERR_TP_CERTGROUP_INCOMPLETE - no URL in issuerAltName
417 * CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE - found and returned issuer
418 * cert, but signature verification needs subsequent retry.
419 * Anything else - gross error, typically from last LDAP/HTTP attempt
420 */
421 CSSM_RETURN tpFetchIssuerFromNet(
422 TPCertInfo &subject,
423 CSSM_CL_HANDLE clHand,
424 CSSM_CSP_HANDLE cspHand,
425 const char *verifyTime,
426 TPCertInfo *&issuer) // RETURNED
427 {
428 CSSM_OID_PTR fieldOid = NULL;
429 CSSM_DATA_PTR fieldValue = NULL; // mallocd by CL
430 CSSM_RETURN crtn;
431 bool hasAIA = false;
432
433 /* look for the Authority Info Access extension first */
434 fieldOid = (CSSM_OID_PTR)&CSSMOID_AuthorityInfoAccess;
435 crtn = subject.fetchField(fieldOid,
436 &fieldValue);
437 hasAIA = (crtn == CSSM_OK);
438 if (!hasAIA) {
439 /* fall back to Issuer Alternative Name extension */
440 fieldOid = (CSSM_OID_PTR)&CSSMOID_IssuerAltName;
441 crtn = subject.fetchField(fieldOid,
442 &fieldValue);
443 }
444 switch(crtn) {
445 case CSSM_OK:
446 break;
447 case CSSMERR_CL_NO_FIELD_VALUES:
448 /* field not present */
449 return CSSMERR_TP_CERTGROUP_INCOMPLETE;
450 default:
451 /* gross error */
452 return crtn;
453 }
454 if(fieldValue->Length != sizeof(CSSM_X509_EXTENSION)) {
455 tpPolicyError("tpFetchIssuerFromNet: malformed CSSM_FIELD");
456 return CSSMERR_TP_UNKNOWN_FORMAT;
457 }
458 CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)fieldValue->Data;
459 CE_GeneralNames *names = (CE_GeneralNames *)cssmExt->value.parsedValue;
460 TPCertInfo *rtnCert = NULL;
461 if (hasAIA) { /* authority info access */
462 CE_AuthorityInfoAccess *access = (CE_AuthorityInfoAccess *)cssmExt->value.parsedValue;
463 for (uint32 index = 0; access && index < access->numAccessDescriptions; index++) {
464 CE_AccessDescription *accessDesc = &access->accessDescriptions[index];
465 CSSM_OID_PTR methodOid = (CSSM_OID_PTR)&accessDesc->accessMethod;
466 /* look for the CA Issuers method */
467 if(methodOid->Data != NULL && methodOid->Length == CSSMOID_CA_ISSUERS.Length &&
468 !memcmp(methodOid->Data, CSSMOID_CA_ISSUERS.Data, methodOid->Length)) {
469 CE_GeneralNames aiaNames = { 1, &accessDesc->accessLocation };
470 /* attempt to fetch cert from named location */
471 crtn = tpFetchViaGeneralNames(&aiaNames,
472 subject,
473 NULL, // issuer - not used
474 NULL, // verifyContext
475 clHand,
476 cspHand,
477 verifyTime,
478 &rtnCert,
479 NULL);
480 if (crtn == CSSM_OK ||
481 crtn == CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE) {
482 break; // got one
483 }
484 }
485 }
486 subject.freeField(fieldOid, fieldValue);
487 }
488 else { /* issuer alt name */
489 crtn = tpFetchViaGeneralNames(names,
490 subject,
491 NULL, // issuer - not used
492 NULL, // verifyContext
493 clHand,
494 cspHand,
495 verifyTime,
496 &rtnCert,
497 NULL);
498 subject.freeField(fieldOid, fieldValue);
499 }
500 switch(crtn) {
501 case CSSM_OK:
502 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE:
503 issuer = rtnCert;
504 break;
505 default:
506 break;
507 }
508 return crtn;
509 }
510
511