andre@0: /* This Source Code Form is subject to the terms of the Mozilla Public andre@0: * License, v. 2.0. If a copy of the MPL was not distributed with this andre@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ andre@0: andre@0: /* andre@0: * Code for dealing with X509.V3 extensions. andre@0: */ andre@0: andre@0: #include "cert.h" andre@0: #include "secitem.h" andre@0: #include "secoid.h" andre@0: #include "secder.h" andre@0: #include "secasn1.h" andre@0: #include "certxutl.h" andre@0: #include "secerr.h" andre@0: andre@0: SECStatus andre@0: CERT_FindCertExtensionByOID(CERTCertificate *cert, SECItem *oid, andre@0: SECItem *value) andre@0: { andre@0: return (cert_FindExtensionByOID (cert->extensions, oid, value)); andre@0: } andre@0: andre@0: andre@0: SECStatus andre@0: CERT_FindCertExtension(const CERTCertificate *cert, int tag, SECItem *value) andre@0: { andre@0: return (cert_FindExtension (cert->extensions, tag, value)); andre@0: } andre@0: andre@0: static void andre@0: SetExts(void *object, CERTCertExtension **exts) andre@0: { andre@0: CERTCertificate *cert = (CERTCertificate *)object; andre@0: andre@0: cert->extensions = exts; andre@0: DER_SetUInteger (cert->arena, &(cert->version), SEC_CERTIFICATE_VERSION_3); andre@0: } andre@0: andre@0: void * andre@0: CERT_StartCertExtensions(CERTCertificate *cert) andre@0: { andre@0: return (cert_StartExtensions ((void *)cert, cert->arena, SetExts)); andre@0: } andre@0: andre@0: /* find the given extension in the certificate of the Issuer of 'cert' */ andre@0: SECStatus andre@0: CERT_FindIssuerCertExtension(CERTCertificate *cert, int tag, SECItem *value) andre@0: { andre@0: CERTCertificate *issuercert; andre@0: SECStatus rv; andre@0: andre@0: issuercert = CERT_FindCertByName(cert->dbhandle, &cert->derIssuer); andre@0: if ( issuercert ) { andre@0: rv = cert_FindExtension(issuercert->extensions, tag, value); andre@0: CERT_DestroyCertificate(issuercert); andre@0: } else { andre@0: rv = SECFailure; andre@0: } andre@0: andre@0: return(rv); andre@0: } andre@0: andre@0: /* find a URL extension in the cert or its CA andre@0: * apply the base URL string if it exists andre@0: */ andre@0: char * andre@0: CERT_FindCertURLExtension(CERTCertificate *cert, int tag, int catag) andre@0: { andre@0: SECStatus rv; andre@0: SECItem urlitem = {siBuffer,0}; andre@0: SECItem baseitem = {siBuffer,0}; andre@0: SECItem urlstringitem = {siBuffer,0}; andre@0: SECItem basestringitem = {siBuffer,0}; andre@0: PLArenaPool *arena = NULL; andre@0: PRBool hasbase; andre@0: char *urlstring; andre@0: char *str; andre@0: int len; andre@0: unsigned int i; andre@0: andre@0: urlstring = NULL; andre@0: andre@0: arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); andre@0: if ( ! arena ) { andre@0: goto loser; andre@0: } andre@0: andre@0: hasbase = PR_FALSE; andre@0: andre@0: rv = cert_FindExtension(cert->extensions, tag, &urlitem); andre@0: if ( rv == SECSuccess ) { andre@0: rv = cert_FindExtension(cert->extensions, SEC_OID_NS_CERT_EXT_BASE_URL, andre@0: &baseitem); andre@0: if ( rv == SECSuccess ) { andre@0: hasbase = PR_TRUE; andre@0: } andre@0: andre@0: } else if ( catag ) { andre@0: /* if the cert doesn't have the extensions, see if the issuer does */ andre@0: rv = CERT_FindIssuerCertExtension(cert, catag, &urlitem); andre@0: if ( rv != SECSuccess ) { andre@0: goto loser; andre@0: } andre@0: rv = CERT_FindIssuerCertExtension(cert, SEC_OID_NS_CERT_EXT_BASE_URL, andre@0: &baseitem); andre@0: if ( rv == SECSuccess ) { andre@0: hasbase = PR_TRUE; andre@0: } andre@0: } else { andre@0: goto loser; andre@0: } andre@0: andre@0: rv = SEC_QuickDERDecodeItem(arena, &urlstringitem, andre@0: SEC_ASN1_GET(SEC_IA5StringTemplate), &urlitem); andre@0: andre@0: if ( rv != SECSuccess ) { andre@0: goto loser; andre@0: } andre@0: if ( hasbase ) { andre@0: rv = SEC_QuickDERDecodeItem(arena, &basestringitem, andre@0: SEC_ASN1_GET(SEC_IA5StringTemplate), andre@0: &baseitem); andre@0: andre@0: if ( rv != SECSuccess ) { andre@0: goto loser; andre@0: } andre@0: } andre@0: andre@0: len = urlstringitem.len + ( hasbase ? basestringitem.len : 0 ) + 1; andre@0: andre@0: str = urlstring = (char *)PORT_Alloc(len); andre@0: if ( urlstring == NULL ) { andre@0: goto loser; andre@0: } andre@0: andre@0: /* copy the URL base first */ andre@0: if ( hasbase ) { andre@0: andre@0: /* if the urlstring has a : in it, then we assume it is an absolute andre@0: * URL, and will not get the base string pre-pended andre@0: */ andre@0: for ( i = 0; i < urlstringitem.len; i++ ) { andre@0: if ( urlstringitem.data[i] == ':' ) { andre@0: goto nobase; andre@0: } andre@0: } andre@0: andre@0: PORT_Memcpy(str, basestringitem.data, basestringitem.len); andre@0: str += basestringitem.len; andre@0: andre@0: } andre@0: andre@0: nobase: andre@0: /* copy the rest (or all) of the URL */ andre@0: PORT_Memcpy(str, urlstringitem.data, urlstringitem.len); andre@0: str += urlstringitem.len; andre@0: andre@0: *str = '\0'; andre@0: goto done; andre@0: andre@0: loser: andre@0: if ( urlstring ) { andre@0: PORT_Free(urlstring); andre@0: } andre@0: andre@0: urlstring = NULL; andre@0: done: andre@0: if ( arena ) { andre@0: PORT_FreeArena(arena, PR_FALSE); andre@0: } andre@0: if ( baseitem.data ) { andre@0: PORT_Free(baseitem.data); andre@0: } andre@0: if ( urlitem.data ) { andre@0: PORT_Free(urlitem.data); andre@0: } andre@0: andre@0: return(urlstring); andre@0: } andre@0: andre@0: /* andre@0: * get the value of the Netscape Certificate Type Extension andre@0: */ andre@0: SECStatus andre@0: CERT_FindNSCertTypeExtension(CERTCertificate *cert, SECItem *retItem) andre@0: { andre@0: andre@0: return (CERT_FindBitStringExtension andre@0: (cert->extensions, SEC_OID_NS_CERT_EXT_CERT_TYPE, retItem)); andre@0: } andre@0: andre@0: andre@0: /* andre@0: * get the value of a string type extension andre@0: */ andre@0: char * andre@0: CERT_FindNSStringExtension(CERTCertificate *cert, int oidtag) andre@0: { andre@0: SECItem wrapperItem, tmpItem = {siBuffer,0}; andre@0: SECStatus rv; andre@0: PLArenaPool *arena = NULL; andre@0: char *retstring = NULL; andre@0: andre@0: wrapperItem.data = NULL; andre@0: tmpItem.data = NULL; andre@0: andre@0: arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); andre@0: andre@0: if ( ! arena ) { andre@0: goto loser; andre@0: } andre@0: andre@0: rv = cert_FindExtension(cert->extensions, oidtag, andre@0: &wrapperItem); andre@0: if ( rv != SECSuccess ) { andre@0: goto loser; andre@0: } andre@0: andre@0: rv = SEC_QuickDERDecodeItem(arena, &tmpItem, andre@0: SEC_ASN1_GET(SEC_IA5StringTemplate), &wrapperItem); andre@0: andre@0: if ( rv != SECSuccess ) { andre@0: goto loser; andre@0: } andre@0: andre@0: retstring = (char *)PORT_Alloc(tmpItem.len + 1 ); andre@0: if ( retstring == NULL ) { andre@0: goto loser; andre@0: } andre@0: andre@0: PORT_Memcpy(retstring, tmpItem.data, tmpItem.len); andre@0: retstring[tmpItem.len] = '\0'; andre@0: andre@0: loser: andre@0: if ( arena ) { andre@0: PORT_FreeArena(arena, PR_FALSE); andre@0: } andre@0: andre@0: if ( wrapperItem.data ) { andre@0: PORT_Free(wrapperItem.data); andre@0: } andre@0: andre@0: return(retstring); andre@0: } andre@0: andre@0: /* andre@0: * get the value of the X.509 v3 Key Usage Extension andre@0: */ andre@0: SECStatus andre@0: CERT_FindKeyUsageExtension(CERTCertificate *cert, SECItem *retItem) andre@0: { andre@0: andre@0: return (CERT_FindBitStringExtension(cert->extensions, andre@0: SEC_OID_X509_KEY_USAGE, retItem)); andre@0: } andre@0: andre@0: /* andre@0: * get the value of the X.509 v3 Key Usage Extension andre@0: */ andre@0: SECStatus andre@0: CERT_FindSubjectKeyIDExtension(CERTCertificate *cert, SECItem *retItem) andre@0: { andre@0: andre@0: SECStatus rv; andre@0: SECItem encodedValue = {siBuffer, NULL, 0 }; andre@0: SECItem decodedValue = {siBuffer, NULL, 0 }; andre@0: andre@0: rv = cert_FindExtension andre@0: (cert->extensions, SEC_OID_X509_SUBJECT_KEY_ID, &encodedValue); andre@0: if (rv == SECSuccess) { andre@0: PLArenaPool * tmpArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); andre@0: if (tmpArena) { andre@0: rv = SEC_QuickDERDecodeItem(tmpArena, &decodedValue, andre@0: SEC_ASN1_GET(SEC_OctetStringTemplate), andre@0: &encodedValue); andre@0: if (rv == SECSuccess) { andre@0: rv = SECITEM_CopyItem(NULL, retItem, &decodedValue); andre@0: } andre@0: PORT_FreeArena(tmpArena, PR_FALSE); andre@0: } else { andre@0: rv = SECFailure; andre@0: } andre@0: } andre@0: SECITEM_FreeItem(&encodedValue, PR_FALSE); andre@0: return rv; andre@0: } andre@0: andre@0: SECStatus andre@0: CERT_FindBasicConstraintExten(CERTCertificate *cert, andre@0: CERTBasicConstraints *value) andre@0: { andre@0: SECItem encodedExtenValue; andre@0: SECStatus rv; andre@0: andre@0: encodedExtenValue.data = NULL; andre@0: encodedExtenValue.len = 0; andre@0: andre@0: rv = cert_FindExtension(cert->extensions, SEC_OID_X509_BASIC_CONSTRAINTS, andre@0: &encodedExtenValue); andre@0: if ( rv != SECSuccess ) { andre@0: return (rv); andre@0: } andre@0: andre@0: rv = CERT_DecodeBasicConstraintValue (value, &encodedExtenValue); andre@0: andre@0: /* free the raw extension data */ andre@0: PORT_Free(encodedExtenValue.data); andre@0: encodedExtenValue.data = NULL; andre@0: andre@0: return(rv); andre@0: } andre@0: andre@0: CERTAuthKeyID * andre@0: CERT_FindAuthKeyIDExten (PLArenaPool *arena, CERTCertificate *cert) andre@0: { andre@0: SECItem encodedExtenValue; andre@0: SECStatus rv; andre@0: CERTAuthKeyID *ret; andre@0: andre@0: encodedExtenValue.data = NULL; andre@0: encodedExtenValue.len = 0; andre@0: andre@0: rv = cert_FindExtension(cert->extensions, SEC_OID_X509_AUTH_KEY_ID, andre@0: &encodedExtenValue); andre@0: if ( rv != SECSuccess ) { andre@0: return (NULL); andre@0: } andre@0: andre@0: ret = CERT_DecodeAuthKeyID (arena, &encodedExtenValue); andre@0: andre@0: PORT_Free(encodedExtenValue.data); andre@0: encodedExtenValue.data = NULL; andre@0: andre@0: return(ret); andre@0: } andre@0: andre@0: SECStatus andre@0: CERT_CheckCertUsage(CERTCertificate *cert, unsigned char usage) andre@0: { andre@0: SECItem keyUsage; andre@0: SECStatus rv; andre@0: andre@0: /* There is no extension, v1 or v2 certificate */ andre@0: if (cert->extensions == NULL) { andre@0: return (SECSuccess); andre@0: } andre@0: andre@0: keyUsage.data = NULL; andre@0: andre@0: /* This code formerly ignored the Key Usage extension if it was andre@0: ** marked non-critical. That was wrong. Since we do understand it, andre@0: ** we are obligated to honor it, whether or not it is critical. andre@0: */ andre@0: rv = CERT_FindKeyUsageExtension(cert, &keyUsage); andre@0: if (rv == SECFailure) { andre@0: rv = (PORT_GetError () == SEC_ERROR_EXTENSION_NOT_FOUND) ? andre@0: SECSuccess : SECFailure; andre@0: } else if (!(keyUsage.data[0] & usage)) { andre@0: PORT_SetError (SEC_ERROR_CERT_USAGES_INVALID); andre@0: rv = SECFailure; andre@0: } andre@0: PORT_Free (keyUsage.data); andre@0: return (rv); andre@0: }