2 * Copyright (c) 2002-2012 Apple Inc. All Rights Reserved.
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
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.
20 * TPNetwork.h - LDAP, HTTP and (eventually) other network tools
23 #include "TPNetwork.h"
24 #include "tpdebugging.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>
33 #define CA_ISSUERS_OID OID_PKIX, 0x30, 0x02
34 #define CA_ISSUERS_OID_LEN OID_PKIX_LENGTH + 2
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
};
44 static CSSM_RETURN
tpDecodeCert(
46 CSSM_DATA
&rtnBlob
) // will be reallocated if needed
48 const unsigned char *inbuf
= (const unsigned char *)rtnBlob
.Data
;
49 unsigned inlen
= (unsigned)rtnBlob
.Length
;
50 unsigned char *outbuf
= NULL
;
52 CSSM_RETURN ortn
= cuConvertPem(inbuf
, inlen
, &outbuf
, &outlen
);
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
);
58 memcpy(rtnP
, outbuf
, outlen
);
60 rtnBlob
.Length
= outlen
;
63 alloc
.free((void *)inbuf
);
68 static CSSM_RETURN
tpFetchViaNet(
70 const CSSM_DATA
*issuer
, // optional
72 CSSM_TIMESTRING verifyTime
, // CRL only
74 CSSM_DATA
&rtnBlob
) // mallocd and RETURNED
76 if(lfType
== LT_Crl
) {
77 return ocspdCRLFetch(alloc
, url
, issuer
,
78 true, true, // cache r/w both enable
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
);
91 static CSSM_RETURN
tpCrlViaNet(
93 const CSSM_DATA
*issuer
, // optional, only if cert and CRL have same issuer
94 TPVerifyContext
&vfyCtx
,
95 TPCertInfo
&forCert
, // for verifyWithContext
98 TPCrlInfo
*crl
= NULL
;
101 Allocator
&alloc
= Allocator::standard();
102 char cssmTime
[CSSM_TIME_STRLEN
+1];
106 /* verifyTime: we want a CRL that's valid right now. */
108 StLock
<Mutex
> _(tpTimeLock());
109 timeAtNowPlus(0, TP_TIME_CSSM
, cssmTime
);
112 crtn
= tpFetchViaNet(url
, issuer
, LT_Crl
, cssmTime
, alloc
, crlData
);
117 crl
= new TPCrlInfo(vfyCtx
.clHand
,
121 NULL
); // verifyTime = Now
124 alloc
.free(crlData
.Data
);
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
131 tpDebug(" bad CRL; flushing from cache and retrying");
133 crtn
= tpFetchViaNet(url
, issuer
, LT_Crl
, cssmTime
, alloc
, crlData
);
134 if(crtn
== CSSM_OK
) {
136 crl
= new TPCrlInfo(vfyCtx
.clHand
,
141 tpDebug(" RECOVERY: good CRL obtained from net");
144 alloc
.free(crlData
.Data
);
145 tpDebug(" bad CRL; recovery FAILED (1)");
146 return CSSMERR_APPLETP_CRL_NOT_FOUND
;
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
;
155 alloc
.free(crlData
.Data
);
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.
165 crtn
= crl
->verifyWithContextNow(vfyCtx
, &forCert
);
166 if(crtn
== CSSM_OK
) {
177 static CSSM_RETURN
tpIssuerCertViaNet(
178 const CSSM_DATA
&url
,
179 CSSM_CL_HANDLE clHand
,
180 CSSM_CSP_HANDLE cspHand
,
181 const char *verifyTime
,
183 TPCertInfo
*&rtnCert
)
185 TPCertInfo
*issuer
= NULL
;
188 Allocator
&alloc
= Allocator::standard();
190 crtn
= tpFetchViaNet(url
, NULL
, LT_Cert
, NULL
, alloc
, certData
);
192 tpErrorLog("tpIssuerCertViaNet: net fetch failed\n");
193 return CSSMERR_APPLETP_CERT_NOT_FOUND_FROM_ISSUER
;
196 issuer
= new TPCertInfo(clHand
,
203 tpErrorLog("tpIssuerCertViaNet: bad cert via net fetch\n");
204 alloc
.free(certData
.Data
);
206 return CSSMERR_APPLETP_BAD_CERT_FROM_ISSUER
;
208 alloc
.free(certData
.Data
);
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
;
216 /* yep, do a sig verify */
217 crtn
= subject
.verifyWithIssuer(issuer
);
219 tpErrorLog("tpIssuerCertViaNet: sig verify fail for cert via net "
221 crtn
= CSSMERR_APPLETP_BAD_CERT_FROM_ISSUER
;
225 assert(issuer
!= NULL
);
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.
238 static CSSM_RETURN
tpFetchViaGeneralNames(
239 const CE_GeneralNames
*names
,
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
,
250 assert(certInfo
|| crlInfo
);
251 assert(!certInfo
|| !crlInfo
);
254 for(unsigned nameDex
=0; nameDex
<names
->numNames
; nameDex
++) {
255 CE_GeneralName
*name
= &names
->generalName
[nameDex
];
256 switch(name
->nameType
) {
258 if(name
->name
.Length
< 5) {
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 */
268 tpDebug(" fetching cert via net");
269 crtn
= tpIssuerCertViaNet(name
->name
,
277 tpDebug(" fetching CRL via net");
278 assert(verifyContext
!= NULL
);
279 crtn
= tpCrlViaNet(name
->name
,
287 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE
: // caller handles
292 /* not found/no good; try again */
295 tpCrlDebug(" tpFetchCrlFromNet: unknown"
296 "nameType (%u)", (unsigned)name
->nameType
);
298 } /* switch nameType */
299 } /* for each name */
301 return CSSMERR_TP_CERTGROUP_INCOMPLETE
;
304 return CSSMERR_APPLETP_CRL_NOT_FOUND
;
309 * Fetch CRL(s) from specified cert if the cert has a cRlDistributionPoint
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
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?
321 CSSM_RETURN
tpFetchCrlFromNet(
323 TPVerifyContext
&vfyCtx
,
324 TPCrlInfo
*&crl
) // RETURNED
326 /* does the cert have a cRlDistributionPoint? */
327 CSSM_DATA_PTR fieldValue
; // mallocd by CL
329 CSSM_RETURN crtn
= cert
.fetchField(&CSSMOID_CrlDistributionPoints
,
334 case CSSMERR_CL_NO_FIELD_VALUES
:
335 /* field not present */
336 return CSSMERR_APPLETP_CRL_NOT_FOUND
;
341 if(fieldValue
->Length
!= sizeof(CSSM_X509_EXTENSION
)) {
342 tpErrorLog("tpFetchCrlFromNet: malformed CSSM_FIELD");
343 return CSSMERR_TP_UNKNOWN_FORMAT
;
345 CSSM_X509_EXTENSION
*cssmExt
= (CSSM_X509_EXTENSION
*)fieldValue
->Data
;
346 CE_CRLDistPointsSyntax
*dps
=
347 (CE_CRLDistPointsSyntax
*)cssmExt
->value
.parsedValue
;
348 TPCrlInfo
*rtnCrl
= NULL
;
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
) {
358 * FIXME if this uses an indirect CRL, we need to follow the
359 * crlIssuer field... TBD.
361 switch(dp
->distPointName
->nameType
) {
362 case CE_CDNT_NameRelativeToCrlIssuer
:
364 tpErrorLog("tpFetchCrlFromNet: "
365 "CE_CDNT_NameRelativeToCrlIssuer not implemented\n");
368 case CE_CDNT_FullName
:
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
375 CE_GeneralNames
*names
= dp
->distPointName
->dpn
.fullName
;
376 crtn
= tpFetchViaGeneralNames(names
,
380 0, // clHand, use the one in vfyCtx
386 } /* CE_CDNT_FullName */
390 tpErrorLog("tpFetchCrlFromNet: "
391 "unknown distPointName->nameType (%u)\n",
392 (unsigned)dp
->distPointName
->nameType
);
394 } /* switch distPointName->nameType */
395 if(crtn
== CSSM_OK
) {
396 /* i.e., tpFetchViaGeneralNames SUCCEEDED */
399 } /* for each distPoints */
401 cert
.freeField(&CSSMOID_CrlDistributionPoints
, fieldValue
);
402 if(crtn
== CSSM_OK
) {
403 assert(rtnCrl
!= NULL
);
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.
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
421 CSSM_RETURN
tpFetchIssuerFromNet(
423 CSSM_CL_HANDLE clHand
,
424 CSSM_CSP_HANDLE cspHand
,
425 const char *verifyTime
,
426 TPCertInfo
*&issuer
) // RETURNED
428 CSSM_OID_PTR fieldOid
= NULL
;
429 CSSM_DATA_PTR fieldValue
= NULL
; // mallocd by CL
433 /* look for the Authority Info Access extension first */
434 fieldOid
= (CSSM_OID_PTR
)&CSSMOID_AuthorityInfoAccess
;
435 crtn
= subject
.fetchField(fieldOid
,
437 hasAIA
= (crtn
== CSSM_OK
);
439 /* fall back to Issuer Alternative Name extension */
440 fieldOid
= (CSSM_OID_PTR
)&CSSMOID_IssuerAltName
;
441 crtn
= subject
.fetchField(fieldOid
,
447 case CSSMERR_CL_NO_FIELD_VALUES
:
448 /* field not present */
449 return CSSMERR_TP_CERTGROUP_INCOMPLETE
;
454 if(fieldValue
->Length
!= sizeof(CSSM_X509_EXTENSION
)) {
455 tpPolicyError("tpFetchIssuerFromNet: malformed CSSM_FIELD");
456 return CSSMERR_TP_UNKNOWN_FORMAT
;
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
,
473 NULL
, // issuer - not used
474 NULL
, // verifyContext
480 if (crtn
== CSSM_OK
||
481 crtn
== CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE
) {
486 subject
.freeField(fieldOid
, fieldValue
);
488 else { /* issuer alt name */
489 crtn
= tpFetchViaGeneralNames(names
,
491 NULL
, // issuer - not used
492 NULL
, // verifyContext
498 subject
.freeField(fieldOid
, fieldValue
);
502 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE
: