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