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