/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is the Netscape security libraries. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 2000 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Ian McGreer * Javier Delgadillo * John Gardiner Myers * Martin v. Loewis * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "chrome/third_party/mozilla_security_manager/nsNSSCertHelper.h" #include #include #include "app/l10n_util.h" #include "base/i18n/number_formatting.h" #include "base/utf_string_conversions.h" #include "grit/generated_resources.h" #include "net/base/net_util.h" namespace { std::string BMPtoUTF8(PRArenaPool* arena, unsigned char* data, unsigned int len) { if (len % 2 != 0) return l10n_util::GetStringUTF8(IDS_CERT_EXTENSION_DUMP_ERROR); unsigned int utf8_val_len = len * 3 + 1; std::vector utf8_val(utf8_val_len); if (!PORT_UCS2_UTF8Conversion(PR_FALSE, data, len, &utf8_val.front(), utf8_val_len, &utf8_val_len)) return l10n_util::GetStringUTF8(IDS_CERT_EXTENSION_DUMP_ERROR); return std::string(reinterpret_cast(&utf8_val.front()), utf8_val_len); } } // namespace namespace mozilla_security_manager { SECOidTag ms_cert_ext_certtype = SEC_OID_UNKNOWN; SECOidTag ms_certsrv_ca_version = SEC_OID_UNKNOWN; SECOidTag ms_nt_principal_name = SEC_OID_UNKNOWN; SECOidTag ms_ntds_replication = SEC_OID_UNKNOWN; #define MICROSOFT_OID 0x2b, 0x6, 0x1, 0x4, 0x1, 0x82, 0x37 static const unsigned char kMsCertExtCerttype[] = {MICROSOFT_OID, 20, 2}; static const unsigned char kMsCertSrvCAVersion[] = {MICROSOFT_OID, 21, 1}; static const unsigned char kMsNTPrincipalName[] = {MICROSOFT_OID, 20, 2, 3}; static const unsigned char kMsNTDSReplication[] = {MICROSOFT_OID, 25, 1}; void RegisterDynamicOids() { if (ms_cert_ext_certtype != SEC_OID_UNKNOWN) return; SECOidData od; od.oid.data = const_cast(kMsCertExtCerttype); od.oid.len = sizeof(kMsCertExtCerttype); od.offset = SEC_OID_UNKNOWN; od.mechanism = CKM_INVALID_MECHANISM; od.supportedExtension = INVALID_CERT_EXTENSION; od.desc = "ms_cert_ext_certtype"; ms_cert_ext_certtype = SECOID_AddEntry(&od); DCHECK_NE(ms_cert_ext_certtype, SEC_OID_UNKNOWN); od.oid.data = const_cast(kMsCertSrvCAVersion); od.oid.len = sizeof(kMsCertSrvCAVersion); od.offset = SEC_OID_UNKNOWN; od.mechanism = CKM_INVALID_MECHANISM; od.supportedExtension = INVALID_CERT_EXTENSION; od.desc = "ms_certsrv_ca_version"; ms_certsrv_ca_version = SECOID_AddEntry(&od); DCHECK_NE(ms_certsrv_ca_version, SEC_OID_UNKNOWN); od.oid.data = const_cast(kMsNTPrincipalName); od.oid.len = sizeof(kMsNTPrincipalName); od.offset = SEC_OID_UNKNOWN; od.mechanism = CKM_INVALID_MECHANISM; od.supportedExtension = INVALID_CERT_EXTENSION; od.desc = "ms_nt_principal_name"; ms_nt_principal_name = SECOID_AddEntry(&od); DCHECK_NE(ms_nt_principal_name, SEC_OID_UNKNOWN); od.oid.data = const_cast(kMsNTDSReplication); od.oid.len = sizeof(kMsNTDSReplication); od.offset = SEC_OID_UNKNOWN; od.mechanism = CKM_INVALID_MECHANISM; od.supportedExtension = INVALID_CERT_EXTENSION; od.desc = "ms_ntds_replication"; ms_ntds_replication = SECOID_AddEntry(&od); DCHECK_NE(ms_ntds_replication, SEC_OID_UNKNOWN); } std::string ProcessRawBytes(SECItem* data) { static const char kHexChars[] = "0123456789ABCDEF"; // Each input byte creates two output hex characters + a space or newline, // except for the last byte. std::string ret(std::max(0u, data->len * 3 - 1), '\0'); for (size_t i = 0; i < data->len; ++i) { unsigned char b = data->data[i]; ret[i * 3] = kHexChars[(b >> 4) & 0xf]; ret[i * 3 + 1] = kHexChars[b & 0xf]; if (i + 1 < data->len) { if ((i + 1) % 16 == 0) ret[i * 3 + 2] = '\n'; else ret[i * 3 + 2] = ' '; } } return ret; } std::string ProcessRawBits(SECItem* data) { SECItem bytedata; bytedata.data = data->data; bytedata.len = data->len / 8; return ProcessRawBytes(&bytedata); } std::string DumpOidString(SECItem* oid) { char* pr_string = CERT_GetOidString(oid); if (pr_string) { std::string rv = pr_string; PR_smprintf_free(pr_string); return rv; } return ProcessRawBytes(oid); } std::string GetOIDText(SECItem* oid) { int string_id; SECOidTag oid_tag = SECOID_FindOIDTag(oid); switch (oid_tag) { case SEC_OID_AVA_COMMON_NAME: string_id = IDS_CERT_OID_AVA_COMMON_NAME; break; case SEC_OID_AVA_STATE_OR_PROVINCE: string_id = IDS_CERT_OID_AVA_STATE_OR_PROVINCE; break; case SEC_OID_AVA_ORGANIZATION_NAME: string_id = IDS_CERT_OID_AVA_ORGANIZATION_NAME; break; case SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME: string_id = IDS_CERT_OID_AVA_ORGANIZATIONAL_UNIT_NAME; break; case SEC_OID_AVA_DN_QUALIFIER: string_id = IDS_CERT_OID_AVA_DN_QUALIFIER; break; case SEC_OID_AVA_COUNTRY_NAME: string_id = IDS_CERT_OID_AVA_COUNTRY_NAME; break; case SEC_OID_AVA_SERIAL_NUMBER: string_id = IDS_CERT_OID_AVA_SERIAL_NUMBER; break; case SEC_OID_AVA_LOCALITY: string_id = IDS_CERT_OID_AVA_LOCALITY; break; case SEC_OID_AVA_DC: string_id = IDS_CERT_OID_AVA_DC; break; case SEC_OID_RFC1274_MAIL: string_id = IDS_CERT_OID_RFC1274_MAIL; break; case SEC_OID_RFC1274_UID: string_id = IDS_CERT_OID_RFC1274_UID; break; case SEC_OID_PKCS9_EMAIL_ADDRESS: string_id = IDS_CERT_OID_PKCS9_EMAIL_ADDRESS; break; case SEC_OID_PKCS1_RSA_ENCRYPTION: string_id = IDS_CERT_OID_PKCS1_RSA_ENCRYPTION; break; case SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION: string_id = IDS_CERT_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION; break; case SEC_OID_PKCS1_MD4_WITH_RSA_ENCRYPTION: string_id = IDS_CERT_OID_PKCS1_MD4_WITH_RSA_ENCRYPTION; break; case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION: string_id = IDS_CERT_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION; break; case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION: string_id = IDS_CERT_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION; break; case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION: string_id = IDS_CERT_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION; break; case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION: string_id = IDS_CERT_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION; break; case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION: string_id = IDS_CERT_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION; break; case SEC_OID_NS_CERT_EXT_CERT_TYPE: string_id = IDS_CERT_EXT_NS_CERT_TYPE; break; case SEC_OID_NS_CERT_EXT_BASE_URL: string_id = IDS_CERT_EXT_NS_CERT_BASE_URL; break; case SEC_OID_NS_CERT_EXT_REVOCATION_URL: string_id = IDS_CERT_EXT_NS_CERT_REVOCATION_URL; break; case SEC_OID_NS_CERT_EXT_CA_REVOCATION_URL: string_id = IDS_CERT_EXT_NS_CA_REVOCATION_URL; break; case SEC_OID_NS_CERT_EXT_CERT_RENEWAL_URL: string_id = IDS_CERT_EXT_NS_CERT_RENEWAL_URL; break; case SEC_OID_NS_CERT_EXT_CA_POLICY_URL: string_id = IDS_CERT_EXT_NS_CA_POLICY_URL; break; case SEC_OID_NS_CERT_EXT_SSL_SERVER_NAME: string_id = IDS_CERT_EXT_NS_SSL_SERVER_NAME; break; case SEC_OID_NS_CERT_EXT_COMMENT: string_id = IDS_CERT_EXT_NS_COMMENT; break; case SEC_OID_NS_CERT_EXT_LOST_PASSWORD_URL: string_id = IDS_CERT_EXT_NS_LOST_PASSWORD_URL; break; case SEC_OID_NS_CERT_EXT_CERT_RENEWAL_TIME: string_id = IDS_CERT_EXT_NS_CERT_RENEWAL_TIME; break; case SEC_OID_X509_SUBJECT_DIRECTORY_ATTR: string_id = IDS_CERT_X509_SUBJECT_DIRECTORY_ATTR; break; case SEC_OID_X509_SUBJECT_KEY_ID: string_id = IDS_CERT_X509_SUBJECT_KEYID; break; case SEC_OID_X509_KEY_USAGE: string_id = IDS_CERT_X509_KEY_USAGE; break; case SEC_OID_X509_SUBJECT_ALT_NAME: string_id = IDS_CERT_X509_SUBJECT_ALT_NAME; break; case SEC_OID_X509_ISSUER_ALT_NAME: string_id = IDS_CERT_X509_ISSUER_ALT_NAME; break; case SEC_OID_X509_BASIC_CONSTRAINTS: string_id = IDS_CERT_X509_BASIC_CONSTRAINTS; break; case SEC_OID_X509_NAME_CONSTRAINTS: string_id = IDS_CERT_X509_NAME_CONSTRAINTS; break; case SEC_OID_X509_CRL_DIST_POINTS: string_id = IDS_CERT_X509_CRL_DIST_POINTS; break; case SEC_OID_X509_CERTIFICATE_POLICIES: string_id = IDS_CERT_X509_CERT_POLICIES; break; case SEC_OID_X509_POLICY_MAPPINGS: string_id = IDS_CERT_X509_POLICY_MAPPINGS; break; case SEC_OID_X509_POLICY_CONSTRAINTS: string_id = IDS_CERT_X509_POLICY_CONSTRAINTS; break; case SEC_OID_X509_AUTH_KEY_ID: string_id = IDS_CERT_X509_AUTH_KEYID; break; case SEC_OID_X509_EXT_KEY_USAGE: string_id = IDS_CERT_X509_EXT_KEY_USAGE; break; case SEC_OID_X509_AUTH_INFO_ACCESS: string_id = IDS_CERT_X509_AUTH_INFO_ACCESS; break; // There are a billionty other OIDs we could add here. I tried to get the // important ones... default: if (oid_tag == ms_cert_ext_certtype) string_id = IDS_CERT_EXT_MS_CERT_TYPE; else if (oid_tag == ms_certsrv_ca_version) string_id = IDS_CERT_EXT_MS_CA_VERSION; else if (oid_tag == ms_nt_principal_name) string_id = IDS_CERT_EXT_MS_NT_PRINCIPAL_NAME; else if (oid_tag == ms_ntds_replication) string_id = IDS_CERT_EXT_MS_NTDS_REPLICATION; else string_id = -1; break; } if (string_id >= 0) return l10n_util::GetStringUTF8(string_id); return DumpOidString(oid); } // Get a display string from a Relative Distinguished Name. std::string ProcessRDN(CERTRDN* rdn) { std::string rv; CERTAVA** avas = rdn->avas; for (size_t i = 0; avas[i] != NULL; ++i) { rv += GetOIDText(&avas[i]->type); SECItem* decode_item = CERT_DecodeAVAValue(&avas[i]->value); if (decode_item) { // TODO(mattm): Pass decode_item to CERT_RFC1485_EscapeAndQuote. rv += " = "; std::string value(reinterpret_cast(decode_item->data), decode_item->len); rv += value; SECITEM_FreeItem(decode_item, PR_TRUE); } rv += '\n'; } return rv; } std::string ProcessName(CERTName* name) { std::string rv; CERTRDN** last_rdn; // Find last non-NULL rdn. for (last_rdn = name->rdns; last_rdn[0]; last_rdn++) {} last_rdn--; for (CERTRDN** rdn = last_rdn; rdn >= name->rdns; rdn--) rv += ProcessRDN(*rdn); return rv; } std::string ProcessBasicConstraints(SECItem* extension_data) { CERTBasicConstraints value; value.pathLenConstraint = -1; if (CERT_DecodeBasicConstraintValue(&value, extension_data) != SECSuccess) return ProcessRawBytes(extension_data); std::string rv; if (value.isCA) rv = l10n_util::GetStringUTF8(IDS_CERT_X509_BASIC_CONSTRAINT_IS_CA); else rv = l10n_util::GetStringUTF8(IDS_CERT_X509_BASIC_CONSTRAINT_IS_NOT_CA); rv += '\n'; if (value.pathLenConstraint != -1) { string16 depth; if (value.pathLenConstraint == CERT_UNLIMITED_PATH_CONSTRAINT) { depth = l10n_util::GetStringUTF16( IDS_CERT_X509_BASIC_CONSTRAINT_PATH_LEN_UNLIMITED); } else { depth = base::FormatNumber(value.pathLenConstraint); } rv += l10n_util::GetStringFUTF8(IDS_CERT_X509_BASIC_CONSTRAINT_PATH_LEN, depth); } return rv; } std::string ProcessGeneralName(PRArenaPool* arena, CERTGeneralName* current) { DCHECK(current); std::string key; std::string value; switch (current->type) { case certOtherName: { key = GetOIDText(¤t->name.OthName.oid); SECOidTag oid_tag = SECOID_FindOIDTag(¤t->name.OthName.oid); if (oid_tag == ms_nt_principal_name) { // The type of this name is apparently nowhere explicitly // documented. However, in the generated templates, it is always // UTF-8. So try to decode this as UTF-8; if that fails, dump the // raw data. SECItem decoded; if (SEC_ASN1DecodeItem(arena, &decoded, SEC_ASN1_GET(SEC_UTF8StringTemplate), ¤t->name.OthName.name) == SECSuccess) { value = std::string(reinterpret_cast(decoded.data), decoded.len); } else { value = ProcessRawBytes(¤t->name.OthName.name); } break; } else if (oid_tag == ms_ntds_replication) { // This should be a 16-byte GUID. SECItem guid; if (SEC_ASN1DecodeItem(arena, &guid, SEC_ASN1_GET(SEC_OctetStringTemplate), ¤t->name.OthName.name) == SECSuccess && guid.len == 16) { unsigned char* d = guid.data; SStringPrintf(&value, "{%.2x%.2x%.2x%.2x-%.2x%.2x-%.2x%.2x-" "%.2x%.2x-%.2x%.2x%.2x%.2x%.2x%.2x}", d[3], d[2], d[1], d[0], d[5], d[4], d[7], d[6], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); } else { value = ProcessRawBytes(¤t->name.OthName.name); } } else { value = ProcessRawBytes(¤t->name.OthName.name); } break; } case certRFC822Name: key = l10n_util::GetStringUTF8(IDS_CERT_GENERAL_NAME_RFC822_NAME); value = std::string(reinterpret_cast(current->name.other.data), current->name.other.len); break; case certDNSName: key = l10n_util::GetStringUTF8(IDS_CERT_GENERAL_NAME_DNS_NAME); value = std::string(reinterpret_cast(current->name.other.data), current->name.other.len); break; case certX400Address: key = l10n_util::GetStringUTF8(IDS_CERT_GENERAL_NAME_X400_ADDRESS); value = ProcessRawBytes(¤t->name.other); break; case certDirectoryName: key = l10n_util::GetStringUTF8(IDS_CERT_GENERAL_NAME_DIRECTORY_NAME); value = ProcessName(¤t->name.directoryName); break; case certEDIPartyName: key = l10n_util::GetStringUTF8(IDS_CERT_GENERAL_NAME_EDI_PARTY_NAME); value = ProcessRawBytes(¤t->name.other); break; case certURI: key = l10n_util::GetStringUTF8(IDS_CERT_GENERAL_NAME_URI); value = std::string(reinterpret_cast(current->name.other.data), current->name.other.len); break; case certIPAddress: { key = l10n_util::GetStringUTF8(IDS_CERT_GENERAL_NAME_IP_ADDRESS); struct addrinfo addr = {0}; if (current->name.other.len == 4) { struct sockaddr_in addr4 = {0}; addr.ai_addr = reinterpret_cast(&addr4); addr.ai_addrlen = sizeof(addr4); addr.ai_family = AF_INET; addr4.sin_family = addr.ai_family; memcpy(&addr4.sin_addr, current->name.other.data, current->name.other.len); value = net::NetAddressToString(&addr); } else if (current->name.other.len == 16) { struct sockaddr_in6 addr6 = {0}; addr.ai_addr = reinterpret_cast(&addr6); addr.ai_addrlen = sizeof(addr6); addr.ai_family = AF_INET6; addr6.sin6_family = addr.ai_family; memcpy(&addr6.sin6_addr, current->name.other.data, current->name.other.len); value = net::NetAddressToString(&addr); } if (value.empty()) { // Invalid IP address. value = ProcessRawBytes(¤t->name.other); } break; } case certRegisterID: key = l10n_util::GetStringUTF8(IDS_CERT_GENERAL_NAME_REGISTERED_ID); value = DumpOidString(¤t->name.other); break; } std::string rv(l10n_util::GetStringFUTF8(IDS_CERT_UNKNOWN_OID_INFO_FORMAT, UTF8ToUTF16(key), UTF8ToUTF16(value))); rv += '\n'; return rv; } std::string ProcessGeneralNames(PRArenaPool* arena, CERTGeneralName* name_list) { std::string rv; CERTGeneralName* current = name_list; do { std::string text = ProcessGeneralName(arena, current); if (text.empty()) break; rv += text + '\n'; current = CERT_GetNextGeneralName(current); } while (current != name_list); return rv; } std::string ProcessAltName(SECItem* extension_data) { CERTGeneralName* name_list; ScopedPRArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); CHECK(arena.get()); name_list = CERT_DecodeAltNameExtension(arena.get(), extension_data); if (!name_list) return l10n_util::GetStringUTF8(IDS_CERT_EXTENSION_DUMP_ERROR); return ProcessGeneralNames(arena.get(), name_list); } std::string ProcessSubjectKeyId(SECItem* extension_data) { SECItem decoded; ScopedPRArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); CHECK(arena.get()); std::string rv; if (SEC_QuickDERDecodeItem(arena.get(), &decoded, SEC_ASN1_GET(SEC_OctetStringTemplate), extension_data) != SECSuccess) { rv = l10n_util::GetStringUTF8(IDS_CERT_EXTENSION_DUMP_ERROR); return rv; } rv = l10n_util::GetStringFUTF8(IDS_CERT_KEYID_FORMAT, ASCIIToUTF16(ProcessRawBytes(&decoded))); return rv; } std::string ProcessAuthKeyId(SECItem* extension_data) { CERTAuthKeyID* ret; ScopedPRArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); std::string rv; CHECK(arena.get()); ret = CERT_DecodeAuthKeyID(arena.get(), extension_data); if (ret->keyID.len > 0) { rv += l10n_util::GetStringFUTF8(IDS_CERT_KEYID_FORMAT, ASCIIToUTF16(ProcessRawBytes(&ret->keyID))); rv += '\n'; } if (ret->authCertIssuer) { rv += l10n_util::GetStringFUTF8( IDS_CERT_ISSUER_FORMAT, UTF8ToUTF16(ProcessGeneralNames(arena.get(), ret->authCertIssuer))); rv += '\n'; } if (ret->authCertSerialNumber.len > 0) { rv += l10n_util::GetStringFUTF8( IDS_CERT_SERIAL_NUMBER_FORMAT, ASCIIToUTF16(ProcessRawBytes(&ret->authCertSerialNumber))); rv += '\n'; } return rv; } std::string ProcessCrlDistPoints(SECItem* extension_data) { std::string rv; CERTCrlDistributionPoints* crldp; CRLDistributionPoint** points; CRLDistributionPoint* point; bool comma; static const struct { int reason; int string_id; } reason_string_map[] = { {RF_UNUSED, IDS_CERT_REVOCATION_REASON_UNUSED}, {RF_KEY_COMPROMISE, IDS_CERT_REVOCATION_REASON_KEY_COMPROMISE}, {RF_CA_COMPROMISE, IDS_CERT_REVOCATION_REASON_CA_COMPROMISE}, {RF_AFFILIATION_CHANGED, IDS_CERT_REVOCATION_REASON_AFFILIATION_CHANGED}, {RF_SUPERSEDED, IDS_CERT_REVOCATION_REASON_SUPERSEDED}, {RF_CESSATION_OF_OPERATION, IDS_CERT_REVOCATION_REASON_CESSATION_OF_OPERATION}, {RF_CERTIFICATE_HOLD, IDS_CERT_REVOCATION_REASON_CERTIFICATE_HOLD}, }; ScopedPRArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); CHECK(arena.get()); crldp = CERT_DecodeCRLDistributionPoints(arena.get(), extension_data); if (!crldp || !crldp->distPoints) { rv = l10n_util::GetStringUTF8(IDS_CERT_EXTENSION_DUMP_ERROR); return rv; } for (points = crldp->distPoints; *points; ++points) { point = *points; switch (point->distPointType) { case generalName: rv += ProcessGeneralName(arena.get(), point->distPoint.fullName); break; case relativeDistinguishedName: rv += ProcessRDN(&point->distPoint.relativeName); break; } if (point->reasons.len) { rv += ' '; comma = false; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(reason_string_map); ++i) { if (point->reasons.data[0] & reason_string_map[i].reason) { if (comma) rv += ','; rv += l10n_util::GetStringUTF8(reason_string_map[i].string_id); } } rv += '\n'; } if (point->crlIssuer) { rv += l10n_util::GetStringFUTF8( IDS_CERT_ISSUER_FORMAT, UTF8ToUTF16(ProcessGeneralNames(arena.get(), point->crlIssuer))); } } return rv; } std::string ProcessAuthInfoAccess(SECItem* extension_data) { std::string rv; CERTAuthInfoAccess** aia; CERTAuthInfoAccess* desc; ScopedPRArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); CHECK(arena.get()); aia = CERT_DecodeAuthInfoAccessExtension(arena.get(), extension_data); if (aia == NULL) return l10n_util::GetStringUTF8(IDS_CERT_EXTENSION_DUMP_ERROR); while (*aia != NULL) { desc = *aia++; string16 location_str = UTF8ToUTF16(ProcessGeneralName(arena.get(), desc->location)); switch (SECOID_FindOIDTag(&desc->method)) { case SEC_OID_PKIX_OCSP: rv += l10n_util::GetStringFUTF8(IDS_CERT_OCSP_RESPONDER_FORMAT, location_str); break; case SEC_OID_PKIX_CA_ISSUERS: rv += l10n_util::GetStringFUTF8(IDS_CERT_CA_ISSUERS_FORMAT, location_str); break; default: rv += l10n_util::GetStringFUTF8(IDS_CERT_UNKNOWN_OID_INFO_FORMAT, UTF8ToUTF16(GetOIDText(&desc->method)), location_str); break; } } return rv; } std::string ProcessIA5String(SECItem* extension_data) { SECItem item; if (SEC_ASN1DecodeItem(NULL, &item, SEC_ASN1_GET(SEC_IA5StringTemplate), extension_data) != SECSuccess) return l10n_util::GetStringUTF8(IDS_CERT_EXTENSION_DUMP_ERROR); std::string rv((char*)item.data, item.len); // ASCII data. PORT_Free(item.data); return rv; } std::string ProcessBMPString(SECItem* extension_data) { std::string rv; SECItem item; ScopedPRArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); CHECK(arena.get()); if (SEC_ASN1DecodeItem(arena.get(), &item, SEC_ASN1_GET(SEC_BMPStringTemplate), extension_data) == SECSuccess) rv = BMPtoUTF8(arena.get(), item.data, item.len); return rv; } struct MaskIdPair { unsigned int mask; int string_id; }; static std::string ProcessBitField(SECItem* bitfield, const MaskIdPair* string_map, size_t len, char separator) { unsigned int bits = 0; std::string rv; for (size_t i = 0; i * 8 < bitfield->len && i < sizeof(bits); ++i) bits |= bitfield->data[i] << (i * 8); for (size_t i = 0; i < len; ++i) { if (bits & string_map[i].mask) { if (!rv.empty()) rv += separator; rv += l10n_util::GetStringUTF8(string_map[i].string_id); } } return rv; } static std::string ProcessBitStringExtension(SECItem* extension_data, const MaskIdPair* string_map, size_t len, char separator) { SECItem decoded; decoded.type = siBuffer; decoded.data = NULL; decoded.len = 0; if (SEC_ASN1DecodeItem(NULL, &decoded, SEC_ASN1_GET(SEC_BitStringTemplate), extension_data) != SECSuccess) return l10n_util::GetStringUTF8(IDS_CERT_EXTENSION_DUMP_ERROR); std::string rv = ProcessBitField(&decoded, string_map, len, separator); PORT_Free(decoded.data); return rv; } std::string ProcessNSCertTypeExtension(SECItem* extension_data) { static const MaskIdPair usage_string_map[] = { {NS_CERT_TYPE_SSL_CLIENT, IDS_CERT_USAGE_SSL_CLIENT}, {NS_CERT_TYPE_SSL_SERVER, IDS_CERT_USAGE_SSL_SERVER}, {NS_CERT_TYPE_EMAIL, IDS_CERT_EXT_NS_CERT_TYPE_EMAIL}, {NS_CERT_TYPE_OBJECT_SIGNING, IDS_CERT_USAGE_OBJECT_SIGNER}, {NS_CERT_TYPE_SSL_CA, IDS_CERT_USAGE_SSL_CA}, {NS_CERT_TYPE_EMAIL_CA, IDS_CERT_EXT_NS_CERT_TYPE_EMAIL_CA}, {NS_CERT_TYPE_OBJECT_SIGNING_CA, IDS_CERT_USAGE_OBJECT_SIGNER}, }; return ProcessBitStringExtension(extension_data, usage_string_map, ARRAYSIZE_UNSAFE(usage_string_map), '\n'); } static const MaskIdPair key_usage_string_map[] = { {KU_DIGITAL_SIGNATURE, IDS_CERT_X509_KEY_USAGE_SIGNING}, {KU_NON_REPUDIATION, IDS_CERT_X509_KEY_USAGE_NONREP}, {KU_KEY_ENCIPHERMENT, IDS_CERT_X509_KEY_USAGE_ENCIPHERMENT}, {KU_DATA_ENCIPHERMENT, IDS_CERT_X509_KEY_USAGE_DATA_ENCIPHERMENT}, {KU_KEY_AGREEMENT, IDS_CERT_X509_KEY_USAGE_KEY_AGREEMENT}, {KU_KEY_CERT_SIGN, IDS_CERT_X509_KEY_USAGE_CERT_SIGNER}, {KU_CRL_SIGN, IDS_CERT_X509_KEY_USAGE_CRL_SIGNER}, {KU_ENCIPHER_ONLY, IDS_CERT_X509_KEY_USAGE_ENCIPHER_ONLY}, // NSS is missing a flag for dechiperOnly, see: // https://bugzilla.mozilla.org/show_bug.cgi?id=549952 }; std::string ProcessKeyUsageBitString(SECItem* bitstring, char sep) { return ProcessBitField(bitstring, key_usage_string_map, arraysize(key_usage_string_map), sep); } std::string ProcessKeyUsageExtension(SECItem* extension_data) { return ProcessBitStringExtension(extension_data, key_usage_string_map, arraysize(key_usage_string_map), '\n'); } std::string ProcessExtKeyUsage(SECItem* extension_data) { std::string rv; CERTOidSequence* extension_key_usage = NULL; extension_key_usage = CERT_DecodeOidSequence(extension_data); if (extension_key_usage == NULL) return l10n_util::GetStringUTF8(IDS_CERT_EXTENSION_DUMP_ERROR); SECItem** oids; SECItem* oid; for (oids = extension_key_usage->oids; oids != NULL && *oids != NULL; ++oids) { // TODO(mattm): Need to either lookup strings here based on the OIDs or add // more OIDS to GetOIDText. (See the strings of the form // CertDumpEKU_ in Mozilla.) oid = *oids; rv += GetOIDText(oid); rv += '\n'; } CERT_DestroyOidSequence(extension_key_usage); return rv; } std::string ProcessExtensionData(SECOidTag oid_tag, SECItem* extension_data) { // This (and its sub-functions) are based on the same-named functions in // security/manager/ssl/src/nsNSSCertHelper.cpp. switch (oid_tag) { case SEC_OID_NS_CERT_EXT_CERT_TYPE: return ProcessNSCertTypeExtension(extension_data); case SEC_OID_X509_KEY_USAGE: return ProcessKeyUsageExtension(extension_data); case SEC_OID_X509_BASIC_CONSTRAINTS: return ProcessBasicConstraints(extension_data); case SEC_OID_X509_EXT_KEY_USAGE: return ProcessExtKeyUsage(extension_data); case SEC_OID_X509_ISSUER_ALT_NAME: case SEC_OID_X509_SUBJECT_ALT_NAME: return ProcessAltName(extension_data); case SEC_OID_X509_SUBJECT_KEY_ID: return ProcessSubjectKeyId(extension_data); case SEC_OID_X509_AUTH_KEY_ID: return ProcessAuthKeyId(extension_data); // TODO(mattm): // case SEC_OID_X509_CERTIFICATE_POLICIES: // return ProcessCertificatePolicies(extension_data, ev_oid_tag); case SEC_OID_X509_CRL_DIST_POINTS: return ProcessCrlDistPoints(extension_data); case SEC_OID_X509_AUTH_INFO_ACCESS: return ProcessAuthInfoAccess(extension_data); case SEC_OID_NS_CERT_EXT_BASE_URL: case SEC_OID_NS_CERT_EXT_REVOCATION_URL: case SEC_OID_NS_CERT_EXT_CA_REVOCATION_URL: case SEC_OID_NS_CERT_EXT_CA_CERT_URL: case SEC_OID_NS_CERT_EXT_CERT_RENEWAL_URL: case SEC_OID_NS_CERT_EXT_CA_POLICY_URL: case SEC_OID_NS_CERT_EXT_HOMEPAGE_URL: case SEC_OID_NS_CERT_EXT_COMMENT: case SEC_OID_NS_CERT_EXT_SSL_SERVER_NAME: case SEC_OID_NS_CERT_EXT_LOST_PASSWORD_URL: return ProcessIA5String(extension_data); default: if (oid_tag == ms_cert_ext_certtype) return ProcessBMPString(extension_data); return ProcessRawBytes(extension_data); } } std::string ProcessSubjectPublicKeyInfo(CERTSubjectPublicKeyInfo* spki) { std::string rv; SECKEYPublicKey* key = SECKEY_ExtractPublicKey(spki); if (key) { switch (key->keyType) { case rsaKey: { rv = l10n_util::GetStringFUTF8( IDS_CERT_RSA_PUBLIC_KEY_DUMP_FORMAT, UintToString16(key->u.rsa.modulus.len * 8), UTF8ToUTF16(ProcessRawBytes(&key->u.rsa.modulus)), UintToString16(key->u.rsa.publicExponent.len * 8), UTF8ToUTF16(ProcessRawBytes(&key->u.rsa.publicExponent))); break; } default: rv = ProcessRawBits(&spki->subjectPublicKey); break; } SECKEY_DestroyPublicKey(key); } return rv; } } // namespace mozilla_security_manager