mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
293 lines
11 KiB
C++
293 lines
11 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This code is made available to you under your choice of the following sets
|
|
* of licensing terms:
|
|
*/
|
|
/* 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/.
|
|
*/
|
|
/* Copyright 2014 Mozilla Contributors
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "pkix/bind.h"
|
|
#include "pkixutil.h"
|
|
|
|
namespace mozilla { namespace pkix {
|
|
|
|
Result
|
|
BackCert::Init()
|
|
{
|
|
Result rv;
|
|
|
|
// Certificate ::= SEQUENCE {
|
|
// tbsCertificate TBSCertificate,
|
|
// signatureAlgorithm AlgorithmIdentifier,
|
|
// signatureValue BIT STRING }
|
|
|
|
Reader tbsCertificate;
|
|
|
|
// The scope of |input| and |certificate| are limited to this block so we
|
|
// don't accidentally confuse them for tbsCertificate later.
|
|
{
|
|
Reader input(der);
|
|
Reader certificate;
|
|
rv = der::ExpectTagAndGetValue(input, der::SEQUENCE, certificate);
|
|
if (rv != Success) {
|
|
return rv;
|
|
}
|
|
rv = der::End(input);
|
|
if (rv != Success) {
|
|
return rv;
|
|
}
|
|
rv = der::SignedData(certificate, tbsCertificate, signedData);
|
|
if (rv != Success) {
|
|
return rv;
|
|
}
|
|
rv = der::End(certificate);
|
|
if (rv != Success) {
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
// TBSCertificate ::= SEQUENCE {
|
|
// version [0] EXPLICIT Version DEFAULT v1,
|
|
// serialNumber CertificateSerialNumber,
|
|
// signature AlgorithmIdentifier,
|
|
// issuer Name,
|
|
// validity Validity,
|
|
// subject Name,
|
|
// subjectPublicKeyInfo SubjectPublicKeyInfo,
|
|
// issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
|
|
// -- If present, version MUST be v2 or v3
|
|
// subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
|
|
// -- If present, version MUST be v2 or v3
|
|
// extensions [3] EXPLICIT Extensions OPTIONAL
|
|
// -- If present, version MUST be v3
|
|
// }
|
|
rv = der::OptionalVersion(tbsCertificate, version);
|
|
if (rv != Success) {
|
|
return rv;
|
|
}
|
|
rv = der::CertificateSerialNumber(tbsCertificate, serialNumber);
|
|
if (rv != Success) {
|
|
return rv;
|
|
}
|
|
// XXX: Ignored. What are we supposed to check? This seems totally redundant
|
|
// with Certificate.signatureAlgorithm. Is it important to check that they
|
|
// are consistent with each other? It doesn't seem to matter!
|
|
SignatureAlgorithm signature;
|
|
rv = der::SignatureAlgorithmIdentifier(tbsCertificate, signature);
|
|
if (rv != Success) {
|
|
return rv;
|
|
}
|
|
rv = der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE, issuer);
|
|
if (rv != Success) {
|
|
return rv;
|
|
}
|
|
rv = der::ExpectTagAndGetValue(tbsCertificate, der::SEQUENCE, validity);
|
|
if (rv != Success) {
|
|
return rv;
|
|
}
|
|
// TODO(bug XXXXXXX): We rely on the the caller of mozilla::pkix to validate
|
|
// that the name is syntactically valid, if they care. In Gecko we do this
|
|
// implicitly by parsing the certificate into a CERTCertificate object.
|
|
// Instead of relying on the caller to do this, we should do it ourselves.
|
|
rv = der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE, subject);
|
|
if (rv != Success) {
|
|
return rv;
|
|
}
|
|
// TODO(bug XXXXXXX): We defer parsing/validating subjectPublicKeyInfo to
|
|
// the point where the public key is needed. For end-entity certificates, we
|
|
// assume that the caller will extract the public key and use it somehow; if
|
|
// they don't do that then we'll never know whether the key is invalid. On
|
|
// the other hand, if the caller never uses the key then in some ways it
|
|
// doesn't matter. Regardless, we should parse and validate
|
|
// subjectPublicKeyKeyInfo internally.
|
|
rv = der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE,
|
|
subjectPublicKeyInfo);
|
|
if (rv != Success) {
|
|
return rv;
|
|
}
|
|
|
|
static const uint8_t CSC = der::CONTEXT_SPECIFIC | der::CONSTRUCTED;
|
|
|
|
// RFC 5280 says: "These fields MUST only appear if the version is 2 or 3
|
|
// (Section 4.1.2.1). These fields MUST NOT appear if the version is 1."
|
|
if (version != der::Version::v1) {
|
|
|
|
// Ignore issuerUniqueID if present.
|
|
if (tbsCertificate.Peek(CSC | 1)) {
|
|
rv = der::ExpectTagAndSkipValue(tbsCertificate, CSC | 1);
|
|
if (rv != Success) {
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
// Ignore subjectUniqueID if present.
|
|
if (tbsCertificate.Peek(CSC | 2)) {
|
|
rv = der::ExpectTagAndSkipValue(tbsCertificate, CSC | 2);
|
|
if (rv != Success) {
|
|
return rv;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Extensions were added in v3, so only accept extensions in v3 certificates.
|
|
// v4 certificates are not defined but there are some certificates issued
|
|
// with v4 that expect v3 decoding. For compatibility reasons we handle them
|
|
// as v3 certificates.
|
|
if (version == der::Version::v3 || version == der::Version::v4) {
|
|
rv = der::OptionalExtensions(tbsCertificate, CSC | 3,
|
|
bind(&BackCert::RememberExtension, this, _1,
|
|
_2, _3, _4));
|
|
if (rv != Success) {
|
|
return rv;
|
|
}
|
|
// The Netscape Certificate Type extension is an obsolete
|
|
// Netscape-proprietary mechanism that we ignore in favor of the standard
|
|
// extensions. However, some CAs have issued certificates with the Netscape
|
|
// Cert Type extension marked critical. Thus, for compatibility reasons, we
|
|
// "understand" this extension by ignoring it when it is not critical, and
|
|
// by ensuring that the equivalent standardized extensions are present when
|
|
// it is marked critical, based on the assumption that the information in
|
|
// the Netscape Cert Type extension is consistent with the information in
|
|
// the standard extensions.
|
|
//
|
|
// Here is a mapping between the Netscape Cert Type extension and the
|
|
// standard extensions:
|
|
//
|
|
// Netscape Cert Type | BasicConstraints.cA | Extended Key Usage
|
|
// --------------------+-----------------------+----------------------
|
|
// SSL Server | false | id_kp_serverAuth
|
|
// SSL Client | false | id_kp_clientAuth
|
|
// S/MIME Client | false | id_kp_emailProtection
|
|
// Object Signing | false | id_kp_codeSigning
|
|
// SSL Server CA | true | id_pk_serverAuth
|
|
// SSL Client CA | true | id_kp_clientAuth
|
|
// S/MIME CA | true | id_kp_emailProtection
|
|
// Object Signing CA | true | id_kp_codeSigning
|
|
if (criticalNetscapeCertificateType.GetLength() > 0 &&
|
|
(basicConstraints.GetLength() == 0 || extKeyUsage.GetLength() == 0)) {
|
|
return Result::ERROR_UNKNOWN_CRITICAL_EXTENSION;
|
|
}
|
|
}
|
|
|
|
return der::End(tbsCertificate);
|
|
}
|
|
|
|
// XXX: The second value is of type |const Input&| instead of type |Input| due
|
|
// to limitations in our std::bind polyfill.
|
|
Result
|
|
BackCert::RememberExtension(Reader& extnID, const Input& extnValue,
|
|
bool critical, /*out*/ bool& understood)
|
|
{
|
|
understood = false;
|
|
|
|
// python DottedOIDToCode.py id-ce-keyUsage 2.5.29.15
|
|
static const uint8_t id_ce_keyUsage[] = {
|
|
0x55, 0x1d, 0x0f
|
|
};
|
|
// python DottedOIDToCode.py id-ce-subjectAltName 2.5.29.17
|
|
static const uint8_t id_ce_subjectAltName[] = {
|
|
0x55, 0x1d, 0x11
|
|
};
|
|
// python DottedOIDToCode.py id-ce-basicConstraints 2.5.29.19
|
|
static const uint8_t id_ce_basicConstraints[] = {
|
|
0x55, 0x1d, 0x13
|
|
};
|
|
// python DottedOIDToCode.py id-ce-nameConstraints 2.5.29.30
|
|
static const uint8_t id_ce_nameConstraints[] = {
|
|
0x55, 0x1d, 0x1e
|
|
};
|
|
// python DottedOIDToCode.py id-ce-certificatePolicies 2.5.29.32
|
|
static const uint8_t id_ce_certificatePolicies[] = {
|
|
0x55, 0x1d, 0x20
|
|
};
|
|
// python DottedOIDToCode.py id-ce-policyConstraints 2.5.29.36
|
|
static const uint8_t id_ce_policyConstraints[] = {
|
|
0x55, 0x1d, 0x24
|
|
};
|
|
// python DottedOIDToCode.py id-ce-extKeyUsage 2.5.29.37
|
|
static const uint8_t id_ce_extKeyUsage[] = {
|
|
0x55, 0x1d, 0x25
|
|
};
|
|
// python DottedOIDToCode.py id-ce-inhibitAnyPolicy 2.5.29.54
|
|
static const uint8_t id_ce_inhibitAnyPolicy[] = {
|
|
0x55, 0x1d, 0x36
|
|
};
|
|
// python DottedOIDToCode.py id-pe-authorityInfoAccess 1.3.6.1.5.5.7.1.1
|
|
static const uint8_t id_pe_authorityInfoAccess[] = {
|
|
0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01
|
|
};
|
|
// python DottedOIDToCode.py Netscape-certificate-type 2.16.840.1.113730.1.1
|
|
static const uint8_t Netscape_certificate_type[] = {
|
|
0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x01
|
|
};
|
|
|
|
Input* out = nullptr;
|
|
|
|
// We already enforce the maximum possible constraints for policies so we
|
|
// can safely ignore even critical policy constraint extensions.
|
|
//
|
|
// XXX: Doing it this way won't allow us to detect duplicate
|
|
// policyConstraints extensions, but that's OK because (and only because) we
|
|
// ignore the extension.
|
|
Input dummyPolicyConstraints;
|
|
|
|
// RFC says "Conforming CAs MUST mark this extension as non-critical" for
|
|
// both authorityKeyIdentifier and subjectKeyIdentifier, and we do not use
|
|
// them for anything, so we totally ignore them here.
|
|
|
|
if (extnID.MatchRest(id_ce_keyUsage)) {
|
|
out = &keyUsage;
|
|
} else if (extnID.MatchRest(id_ce_subjectAltName)) {
|
|
out = &subjectAltName;
|
|
} else if (extnID.MatchRest(id_ce_basicConstraints)) {
|
|
out = &basicConstraints;
|
|
} else if (extnID.MatchRest(id_ce_nameConstraints)) {
|
|
out = &nameConstraints;
|
|
} else if (extnID.MatchRest(id_ce_certificatePolicies)) {
|
|
out = &certificatePolicies;
|
|
} else if (extnID.MatchRest(id_ce_policyConstraints)) {
|
|
out = &dummyPolicyConstraints;
|
|
} else if (extnID.MatchRest(id_ce_extKeyUsage)) {
|
|
out = &extKeyUsage;
|
|
} else if (extnID.MatchRest(id_ce_inhibitAnyPolicy)) {
|
|
out = &inhibitAnyPolicy;
|
|
} else if (extnID.MatchRest(id_pe_authorityInfoAccess)) {
|
|
out = &authorityInfoAccess;
|
|
} else if (extnID.MatchRest(Netscape_certificate_type) && critical) {
|
|
out = &criticalNetscapeCertificateType;
|
|
}
|
|
|
|
if (out) {
|
|
// Don't allow an empty value for any extension we understand. This way, we
|
|
// can test out->GetLength() != 0 or out->Init() to check for duplicates.
|
|
if (extnValue.GetLength() == 0) {
|
|
return Result::ERROR_EXTENSION_VALUE_INVALID;
|
|
}
|
|
if (out->Init(extnValue) != Success) {
|
|
// Duplicate extension
|
|
return Result::ERROR_EXTENSION_VALUE_INVALID;
|
|
}
|
|
understood = true;
|
|
}
|
|
|
|
return Success;
|
|
}
|
|
|
|
} } // namespace mozilla::pkix
|