1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chromeos/network/onc/onc_parsed_certificates.h"
6 
7 #include <string>
8 #include <vector>
9 
10 #include "base/base64.h"
11 #include "base/logging.h"
12 #include "base/notreached.h"
13 #include "base/optional.h"
14 #include "base/strings/string_util.h"
15 #include "base/values.h"
16 #include "chromeos/network/onc/onc_utils.h"
17 #include "components/onc/onc_constants.h"
18 #include "net/cert/x509_certificate.h"
19 
20 namespace chromeos {
21 namespace onc {
22 
23 namespace {
24 
25 enum class CertificateType { kServer, kAuthority, kClient };
26 
27 // Parses the "Scope" of a policy-provided certificate.
28 // If a Scope element is not present, returns CertificateScope::Default().
29 // If a Scope element is present but malformed, returns an empty base::Optional.
ParseCertScope(const base::Value & onc_certificate)30 base::Optional<CertificateScope> ParseCertScope(
31     const base::Value& onc_certificate) {
32   const base::Value* scope_dict = onc_certificate.FindKeyOfType(
33       ::onc::certificate::kScope, base::Value::Type::DICTIONARY);
34   if (!scope_dict)
35     return CertificateScope::Default();
36 
37   return CertificateScope::ParseFromOncValue(*scope_dict);
38 }
39 
40 // Returns true if the certificate described by |onc_certificate| requests web
41 // trust.
HasWebTrustFlag(const base::Value & onc_certificate)42 bool HasWebTrustFlag(const base::Value& onc_certificate) {
43   DCHECK(onc_certificate.is_dict());
44 
45   bool web_trust_flag = false;
46   const base::Value* trust_list = onc_certificate.FindKeyOfType(
47       ::onc::certificate::kTrustBits, base::Value::Type::LIST);
48   if (!trust_list)
49     return false;
50 
51   for (const base::Value& trust_entry : trust_list->GetList()) {
52     DCHECK(trust_entry.is_string());
53 
54     if (trust_entry.GetString() == ::onc::certificate::kWeb) {
55       // "Web" implies that the certificate is to be trusted for SSL
56       // identification.
57       web_trust_flag = true;
58     } else {
59       // Trust bits should only increase trust and never restrict. Thus,
60       // ignoring unknown bits should be safe.
61       LOG(WARNING) << "Certificate contains unknown trust type "
62                    << trust_entry.GetString();
63     }
64   }
65 
66   return web_trust_flag;
67 }
68 
69 // Converts the ONC string certificate type into the CertificateType enum.
70 // Returns |base::nullopt| if the certificate type was not understood.
GetCertTypeAsEnum(const std::string & cert_type)71 base::Optional<CertificateType> GetCertTypeAsEnum(
72     const std::string& cert_type) {
73   if (cert_type == ::onc::certificate::kServer) {
74     return CertificateType::kServer;
75   }
76 
77   if (cert_type == ::onc::certificate::kAuthority) {
78     return CertificateType::kAuthority;
79   }
80 
81   if (cert_type == ::onc::certificate::kClient) {
82     return CertificateType::kClient;
83   }
84 
85   return base::nullopt;
86 }
87 
88 }  // namespace
89 
90 OncParsedCertificates::ServerOrAuthorityCertificate::
ServerOrAuthorityCertificate(CertificateScope scope,Type type,const std::string & guid,const scoped_refptr<net::X509Certificate> & certificate,bool web_trust_requested)91     ServerOrAuthorityCertificate(
92         CertificateScope scope,
93         Type type,
94         const std::string& guid,
95         const scoped_refptr<net::X509Certificate>& certificate,
96         bool web_trust_requested)
97     : scope_(scope),
98       type_(type),
99       guid_(guid),
100       certificate_(certificate),
101       web_trust_requested_(web_trust_requested) {}
102 
103 OncParsedCertificates::ServerOrAuthorityCertificate::
104     ServerOrAuthorityCertificate(const ServerOrAuthorityCertificate& other) =
105         default;
106 
107 OncParsedCertificates::ServerOrAuthorityCertificate&
108 OncParsedCertificates::ServerOrAuthorityCertificate::operator=(
109     const ServerOrAuthorityCertificate& other) = default;
110 
111 OncParsedCertificates::ServerOrAuthorityCertificate::
112     ServerOrAuthorityCertificate(ServerOrAuthorityCertificate&& other) =
113         default;
114 
115 OncParsedCertificates::ServerOrAuthorityCertificate::
116     ~ServerOrAuthorityCertificate() = default;
117 
operator ==(const ServerOrAuthorityCertificate & other) const118 bool OncParsedCertificates::ServerOrAuthorityCertificate::operator==(
119     const ServerOrAuthorityCertificate& other) const {
120   if (scope() != other.scope())
121     return false;
122 
123   if (type() != other.type())
124     return false;
125 
126   if (guid() != other.guid())
127     return false;
128 
129   if (!certificate()->EqualsExcludingChain(other.certificate().get()))
130     return false;
131 
132   if (web_trust_requested() != other.web_trust_requested())
133     return false;
134 
135   return true;
136 }
137 
operator !=(const ServerOrAuthorityCertificate & other) const138 bool OncParsedCertificates::ServerOrAuthorityCertificate::operator!=(
139     const ServerOrAuthorityCertificate& other) const {
140   return !(*this == other);
141 }
142 
ClientCertificate(const std::string & guid,const std::string & pkcs12_data)143 OncParsedCertificates::ClientCertificate::ClientCertificate(
144     const std::string& guid,
145     const std::string& pkcs12_data)
146     : guid_(guid), pkcs12_data_(pkcs12_data) {}
147 
148 OncParsedCertificates::ClientCertificate::ClientCertificate(
149     const ClientCertificate& other) = default;
150 
151 OncParsedCertificates::ClientCertificate&
152 OncParsedCertificates::ClientCertificate::operator=(
153     const ClientCertificate& other) = default;
154 
155 OncParsedCertificates::ClientCertificate::ClientCertificate(
156     ClientCertificate&& other) = default;
157 
158 OncParsedCertificates::ClientCertificate::~ClientCertificate() = default;
159 
operator ==(const ClientCertificate & other) const160 bool OncParsedCertificates::ClientCertificate::operator==(
161     const ClientCertificate& other) const {
162   if (guid() != other.guid())
163     return false;
164 
165   if (pkcs12_data() != other.pkcs12_data())
166     return false;
167 
168   return true;
169 }
170 
operator !=(const ClientCertificate & other) const171 bool OncParsedCertificates::ClientCertificate::operator!=(
172     const ClientCertificate& other) const {
173   return !(*this == other);
174 }
175 
OncParsedCertificates()176 OncParsedCertificates::OncParsedCertificates()
177     : OncParsedCertificates(base::ListValue()) {}
178 
OncParsedCertificates(const base::Value & onc_certificates)179 OncParsedCertificates::OncParsedCertificates(
180     const base::Value& onc_certificates) {
181   if (!onc_certificates.is_list()) {
182     LOG(WARNING) << "Value is not a list";
183     has_error_ = true;
184     return;
185   }
186 
187   for (size_t i = 0; i < onc_certificates.GetList().size(); ++i) {
188     const base::Value& onc_certificate = onc_certificates.GetList()[i];
189     DCHECK(onc_certificate.is_dict());
190 
191     VLOG(2) << "Parsing certificate at index " << i << ": " << onc_certificate;
192 
193     if (!ParseCertificate(onc_certificate)) {
194       has_error_ = true;
195       LOG(ERROR) << "Cannot parse certificate at index " << i;
196     } else {
197       VLOG(2) << "Successfully parsed certificate at index " << i;
198     }
199   }
200 }
201 
202 OncParsedCertificates::~OncParsedCertificates() = default;
203 
ParseCertificate(const base::Value & onc_certificate)204 bool OncParsedCertificates::ParseCertificate(
205     const base::Value& onc_certificate) {
206   const base::Value* guid_key = onc_certificate.FindKeyOfType(
207       ::onc::certificate::kGUID, base::Value::Type::STRING);
208   DCHECK(guid_key);
209   std::string guid = guid_key->GetString();
210 
211   const base::Value* type_key = onc_certificate.FindKeyOfType(
212       ::onc::certificate::kType, base::Value::Type::STRING);
213   DCHECK(type_key);
214   base::Optional<CertificateType> type_opt =
215       GetCertTypeAsEnum(type_key->GetString());
216   if (!type_opt)
217     return false;
218 
219   switch (type_opt.value()) {
220     case CertificateType::kServer:
221       return ParseServerOrCaCertificate(
222           ServerOrAuthorityCertificate::Type::kServer, guid, onc_certificate);
223     case CertificateType::kAuthority:
224       return ParseServerOrCaCertificate(
225           ServerOrAuthorityCertificate::Type::kAuthority, guid,
226           onc_certificate);
227     case CertificateType::kClient:
228       return ParseClientCertificate(guid, onc_certificate);
229   }
230   NOTREACHED();
231   return false;
232 }
233 
ParseServerOrCaCertificate(ServerOrAuthorityCertificate::Type type,const std::string & guid,const base::Value & onc_certificate)234 bool OncParsedCertificates::ParseServerOrCaCertificate(
235     ServerOrAuthorityCertificate::Type type,
236     const std::string& guid,
237     const base::Value& onc_certificate) {
238   base::Optional<CertificateScope> scope = ParseCertScope(onc_certificate);
239   if (!scope) {
240     LOG(ERROR) << "Certificate has malformed 'Scope'";
241     return false;
242   }
243 
244   bool web_trust_requested = HasWebTrustFlag(onc_certificate);
245   const base::Value* x509_data_key = onc_certificate.FindKeyOfType(
246       ::onc::certificate::kX509, base::Value::Type::STRING);
247   if (!x509_data_key || x509_data_key->GetString().empty()) {
248     LOG(ERROR) << "Certificate missing " << ::onc::certificate::kX509
249                << " certificate data.";
250     return false;
251   }
252 
253   std::string certificate_der_data = DecodePEM(x509_data_key->GetString());
254   if (certificate_der_data.empty()) {
255     LOG(ERROR) << "Unable to create certificate from PEM encoding.";
256     return false;
257   }
258 
259   scoped_refptr<net::X509Certificate> certificate =
260       net::X509Certificate::CreateFromBytes(certificate_der_data.data(),
261                                             certificate_der_data.length());
262   if (!certificate) {
263     LOG(ERROR) << "Unable to create certificate from PEM encoding.";
264     return false;
265   }
266 
267   server_or_authority_certificates_.push_back(ServerOrAuthorityCertificate(
268       scope.value(), type, guid, certificate, web_trust_requested));
269   return true;
270 }
271 
ParseClientCertificate(const std::string & guid,const base::Value & onc_certificate)272 bool OncParsedCertificates::ParseClientCertificate(
273     const std::string& guid,
274     const base::Value& onc_certificate) {
275   const base::Value* base64_pkcs12_data_key = onc_certificate.FindKeyOfType(
276       ::onc::certificate::kPKCS12, base::Value::Type::STRING);
277   if (!base64_pkcs12_data_key || base64_pkcs12_data_key->GetString().empty()) {
278     LOG(ERROR) << "PKCS12 data is missing for client certificate.";
279     return false;
280   }
281 
282   std::string base64_pkcs12_data;
283   base::RemoveChars(base64_pkcs12_data_key->GetString(), "\n",
284                     &base64_pkcs12_data);
285   std::string pkcs12_data;
286   if (!base::Base64Decode(base64_pkcs12_data, &pkcs12_data)) {
287     LOG(ERROR) << "Unable to base64 decode PKCS#12 data: \""
288                << base64_pkcs12_data_key->GetString() << "\".";
289     return false;
290   }
291 
292   client_certificates_.push_back(ClientCertificate(guid, pkcs12_data));
293   return true;
294 }
295 
296 }  // namespace onc
297 }  // namespace chromeos
298