/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* * Find the issuerName in a DER encoded certificate
*/ const SEC_ASN1Template SEC_CertIssuerTemplate[] = {
{ SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECItem) },
{ SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
0, SEC_ASN1_SUB(SEC_SkipTemplate) }, /* version */
{ SEC_ASN1_SKIP }, /* serial number */
{ SEC_ASN1_SKIP }, /* signature algorithm */
{ SEC_ASN1_ANY, 0, NULL }, /* issuer */
{ SEC_ASN1_SKIP_REST },
{ 0 }
}; /* * Find the subjectName in a DER encoded certificate
*/ const SEC_ASN1Template SEC_CertSerialNumberTemplate[] = {
{ SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECItem) },
{ SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
0, SEC_ASN1_SUB(SEC_SkipTemplate) }, /* version */
{ SEC_ASN1_ANY, 0, NULL }, /* serial number */
{ SEC_ASN1_SKIP_REST },
{ 0 }
};
/* * Find the issuer and serialNumber in a DER encoded certificate. * This data is used as the database lookup key since its the unique * identifier of a certificate.
*/ const SEC_ASN1Template CERT_CertKeyTemplate[] = {
{ SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTCertKey) },
{ SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
0, SEC_ASN1_SUB(SEC_SkipTemplate) }, /* version */
{ SEC_ASN1_INTEGER, offsetof(CERTCertKey, serialNumber) },
{ SEC_ASN1_SKIP }, /* signature algorithm */
{ SEC_ASN1_ANY, offsetof(CERTCertKey, derIssuer) },
{ SEC_ASN1_SKIP_REST },
{ 0 }
};
/* copy the serialNumber */
PORT_Memcpy(key->data, sn->data, sn->len);
/* copy the issuer */
PORT_Memcpy(&key->data[sn->len], issuer->data, issuer->len);
return (SECSuccess);
loser: return (SECFailure);
}
/* * Extract the subject name from a DER certificate
*/
SECStatus
CERT_NameFromDERCert(SECItem *derCert, SECItem *derName)
{ int rv;
PLArenaPool *arena;
CERTSignedData sd; void *tmpptr;
/* * Generate a database key, based on serial number and issuer, from a * DER certificate.
*/
SECStatus
CERT_KeyFromDERCert(PLArenaPool *reqArena, SECItem *derCert, SECItem *key)
{ int rv;
CERTSignedData sd;
CERTCertKey certkey;
if (!reqArena) {
PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure;
}
/* * fill in keyUsage field of the cert based on the cert extension * if the extension is not critical, then we allow all uses
*/ static SECStatus
GetKeyUsage(CERTCertificate *cert)
{
SECStatus rv;
SECItem tmpitem;
rv = CERT_FindKeyUsageExtension(cert, &tmpitem); if (rv == SECSuccess) { /* remember the actual value of the extension */
cert->rawKeyUsage = tmpitem.len ? tmpitem.data[0] : 0;
cert->keyUsagePresent = PR_TRUE;
cert->keyUsage = cert->rawKeyUsage;
PORT_Free(tmpitem.data);
tmpitem.data = NULL;
} else { /* if the extension is not present, then we allow all uses */
cert->keyUsage = KU_ALL;
cert->rawKeyUsage = KU_ALL;
cert->keyUsagePresent = PR_FALSE;
}
if (CERT_GovtApprovedBitSet(cert)) {
cert->keyUsage |= KU_NS_GOVT_APPROVED;
cert->rawKeyUsage |= KU_NS_GOVT_APPROVED;
}
if (seq != NULL) {
oids = seq->oids; while (oids != NULL && *oids != NULL) {
oid = *oids; if (SECOID_FindOIDTag(oid) == tagnum) {
rv = SECSuccess; break;
}
oids++;
}
} return rv;
}
/* * fill in nsCertType field of the cert based on the cert extension
*/
SECStatus
cert_GetCertType(CERTCertificate *cert)
{
PRUint32 nsCertType;
if (cert->nsCertType) { /* once set, no need to recalculate */ return SECSuccess;
}
nsCertType = cert_ComputeCertType(cert);
/* Assert that it is safe to cast &cert->nsCertType to "PRInt32 *" */
PORT_Assert(sizeof(cert->nsCertType) == sizeof(PRInt32));
PR_ATOMIC_SET((PRInt32 *)&cert->nsCertType, nsCertType); return SECSuccess;
}
PRBool
cert_IsIPsecOID(CERTOidSequence *extKeyUsage)
{ if (findOIDinOIDSeqByTagNum(
extKeyUsage, SEC_OID_EXT_KEY_USAGE_IPSEC_IKE) == SECSuccess) { return PR_TRUE;
} if (findOIDinOIDSeqByTagNum(
extKeyUsage, SEC_OID_IPSEC_IKE_END) == SECSuccess) { return PR_TRUE;
} if (findOIDinOIDSeqByTagNum(
extKeyUsage, SEC_OID_IPSEC_IKE_INTERMEDIATE) == SECSuccess) { return PR_TRUE;
} /* these are now deprecated, but may show up. Treat them the same as IKE */ if (findOIDinOIDSeqByTagNum(
extKeyUsage, SEC_OID_EXT_KEY_USAGE_IPSEC_END) == SECSuccess) { return PR_TRUE;
} if (findOIDinOIDSeqByTagNum(
extKeyUsage, SEC_OID_EXT_KEY_USAGE_IPSEC_TUNNEL) == SECSuccess) { return PR_TRUE;
} if (findOIDinOIDSeqByTagNum(
extKeyUsage, SEC_OID_EXT_KEY_USAGE_IPSEC_USER) == SECSuccess) { return PR_TRUE;
} /* this one should probably be in cert_ComputeCertType and set all usages? */ if (findOIDinOIDSeqByTagNum(
extKeyUsage, SEC_OID_X509_ANY_EXT_KEY_USAGE) == SECSuccess) { return PR_TRUE;
} return PR_FALSE;
}
/* free tmpitem data pointer to avoid memory leak */
PORT_Free(tmpitem.data);
tmpitem.data = NULL;
/* * for this release, we will allow SSL certs with an email address * to be used for email
*/ if ((nsCertType & NS_CERT_TYPE_SSL_CLIENT) && cert->emailAddr &&
cert->emailAddr[0]) {
nsCertType |= NS_CERT_TYPE_EMAIL;
} /* * for this release, we will allow SSL intermediate CAs to be * email intermediate CAs too.
*/ if (nsCertType & NS_CERT_TYPE_SSL_CA) {
nsCertType |= NS_CERT_TYPE_EMAIL_CA;
} /* * allow a cert with the extended key usage of EMail Protect * to be used for email or as an email CA, if basic constraints * indicates that it is a CA.
*/ if (findOIDinOIDSeqByTagNum(extKeyUsage,
SEC_OID_EXT_KEY_USAGE_EMAIL_PROTECT) ==
SECSuccess) {
nsCertType |= isCA ? NS_CERT_TYPE_EMAIL_CA : NS_CERT_TYPE_EMAIL;
} if (findOIDinOIDSeqByTagNum(
extKeyUsage, SEC_OID_EXT_KEY_USAGE_SERVER_AUTH) == SECSuccess) {
nsCertType |= isCA ? NS_CERT_TYPE_SSL_CA : NS_CERT_TYPE_SSL_SERVER;
} /* * Treat certs with step-up OID as also having SSL server type. * COMODO needs this behaviour until June 2020. See Bug 737802.
*/ if (findOIDinOIDSeqByTagNum(extKeyUsage,
SEC_OID_NS_KEY_USAGE_GOVT_APPROVED) ==
SECSuccess) {
nsCertType |= isCA ? NS_CERT_TYPE_SSL_CA : NS_CERT_TYPE_SSL_SERVER;
} if (findOIDinOIDSeqByTagNum(
extKeyUsage, SEC_OID_EXT_KEY_USAGE_CLIENT_AUTH) == SECSuccess) {
nsCertType |= isCA ? NS_CERT_TYPE_SSL_CA : NS_CERT_TYPE_SSL_CLIENT;
} if (cert_IsIPsecOID(extKeyUsage)) {
nsCertType |= isCA ? NS_CERT_TYPE_IPSEC_CA : NS_CERT_TYPE_IPSEC;
} if (findOIDinOIDSeqByTagNum(
extKeyUsage, SEC_OID_EXT_KEY_USAGE_CODE_SIGN) == SECSuccess) {
nsCertType |= isCA ? NS_CERT_TYPE_OBJECT_SIGNING_CA : NS_CERT_TYPE_OBJECT_SIGNING;
} if (findOIDinOIDSeqByTagNum(
extKeyUsage, SEC_OID_EXT_KEY_USAGE_TIME_STAMP) == SECSuccess) {
nsCertType |= EXT_KEY_USAGE_TIME_STAMP;
} if (findOIDinOIDSeqByTagNum(extKeyUsage, SEC_OID_OCSP_RESPONDER) ==
SECSuccess) {
nsCertType |= EXT_KEY_USAGE_STATUS_RESPONDER;
}
} else { /* If no NS Cert Type extension and no EKU extension, then */
nsCertType = 0; if (CERT_IsCACert(cert, &nsCertType))
nsCertType |= EXT_KEY_USAGE_STATUS_RESPONDER; /* if the basic constraint extension says the cert is a CA, then
allow SSL CA and EMAIL CA and Status Responder */ if (isCA) {
nsCertType |= (NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA |
EXT_KEY_USAGE_STATUS_RESPONDER);
} /* allow any ssl or email (no ca or object signing. */
nsCertType |= NS_CERT_TYPE_SSL_CLIENT | NS_CERT_TYPE_SSL_SERVER |
NS_CERT_TYPE_EMAIL;
}
/* IPSEC is allowed to use SSL client and server certs as well as email certs */ if (nsCertType & (NS_CERT_TYPE_SSL_CLIENT | NS_CERT_TYPE_SSL_SERVER | NS_CERT_TYPE_EMAIL)) {
nsCertType |= NS_CERT_TYPE_IPSEC;
} if (nsCertType & (NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA)) {
nsCertType |= NS_CERT_TYPE_IPSEC_CA;
}
if (encodedExtKeyUsage.data != NULL) {
PORT_Free(encodedExtKeyUsage.data);
} if (extKeyUsage != NULL) {
CERT_DestroyOidSequence(extKeyUsage);
} return nsCertType;
}
/* * cert_GetKeyID() - extract or generate the subjectKeyID from a certificate
*/
SECStatus
cert_GetKeyID(CERTCertificate *cert)
{
SECItem tmpitem;
SECStatus rv;
cert->subjectKeyID.len = 0;
/* see of the cert has a key identifier extension */
rv = CERT_FindSubjectKeyIDExtension(cert, &tmpitem); if (rv == SECSuccess) {
cert->subjectKeyID.data =
(unsignedchar *)PORT_ArenaAlloc(cert->arena, tmpitem.len); if (cert->subjectKeyID.data != NULL) {
PORT_Memcpy(cert->subjectKeyID.data, tmpitem.data, tmpitem.len);
cert->subjectKeyID.len = tmpitem.len;
cert->keyIDGenerated = PR_FALSE;
}
PORT_Free(tmpitem.data);
}
/* if the cert doesn't have a key identifier extension, then generate one*/ if (cert->subjectKeyID.len == 0) { /* * pkix says that if the subjectKeyID is not present, then we should * use the SHA-1 hash of the DER-encoded publicKeyInfo from the cert
*/
cert->subjectKeyID.data =
(unsignedchar *)PORT_ArenaAlloc(cert->arena, SHA1_LENGTH); if (cert->subjectKeyID.data != NULL) {
rv = PK11_HashBuf(SEC_OID_SHA1, cert->subjectKeyID.data,
cert->derPublicKey.data, cert->derPublicKey.len); if (rv == SECSuccess) {
cert->subjectKeyID.len = SHA1_LENGTH;
}
}
}
/* cache the authKeyID extension, if present */
cert->authKeyID = CERT_FindAuthKeyIDExten(cert->arena, cert);
/* it MUST be self-issued to be a root */ if (cert->derIssuer.len == 0 ||
!SECITEM_ItemsAreEqual(&cert->derIssuer, &cert->derSubject)) { return PR_FALSE;
}
/* check the authKeyID extension */ if (cert->authKeyID) { /* authority key identifier is present */ if (cert->authKeyID->keyID.len > 0) { /* the keyIdentifier field is set, look for subjectKeyID */
rv = CERT_FindSubjectKeyIDExtension(cert, &tmpitem); if (rv == SECSuccess) {
PRBool match; /* also present, they MUST match for it to be a root */
match =
SECITEM_ItemsAreEqual(&cert->authKeyID->keyID, &tmpitem);
PORT_Free(tmpitem.data); if (!match) return PR_FALSE; /* else fall through */
} else { /* the subject key ID is required when AKI is present */ return PR_FALSE;
}
} if (cert->authKeyID->authCertIssuer) {
SECItem *caName;
caName = (SECItem *)CERT_GetGeneralNameByType(
cert->authKeyID->authCertIssuer, certDirectoryName, PR_TRUE); if (caName) { if (!SECITEM_ItemsAreEqual(&cert->derIssuer, caName)) { return PR_FALSE;
} /* else fall through */
} /* else ??? could not get general name as directory name? */
} if (cert->authKeyID->authCertSerialNumber.len > 0) { if (!SECITEM_ItemsAreEqual(
&cert->serialNumber,
&cert->authKeyID->authCertSerialNumber)) { return PR_FALSE;
} /* else fall through */
} /* all of the AKI fields that were present passed the test */ return PR_TRUE;
} /* else the AKI was not present, so this is a root */ return PR_TRUE;
}
/* * take a DER certificate and decode it into a certificate structure
*/
CERTCertificate *
CERT_DecodeDERCertificate(SECItem *derSignedCert, PRBool copyDER, char *nickname)
{
CERTCertificate *cert;
PLArenaPool *arena; void *data; int rv; int len; char *tmpname;
/* make a new arena */
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (copyDER) { /* copy the DER data for the cert into this arena */
data = (void *)PORT_ArenaAlloc(arena, derSignedCert->len); if (!data) { goto loser;
}
cert->derCert.data = (unsignedchar *)data;
cert->derCert.len = derSignedCert->len;
PORT_Memcpy(data, derSignedCert->data, derSignedCert->len);
} else { /* point to passed in DER data */
cert->derCert = *derSignedCert;
}
/* decode the certificate info */
rv = SEC_QuickDERDecodeItem(arena, cert, SEC_SignedCertificateTemplate,
&cert->derCert);
if (rv) { goto loser;
}
if (cert_HasUnknownCriticalExten(cert->extensions) == PR_TRUE) {
cert->options.bits.hasUnsupportedCriticalExt = PR_TRUE;
}
/* generate and save the database key for the cert */
rv = CERT_KeyFromIssuerAndSN(arena, &cert->derIssuer, &cert->serialNumber,
&cert->certKey); if (rv) { goto loser;
}
/* set the nickname */ if (nickname == NULL) {
cert->nickname = NULL;
} else { /* copy and install the nickname */
len = PORT_Strlen(nickname) + 1;
cert->nickname = (char *)PORT_ArenaAlloc(arena, len); if (cert->nickname == NULL) { goto loser;
}
PORT_Memcpy(cert->nickname, nickname, len);
}
/* set the email address */
cert->emailAddr = cert_GetCertificateEmailAddresses(cert);
/* initialize the subjectKeyID */
rv = cert_GetKeyID(cert); if (rv != SECSuccess) { goto loser;
}
void
CERT_DestroyValidity(CERTValidity *v)
{ if (v && v->arena) {
PORT_FreeArena(v->arena, PR_FALSE);
} return;
}
/* ** Amount of time that a certifiate is allowed good before it is actually ** good. This is used for pending certificates, ones that are about to be ** valid. The slop is designed to allow for some variance in the clocks ** of the machine checking the certificate.
*/ #define PENDING_SLOP (24L * 60L * 60L) /* seconds per day */ static PRInt32 pendingSlop = PENDING_SLOP; /* seconds */
/* convert DER not-before time */
rv = DER_DecodeTimeChoice(notBefore, &c->validity.notBefore); if (rv) { return (SECFailure);
}
/* convert DER not-after time */
rv = DER_DecodeTimeChoice(notAfter, &c->validity.notAfter); if (rv) { return (SECFailure);
}
return (SECSuccess);
}
/* * Check the validity times of a certificate
*/
SECCertTimeValidity
CERT_CheckCertValidTimes(const CERTCertificate *c, PRTime t,
PRBool allowOverride)
{
PRTime notBefore, notAfter, llPendingSlop, tmp1;
SECStatus rv;
if (!c) {
PORT_SetError(SEC_ERROR_INVALID_ARGS); return (secCertTimeUndetermined);
} /* if cert is already marked OK, then don't bother to check */ if (allowOverride && c->timeOK) { return (secCertTimeValid);
}
rv = CERT_GetCertTimes(c, ¬Before, ¬After);
if (rv) { return (secCertTimeExpired); /*XXX is this the right thing to do here?*/
}
SECStatus
SEC_GetCrlTimes(CERTCrl *date, PRTime *notBefore, PRTime *notAfter)
{ int rv;
/* convert DER not-before time */
rv = DER_DecodeTimeChoice(notBefore, &date->lastUpdate); if (rv) { return (SECFailure);
}
/* convert DER not-after time */ if (date->nextUpdate.data) {
rv = DER_DecodeTimeChoice(notAfter, &date->nextUpdate); if (rv) { return (SECFailure);
}
} else {
LL_I2L(*notAfter, 0L);
} return (SECSuccess);
}
/* These routines should probably be combined with the cert * routines using an common extraction routine.
*/
SECCertTimeValidity
SEC_CheckCrlTimes(CERTCrl *crl, PRTime t)
{
PRTime notBefore, notAfter, llPendingSlop, tmp1;
SECStatus rv;
if (!crl) {
PORT_SetError(SEC_ERROR_INVALID_ARGS); return (secCertTimeUndetermined);
}
/* If next update is omitted and the test for notBefore passes, then we assume that the crl is up to date.
*/ if (LL_IS_ZERO(notAfter)) { return (secCertTimeValid);
}
if (LL_CMP(t, >, notAfter)) {
PORT_SetError(SEC_ERROR_CRL_EXPIRED); return (secCertTimeExpired);
}
/* * check the key usage of a cert against a set of required values
*/
SECStatus
CERT_CheckKeyUsage(CERTCertificate *cert, unsignedint requiredUsage)
{ if (!cert) {
PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure;
} /* choose between key agreement or key encipherment based on key * type in cert
*/ if (requiredUsage & KU_KEY_AGREEMENT_OR_ENCIPHERMENT) {
KeyType keyType = CERT_GetCertKeyType(&cert->subjectPublicKeyInfo); /* turn off the special bit */
requiredUsage &= (~KU_KEY_AGREEMENT_OR_ENCIPHERMENT);
switch (keyType) { case rsaKey:
requiredUsage |= KU_KEY_ENCIPHERMENT; break; case rsaPssKey: case dsaKey:
requiredUsage |= KU_DIGITAL_SIGNATURE; break; case dhKey:
requiredUsage |= KU_KEY_AGREEMENT; break; case ecKey: /* Accept either signature or agreement. */ if (!(cert->keyUsage &
(KU_DIGITAL_SIGNATURE | KU_KEY_AGREEMENT))) goto loser; break; default: goto loser;
}
}
/* Allow either digital signature or non-repudiation */ if (requiredUsage & KU_DIGITAL_SIGNATURE_OR_NON_REPUDIATION) { /* turn off the special bit */
requiredUsage &= (~KU_DIGITAL_SIGNATURE_OR_NON_REPUDIATION);
if (!(cert->keyUsage & (KU_DIGITAL_SIGNATURE | KU_NON_REPUDIATION))) goto loser;
}
if ((cert->keyUsage & requiredUsage) == requiredUsage) return SECSuccess;
/* * Allow use of default cert database, so that apps(such as mozilla) don't * have to pass the handle all over the place.
*/ static CERTCertDBHandle *default_cert_db_handle = 0;
/* ** Add a domain name to the list of names that the user has explicitly ** allowed (despite cert name mismatches) for use with a server cert.
*/
SECStatus
CERT_AddOKDomainName(CERTCertificate *cert, constchar *hn)
{
CERTOKDomainName *domainOK; int newNameLen;
if (!hn || !(newNameLen = strlen(hn))) {
PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure;
}
domainOK = (CERTOKDomainName *)PORT_ArenaZAlloc(cert->arena, sizeof(*domainOK)); if (!domainOK) { return SECFailure; /* error code is already set. */
}
domainOK->name = (char *)PORT_ArenaZAlloc(cert->arena, newNameLen + 1); if (!domainOK->name) { return SECFailure; /* error code is already set. */
}
/* put at head of list. */
domainOK->next = cert->domainOK;
cert->domainOK = domainOK; return SECSuccess;
}
/* returns SECSuccess if hn matches pattern cn, ** returns SECFailure with SSL_ERROR_BAD_CERT_DOMAIN if no match, ** returns SECFailure with some other error code if another error occurs. ** ** This function may modify string cn, so caller must pass a modifiable copy.
*/ static SECStatus
cert_TestHostName(char *cn, constchar *hn)
{ staticint useShellExp = -1;
if (useShellExp < 0) {
useShellExp = (NULL != PR_GetEnvSecure("NSS_USE_SHEXP_IN_CERT_NAME"));
} if (useShellExp) { /* Backward compatible code, uses Shell Expressions (SHEXP). */ int regvalid = PORT_RegExpValid(cn); if (regvalid != NON_SXP) {
SECStatus rv; /* cn is a regular expression, try to match the shexp */ int match = PORT_RegExpCaseSearch(hn, cn);
/* For a cn pattern to be considered valid, the wildcard character... * - may occur only in a DNS name with at least 3 components, and * - may occur only as last character in the first component, and * - may be preceded by additional characters, and * - must not be preceded by an IDNA ACE prefix (xn--)
*/ if (wildcard && secondcndot && secondcndot[1] && firsthndot &&
firstcndot - wildcard == 1 /* wildcard is last char in first component */
&& secondcndot - firstcndot > 1 /* second component is non-empty */
&& PORT_Strrchr(cn, '*') == wildcard /* only one wildcard in cn */
&& !PORT_Strncasecmp(cn, hn, wildcard - cn) &&
!PORT_Strcasecmp(firstcndot, firsthndot) /* If hn starts with xn--, then cn must start with wildcard */
&& (PORT_Strncasecmp(hn, "xn--", 4) || wildcard == cn)) { /* valid wildcard pattern match */ return SECSuccess;
}
} /* String cn has no wildcard or shell expression. * Compare entire string hn with cert name.
*/ if (PORT_Strcasecmp(hn, cn) == 0) { return SECSuccess;
}
nameList = current = CERT_DecodeAltNameExtension(arena, &subAltName); if (!current) goto fail;
do { switch (current->type) { case certDNSName: if (!isIPaddr) { /* DNS name current->name.other.data is not null terminated. ** so must copy it.
*/ int cnLen = current->name.other.len;
rv = CERT_RFC1485_EscapeAndQuote(
cn, cnBufLen, (char *)current->name.other.data, cnLen); if (rv != SECSuccess &&
PORT_GetError() == SEC_ERROR_OUTPUT_LEN) {
cnBufLen =
cnLen * 3 + 3; /* big enough for worst case */
cn = (char *)PORT_ArenaAlloc(arena, cnBufLen); if (!cn) goto fail;
rv = CERT_RFC1485_EscapeAndQuote(
cn, cnBufLen, (char *)current->name.other.data,
cnLen);
} if (rv == SECSuccess)
rv = cert_TestHostName(cn, hn); if (rv == SECSuccess) goto finish;
}
DNSextCount++; break; case certIPAddress: if (isIPaddr) { int match = 0;
PRIPv6Addr v6Addr; if (current->name.other.len == 4 && /* IP v4 address */
netAddr.inet.family == PR_AF_INET) {
match = !memcmp(&netAddr.inet.ip,
current->name.other.data, 4);
} elseif (current->name.other.len ==
16 && /* IP v6 address */
netAddr.ipv6.family == PR_AF_INET6) {
match = !memcmp(&netAddr.ipv6.ip,
current->name.other.data, 16);
} elseif (current->name.other.len ==
16 && /* IP v6 address */
netAddr.inet.family == PR_AF_INET) { /* convert netAddr to ipv6, then compare. */ /* ipv4 must be in Network Byte Order on input. */
PR_ConvertIPv4AddrToIPv6(netAddr.inet.ip, &v6Addr);
match = !memcmp(&v6Addr, current->name.other.data, 16);
} elseif (current->name.other.len == 4 && /* IP v4 address */
netAddr.inet.family == PR_AF_INET6) { /* convert netAddr to ipv6, then compare. */
PRUint32 ipv4 = (current->name.other.data[0] << 24) |
(current->name.other.data[1] << 16) |
(current->name.other.data[2] << 8) |
current->name.other.data[3]; /* ipv4 must be in Network Byte Order on input. */
PR_ConvertIPv4AddrToIPv6(PR_htonl(ipv4), &v6Addr);
match = !memcmp(&netAddr.ipv6.ip, &v6Addr, 16);
} if (match) {
rv = SECSuccess; goto finish;
}
}
IPextCount++; break; default: break;
}
current = CERT_GetNextGeneralName(current);
} while (current != nameList);
fail:
if (!(isIPaddr ? IPextCount : DNSextCount)) { /* no relevant value in the extension was found. */
PORT_SetError(SEC_ERROR_EXTENSION_NOT_FOUND);
} else {
PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
}
rv = SECFailure;
finish:
/* Don't free nameList, it's part of the arena. */ if (arena) {
PORT_FreeArena(arena, PR_FALSE);
}
if (subAltName.data) {
SECITEM_FreeItem(&subAltName, PR_FALSE);
}
return rv;
}
/* * If found: * - subAltName contains the extension (caller must free) * - return value is the decoded namelist (allocated off arena) * if not found, or if failure to decode: * - return value is NULL
*/
CERTGeneralName *
cert_GetSubjectAltNameList(const CERTCertificate *cert, PLArenaPool *arena)
{
CERTGeneralName *nameList = NULL;
SECStatus rv = SECFailure;
SECItem subAltName;
current = firstName; do { switch (current->type) { case certDNSName: case certIPAddress:
++count; break; default: break;
}
current = CERT_GetNextGeneralName(current);
} while (current != firstName);
/* will fill nickNames, * will allocate all data from nickNames->arena, * numberOfGeneralNames should have been obtained from cert_CountDNSPatterns, * will ensure the numberOfGeneralNames matches the number of output entries.
*/
SECStatus
cert_GetDNSPatternsFromGeneralNames(CERTGeneralName *firstName,
PRUint32 numberOfGeneralNames,
CERTCertNicknames *nickNames)
{
CERTGeneralName *currentInput; char **currentOutput;
if (!firstName || !nickNames || !numberOfGeneralNames) return SECFailure;
/* * Collect all valid DNS names from the given cert. * The output arena will reference some temporaray data, * but this saves us from dealing with two arenas. * The caller may free all data by freeing CERTCertNicknames->arena.
*/
CERTCertNicknames *
CERT_GetValidDNSPatternsFromCert(CERTCertificate *cert)
{
CERTGeneralName *generalNames;
CERTCertNicknames *nickNames;
PLArenaPool *arena; char *singleName;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (!arena) { return NULL;
}
if (numNames) {
rv_getnames = cert_GetDNSPatternsFromGeneralNames(
generalNames, numNames, nickNames);
}
/* if there were names, we'll exit now, either with success or failure
*/ if (numNames) { if (rv_getnames == SECSuccess) { return nickNames;
}
/* failure to produce output */
PORT_FreeArena(arena, PR_FALSE); return NULL;
}
}
/* no SAN extension or no names found in extension */
singleName = CERT_GetCommonName(&cert->subject); if (singleName) {
nickNames->numnicknames = 1;
nickNames->nicknames = PORT_ArenaAlloc(arena, sizeof(char *)); if (nickNames->nicknames) {
*nickNames->nicknames = PORT_ArenaStrdup(arena, singleName);
}
PORT_Free(singleName);
/* Did we allocate both the buffer of pointers and the string? */ if (nickNames->nicknames && *nickNames->nicknames) { return nickNames;
}
}
PORT_FreeArena(arena, PR_FALSE); return NULL;
}
/* Make sure that the name of the host we are connecting to matches the * name that is incoded in the common-name component of the certificate * that they are using.
*/
SECStatus
CERT_VerifyCertName(const CERTCertificate *cert, constchar *hn)
{ char *cn;
SECStatus rv;
CERTOKDomainName *domainOK;
if (!hn || !strlen(hn)) {
PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure;
}
/* if the name is one that the user has already approved, it's OK. */ for (domainOK = cert->domainOK; domainOK; domainOK = domainOK->next) { if (0 == PORT_Strcasecmp(hn, domainOK->name)) { return SECSuccess;
}
}
/* Per RFC 2818, if the SubjectAltName extension is present, it must ** be used as the cert's identity.
*/
rv = cert_VerifySubjectAltName(cert, hn); if (rv == SECSuccess || PORT_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) return rv;
comp = SECITEM_CompareItem(&c1->derCert, &c2->derCert); if (comp == SECEqual) { /* certs are the same */ return (PR_TRUE);
}
/* check if they are issued by the same CA */
comp = SECITEM_CompareItem(&c1->derIssuer, &c2->derIssuer); if (comp != SECEqual) { /* different issuer */ return (PR_FALSE);
}
/* check country name */
c1str = CERT_GetCountryName(&c1->subject);
c2str = CERT_GetCountryName(&c2->subject);
eq = StringsEqual(c1str, c2str);
PORT_Free(c1str);
PORT_Free(c2str); if (eq != SECSuccess) { return (PR_FALSE);
}
#ifdef NOTDEF /* check orgUnit name */ /* * We need to revisit this and decide which fields should be allowed to be * different
*/
c1str = CERT_GetOrgUnitName(&c1->subject);
c2str = CERT_GetOrgUnitName(&c2->subject);
eq = StringsEqual(c1str, c2str);
PORT_Free(c1str);
PORT_Free(c2str); if (eq != SECSuccess) { return (PR_FALSE);
} #endif
return (PR_TRUE); /* all fields but common name are the same */
}
/* CERT_CertChainFromCert and CERT_DestroyCertificateList moved
to certhigh.c */
staticint
cert_Version(CERTCertificate *cert)
{ int version = 0; if (cert && cert->version.data && cert->version.len) {
version = DER_GetInteger(&cert->version); if (version < 0)
version = 0;
} return version;
}
/* * Does a cert belong to a CA? We decide based on perm database trust * flags, Netscape Cert Type Extension, and KeyUsage Extension.
*/
PRBool
CERT_IsCACert(CERTCertificate *cert, unsignedint *rettype)
{ unsignedint cType = cert->nsCertType;
PRBool ret = PR_FALSE;
/* * Check if the constraints are available and it's a CA, OR if it's * a X.509 v1 Root CA.
*/
CERTBasicConstraints constraints; if ((CERT_FindBasicConstraintExten(cert, &constraints) == SECSuccess &&
constraints.isCA) ||
(cert->isRoot && cert_Version(cert) < SEC_CERTIFICATE_VERSION_3))
cType |= (NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA);
if (LL_CMP(notAfterA, !=, notAfterB)) { /* one cert validity goes farther into the future, select it */ return LL_CMP(notAfterA, <, notAfterB) ? certValidityChooseB
: certValidityChooseA;
} /* the two certs have the same expiration date */
PORT_Assert(LL_CMP(notAfterA, ==, notAfterB)); /* do they also have the same start date ? */ if (LL_CMP(notBeforeA, ==, notBeforeB)) { return certValidityEqual;
} /* choose cert with the later start date */ return LL_CMP(notBeforeA, <, notBeforeB) ? certValidityChooseB
: certValidityChooseA;
}
/* * is certa newer than certb? If one is expired, pick the other one.
*/
PRBool
CERT_IsNewer(CERTCertificate *certa, CERTCertificate *certb)
{
PRTime notBeforeA, notAfterA, notBeforeB, notAfterB, now;
SECStatus rv;
PRBool newerbefore, newerafter;
if (newerbefore && newerafter) { return (PR_TRUE);
}
if ((!newerbefore) && (!newerafter)) { return (PR_FALSE);
}
/* get current time */
now = PR_Now();
if (newerbefore) { /* cert A was issued after cert B, but expires sooner */ /* if A is expired, then pick B */ if (LL_CMP(notAfterA, <, now)) { return (PR_FALSE);
} return (PR_TRUE);
} else { /* cert B was issued after cert A, but expires sooner */ /* if B is expired, then pick A */ if (LL_CMP(notAfterB, <, now)) { return (PR_TRUE);
} return (PR_FALSE);
}
}
if (ncerts) {
certs = PORT_ZNewArray(CERTCertificate *, ncerts); if (certs == NULL) { return (SECFailure);
}
/* decode all of the certs into the temporary DB */ for (i = 0, fcerts = 0; i < ncerts; i++) {
certs[fcerts] = CERT_NewTempCertificate(certdb, derCerts[i], NULL,
PR_FALSE, PR_TRUE); if (certs[fcerts]) {
SECItem subjKeyID = { siBuffer, NULL, 0 }; if (CERT_FindSubjectKeyIDExtension(certs[fcerts], &subjKeyID) ==
SECSuccess) { if (subjKeyID.data) {
cert_AddSubjectKeyIDMapping(&subjKeyID, certs[fcerts]);
}
SECITEM_FreeItem(&subjKeyID, PR_FALSE);
}
fcerts++;
}
}
if (keepCerts) { for (i = 0; i < fcerts; i++) { char *canickname = NULL;
PRBool isCA;
if (isCA && (fcerts > 1)) { /* if we are importing only a single cert and specifying * a nickname, we want to use that nickname if it a CA, * otherwise if there are more than one cert, we don't * know which cert it belongs to. But we still may try * the individual canickname from the cert itself.
*/ /* Bug 1192442 - propagate errors from these calls. */
(void)CERT_AddTempCertToPerm(certs[i], canickname, NULL);
} else {
(void)CERT_AddTempCertToPerm(
certs[i], nickname ? nickname : canickname, NULL);
}
PORT_Free(canickname); /* don't care if it fails - keep going */
}
}
}
if (retCerts) {
*retCerts = certs;
} else { if (certs) {
CERT_DestroyCertArray(certs, fcerts);
}
}
/* * a real list of certificates - need to convert CERTCertificateList * stuff and ASN 1 encoder/decoder over to using this...
*/
CERTCertList *
CERT_NewCertList(void)
{
PLArenaPool *arena = NULL;
CERTCertList *ret = NULL;
/* * Sort callback function to determine if cert a is newer than cert b. * Not valid certs are considered older than valid certs.
*/
PRBool
CERT_SortCBValidity(CERTCertificate *certa, CERTCertificate *certb, void *arg)
{
PRTime sorttime;
PRTime notBeforeA, notAfterA, notBeforeB, notAfterB;
SECStatus rv;
PRBool newerbefore, newerafter;
PRBool aNotValid = PR_FALSE, bNotValid = PR_FALSE;
/* check if A is valid at sorttime */ if (CERT_CheckCertValidTimes(certa, sorttime, PR_FALSE) !=
secCertTimeValid) {
aNotValid = PR_TRUE;
}
/* check if B is valid at sorttime */ if (CERT_CheckCertValidTimes(certb, sorttime, PR_FALSE) !=
secCertTimeValid) {
bNotValid = PR_TRUE;
}
/* a is valid, b is not */ if (bNotValid && (!aNotValid)) { return (PR_TRUE);
}
/* b is valid, a is not */ if (aNotValid && (!bNotValid)) { return (PR_FALSE);
}
/* a and b are either valid or not valid */ if (newerbefore && newerafter) { return (PR_TRUE);
}
if ((!newerbefore) && (!newerafter)) { return (PR_FALSE);
}
if (newerbefore) { /* cert A was issued after cert B, but expires sooner */ return (PR_TRUE);
} else { /* cert B was issued after cert A, but expires sooner */ return (PR_FALSE);
}
}
/* if cert is already in the list, then don't add it again */ if (cert == head->cert) { /*XXX*/ /* don't keep a reference */
CERT_DestroyCertificate(cert); goto done;
}
ret = (*f)(cert, head->cert, arg); /* if sort function succeeds, then insert before current node */ if (ret) {
PR_INSERT_BEFORE(&node->links, &head->links); goto done;
}
head = CERT_LIST_NEXT(head);
} /* if we get to the end, then just insert it at the tail */
PR_INSERT_BEFORE(&node->links, &certs->list);
/* This routine is here because pcertdb.c still has a call to it. * The SMIME profile code in pcertdb.c should be split into high (find * the email cert) and low (store the profile) code. At that point, we * can move this to certhigh.c where it belongs. * * remove certs from a list that don't have keyUsage and certType * that match the given usage.
*/
SECStatus
CERT_FilterCertListByUsage(CERTCertList *certList, SECCertUsage usage,
PRBool ca)
{ unsignedint requiredKeyUsage; unsignedint requiredCertType;
CERTCertListNode *node, *savenode;
SECStatus rv;
if (certList == NULL) goto loser;
rv = CERT_KeyUsageAndTypeForCertUsage(usage, ca, &requiredKeyUsage,
&requiredCertType); if (rv != SECSuccess) { goto loser;
}
node = CERT_LIST_HEAD(certList);
while (!CERT_LIST_END(node, certList)) {
PRBool bad = (PRBool)(!node->cert);
/* bad key usage ? */ if (!bad &&
CERT_CheckKeyUsage(node->cert, requiredKeyUsage) != SECSuccess) {
bad = PR_TRUE;
} /* bad cert type ? */ if (!bad) { unsignedint certType = 0; if (ca) { /* This function returns a more comprehensive cert type that * takes trust flags into consideration. Should probably * fix the cert decoding code to do this.
*/
(void)CERT_IsCACert(node->cert, &certType);
} else {
certType = node->cert->nsCertType;
} if (!(certType & requiredCertType)) {
bad = PR_TRUE;
}
}
if (bad) { /* remove the node if it is bad */
savenode = CERT_LIST_NEXT(node);
CERT_RemoveCertListNode(node);
node = savenode;
} else {
node = CERT_LIST_NEXT(node);
}
} return (SECSuccess);
while (!CERT_LIST_END(node, certList)) {
cert = node->cert; if (PR_TRUE != CERT_IsUserCert(cert)) { /* Not a User Cert, so remove this cert from the list */
freenode = node;
node = CERT_LIST_NEXT(node);
CERT_RemoveCertListNode(freenode);
} else { /* Is a User cert, so leave it in the list */
node = CERT_LIST_NEXT(node);
}
}
return (SECSuccess);
}
/* return true if cert is in the list */
PRBool
CERT_IsInList(const CERTCertificate *cert, const CERTCertList *certList)
{
CERTCertListNode *node; for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList);
node = CERT_LIST_NEXT(node)) { if (node->cert == cert) { return PR_TRUE;
}
} return PR_FALSE;
}
/* returned certList is the intersection of the certs on certList and the
* certs on filterList */
SECStatus
CERT_FilterCertListByCertList(CERTCertList *certList, const CERTCertList *filterList)
{
CERTCertListNode *node, *freenode;
CERTCertificate *cert;
if (!certList) { return SECFailure;
}
if (!filterList || CERT_LIST_EMPTY(certList)) { /* if the filterList is empty, just clear out certList and return */ for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList);) {
freenode = node;
node = CERT_LIST_NEXT(node);
CERT_RemoveCertListNode(freenode);
} return SECSuccess;
}
node = CERT_LIST_HEAD(certList);
while (!CERT_LIST_END(node, certList)) {
cert = node->cert; if (!CERT_IsInList(cert, filterList)) { // no matching cert on filter list, remove it from certlist */
freenode = node;
node = CERT_LIST_NEXT(node);
CERT_RemoveCertListNode(freenode);
} else { /* matching cert, keep it around */
node = CERT_LIST_NEXT(node);
}
}
/* we could try to match the nickname to the individual cert, * but nickname parsing is quite complicated, so it's best just * to use the existing code and get a list of certs that match the * nickname. We can then compare that list with our input cert list
* and return only those certs that are on both. */
nameList = PK11_FindCertsFromNickname(nickname, pwarg);
/* namelist could be NULL, this will force certList to become empty */
rv = CERT_FilterCertListByCertList(certList, nameList); /* CERT_DestroyCertList can now accept a NULL pointer */
CERT_DestroyCertList(nameList); return rv;
}
static PZLock *certRefCountLock = NULL;
/* * Acquire the cert reference count lock * There is currently one global lock for all certs, but I'm putting a cert * arg here so that it will be easy to make it per-cert in the future if * that turns out to be necessary.
*/ void
CERT_LockCertRefCount(CERTCertificate *cert)
{
PORT_Assert(certRefCountLock != NULL);
PZ_Lock(certRefCountLock); return;
}
/* * Acquire the cert trust lock * There is currently one global lock for all certs, but I'm putting a cert * arg here so that it will be easy to make it per-cert in the future if * that turns out to be necessary.
*/ void
CERT_LockCertTrust(const CERTCertificate *cert)
{
PORT_Assert(certTrustLock != NULL);
PZ_Lock(certTrustLock);
}
/* Maybe[Lock, Unlock] variants are only to be used by * CERT_DestroyCertificate, since an application could
* call this after NSS_Shutdown destroys cert locks. */ void
CERT_MaybeLockCertTempPerm(const CERTCertificate *cert)
{ if (certTempPermCertLock) {
PZ_Lock(certTempPermCertLock);
}
}
/* * Get the StatusConfig data for this handle
*/
CERTStatusConfig *
CERT_GetStatusConfig(CERTCertDBHandle *handle)
{ return handle->statusConfig;
}
/* * Set the StatusConfig data for this handle. There * should not be another configuration set.
*/ void
CERT_SetStatusConfig(CERTCertDBHandle *handle, CERTStatusConfig *statusConfig)
{
PORT_Assert(handle->statusConfig == NULL);
handle->statusConfig = statusConfig;
}
/* * Code for dealing with subjKeyID to cert mappings.
*/
SECStatus
cert_CreateSubjectKeyIDSlotCheckHash(void)
{ /* * This hash is used to remember the series of a slot * when we last checked for user certs
*/
gSubjKeyIDSlotCheckHash =
PL_NewHashTable(0, SECITEM_Hash, SECITEM_HashCompare,
SECITEM_HashCompare, &cert_AllocOps, NULL); if (!gSubjKeyIDSlotCheckHash) {
PORT_SetError(SEC_ERROR_NO_MEMORY); return SECFailure;
}
gSubjKeyIDSlotCheckLock = PR_NewLock(); if (!gSubjKeyIDSlotCheckLock) {
PL_HashTableDestroy(gSubjKeyIDSlotCheckHash);
gSubjKeyIDSlotCheckHash = NULL;
PORT_SetError(SEC_ERROR_NO_MEMORY); return SECFailure;
} return SECSuccess;
}
PR_Lock(gSubjKeyIDLock); /* The hash table implementation does not free up the memory * associated with the key of an already existing entry if we add a * duplicate, so we would wind up leaking the previously allocated * key if we don't remove before adding.
*/
oldVal = (SECItem *)PL_HashTableLookup(gSubjKeyIDHash, subjKeyID); if (oldVal) {
PL_HashTableRemove(gSubjKeyIDHash, subjKeyID);
}
PR_Lock(gSubjKeyIDSlotCheckLock);
oldSeries = (SECItem *)PL_HashTableLookup(gSubjKeyIDSlotCheckHash, slotid); if (oldSeries) { /* * make sure we don't leak the key of an existing entry * (similar to cert_AddSubjectKeyIDMapping, see comment there)
*/
PL_HashTableRemove(gSubjKeyIDSlotCheckHash, slotid);
}
rv = (PL_HashTableAdd(gSubjKeyIDSlotCheckHash, newSlotid, newSeries))
? SECSuccess
: SECFailure;
PR_Unlock(gSubjKeyIDSlotCheckLock); if (rv == SECSuccess) { return rv;
}
loser: if (newSlotid) {
SECITEM_FreeItem(newSlotid, PR_TRUE);
} if (newSeries) {
SECITEM_FreeItem(newSeries, PR_TRUE);
} return rv;
}
int
cert_SubjectKeyIDSlotCheckSeries(SECItem *slotid)
{
SECItem *seriesItem = NULL; int series;
if (!gSubjKeyIDSlotCheckLock) {
PORT_SetError(SEC_ERROR_NOT_INITIALIZED); return -1;
}
PR_Lock(gSubjKeyIDSlotCheckLock);
seriesItem = (SECItem *)PL_HashTableLookup(gSubjKeyIDSlotCheckHash, slotid);
PR_Unlock(gSubjKeyIDSlotCheckLock); /* getting a null series just means we haven't registered one yet,
* just return 0 */ if (seriesItem == NULL) { return 0;
} /* if we got a series back, assert if it's not the proper length. */
PORT_Assert(seriesItem->len == sizeof(int)); if (seriesItem->len != sizeof(int)) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return -1;
}
PORT_Memcpy(&series, seriesItem->data, sizeof(int)); return series;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.