Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/security/pkix/lib/pkixcert.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This code is made available to you under your choice of the following sets
4
 * of licensing terms:
5
 */
6
/* This Source Code Form is subject to the terms of the Mozilla Public
7
 * License, v. 2.0. If a copy of the MPL was not distributed with this
8
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9
 */
10
/* Copyright 2014 Mozilla Contributors
11
 *
12
 * Licensed under the Apache License, Version 2.0 (the "License");
13
 * you may not use this file except in compliance with the License.
14
 * You may obtain a copy of the License at
15
 *
16
 *     http://www.apache.org/licenses/LICENSE-2.0
17
 *
18
 * Unless required by applicable law or agreed to in writing, software
19
 * distributed under the License is distributed on an "AS IS" BASIS,
20
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
 * See the License for the specific language governing permissions and
22
 * limitations under the License.
23
 */
24
25
#include "pkixutil.h"
26
27
namespace mozilla { namespace pkix {
28
29
Result
30
BackCert::Init()
31
0
{
32
0
  Result rv;
33
0
34
0
  // Certificate  ::=  SEQUENCE  {
35
0
  //         tbsCertificate       TBSCertificate,
36
0
  //         signatureAlgorithm   AlgorithmIdentifier,
37
0
  //         signatureValue       BIT STRING  }
38
0
39
0
  Reader tbsCertificate;
40
0
41
0
  // The scope of |input| and |certificate| are limited to this block so we
42
0
  // don't accidentally confuse them for tbsCertificate later.
43
0
  {
44
0
    Reader certificate;
45
0
    rv = der::ExpectTagAndGetValueAtEnd(der, der::SEQUENCE, certificate);
46
0
    if (rv != Success) {
47
0
      return rv;
48
0
    }
49
0
    rv = der::SignedData(certificate, tbsCertificate, signedData);
50
0
    if (rv != Success) {
51
0
      return rv;
52
0
    }
53
0
    rv = der::End(certificate);
54
0
    if (rv != Success) {
55
0
      return rv;
56
0
    }
57
0
  }
58
0
59
0
  // TBSCertificate  ::=  SEQUENCE  {
60
0
  //      version         [0]  EXPLICIT Version DEFAULT v1,
61
0
  //      serialNumber         CertificateSerialNumber,
62
0
  //      signature            AlgorithmIdentifier,
63
0
  //      issuer               Name,
64
0
  //      validity             Validity,
65
0
  //      subject              Name,
66
0
  //      subjectPublicKeyInfo SubjectPublicKeyInfo,
67
0
  //      issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
68
0
  //                           -- If present, version MUST be v2 or v3
69
0
  //      subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
70
0
  //                           -- If present, version MUST be v2 or v3
71
0
  //      extensions      [3]  EXPLICIT Extensions OPTIONAL
72
0
  //                           -- If present, version MUST be v3
73
0
  //      }
74
0
  rv = der::OptionalVersion(tbsCertificate, version);
75
0
  if (rv != Success) {
76
0
    return rv;
77
0
  }
78
0
  rv = der::CertificateSerialNumber(tbsCertificate, serialNumber);
79
0
  if (rv != Success) {
80
0
    return rv;
81
0
  }
82
0
  rv = der::ExpectTagAndGetValue(tbsCertificate, der::SEQUENCE, signature);
83
0
  if (rv != Success) {
84
0
    return rv;
85
0
  }
86
0
  rv = der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE, issuer);
87
0
  if (rv != Success) {
88
0
    return rv;
89
0
  }
90
0
  rv = der::ExpectTagAndGetValue(tbsCertificate, der::SEQUENCE, validity);
91
0
  if (rv != Success) {
92
0
    return rv;
93
0
  }
94
0
  // TODO(bug XXXXXXX): We rely on the the caller of mozilla::pkix to validate
95
0
  // that the name is syntactically valid, if they care. In Gecko we do this
96
0
  // implicitly by parsing the certificate into a CERTCertificate object.
97
0
  // Instead of relying on the caller to do this, we should do it ourselves.
98
0
  rv = der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE, subject);
99
0
  if (rv != Success) {
100
0
    return rv;
101
0
  }
102
0
  rv = der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE,
103
0
                               subjectPublicKeyInfo);
