OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chromeos/network/onc/onc_utils.h" | 5 #include "chromeos/network/onc/onc_utils.h" |
6 | 6 |
7 #include "base/base64.h" | 7 #include "base/base64.h" |
8 #include "base/json/json_reader.h" | 8 #include "base/json/json_reader.h" |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/metrics/histogram.h" | 10 #include "base/metrics/histogram.h" |
11 #include "base/strings/string_util.h" | 11 #include "base/strings/string_util.h" |
12 #include "base/values.h" | 12 #include "base/values.h" |
13 #include "chromeos/network/network_event_log.h" | 13 #include "chromeos/network/network_event_log.h" |
14 #include "chromeos/network/onc/onc_mapper.h" | 14 #include "chromeos/network/onc/onc_mapper.h" |
15 #include "chromeos/network/onc/onc_signature.h" | 15 #include "chromeos/network/onc/onc_signature.h" |
16 #include "chromeos/network/onc/onc_utils.h" | 16 #include "chromeos/network/onc/onc_utils.h" |
17 #include "chromeos/network/onc/onc_validator.h" | 17 #include "chromeos/network/onc/onc_validator.h" |
18 #include "crypto/encryptor.h" | 18 #include "crypto/encryptor.h" |
19 #include "crypto/hmac.h" | 19 #include "crypto/hmac.h" |
20 #include "crypto/symmetric_key.h" | 20 #include "crypto/symmetric_key.h" |
| 21 #include "net/cert/pem_tokenizer.h" |
21 | 22 |
22 #define ONC_LOG_WARNING(message) NET_LOG_WARNING("ONC", message) | 23 #define ONC_LOG_WARNING(message) NET_LOG_WARNING("ONC", message) |
23 #define ONC_LOG_ERROR(message) NET_LOG_ERROR("ONC", message) | 24 #define ONC_LOG_ERROR(message) NET_LOG_ERROR("ONC", message) |
24 | 25 |
25 namespace chromeos { | 26 namespace chromeos { |
26 namespace onc { | 27 namespace onc { |
27 | 28 |
28 namespace { | 29 namespace { |
29 | 30 |
30 const char kUnableToDecrypt[] = "Unable to decrypt encrypted ONC"; | 31 const char kUnableToDecrypt[] = "Unable to decrypt encrypted ONC"; |
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
172 void ExpandField(const std::string fieldname, | 173 void ExpandField(const std::string fieldname, |
173 const StringSubstitution& substitution, | 174 const StringSubstitution& substitution, |
174 base::DictionaryValue* onc_object) { | 175 base::DictionaryValue* onc_object) { |
175 std::string user_string; | 176 std::string user_string; |
176 if (!onc_object->GetStringWithoutPathExpansion(fieldname, &user_string)) | 177 if (!onc_object->GetStringWithoutPathExpansion(fieldname, &user_string)) |
177 return; | 178 return; |
178 | 179 |
179 std::string login_id; | 180 std::string login_id; |
180 if (substitution.GetSubstitute(substitutes::kLoginIDField, &login_id)) { | 181 if (substitution.GetSubstitute(substitutes::kLoginIDField, &login_id)) { |
181 ReplaceSubstringsAfterOffset(&user_string, 0, | 182 ReplaceSubstringsAfterOffset(&user_string, 0, |
182 onc::substitutes::kLoginIDField, | 183 substitutes::kLoginIDField, |
183 login_id); | 184 login_id); |
184 } | 185 } |
185 | 186 |
186 std::string email; | 187 std::string email; |
187 if (substitution.GetSubstitute(substitutes::kEmailField, &email)) { | 188 if (substitution.GetSubstitute(substitutes::kEmailField, &email)) { |
188 ReplaceSubstringsAfterOffset(&user_string, 0, | 189 ReplaceSubstringsAfterOffset(&user_string, 0, |
189 onc::substitutes::kEmailField, | 190 substitutes::kEmailField, |
190 email); | 191 email); |
191 } | 192 } |
192 | 193 |
193 onc_object->SetStringWithoutPathExpansion(fieldname, user_string); | 194 onc_object->SetStringWithoutPathExpansion(fieldname, user_string); |
194 } | 195 } |
195 | 196 |
196 void ExpandStringsInOncObject( | 197 void ExpandStringsInOncObject( |
197 const OncValueSignature& signature, | 198 const OncValueSignature& signature, |
198 const StringSubstitution& substitution, | 199 const StringSubstitution& substitution, |
199 base::DictionaryValue* onc_object) { | 200 base::DictionaryValue* onc_object) { |
(...skipping 17 matching lines...) Expand all Loading... |
217 if (!field_signature) | 218 if (!field_signature) |
218 continue; | 219 continue; |
219 | 220 |
220 ExpandStringsInOncObject(*field_signature->value_signature, | 221 ExpandStringsInOncObject(*field_signature->value_signature, |
221 substitution, inner_object); | 222 substitution, inner_object); |
222 } | 223 } |
223 } | 224 } |
224 | 225 |
225 namespace { | 226 namespace { |
226 | 227 |
227 class OncMaskValues : public onc::Mapper { | 228 class OncMaskValues : public Mapper { |
228 public: | 229 public: |
229 static scoped_ptr<base::DictionaryValue> Mask( | 230 static scoped_ptr<base::DictionaryValue> Mask( |
230 const onc::OncValueSignature& signature, | 231 const OncValueSignature& signature, |
231 const base::DictionaryValue& onc_object, | 232 const base::DictionaryValue& onc_object, |
232 const std::string& mask) { | 233 const std::string& mask) { |
233 OncMaskValues masker(mask); | 234 OncMaskValues masker(mask); |
234 bool unused_error; | 235 bool unused_error; |
235 return masker.MapObject(signature, onc_object, &unused_error); | 236 return masker.MapObject(signature, onc_object, &unused_error); |
236 } | 237 } |
237 | 238 |
238 protected: | 239 protected: |
239 explicit OncMaskValues(const std::string& mask) | 240 explicit OncMaskValues(const std::string& mask) |
240 : mask_(mask) { | 241 : mask_(mask) { |
241 } | 242 } |
242 | 243 |
243 virtual scoped_ptr<base::Value> MapField( | 244 virtual scoped_ptr<base::Value> MapField( |
244 const std::string& field_name, | 245 const std::string& field_name, |
245 const onc::OncValueSignature& object_signature, | 246 const OncValueSignature& object_signature, |
246 const base::Value& onc_value, | 247 const base::Value& onc_value, |
247 bool* found_unknown_field, | 248 bool* found_unknown_field, |
248 bool* error) OVERRIDE { | 249 bool* error) OVERRIDE { |
249 if (onc::FieldIsCredential(object_signature, field_name)) { | 250 if (FieldIsCredential(object_signature, field_name)) { |
250 return scoped_ptr<base::Value>(new base::StringValue(mask_)); | 251 return scoped_ptr<base::Value>(new base::StringValue(mask_)); |
251 } else { | 252 } else { |
252 return onc::Mapper::MapField(field_name, object_signature, onc_value, | 253 return Mapper::MapField(field_name, object_signature, onc_value, |
253 found_unknown_field, error); | 254 found_unknown_field, error); |
254 } | 255 } |
255 } | 256 } |
256 | 257 |
257 // Mask to insert in place of the sensitive values. | 258 // Mask to insert in place of the sensitive values. |
258 std::string mask_; | 259 std::string mask_; |
259 }; | 260 }; |
260 | 261 |
261 } // namespace | 262 } // namespace |
262 | 263 |
263 scoped_ptr<base::DictionaryValue> MaskCredentialsInOncObject( | 264 scoped_ptr<base::DictionaryValue> MaskCredentialsInOncObject( |
264 const onc::OncValueSignature& signature, | 265 const OncValueSignature& signature, |
265 const base::DictionaryValue& onc_object, | 266 const base::DictionaryValue& onc_object, |
266 const std::string& mask) { | 267 const std::string& mask) { |
267 return OncMaskValues::Mask(signature, onc_object, mask); | 268 return OncMaskValues::Mask(signature, onc_object, mask); |
268 } | 269 } |
269 | 270 |
270 bool ParseAndValidateOncForImport( | 271 bool ParseAndValidateOncForImport(const std::string& onc_blob, |
271 const std::string& onc_blob, | 272 ONCSource onc_source, |
272 chromeos::onc::ONCSource onc_source, | 273 const std::string& passphrase, |
273 const std::string& passphrase, | 274 base::ListValue* network_configs, |
274 base::ListValue* network_configs, | 275 base::ListValue* certificates) { |
275 base::ListValue* certificates) { | |
276 certificates->Clear(); | 276 certificates->Clear(); |
277 network_configs->Clear(); | 277 network_configs->Clear(); |
278 if (onc_blob.empty()) | 278 if (onc_blob.empty()) |
279 return true; | 279 return true; |
280 | 280 |
281 scoped_ptr<base::DictionaryValue> toplevel_onc = | 281 scoped_ptr<base::DictionaryValue> toplevel_onc = |
282 onc::ReadDictionaryFromJson(onc_blob); | 282 ReadDictionaryFromJson(onc_blob); |
283 if (toplevel_onc.get() == NULL) { | 283 if (toplevel_onc.get() == NULL) { |
284 LOG(ERROR) << "ONC loaded from " << onc::GetSourceAsString(onc_source) | 284 LOG(ERROR) << "ONC loaded from " << GetSourceAsString(onc_source) |
285 << " is not a valid JSON dictionary."; | 285 << " is not a valid JSON dictionary."; |
286 return false; | 286 return false; |
287 } | 287 } |
288 | 288 |
289 // Check and see if this is an encrypted ONC file. If so, decrypt it. | 289 // Check and see if this is an encrypted ONC file. If so, decrypt it. |
290 std::string onc_type; | 290 std::string onc_type; |
291 toplevel_onc->GetStringWithoutPathExpansion(onc::toplevel_config::kType, | 291 toplevel_onc->GetStringWithoutPathExpansion(toplevel_config::kType, |
292 &onc_type); | 292 &onc_type); |
293 if (onc_type == onc::toplevel_config::kEncryptedConfiguration) { | 293 if (onc_type == toplevel_config::kEncryptedConfiguration) { |
294 toplevel_onc = onc::Decrypt(passphrase, *toplevel_onc); | 294 toplevel_onc = Decrypt(passphrase, *toplevel_onc); |
295 if (toplevel_onc.get() == NULL) { | 295 if (toplevel_onc.get() == NULL) { |
296 LOG(ERROR) << "Couldn't decrypt the ONC from " | 296 LOG(ERROR) << "Couldn't decrypt the ONC from " |
297 << onc::GetSourceAsString(onc_source); | 297 << GetSourceAsString(onc_source); |
298 return false; | 298 return false; |
299 } | 299 } |
300 } | 300 } |
301 | 301 |
302 bool from_policy = (onc_source == onc::ONC_SOURCE_USER_POLICY || | 302 bool from_policy = (onc_source == ONC_SOURCE_USER_POLICY || |
303 onc_source == onc::ONC_SOURCE_DEVICE_POLICY); | 303 onc_source == ONC_SOURCE_DEVICE_POLICY); |
304 | 304 |
305 // Validate the ONC dictionary. We are liberal and ignore unknown field | 305 // Validate the ONC dictionary. We are liberal and ignore unknown field |
306 // names and ignore invalid field names in kRecommended arrays. | 306 // names and ignore invalid field names in kRecommended arrays. |
307 onc::Validator validator(false, // Ignore unknown fields. | 307 Validator validator(false, // Ignore unknown fields. |
308 false, // Ignore invalid recommended field names. | 308 false, // Ignore invalid recommended field names. |
309 true, // Fail on missing fields. | 309 true, // Fail on missing fields. |
310 from_policy); | 310 from_policy); |
311 validator.SetOncSource(onc_source); | 311 validator.SetOncSource(onc_source); |
312 | 312 |
313 onc::Validator::Result validation_result; | 313 Validator::Result validation_result; |
314 toplevel_onc = validator.ValidateAndRepairObject( | 314 toplevel_onc = validator.ValidateAndRepairObject( |
315 &onc::kToplevelConfigurationSignature, | 315 &kToplevelConfigurationSignature, |
316 *toplevel_onc, | 316 *toplevel_onc, |
317 &validation_result); | 317 &validation_result); |
318 | 318 |
319 if (from_policy) { | 319 if (from_policy) { |
320 UMA_HISTOGRAM_BOOLEAN("Enterprise.ONC.PolicyValidation", | 320 UMA_HISTOGRAM_BOOLEAN("Enterprise.ONC.PolicyValidation", |
321 validation_result == onc::Validator::VALID); | 321 validation_result == Validator::VALID); |
322 } | 322 } |
323 | 323 |
324 bool success = true; | 324 bool success = true; |
325 if (validation_result == onc::Validator::VALID_WITH_WARNINGS) { | 325 if (validation_result == Validator::VALID_WITH_WARNINGS) { |
326 LOG(WARNING) << "ONC from " << onc::GetSourceAsString(onc_source) | 326 LOG(WARNING) << "ONC from " << GetSourceAsString(onc_source) |
327 << " produced warnings."; | 327 << " produced warnings."; |
328 success = false; | 328 success = false; |
329 } else if (validation_result == onc::Validator::INVALID || | 329 } else if (validation_result == Validator::INVALID || toplevel_onc == NULL) { |
330 toplevel_onc == NULL) { | 330 LOG(ERROR) << "ONC from " << GetSourceAsString(onc_source) |
331 LOG(ERROR) << "ONC from " << onc::GetSourceAsString(onc_source) | |
332 << " is invalid and couldn't be repaired."; | 331 << " is invalid and couldn't be repaired."; |
333 return false; | 332 return false; |
334 } | 333 } |
335 | 334 |
336 base::ListValue* validated_certs = NULL; | 335 base::ListValue* validated_certs = NULL; |
337 if (toplevel_onc->GetListWithoutPathExpansion( | 336 if (toplevel_onc->GetListWithoutPathExpansion(toplevel_config::kCertificates, |
338 onc::toplevel_config::kCertificates, &validated_certs)) { | 337 &validated_certs)) { |
339 certificates->Swap(validated_certs); | 338 certificates->Swap(validated_certs); |
340 } | 339 } |
341 | 340 |
342 base::ListValue* validated_networks = NULL; | 341 base::ListValue* validated_networks = NULL; |
343 if (toplevel_onc->GetListWithoutPathExpansion( | 342 if (toplevel_onc->GetListWithoutPathExpansion( |
344 onc::toplevel_config::kNetworkConfigurations, &validated_networks)) { | 343 toplevel_config::kNetworkConfigurations, &validated_networks)) { |
345 network_configs->Swap(validated_networks); | 344 network_configs->Swap(validated_networks); |
346 } | 345 } |
347 | 346 |
348 return success; | 347 return success; |
349 } | 348 } |
350 | 349 |
| 350 scoped_refptr<net::X509Certificate> DecodePEMCertificate( |
| 351 const std::string& pem_encoded, |
| 352 const std::string& nickname) { |
| 353 // The PEM block header used for DER certificates |
| 354 static const char kCertificateHeader[] = "CERTIFICATE"; |
| 355 // This is an older PEM marker for DER certificates. |
| 356 static const char kX509CertificateHeader[] = "X509 CERTIFICATE"; |
| 357 |
| 358 std::vector<std::string> pem_headers; |
| 359 pem_headers.push_back(kCertificateHeader); |
| 360 pem_headers.push_back(kX509CertificateHeader); |
| 361 |
| 362 net::PEMTokenizer pem_tokenizer(pem_encoded, pem_headers); |
| 363 std::string decoded; |
| 364 if (pem_tokenizer.GetNext()) { |
| 365 decoded = pem_tokenizer.data(); |
| 366 } else { |
| 367 // If we failed to read the data as a PEM file, then try plain base64 decode |
| 368 // in case the PEM marker strings are missing. For this to work, there has |
| 369 // to be no white space, and it has to only contain the base64-encoded data. |
| 370 if (!base::Base64Decode(pem_encoded, &decoded)) { |
| 371 LOG(ERROR) << "Unable to base64 decode X509 data: " << pem_encoded; |
| 372 return scoped_refptr<net::X509Certificate>(); |
| 373 } |
| 374 } |
| 375 |
| 376 scoped_refptr<net::X509Certificate> cert = |
| 377 net::X509Certificate::CreateFromBytesWithNickname(decoded.data(), |
| 378 decoded.size(), |
| 379 nickname.c_str()); |
| 380 LOG_IF(ERROR, !cert) << "Couldn't create certificate from X509 data: " |
| 381 << decoded; |
| 382 return cert; |
| 383 } |
| 384 |
351 } // namespace onc | 385 } // namespace onc |
352 } // namespace chromeos | 386 } // namespace chromeos |
OLD | NEW |