/* * Does this cert have a CrlDistributionPoints extension? We don't parse it, we * just tell the caller whether or not it has one. */ static bool tpCertHasCrlDistPt( TPCertInfo &cert) { CSSM_DATA_PTR fieldValue; CSSM_RETURN crtn = cert.fetchField(&CSSMOID_CrlDistributionPoints, &fieldValue); if(crtn) { return false; } else { cert.freeField(&CSSMOID_CrlDistributionPoints, fieldValue); return true; } }
/* * Get current CRL status for a certificate and its issuers. * * Possible results: * * CSSM_OK (we have a valid CRL; certificate is not revoked) * CSSMERR_TP_CERT_REVOKED (we have a valid CRL; certificate is revoked) * CSSMERR_APPLETP_NETWORK_FAILURE (CRL not available, download in progress) * CSSMERR_APPLETP_CRL_NOT_FOUND (CRL not available, and not being fetched) * CSSMERR_TP_INTERNAL_ERROR (unexpected error) * * Note that ocspdCRLStatus does NOT wait for the CRL to be downloaded before * returning, nor does it initiate a CRL download. */ static CSSM_RETURN tpGetCrlStatusForCert( TPCertInfo &subject, const CSSM_DATA &issuers) { CSSM_DATA *serialNumber=NULL; CSSM_RETURN crtn = subject.fetchField(&CSSMOID_X509V1SerialNumber, &serialNumber); if(crtn || !serialNumber) { return CSSMERR_TP_INTERNAL_ERROR; } crtn = ocspdCRLStatus(*serialNumber, issuers, subject.issuerName(), NULL); subject.freeField(&CSSMOID_X509V1SerialNumber, serialNumber); 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; }
/* * Returns true if we are to allow/trust the specified * cert as a PKINIT-only anchor. */ static bool tpCheckPkinitServerCert( TPCertGroup &certGroup) { /* * Basic requirement: exactly one cert, self-signed. * The numCerts == 1 requirement might change... */ unsigned numCerts = certGroup.numCerts(); if(numCerts != 1) { tpDebug("tpCheckPkinitServerCert: too many certs"); return false; } /* end of chain... */ TPCertInfo *theCert = certGroup.certAtIndex(numCerts - 1); if(!theCert->isSelfSigned()) { tpDebug("tpCheckPkinitServerCert: 1 cert, not self-signed"); return false; } const CSSM_DATA *subjectName = theCert->subjectName(); /* * Open the magic keychain. * We're going up and over the Sec layer here, not generally * kosher, but this is a temp hack. */ OSStatus ortn; SecKeychainRef kcRef = NULL; string fullPathName; const char *homeDir = getenv("HOME"); if (homeDir == NULL) { // If $HOME is unset get the current user's home directory // from the passwd file. uid_t uid = geteuid(); if (!uid) uid = getuid(); struct passwd *pw = getpwuid(uid); if (!pw) { return false; } homeDir = pw->pw_dir; } fullPathName = homeDir; fullPathName += "/Library/Application Support/PKINIT/TrustedServers.keychain"; ortn = SecKeychainOpen(fullPathName.c_str(), &kcRef); if(ortn) { tpDebug("tpCheckPkinitServerCert: keychain not found (1)"); return false; } /* subsequent errors to errOut: */ bool ourRtn = false; SecKeychainStatus kcStatus; CSSM_DATA_PTR subjSerial = NULL; CSSM_RETURN crtn; SecKeychainSearchRef srchRef = NULL; SecKeychainAttributeList attrList; SecKeychainAttribute attrs[2]; SecKeychainItemRef foundItem = NULL; ortn = SecKeychainGetStatus(kcRef, &kcStatus); if(ortn) { tpDebug("tpCheckPkinitServerCert: keychain not found (2)"); goto errOut; } /* * We already have this cert's normalized name; get its * serial number. */ crtn = theCert->fetchField(&CSSMOID_X509V1SerialNumber, &subjSerial); if(crtn) { /* should never happen */ tpDebug("tpCheckPkinitServerCert: error fetching serial number"); goto errOut; } attrs[0].tag = kSecSubjectItemAttr; attrs[0].length = subjectName->Length; attrs[0].data = subjectName->Data; attrs[1].tag = kSecSerialNumberItemAttr; attrs[1].length = subjSerial->Length; attrs[1].data = subjSerial->Data; attrList.count = 2; attrList.attr = attrs; ortn = SecKeychainSearchCreateFromAttributes(kcRef, kSecCertificateItemClass, &attrList, &srchRef); if(ortn) { tpDebug("tpCheckPkinitServerCert: search failure"); goto errOut; } for(;;) { ortn = SecKeychainSearchCopyNext(srchRef, &foundItem); if(ortn) { tpDebug("tpCheckPkinitServerCert: end search"); break; } /* found a matching cert; do byte-for-byte compare */ CSSM_DATA certData; ortn = SecCertificateGetData((SecCertificateRef)foundItem, &certData); if(ortn) { tpDebug("tpCheckPkinitServerCert: SecCertificateGetData failure"); continue; } if(tpCompareCssmData(&certData, theCert->itemData())){ tpDebug("tpCheckPkinitServerCert: FOUND CERT"); ourRtn = true; break; } tpDebug("tpCheckPkinitServerCert: skipping matching cert"); CFRelease(foundItem); foundItem = NULL; } errOut: CFRELEASE(kcRef); CFRELEASE(srchRef); CFRELEASE(foundItem); if(subjSerial != NULL) { theCert->freeField(&CSSMOID_X509V1SerialNumber, subjSerial); } return ourRtn; }
/* * 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; }