+++ /dev/null
-/*
- * Copyright (c) 2002-2012 Apple Inc. All Rights Reserved.
- *
- * The contents of this file constitute Original Code as defined in and are
- * subject to the Apple Public Source License Version 1.2 (the 'License').
- * You may not use this file except in compliance with the License. Please obtain
- * a copy of the License at http://www.apple.com/publicsource and read it before
- * using this file.
- *
- * This Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
- * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
- * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
- * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
- * specific language governing rights and limitations under the License.
- */
-
-
-/*
- * TPNetwork.h - LDAP, HTTP and (eventually) other network tools
- */
-
-#include "TPNetwork.h"
-#include "tpdebugging.h"
-#include "tpTime.h"
-#include "cuEnc64.h"
-#include <Security/cssmtype.h>
-#include <Security/cssmapple.h>
-#include <Security/oidscert.h>
-#include <security_utilities/logging.h>
-#include <security_ocspd/ocspdClient.h>
-
-#define CA_ISSUERS_OID OID_PKIX, 0x30, 0x02
-#define CA_ISSUERS_OID_LEN OID_PKIX_LENGTH + 2
-
-static const uint8 OID_CA_ISSUERS[] = {CA_ISSUERS_OID};
-const CSSM_OID CSSMOID_CA_ISSUERS = {CA_ISSUERS_OID_LEN, (uint8 *)OID_CA_ISSUERS};
-
-typedef enum {
- LT_Crl = 1,
- LT_Cert
-} LF_Type;
-
-static CSSM_RETURN tpDecodeCert(
- Allocator &alloc,
- CSSM_DATA &rtnBlob) // will be reallocated if needed
-{
- const unsigned char *inbuf = (const unsigned char *)rtnBlob.Data;
- unsigned inlen = (unsigned)rtnBlob.Length;
- unsigned char *outbuf = NULL;
- unsigned outlen = 0;
- CSSM_RETURN ortn = cuConvertPem(inbuf, inlen, &outbuf, &outlen);
-
- if(ortn == 0 && outbuf != NULL) {
- /* Decoded result needs to be malloc'd via input allocator */
- unsigned char *rtnP = (unsigned char *) alloc.malloc(outlen);
- if(rtnP != NULL) {
- memcpy(rtnP, outbuf, outlen);
- rtnBlob.Data = rtnP;
- rtnBlob.Length = outlen;
- }
- free(outbuf);
- alloc.free((void *)inbuf);
- }
- return ortn;
-}
-
-static CSSM_RETURN tpFetchViaNet(
- const CSSM_DATA &url,
- const CSSM_DATA *issuer, // optional
- LF_Type lfType,
- CSSM_TIMESTRING verifyTime, // CRL only
- Allocator &alloc,
- CSSM_DATA &rtnBlob) // mallocd and RETURNED
-{
- if(lfType == LT_Crl) {
- return ocspdCRLFetch(alloc, url, issuer,
- true, true, // cache r/w both enable
- verifyTime, rtnBlob);
- }
- else {
- CSSM_RETURN result = ocspdCertFetch(alloc, url, rtnBlob);
- if(result == CSSM_OK) {
- /* The data might be in PEM format; if so, convert it here */
- (void)tpDecodeCert(alloc, rtnBlob);
- }
- return result;
- }
-}
-
-static CSSM_RETURN tpCrlViaNet(
- const CSSM_DATA &url,
- const CSSM_DATA *issuer, // optional, only if cert and CRL have same issuer
- TPVerifyContext &vfyCtx,
- TPCertInfo &forCert, // for verifyWithContext
- TPCrlInfo *&rtnCrl)
-{
- TPCrlInfo *crl = NULL;
- CSSM_DATA crlData;
- CSSM_RETURN crtn;
- Allocator &alloc = Allocator::standard();
- char cssmTime[CSSM_TIME_STRLEN+1];
-
- rtnCrl = NULL;
-
- /* verifyTime: we want a CRL that's valid right now. */
- {
- StLock<Mutex> _(tpTimeLock());
- timeAtNowPlus(0, TIME_CSSM, cssmTime);
- }
-
- crtn = tpFetchViaNet(url, issuer, LT_Crl, cssmTime, alloc, crlData);
- if(crtn) {
- return crtn;
- }
- try {
- crl = new TPCrlInfo(vfyCtx.clHand,
- vfyCtx.cspHand,
- &crlData,
- TIC_CopyData,
- NULL); // verifyTime = Now
- }
- catch(...) {
- alloc.free(crlData.Data);
-
- /*
- * There is a slight possibility of recovering from this error. In case
- * the CRL came from disk cache, flush the cache and try to get the CRL
- * from the net.
- */
- tpDebug(" bad CRL; flushing from cache and retrying");
- ocspdCRLFlush(url);
- crtn = tpFetchViaNet(url, issuer, LT_Crl, cssmTime, alloc, crlData);
- if(crtn == CSSM_OK) {
- try {
- crl = new TPCrlInfo(vfyCtx.clHand,
- vfyCtx.cspHand,
- &crlData,
- TIC_CopyData,
- NULL);
- tpDebug(" RECOVERY: good CRL obtained from net");
- }
- catch(...) {
- alloc.free(crlData.Data);
- tpDebug(" bad CRL; recovery FAILED (1)");
- return CSSMERR_APPLETP_CRL_NOT_FOUND;
- }
- }
- else {
- /* it was in cache but we can't find it on the net */
- tpDebug(" bad CRL; recovery FAILED (2)");
- return CSSMERR_APPLETP_CRL_NOT_FOUND;
- }
- }
- alloc.free(crlData.Data);
-
- /*
- * Full CRL verify.
- * The verify time in the TPVerifyContext is the time at which various
- * entities (CRL and its own cert chain) are to be verified; that's
- * NULL for "right now". The current vfyCtx.verifyTime is the time at
- * which the cert's revocation status to be determined; this call to
- * verifyWithContextNow() doesn't do that.
- */
- crtn = crl->verifyWithContextNow(vfyCtx, &forCert);
- if(crtn == CSSM_OK) {
- crl->uri(url);
- }
- else {
- delete crl;
- crl = NULL;
- }
- rtnCrl = crl;
- return crtn;
-}
-
-static CSSM_RETURN tpIssuerCertViaNet(
- const CSSM_DATA &url,
- CSSM_CL_HANDLE clHand,
- CSSM_CSP_HANDLE cspHand,
- const char *verifyTime,
- TPCertInfo &subject,
- TPCertInfo *&rtnCert)
-{
- TPCertInfo *issuer = NULL;
- CSSM_DATA certData;
- CSSM_RETURN crtn;
- Allocator &alloc = Allocator::standard();
-
- crtn = tpFetchViaNet(url, NULL, LT_Cert, NULL, alloc, certData);
- if(crtn) {
- tpErrorLog("tpIssuerCertViaNet: net fetch failed\n");
- return CSSMERR_APPLETP_CERT_NOT_FOUND_FROM_ISSUER;
- }
- try {
- issuer = new TPCertInfo(clHand,
- cspHand,
- &certData,
- TIC_CopyData,
- verifyTime);
- }
- catch(...) {
- tpErrorLog("tpIssuerCertViaNet: bad cert via net fetch\n");
- alloc.free(certData.Data);
- rtnCert = NULL;
- return CSSMERR_APPLETP_BAD_CERT_FROM_ISSUER;
- }
- alloc.free(certData.Data);
-
- /* subject/issuer match? */
- if(!issuer->isIssuerOf(subject)) {
- tpErrorLog("tpIssuerCertViaNet: wrong issuer cert via net fetch\n");
- crtn = CSSMERR_APPLETP_BAD_CERT_FROM_ISSUER;
- }
- else {
- /* yep, do a sig verify */
- crtn = subject.verifyWithIssuer(issuer);
- if(crtn) {
- tpErrorLog("tpIssuerCertViaNet: sig verify fail for cert via net "
- "fetch\n");
- crtn = CSSMERR_APPLETP_BAD_CERT_FROM_ISSUER;
- }
- }
- if(crtn) {
- assert(issuer != NULL);
- delete issuer;
- issuer = NULL;
- }
- rtnCert = issuer;
- return crtn;
-}
-
-/*
- * Fetch a CRL or a cert via a GeneralNames.
- * Shared by cert and CRL code to avoid duplicating GeneralNames traversal
- * code, despite the awkward interface for this function.
- */
-static CSSM_RETURN tpFetchViaGeneralNames(
- const CE_GeneralNames *names,
- TPCertInfo &forCert,
- const CSSM_DATA *issuer, // optional, and only for CRLs
- TPVerifyContext *verifyContext, // only for CRLs
- CSSM_CL_HANDLE clHand, // only for certs
- CSSM_CSP_HANDLE cspHand, // only for certs
- const char *verifyTime, // optional
- /* exactly one must be non-NULL, that one is returned */
- TPCertInfo **certInfo,
- TPCrlInfo **crlInfo)
-{
- assert(certInfo || crlInfo);
- assert(!certInfo || !crlInfo);
- CSSM_RETURN crtn;
-
- for(unsigned nameDex=0; nameDex<names->numNames; nameDex++) {
- CE_GeneralName *name = &names->generalName[nameDex];
- switch(name->nameType) {
- case GNT_URI:
- if(name->name.Length < 5) {
- continue;
- }
- if(strncmp((char *)name->name.Data, "ldap:", 5) &&
- strncmp((char *)name->name.Data, "http:", 5) &&
- strncmp((char *)name->name.Data, "https:", 6)) {
- /* eventually handle other schemes here */
- continue;
- }
- if(certInfo) {
- tpDebug(" fetching cert via net");
- crtn = tpIssuerCertViaNet(name->name,
- clHand,
- cspHand,
- verifyTime,
- forCert,
- *certInfo);
- }
- else {
- tpDebug(" fetching CRL via net");
- assert(verifyContext != NULL);
- crtn = tpCrlViaNet(name->name,
- issuer,
- *verifyContext,
- forCert,
- *crlInfo);
- }
- switch(crtn) {
- case CSSM_OK:
- case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE: // caller handles
- return crtn;
- default:
- break;
- }
- /* not found/no good; try again */
- break;
- default:
- tpCrlDebug(" tpFetchCrlFromNet: unknown"
- "nameType (%u)", (unsigned)name->nameType);
- break;
- } /* switch nameType */
- } /* for each name */
- if(certInfo) {
- return CSSMERR_TP_CERTGROUP_INCOMPLETE;
- }
- else {
- return CSSMERR_APPLETP_CRL_NOT_FOUND;
- }
-}
-
-/*
- * Fetch CRL(s) from specified cert if the cert has a cRlDistributionPoint
- * extension.
- *
- * Return values:
- * CSSM_OK - found and returned fully verified CRL
- * CSSMERR_APPLETP_CRL_NOT_FOUND - no CRL in cRlDistributionPoint
- * Anything else - gross error, typically from last LDAP/HTTP attempt
- *
- * FIXME - this whole mechanism sort of falls apart if verifyContext.verifyTime
- * is non-NULL. How are we supposed to get the CRL which was valid at
- * a specified time in the past?
- */
-CSSM_RETURN tpFetchCrlFromNet(
- TPCertInfo &cert,
- TPVerifyContext &vfyCtx,
- TPCrlInfo *&crl) // RETURNED
-{
- /* does the cert have a cRlDistributionPoint? */
- CSSM_DATA_PTR fieldValue; // mallocd by CL
-
- CSSM_RETURN crtn = cert.fetchField(&CSSMOID_CrlDistributionPoints,
- &fieldValue);
- switch(crtn) {
- case CSSM_OK:
- break;
- case CSSMERR_CL_NO_FIELD_VALUES:
- /* field not present */
- return CSSMERR_APPLETP_CRL_NOT_FOUND;
- default:
- /* gross error */
- return crtn;
- }
- if(fieldValue->Length != sizeof(CSSM_X509_EXTENSION)) {
- tpErrorLog("tpFetchCrlFromNet: malformed CSSM_FIELD");
- return CSSMERR_TP_UNKNOWN_FORMAT;
- }
- CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)fieldValue->Data;
- CE_CRLDistPointsSyntax *dps =
- (CE_CRLDistPointsSyntax *)cssmExt->value.parsedValue;
- TPCrlInfo *rtnCrl = NULL;
-
- /* default return if we don't find anything */
- crtn = CSSMERR_APPLETP_CRL_NOT_FOUND;
- for(unsigned dex=0; dex<dps->numDistPoints; dex++) {
- CE_CRLDistributionPoint *dp = &dps->distPoints[dex];
- if(dp->distPointName == NULL) {
- continue;
- }
- /*
- * FIXME if this uses an indirect CRL, we need to follow the
- * crlIssuer field... TBD.
- */
- switch(dp->distPointName->nameType) {
- case CE_CDNT_NameRelativeToCrlIssuer:
- /* not yet */
- tpErrorLog("tpFetchCrlFromNet: "
- "CE_CDNT_NameRelativeToCrlIssuer not implemented\n");
- break;
-
- case CE_CDNT_FullName:
- {
- /*
- * Since we don't support indirect CRLs (yet), we always pass
- * the cert-to-be-verified's issuer as the CRL issuer for
- * cache lookup.
- */
- CE_GeneralNames *names = dp->distPointName->dpn.fullName;
- crtn = tpFetchViaGeneralNames(names,
- cert,
- cert.issuerName(),
- &vfyCtx,
- 0, // clHand, use the one in vfyCtx
- 0, // cspHand, ditto
- vfyCtx.verifyTime,
- NULL,
- &rtnCrl);
- break;
- } /* CE_CDNT_FullName */
-
- default:
- /* not yet */
- tpErrorLog("tpFetchCrlFromNet: "
- "unknown distPointName->nameType (%u)\n",
- (unsigned)dp->distPointName->nameType);
- break;
- } /* switch distPointName->nameType */
- if(crtn == CSSM_OK) {
- /* i.e., tpFetchViaGeneralNames SUCCEEDED */
- break;
- }
- } /* for each distPoints */
-
- cert.freeField(&CSSMOID_CrlDistributionPoints, fieldValue);
- if(crtn == CSSM_OK) {
- assert(rtnCrl != NULL);
- crl = rtnCrl;
- }
- return crtn;
-}
-
-/*
- * Fetch issuer cert of specified cert if the cert has an issuerAltName
- * with a URI. If non-NULL cert is returned, it has passed subject/issuer
- * name comparison and signature verification with target cert.
- *
- * Return values:
- * CSSM_OK - found and returned issuer cert
- * CSSMERR_TP_CERTGROUP_INCOMPLETE - no URL in issuerAltName
- * CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE - found and returned issuer
- * cert, but signature verification needs subsequent retry.
- * Anything else - gross error, typically from last LDAP/HTTP attempt
- */
-CSSM_RETURN tpFetchIssuerFromNet(
- TPCertInfo &subject,
- CSSM_CL_HANDLE clHand,
- CSSM_CSP_HANDLE cspHand,
- const char *verifyTime,
- TPCertInfo *&issuer) // RETURNED
-{
- CSSM_OID_PTR fieldOid = NULL;
- CSSM_DATA_PTR fieldValue = NULL; // mallocd by CL
- CSSM_RETURN crtn;
- bool hasAIA = false;
-
- /* look for the Authority Info Access extension first */
- fieldOid = (CSSM_OID_PTR)&CSSMOID_AuthorityInfoAccess;
- crtn = subject.fetchField(fieldOid,
- &fieldValue);
- hasAIA = (crtn == CSSM_OK);
- if (!hasAIA) {
- /* fall back to Issuer Alternative Name extension */
- fieldOid = (CSSM_OID_PTR)&CSSMOID_IssuerAltName;
- crtn = subject.fetchField(fieldOid,
- &fieldValue);
- }
- switch(crtn) {
- case CSSM_OK:
- break;
- case CSSMERR_CL_NO_FIELD_VALUES:
- /* field not present */
- return CSSMERR_TP_CERTGROUP_INCOMPLETE;
- default:
- /* gross error */
- return crtn;
- }
- if(fieldValue->Length != sizeof(CSSM_X509_EXTENSION)) {
- tpPolicyError("tpFetchIssuerFromNet: malformed CSSM_FIELD");
- return CSSMERR_TP_UNKNOWN_FORMAT;
- }
- CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)fieldValue->Data;
- CE_GeneralNames *names = (CE_GeneralNames *)cssmExt->value.parsedValue;
- TPCertInfo *rtnCert = NULL;
- if (hasAIA) { /* authority info access */
- CE_AuthorityInfoAccess *access = (CE_AuthorityInfoAccess *)cssmExt->value.parsedValue;
- for (uint32 index = 0; access && index < access->numAccessDescriptions; index++) {
- CE_AccessDescription *accessDesc = &access->accessDescriptions[index];
- CSSM_OID_PTR methodOid = (CSSM_OID_PTR)&accessDesc->accessMethod;
- /* look for the CA Issuers method */
- if(methodOid->Data != NULL && methodOid->Length == CSSMOID_CA_ISSUERS.Length &&
- !memcmp(methodOid->Data, CSSMOID_CA_ISSUERS.Data, methodOid->Length)) {
- CE_GeneralNames aiaNames = { 1, &accessDesc->accessLocation };
- /* attempt to fetch cert from named location */
- crtn = tpFetchViaGeneralNames(&aiaNames,
- subject,
- NULL, // issuer - not used
- NULL, // verifyContext
- clHand,
- cspHand,
- verifyTime,
- &rtnCert,
- NULL);
- if (crtn == CSSM_OK ||
- crtn == CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE) {
- break; // got one
- }
- }
- }
- subject.freeField(fieldOid, fieldValue);
- }
- else { /* issuer alt name */
- crtn = tpFetchViaGeneralNames(names,
- subject,
- NULL, // issuer - not used
- NULL, // verifyContext
- clHand,
- cspHand,
- verifyTime,
- &rtnCert,
- NULL);
- subject.freeField(fieldOid, fieldValue);
- }
- switch(crtn) {
- case CSSM_OK:
- case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE:
- issuer = rtnCert;
- break;
- default:
- break;
- }
- return crtn;
-}
-
-