/* ***** 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 #include #include "base/i18n/number_formatting.h" #include "base/lazy_instance.h" #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "chrome/common/net/x509_certificate_model.h" #include "crypto/scoped_nss_types.h" #include "chrome/grit/generated_resources.h" #include "net/base/ip_endpoint.h" #include "net/base/net_util.h" #include "ui/base/l10n/l10n_util.h" #if !defined(CERTDB_TERMINAL_RECORD) /* NSS 3.13 renames CERTDB_VALID_PEER to CERTDB_TERMINAL_RECORD * and marks CERTDB_VALID_PEER as deprecated. * If we're using an older version, rename it ourselves. */ #define CERTDB_TERMINAL_RECORD CERTDB_VALID_PEER #endif 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); } SECOidTag RegisterDynamicOid(const char* oid_string) { SECOidTag rv = SEC_OID_UNKNOWN; unsigned char buffer[1024]; SECOidData od; od.oid.type = siDEROID; od.oid.data = buffer; od.oid.len = sizeof(buffer); if (SEC_StringToOID(NULL, &od.oid, oid_string, 0) == SECSuccess) { od.offset = SEC_OID_UNKNOWN; od.mechanism = CKM_INVALID_MECHANISM; od.supportedExtension = INVALID_CERT_EXTENSION; od.desc = oid_string; rv = SECOID_AddEntry(&od); } DCHECK_NE(rv, SEC_OID_UNKNOWN) << oid_string; return rv; } // Format a SECItem as a space separated string, with 16 bytes on each line. std::string ProcessRawBytes(SECItem* data) { return x509_certificate_model::ProcessRawBytes(data->data, data->len); } 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; SECOidTag eku_ms_individual_code_signing = SEC_OID_UNKNOWN; SECOidTag eku_ms_commercial_code_signing = SEC_OID_UNKNOWN; SECOidTag eku_ms_trust_list_signing = SEC_OID_UNKNOWN; SECOidTag eku_ms_time_stamping = SEC_OID_UNKNOWN; SECOidTag eku_ms_server_gated_crypto = SEC_OID_UNKNOWN; SECOidTag eku_ms_encrypting_file_system = SEC_OID_UNKNOWN; SECOidTag eku_ms_file_recovery = SEC_OID_UNKNOWN; SECOidTag eku_ms_windows_hardware_driver_verification = SEC_OID_UNKNOWN; SECOidTag eku_ms_qualified_subordination = SEC_OID_UNKNOWN; SECOidTag eku_ms_key_recovery = SEC_OID_UNKNOWN; SECOidTag eku_ms_document_signing = SEC_OID_UNKNOWN; SECOidTag eku_ms_lifetime_signing = SEC_OID_UNKNOWN; SECOidTag eku_ms_smart_card_logon = SEC_OID_UNKNOWN; SECOidTag eku_ms_key_recovery_agent = SEC_OID_UNKNOWN; SECOidTag eku_netscape_international_step_up = SEC_OID_UNKNOWN; SECOidTag cert_attribute_business_category = SEC_OID_UNKNOWN; SECOidTag cert_attribute_ev_incorporation_country = SEC_OID_UNKNOWN; class DynamicOidRegisterer { public: DynamicOidRegisterer() { ms_cert_ext_certtype = RegisterDynamicOid("1.3.6.1.4.1.311.20.2"); ms_certsrv_ca_version = RegisterDynamicOid("1.3.6.1.4.1.311.21.1"); ms_nt_principal_name = RegisterDynamicOid("1.3.6.1.4.1.311.20.2.3"); ms_ntds_replication = RegisterDynamicOid("1.3.6.1.4.1.311.25.1"); eku_ms_individual_code_signing = RegisterDynamicOid("1.3.6.1.4.1.311.2.1.21"); eku_ms_commercial_code_signing = RegisterDynamicOid("1.3.6.1.4.1.311.2.1.22"); eku_ms_trust_list_signing = RegisterDynamicOid("1.3.6.1.4.1.311.10.3.1"); eku_ms_time_stamping = RegisterDynamicOid("1.3.6.1.4.1.311.10.3.2"); eku_ms_server_gated_crypto = RegisterDynamicOid("1.3.6.1.4.1.311.10.3.3"); eku_ms_encrypting_file_system = RegisterDynamicOid("1.3.6.1.4.1.311.10.3.4"); eku_ms_file_recovery = RegisterDynamicOid("1.3.6.1.4.1.311.10.3.4.1"); eku_ms_windows_hardware_driver_verification = RegisterDynamicOid( "1.3.6.1.4.1.311.10.3.5"); eku_ms_qualified_subordination = RegisterDynamicOid( "1.3.6.1.4.1.311.10.3.10"); eku_ms_key_recovery = RegisterDynamicOid("1.3.6.1.4.1.311.10.3.11"); eku_ms_document_signing = RegisterDynamicOid("1.3.6.1.4.1.311.10.3.12"); eku_ms_lifetime_signing = RegisterDynamicOid("1.3.6.1.4.1.311.10.3.13"); eku_ms_smart_card_logon = RegisterDynamicOid("1.3.6.1.4.1.311.20.2.2"); eku_ms_key_recovery_agent = RegisterDynamicOid("1.3.6.1.4.1.311.21.6"); eku_netscape_international_step_up = RegisterDynamicOid( "2.16.840.1.113730.4.1"); // These two OIDs will be built-in as SEC_OID_BUSINESS_CATEGORY and // SEC_OID_EV_INCORPORATION_COUNTRY starting in NSS 3.13. Until then, // we need to add them dynamically. cert_attribute_business_category = RegisterDynamicOid("2.5.4.15"); cert_attribute_ev_incorporation_country = RegisterDynamicOid( "1.3.6.1.4.1.311.60.2.1.3"); } }; static base::LazyInstance::Leaky g_dynamic_oid_registerer = LAZY_INSTANCE_INITIALIZER; } // namespace namespace mozilla_security_manager { 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) { g_dynamic_oid_registerer.Get(); 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; case SEC_OID_EXT_KEY_USAGE_SERVER_AUTH: string_id = IDS_CERT_EKU_TLS_WEB_SERVER_AUTHENTICATION; break; case SEC_OID_EXT_KEY_USAGE_CLIENT_AUTH: string_id = IDS_CERT_EKU_TLS_WEB_CLIENT_AUTHENTICATION; break; case SEC_OID_EXT_KEY_USAGE_CODE_SIGN: string_id = IDS_CERT_EKU_CODE_SIGNING; break; case SEC_OID_EXT_KEY_USAGE_EMAIL_PROTECT: string_id = IDS_CERT_EKU_EMAIL_PROTECTION; break; case SEC_OID_EXT_KEY_USAGE_TIME_STAMP: string_id = IDS_CERT_EKU_TIME_STAMPING; break; case SEC_OID_OCSP_RESPONDER: string_id = IDS_CERT_EKU_OCSP_SIGNING; break; case SEC_OID_PKIX_CPS_POINTER_QUALIFIER: string_id = IDS_CERT_PKIX_CPS_POINTER_QUALIFIER; break; case SEC_OID_PKIX_USER_NOTICE_QUALIFIER: string_id = IDS_CERT_PKIX_USER_NOTICE_QUALIFIER; break; case SEC_OID_UNKNOWN: string_id = -1; 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 if (oid_tag == eku_ms_individual_code_signing) string_id = IDS_CERT_EKU_MS_INDIVIDUAL_CODE_SIGNING; else if (oid_tag == eku_ms_commercial_code_signing) string_id = IDS_CERT_EKU_MS_COMMERCIAL_CODE_SIGNING; else if (oid_tag == eku_ms_trust_list_signing) string_id = IDS_CERT_EKU_MS_TRUST_LIST_SIGNING; else if (oid_tag == eku_ms_time_stamping) string_id = IDS_CERT_EKU_MS_TIME_STAMPING; else if (oid_tag == eku_ms_server_gated_crypto) string_id = IDS_CERT_EKU_MS_SERVER_GATED_CRYPTO; else if (oid_tag == eku_ms_encrypting_file_system) string_id = IDS_CERT_EKU_MS_ENCRYPTING_FILE_SYSTEM; else if (oid_tag == eku_ms_file_recovery) string_id = IDS_CERT_EKU_MS_FILE_RECOVERY; else if (oid_tag == eku_ms_windows_hardware_driver_verification) string_id = IDS_CERT_EKU_MS_WINDOWS_HARDWARE_DRIVER_VERIFICATION; else if (oid_tag == eku_ms_qualified_subordination) string_id = IDS_CERT_EKU_MS_QUALIFIED_SUBORDINATION; else if (oid_tag == eku_ms_key_recovery) string_id = IDS_CERT_EKU_MS_KEY_RECOVERY; else if (oid_tag == eku_ms_document_signing) string_id = IDS_CERT_EKU_MS_DOCUMENT_SIGNING; else if (oid_tag == eku_ms_lifetime_signing) string_id = IDS_CERT_EKU_MS_LIFETIME_SIGNING; else if (oid_tag == eku_ms_smart_card_logon) string_id = IDS_CERT_EKU_MS_SMART_CARD_LOGON; else if (oid_tag == eku_ms_key_recovery_agent) string_id = IDS_CERT_EKU_MS_KEY_RECOVERY_AGENT; else if (oid_tag == eku_netscape_international_step_up) string_id = IDS_CERT_EKU_NETSCAPE_INTERNATIONAL_STEP_UP; else if (oid_tag == cert_attribute_business_category) string_id = IDS_CERT_OID_BUSINESS_CATEGORY; else if (oid_tag == cert_attribute_ev_incorporation_country) string_id = IDS_CERT_OID_EV_INCORPORATION_COUNTRY; 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); if (SECOID_FindOIDTag(&avas[i]->type) == SEC_OID_AVA_COMMON_NAME) value = x509_certificate_model::ProcessIDN(value); 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) { base::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); // g_dynamic_oid_registerer.Get() will have been run by GetOIDText. 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; base::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); value = x509_certificate_model::ProcessIDN(value); 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); net::IPAddressNumber ip( current->name.other.data, current->name.other.data + current->name.other.len); if (net::GetAddressFamily(ip) != net::ADDRESS_FAMILY_UNSPECIFIED) { value = net::IPAddressToString(ip); } else { // 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, base::UTF8ToUTF16(key), base::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; current = CERT_GetNextGeneralName(current); } while (current != name_list); return rv; } std::string ProcessAltName(SECItem* extension_data) { CERTGeneralName* name_list; crypto::ScopedPLArenaPool 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; crypto::ScopedPLArenaPool 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, base::ASCIIToUTF16(ProcessRawBytes(&decoded))); return rv; } std::string ProcessAuthKeyId(SECItem* extension_data) { CERTAuthKeyID* ret; crypto::ScopedPLArenaPool 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, base::ASCIIToUTF16( ProcessRawBytes(&ret->keyID))); rv += '\n'; } if (ret->authCertIssuer) { rv += l10n_util::GetStringFUTF8( IDS_CERT_ISSUER_FORMAT, base::UTF8ToUTF16( ProcessGeneralNames(arena.get(), ret->authCertIssuer))); rv += '\n'; } if (ret->authCertSerialNumber.len > 0) { rv += l10n_util::GetStringFUTF8( IDS_CERT_SERIAL_NUMBER_FORMAT, base::ASCIIToUTF16(ProcessRawBytes(&ret->authCertSerialNumber))); rv += '\n'; } return rv; } std::string ProcessUserNotice(SECItem* der_notice) { CERTUserNotice* notice = CERT_DecodeUserNotice(der_notice); if (!notice) return ProcessRawBytes(der_notice); std::string rv; if (notice->noticeReference.organization.len != 0) { switch (notice->noticeReference.organization.type) { case siAsciiString: case siVisibleString: case siUTF8String: rv += std::string( reinterpret_cast(notice->noticeReference.organization.data), notice->noticeReference.organization.len); break; case siBMPString: rv += ProcessBMPString(¬ice->noticeReference.organization); break; default: break; } rv += " - "; SECItem** itemList = notice->noticeReference.noticeNumbers; while (*itemList) { unsigned long number; if (SEC_ASN1DecodeInteger(*itemList, &number) == SECSuccess) { if (itemList != notice->noticeReference.noticeNumbers) rv += ", "; rv += '#'; rv += base::UTF16ToUTF8(base::UintToString16(number)); } itemList++; } } if (notice->displayText.len != 0) { rv += "\n "; switch (notice->displayText.type) { case siAsciiString: case siVisibleString: case siUTF8String: rv += std::string(reinterpret_cast(notice->displayText.data), notice->displayText.len); break; case siBMPString: rv += ProcessBMPString(¬ice->displayText); break; default: break; } } CERT_DestroyUserNotice(notice); return rv; } std::string ProcessCertificatePolicies(SECItem* extension_data) { std::string rv; CERTCertificatePolicies* policies = CERT_DecodeCertificatePoliciesExtension( extension_data); if (!policies) return l10n_util::GetStringUTF8(IDS_CERT_EXTENSION_DUMP_ERROR); CERTPolicyInfo** policyInfos = policies->policyInfos; while (*policyInfos) { CERTPolicyInfo* policyInfo = *policyInfos++; std::string key = GetOIDText(&policyInfo->policyID); // If we have policy qualifiers, display the oid text // with a ':', otherwise just put the oid text and a newline. // TODO(mattm): Add extra note if this is the ev oid? (It's a bit // complicated, since we don't want to do the EV check synchronously.) if (policyInfo->policyQualifiers) { rv += l10n_util::GetStringFUTF8(IDS_CERT_MULTILINE_INFO_START_FORMAT, base::UTF8ToUTF16(key)); } else { rv += key; } rv += '\n'; if (policyInfo->policyQualifiers) { // Add all qualifiers on separate lines, indented. CERTPolicyQualifier** policyQualifiers = policyInfo->policyQualifiers; while (*policyQualifiers != NULL) { rv += " "; CERTPolicyQualifier* policyQualifier = *policyQualifiers++; rv += l10n_util::GetStringFUTF8( IDS_CERT_MULTILINE_INFO_START_FORMAT, base::UTF8ToUTF16(GetOIDText(&policyQualifier->qualifierID))); switch(policyQualifier->oid) { case SEC_OID_PKIX_CPS_POINTER_QUALIFIER: rv += " "; /* The CPS pointer ought to be the cPSuri alternative of the Qualifier choice. */ rv += ProcessIA5String(&policyQualifier->qualifierValue); break; case SEC_OID_PKIX_USER_NOTICE_QUALIFIER: rv += ProcessUserNotice(&policyQualifier->qualifierValue); break; default: rv += ProcessRawBytes(&policyQualifier->qualifierValue); break; } rv += '\n'; } } } CERT_DestroyCertificatePoliciesExtension(policies); 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}, }; crypto::ScopedPLArenaPool 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: // generalName is a typo in upstream NSS; fullName is actually a // GeneralNames (SEQUENCE OF GeneralName). See Mozilla Bug #615100. rv += ProcessGeneralNames(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(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); comma = true; } } rv += '\n'; } if (point->crlIssuer) { rv += l10n_util::GetStringFUTF8( IDS_CERT_ISSUER_FORMAT, base::UTF8ToUTF16( ProcessGeneralNames(arena.get(), point->crlIssuer))); } } return rv; } std::string ProcessAuthInfoAccess(SECItem* extension_data) { std::string rv; CERTAuthInfoAccess** aia; CERTAuthInfoAccess* desc; crypto::ScopedPLArenaPool 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++; base::string16 location_str = base::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, base::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; crypto::ScopedPLArenaPool 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(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) { oid = *oids; std::string oid_dump = DumpOidString(oid); std::string oid_text = GetOIDText(oid); // If oid is one we recognize, oid_text will have a text description of the // OID, which we display along with the oid_dump. If we don't recognize the // OID, GetOIDText will return the same value as DumpOidString, so just // display the OID alone. if (oid_dump == oid_text) rv += oid_dump; else rv += l10n_util::GetStringFUTF8(IDS_CERT_EXT_KEY_USAGE_FORMAT, base::UTF8ToUTF16(oid_text), base::UTF8ToUTF16(oid_dump)); rv += '\n'; } CERT_DestroyOidSequence(extension_key_usage); return rv; } std::string ProcessExtensionData(CERTCertExtension* extension) { g_dynamic_oid_registerer.Get(); SECOidTag oid_tag = SECOID_FindOIDTag(&extension->id); SECItem* extension_data = &extension->value; // 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); case SEC_OID_X509_CERTIFICATE_POLICIES: return ProcessCertificatePolicies(extension_data); 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, base::UintToString16(key->u.rsa.modulus.len * 8), base::UTF8ToUTF16(ProcessRawBytes(&key->u.rsa.modulus)), base::UintToString16(key->u.rsa.publicExponent.len * 8), base::UTF8ToUTF16(ProcessRawBytes(&key->u.rsa.publicExponent))); break; } default: rv = x509_certificate_model::ProcessRawBits( spki->subjectPublicKey.data, spki->subjectPublicKey.len); break; } SECKEY_DestroyPublicKey(key); } return rv; } net::CertType GetCertType(CERTCertificate *cert) { CERTCertTrust trust = {0}; CERT_GetCertTrust(cert, &trust); unsigned all_flags = trust.sslFlags | trust.emailFlags | trust.objectSigningFlags; if (cert->nickname && (all_flags & CERTDB_USER)) return net::USER_CERT; if ((all_flags & CERTDB_VALID_CA) || CERT_IsCACert(cert, NULL)) return net::CA_CERT; // TODO(mattm): http://crbug.com/128633. if (trust.sslFlags & CERTDB_TERMINAL_RECORD) return net::SERVER_CERT; return net::OTHER_CERT; } } // namespace mozilla_security_manager