104
0
  if (rv != Success) {
105
0
    return rv;
106
0
  }
107
0
108
0
  static const uint8_t CSC = der::CONTEXT_SPECIFIC | der::CONSTRUCTED;
109
0
110
0
  // According to RFC 5280, all fields below this line are forbidden for
111
0
  // certificate versions less than v3.  However, for compatibility reasons,
112
0
  // we parse v1/v2 certificates in the same way as v3 certificates.  So if
113
0
  // these fields appear in a v1 certificate, they will be used.
114
0
115
0
  // Ignore issuerUniqueID if present.
116
0
  if (tbsCertificate.Peek(CSC | 1)) {
117
0
    rv = der::ExpectTagAndSkipValue(tbsCertificate, CSC | 1);
118
0
    if (rv != Success) {
119
0
      return rv;
120
0
    }
121
0
  }
122
0
123
0
  // Ignore subjectUniqueID if present.
124
0
  if (tbsCertificate.Peek(CSC | 2)) {
125
0
    rv = der::ExpectTagAndSkipValue(tbsCertificate, CSC | 2);
126
0
    if (rv != Success) {
127
0
      return rv;
128
0
    }
129
0
  }
130
0
131
0
  rv = der::OptionalExtensions(
132
0
         tbsCertificate, CSC | 3,
133
0
         [this](Reader& extnID, const Input& extnValue, bool critical,
134
0
                /*out*/ bool& understood) {
135
0
           return RememberExtension(extnID, extnValue, critical, understood);
136
0
         });
137
0
  if (rv != Success) {
138
0
    return rv;
139
0
  }
140
0
141
0
  // The Netscape Certificate Type extension is an obsolete
142
0
  // Netscape-proprietary mechanism that we ignore in favor of the standard
143
0
  // extensions. However, some CAs have issued certificates with the Netscape
144
0
  // Cert Type extension marked critical. Thus, for compatibility reasons, we
145
0
  // "understand" this extension by ignoring it when it is not critical, and
146
0
  // by ensuring that the equivalent standardized extensions are present when
147
0
  // it is marked critical, based on the assumption that the information in
148
0
  // the Netscape Cert Type extension is consistent with the information in
149
0
  // the standard extensions.
150
0
  //
151
0
  // Here is a mapping between the Netscape Cert Type extension and the
152
0
  // standard extensions:
153
0
  //
154
0
  // Netscape Cert Type  |  BasicConstraints.cA  |  Extended Key Usage
155
0
  // --------------------+-----------------------+----------------------
156
0
  // SSL Server          |  false                |  id_kp_serverAuth
157
0
  // SSL Client          |  false                |  id_kp_clientAuth
158
0
  // S/MIME Client       |  false                |  id_kp_emailProtection
159
0
  // Object Signing      |  false                |  id_kp_codeSigning
160
0
  // SSL Server CA       |  true                 |  id_kp_serverAuth
161
0
  // SSL Client CA       |  true                 |  id_kp_clientAuth
162
0
  // S/MIME CA           |  true                 |  id_kp_emailProtection
163
0
  // Object Signing CA   |  true                 |  id_kp_codeSigning
164
0
  if (criticalNetscapeCertificateType.GetLength() > 0 &&
165
0
      (basicConstraints.GetLength() == 0 || extKeyUsage.GetLength() == 0)) {
166
0
    return Result::ERROR_UNKNOWN_CRITICAL_EXTENSION;
167
0
  }
168
0
169
0
  return der::End(tbsCertificate);
170
0
}
171
172
Result
173
BackCert::RememberExtension(Reader& extnID, Input extnValue,
174
                            bool critical, /*out*/ bool& understood)
