| /* 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/. */ |
| |
| /* |
| * Code for dealing with X509.V3 extensions. |
| */ |
| |
| #include "cert.h" |
| #include "secitem.h" |
| #include "secoid.h" |
| #include "secder.h" |
| #include "secasn1.h" |
| #include "certxutl.h" |
| #include "secerr.h" |
| |
| SECStatus |
| CERT_FindCertExtensionByOID(CERTCertificate *cert, SECItem *oid, SECItem *value) |
| { |
| return (cert_FindExtensionByOID(cert->extensions, oid, value)); |
| } |
| |
| SECStatus |
| CERT_FindCertExtension(const CERTCertificate *cert, int tag, SECItem *value) |
| { |
| return (cert_FindExtension(cert->extensions, tag, value)); |
| } |
| |
| static void |
| SetExts(void *object, CERTCertExtension **exts) |
| { |
| CERTCertificate *cert = (CERTCertificate *)object; |
| |
| cert->extensions = exts; |
| DER_SetUInteger(cert->arena, &(cert->version), SEC_CERTIFICATE_VERSION_3); |
| } |
| |
| void * |
| CERT_StartCertExtensions(CERTCertificate *cert) |
| { |
| return (cert_StartExtensions((void *)cert, cert->arena, SetExts)); |
| } |
| |
| /* |
| * get the value of the Netscape Certificate Type Extension |
| */ |
| SECStatus |
| CERT_FindNSCertTypeExtension(CERTCertificate *cert, SECItem *retItem) |
| { |
| |
| return (CERT_FindBitStringExtension( |
| cert->extensions, SEC_OID_NS_CERT_EXT_CERT_TYPE, retItem)); |
| } |
| |
| /* |
| * get the value of a string type extension |
| */ |
| char * |
| CERT_FindNSStringExtension(CERTCertificate *cert, int oidtag) |
| { |
| SECItem wrapperItem, tmpItem = { siBuffer, 0 }; |
| SECStatus rv; |
| PLArenaPool *arena = NULL; |
| char *retstring = NULL; |
| |
| wrapperItem.data = NULL; |
| tmpItem.data = NULL; |
| |
| arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
| |
| if (!arena) { |
| goto loser; |
| } |
| |
| rv = cert_FindExtension(cert->extensions, oidtag, &wrapperItem); |
| if (rv != SECSuccess) { |
| goto loser; |
| } |
| |
| rv = SEC_QuickDERDecodeItem( |
| arena, &tmpItem, SEC_ASN1_GET(SEC_IA5StringTemplate), &wrapperItem); |
| |
| if (rv != SECSuccess) { |
| goto loser; |
| } |
| |
| retstring = (char *)PORT_Alloc(tmpItem.len + 1); |
| if (retstring == NULL) { |
| goto loser; |
| } |
| |
| PORT_Memcpy(retstring, tmpItem.data, tmpItem.len); |
| retstring[tmpItem.len] = '\0'; |
| |
| loser: |
| if (arena) { |
| PORT_FreeArena(arena, PR_FALSE); |
| } |
| |
| if (wrapperItem.data) { |
| PORT_Free(wrapperItem.data); |
| } |
| |
| return (retstring); |
| } |
| |
| /* |
| * get the value of the X.509 v3 Key Usage Extension |
| */ |
| SECStatus |
| CERT_FindKeyUsageExtension(CERTCertificate *cert, SECItem *retItem) |
| { |
| |
| return (CERT_FindBitStringExtension(cert->extensions, |
| SEC_OID_X509_KEY_USAGE, retItem)); |
| } |
| |
| /* |
| * get the value of the X.509 v3 Key Usage Extension |
| */ |
| SECStatus |
| CERT_FindSubjectKeyIDExtension(CERTCertificate *cert, SECItem *retItem) |
| { |
| |
| SECStatus rv; |
| SECItem encodedValue = { siBuffer, NULL, 0 }; |
| SECItem decodedValue = { siBuffer, NULL, 0 }; |
| |
| rv = cert_FindExtension(cert->extensions, SEC_OID_X509_SUBJECT_KEY_ID, |
| &encodedValue); |
| if (rv == SECSuccess) { |
| PORTCheapArenaPool tmpArena; |
| PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE); |
| rv = SEC_QuickDERDecodeItem(&tmpArena.arena, &decodedValue, |
| SEC_ASN1_GET(SEC_OctetStringTemplate), |
| &encodedValue); |
| if (rv == SECSuccess) { |
| rv = SECITEM_CopyItem(NULL, retItem, &decodedValue); |
| } |
| PORT_DestroyCheapArena(&tmpArena); |
| } |
| SECITEM_FreeItem(&encodedValue, PR_FALSE); |
| return rv; |
| } |
| |
| SECStatus |
| CERT_FindBasicConstraintExten(CERTCertificate *cert, |
| CERTBasicConstraints *value) |
| { |
| SECItem encodedExtenValue; |
| SECStatus rv; |
| |
| encodedExtenValue.data = NULL; |
| encodedExtenValue.len = 0; |
| |
| rv = cert_FindExtension(cert->extensions, SEC_OID_X509_BASIC_CONSTRAINTS, |
| &encodedExtenValue); |
| if (rv != SECSuccess) { |
| return (rv); |
| } |
| |
| rv = CERT_DecodeBasicConstraintValue(value, &encodedExtenValue); |
| |
| /* free the raw extension data */ |
| PORT_Free(encodedExtenValue.data); |
| encodedExtenValue.data = NULL; |
| |
| return (rv); |
| } |
| |
| CERTAuthKeyID * |
| CERT_FindAuthKeyIDExten(PLArenaPool *arena, CERTCertificate *cert) |
| { |
| SECItem encodedExtenValue; |
| SECStatus rv; |
| CERTAuthKeyID *ret; |
| |
| encodedExtenValue.data = NULL; |
| encodedExtenValue.len = 0; |
| |
| rv = cert_FindExtension(cert->extensions, SEC_OID_X509_AUTH_KEY_ID, |
| &encodedExtenValue); |
| if (rv != SECSuccess) { |
| return (NULL); |
| } |
| |
| ret = CERT_DecodeAuthKeyID(arena, &encodedExtenValue); |
| |
| PORT_Free(encodedExtenValue.data); |
| encodedExtenValue.data = NULL; |
| |
| return (ret); |
| } |
| |
| SECStatus |
| CERT_CheckCertUsage(CERTCertificate *cert, unsigned char usage) |
| { |
| SECItem keyUsage; |
| SECStatus rv; |
| |
| /* There is no extension, v1 or v2 certificate */ |
| if (cert->extensions == NULL) { |
| return (SECSuccess); |
| } |
| |
| keyUsage.data = NULL; |
| |
| /* This code formerly ignored the Key Usage extension if it was |
| ** marked non-critical. That was wrong. Since we do understand it, |
| ** we are obligated to honor it, whether or not it is critical. |
| */ |
| rv = CERT_FindKeyUsageExtension(cert, &keyUsage); |
| if (rv == SECFailure) { |
| rv = (PORT_GetError() == SEC_ERROR_EXTENSION_NOT_FOUND) ? SECSuccess |
| : SECFailure; |
| } else if (!keyUsage.data || !(keyUsage.data[0] & usage)) { |
| PORT_SetError(SEC_ERROR_CERT_USAGES_INVALID); |
| rv = SECFailure; |
| } |
| PORT_Free(keyUsage.data); |
| return (rv); |
| } |