| 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 |