175
0
{
176
0
  understood = false;
177
0
178
0
  // python DottedOIDToCode.py id-ce-keyUsage 2.5.29.15
179
0
  static const uint8_t id_ce_keyUsage[] = {
180
0
    0x55, 0x1d, 0x0f
181
0
  };
182
0
  // python DottedOIDToCode.py id-ce-subjectAltName 2.5.29.17
183
0
  static const uint8_t id_ce_subjectAltName[] = {
184
0
    0x55, 0x1d, 0x11
185
0
  };
186
0
  // python DottedOIDToCode.py id-ce-basicConstraints 2.5.29.19
187
0
  static const uint8_t id_ce_basicConstraints[] = {
188
0
    0x55, 0x1d, 0x13
189
0
  };
190
0
  // python DottedOIDToCode.py id-ce-nameConstraints 2.5.29.30
191
0
  static const uint8_t id_ce_nameConstraints[] = {
192
0
    0x55, 0x1d, 0x1e
193
0
  };
194
0
  // python DottedOIDToCode.py id-ce-certificatePolicies 2.5.29.32
195
0
  static const uint8_t id_ce_certificatePolicies[] = {
196
0
    0x55, 0x1d, 0x20
197
0
  };
198
0
  // python DottedOIDToCode.py id-ce-policyConstraints 2.5.29.36
199
0
  static const uint8_t id_ce_policyConstraints[] = {
200
0
    0x55, 0x1d, 0x24
201
0
  };
202
0
  // python DottedOIDToCode.py id-ce-extKeyUsage 2.5.29.37
203
0
  static const uint8_t id_ce_extKeyUsage[] = {
204
0
    0x55, 0x1d, 0x25
205
0
  };
206
0
  // python DottedOIDToCode.py id-ce-inhibitAnyPolicy 2.5.29.54
207
0
  static const uint8_t id_ce_inhibitAnyPolicy[] = {
208
0
    0x55, 0x1d, 0x36
209
0
  };
210
0
  // python DottedOIDToCode.py id-pe-authorityInfoAccess 1.3.6.1.5.5.7.1.1
211
0
  static const uint8_t id_pe_authorityInfoAccess[] = {
212
0
    0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01
213
0
  };
214
0
  // python DottedOIDToCode.py id-pkix-ocsp-nocheck 1.3.6.1.5.5.7.48.1.5
215
0
  static const uint8_t id_pkix_ocsp_nocheck[] = {
216
0
    0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x05
217
0
  };
218
0
  // python DottedOIDToCode.py Netscape-certificate-type 2.16.840.1.113730.1.1
219
0
  static const uint8_t Netscape_certificate_type[] = {
220
0
    0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x01
221
0
  };
222
0
  // python DottedOIDToCode.py id-pe-tlsfeature 1.3.6.1.5.5.7.1.24
223
0
  static const uint8_t id_pe_tlsfeature[] = {
224
0
    0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x18
225
0
  };
226
0
  // python DottedOIDToCode.py id-embeddedSctList 1.3.6.1.4.1.11129.2.4.2
227
0
  // See Section 3.3 of RFC 6962.
228
0
  static const uint8_t id_embeddedSctList[] = {
229
0
    0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x04, 0x02
230
0
  };
231
0
232
0
  Input* out = nullptr;
233
0
234
0
  // We already enforce the maximum possible constraints for policies so we
235
0
  // can safely ignore even critical policy constraint extensions.
236
0
  //
237
0
  // XXX: Doing it this way won't allow us to detect duplicate
238
0
  // policyConstraints extensions, but that's OK because (and only because) we
239
0
  // ignore the extension.
240
0
  Input dummyPolicyConstraints;
241
0
242
0
  // We don't need to save the contents of this extension if it is present. We
243
0
  // just need to handle its presence (it is essentially ignored right now).
244
0
  Input dummyOCSPNocheck;
245
0
246
0
  // For compatibility reasons, for some extensions we have to allow empty
247
0
  // extension values. This would normally interfere with our duplicate
248
0
  // extension checking code. However, as long as the extensions we allow to
249
0
  // have empty values are also the ones we implicitly allow duplicates of,
250
0
  // this will work fine.
251
0
  bool emptyValueAllowed = false;
252
0
253
0
  // RFC says "Conforming CAs MUST mark this extension as non-critical" for
254
0
  // both authorityKeyIdentifier and subjectKeyIdentifier, and we do not use
255
0
  // them for anything, so we totally ignore them here.
256
0
257
0
  if (extnID.MatchRest(id_ce_keyUsage)) {
258
0
    out = &keyUsage;
259
0
  } else if (extnID.MatchRest(id_ce_subjectAltName)) {
260
0
    out = &subjectAltName;
261
0
  } else if (extnID.MatchRest(id_ce_basicConstraints)) {
262
0
    out = &basicConstraints;
263
0
  } else if (extnID.MatchRest(id_ce_nameConstraints)) {
264
0
    out = &nameConstraints;
265
0
  } else if (extnID.MatchRest(id_ce_certificatePolicies)) {
266
0
    out = &certificatePolicies;
267
0
  } else if (extnID.MatchRest(id_ce_policyConstraints)) {
268
0
    out = &dummyPolicyConstraints;
269
0
  } else if (extnID.MatchRest(id_ce_extKeyUsage)) {
270
0
    out = &extKeyUsage;
271
0
  } else if (extnID.MatchRest(id_ce_inhibitAnyPolicy)) {
272
0
    out = &inhibitAnyPolicy;
273
0
  } else if (extnID.MatchRest(id_pe_authorityInfoAccess)) {
274
0
    out = &authorityInfoAccess;
275
0
  } else if (extnID.MatchRest(id_pe_tlsfeature)) {
276
0
    out = &requiredTLSFeatures;
277
0
  } else if (extnID.MatchRest(id_embeddedSctList)) {
278
0
    out = &signedCertificateTimestamps;
279
0
  } else if (extnID.MatchRest(id_pkix_ocsp_nocheck) && critical) {
280
0
    // We need to make sure we don't reject delegated OCSP response signing
281
0
    // certificates that contain the id-pkix-ocsp-nocheck extension marked as
282
0
    // critical when validating OCSP responses. Without this, an application
283
0
    // that implements soft-fail OCSP might ignore a valid Revoked or Unknown
284
0
    // response, and an application that implements hard-fail OCSP might fail
285
0
    // to connect to a server given a valid Good response.
286
0
    out = &dummyOCSPNocheck;
287
0
    // We allow this extension to have an empty value.
288
0
    // See http://comments.gmane.org/gmane.ietf.x509/30947
289
0
    emptyValueAllowed = true;
290
0
  } else if (extnID.MatchRest(Netscape_certificate_type) && critical) {
291
0
    out = &criticalNetscapeCertificateType;
292
0
  }
293
0
294
0
  if (out) {
295
0
    // Don't allow an empty value for any extension we understand. This way, we
296
0
    // can test out->GetLength() != 0 or out->Init() to check for duplicates.
297
0
    if (extnValue.GetLength() == 0 && !emptyValueAllowed) {
298
0
      return Result::ERROR_EXTENSION_VALUE_INVALID;
299
0
    }
300
0
    if (out->Init(extnValue) != Success) {
301
0
      // Duplicate extension
302
0
      return Result::ERROR_EXTENSION_VALUE_INVALID;
303
0
    }
304
0
    understood = true;
305
0
  }
306
0
307
0
  return Success;
308
0
}
309
310
Result
311
ExtractSignedCertificateTimestampListFromExtension(Input extnValue,
312
                                                   Input& sctList)
313
0
{
314
0
  Reader decodedValue;
315
0
  Result rv = der::ExpectTagAndGetValueAtEnd(extnValue, der::OCTET_STRING,
316
0
                                             decodedValue);
317
0
  if (rv != Success) {
318
0
    return rv;
319
0
  }
320
0
  return decodedValue.SkipToEnd(sctList);
321
0
}
322
323
} } // namespace mozilla::pkix