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
3 // found in the LICENSE file.
4
5 #include "chromeos/network/onc/onc_validator.h"
6
7 #include <algorithm>
8 #include <string>
9
10 #include "base/json/json_writer.h"
11 #include "base/logging.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_util.h"
14 #include "base/values.h"
15 #include "chromeos/network/onc/onc_signature.h"
16 #include "components/onc/onc_constants.h"
17
18 namespace chromeos {
19 namespace onc {
20
21 namespace {
22
23 // Copied from policy/configuration_policy_handler.cc.
24 // TODO(pneubeck): move to a common place like base/.
ValueTypeToString(base::Value::Type type)25 std::string ValueTypeToString(base::Value::Type type) {
26 static const char* strings[] = {
27 "null",
28 "boolean",
29 "integer",
30 "double",
31 "string",
32 "binary",
33 "dictionary",
34 "list"
35 };
36 CHECK(static_cast<size_t>(type) < arraysize(strings));
37 return strings[type];
38 }
39
40 } // namespace
41
Validator(bool error_on_unknown_field,bool error_on_wrong_recommended,bool error_on_missing_field,bool managed_onc)42 Validator::Validator(bool error_on_unknown_field,
43 bool error_on_wrong_recommended,
44 bool error_on_missing_field,
45 bool managed_onc)
46 : error_on_unknown_field_(error_on_unknown_field),
47 error_on_wrong_recommended_(error_on_wrong_recommended),
48 error_on_missing_field_(error_on_missing_field),
49 managed_onc_(managed_onc),
50 onc_source_(::onc::ONC_SOURCE_NONE) {}
51
~Validator()52 Validator::~Validator() {}
53
ValidateAndRepairObject(const OncValueSignature * object_signature,const base::DictionaryValue & onc_object,Result * result)54 scoped_ptr<base::DictionaryValue> Validator::ValidateAndRepairObject(
55 const OncValueSignature* object_signature,
56 const base::DictionaryValue& onc_object,
57 Result* result) {
58 CHECK(object_signature != NULL);
59 *result = VALID;
60 error_or_warning_found_ = false;
61 bool error = false;
62 scoped_ptr<base::Value> result_value =
63 MapValue(*object_signature, onc_object, &error);
64 if (error) {
65 *result = INVALID;
66 result_value.reset();
67 } else if (error_or_warning_found_) {
68 *result = VALID_WITH_WARNINGS;
69 }
70 // The return value should be NULL if, and only if, |result| equals INVALID.
71 DCHECK_EQ(result_value.get() == NULL, *result == INVALID);
72
73 base::DictionaryValue* result_dict = NULL;
74 if (result_value.get() != NULL) {
75 result_value.release()->GetAsDictionary(&result_dict);
76 CHECK(result_dict != NULL);
77 }
78
79 return make_scoped_ptr(result_dict);
80 }
81
MapValue(const OncValueSignature & signature,const base::Value & onc_value,bool * error)82 scoped_ptr<base::Value> Validator::MapValue(const OncValueSignature& signature,
83 const base::Value& onc_value,
84 bool* error) {
85 if (onc_value.GetType() != signature.onc_type) {
86 LOG(ERROR) << MessageHeader() << "Found value '" << onc_value
87 << "' of type '" << ValueTypeToString(onc_value.GetType())
88 << "', but type '" << ValueTypeToString(signature.onc_type)
89 << "' is required.";
90 error_or_warning_found_ = *error = true;
91 return scoped_ptr<base::Value>();
92 }
93
94 scoped_ptr<base::Value> repaired =
95 Mapper::MapValue(signature, onc_value, error);
96 if (repaired.get() != NULL)
97 CHECK_EQ(repaired->GetType(), signature.onc_type);
98 return repaired.Pass();
99 }
100
MapObject(const OncValueSignature & signature,const base::DictionaryValue & onc_object,bool * error)101 scoped_ptr<base::DictionaryValue> Validator::MapObject(
102 const OncValueSignature& signature,
103 const base::DictionaryValue& onc_object,
104 bool* error) {
105 scoped_ptr<base::DictionaryValue> repaired(new base::DictionaryValue);
106
107 bool valid = ValidateObjectDefault(signature, onc_object, repaired.get());
108 if (valid) {
109 if (&signature == &kToplevelConfigurationSignature)
110 valid = ValidateToplevelConfiguration(repaired.get());
111 else if (&signature == &kNetworkConfigurationSignature)
112 valid = ValidateNetworkConfiguration(repaired.get());
113 else if (&signature == &kEthernetSignature)
114 valid = ValidateEthernet(repaired.get());
115 else if (&signature == &kIPConfigSignature)
116 valid = ValidateIPConfig(repaired.get());
117 else if (&signature == &kWiFiSignature)
118 valid = ValidateWiFi(repaired.get());
119 else if (&signature == &kVPNSignature)
120 valid = ValidateVPN(repaired.get());
121 else if (&signature == &kIPsecSignature)
122 valid = ValidateIPsec(repaired.get());
123 else if (&signature == &kOpenVPNSignature)
124 valid = ValidateOpenVPN(repaired.get());
125 else if (&signature == &kVerifyX509Signature)
126 valid = ValidateVerifyX509(repaired.get());
127 else if (&signature == &kCertificatePatternSignature)
128 valid = ValidateCertificatePattern(repaired.get());
129 else if (&signature == &kProxySettingsSignature)
130 valid = ValidateProxySettings(repaired.get());
131 else if (&signature == &kProxyLocationSignature)
132 valid = ValidateProxyLocation(repaired.get());
133 else if (&signature == &kEAPSignature)
134 valid = ValidateEAP(repaired.get());
135 else if (&signature == &kCertificateSignature)
136 valid = ValidateCertificate(repaired.get());
137 }
138
139 if (valid) {
140 return repaired.Pass();
141 } else {
142 DCHECK(error_or_warning_found_);
143 error_or_warning_found_ = *error = true;
144 return scoped_ptr<base::DictionaryValue>();
145 }
146 }
147
MapField(const std::string & field_name,const OncValueSignature & object_signature,const base::Value & onc_value,bool * found_unknown_field,bool * error)148 scoped_ptr<base::Value> Validator::MapField(
149 const std::string& field_name,
150 const OncValueSignature& object_signature,
151 const base::Value& onc_value,
152 bool* found_unknown_field,
153 bool* error) {
154 path_.push_back(field_name);
155 bool current_field_unknown = false;
156 scoped_ptr<base::Value> result = Mapper::MapField(
157 field_name, object_signature, onc_value, ¤t_field_unknown, error);
158
159 DCHECK_EQ(field_name, path_.back());
160 path_.pop_back();
161
162 if (current_field_unknown) {
163 error_or_warning_found_ = *found_unknown_field = true;
164 std::string message = MessageHeader() + "Field name '" + field_name +
165 "' is unknown.";
166 if (error_on_unknown_field_)
167 LOG(ERROR) << message;
168 else
169 LOG(WARNING) << message;
170 }
171
172 return result.Pass();
173 }
174
MapArray(const OncValueSignature & array_signature,const base::ListValue & onc_array,bool * nested_error)175 scoped_ptr<base::ListValue> Validator::MapArray(
176 const OncValueSignature& array_signature,
177 const base::ListValue& onc_array,
178 bool* nested_error) {
179 bool nested_error_in_current_array = false;
180 scoped_ptr<base::ListValue> result = Mapper::MapArray(
181 array_signature, onc_array, &nested_error_in_current_array);
182
183 // Drop individual networks and certificates instead of rejecting all of
184 // the configuration.
185 if (nested_error_in_current_array &&
186 &array_signature != &kNetworkConfigurationListSignature &&
187 &array_signature != &kCertificateListSignature) {
188 *nested_error = nested_error_in_current_array;
189 }
190 return result.Pass();
191 }
192
MapEntry(int index,const OncValueSignature & signature,const base::Value & onc_value,bool * error)193 scoped_ptr<base::Value> Validator::MapEntry(int index,
194 const OncValueSignature& signature,
195 const base::Value& onc_value,
196 bool* error) {
197 std::string str = base::IntToString(index);
198 path_.push_back(str);
199 scoped_ptr<base::Value> result =
200 Mapper::MapEntry(index, signature, onc_value, error);
201 DCHECK_EQ(str, path_.back());
202 path_.pop_back();
203 return result.Pass();
204 }
205
ValidateObjectDefault(const OncValueSignature & signature,const base::DictionaryValue & onc_object,base::DictionaryValue * result)206 bool Validator::ValidateObjectDefault(const OncValueSignature& signature,
207 const base::DictionaryValue& onc_object,
208 base::DictionaryValue* result) {
209 bool found_unknown_field = false;
210 bool nested_error_occured = false;
211 MapFields(signature, onc_object, &found_unknown_field, &nested_error_occured,
212 result);
213
214 if (found_unknown_field && error_on_unknown_field_) {
215 DVLOG(1) << "Unknown field names are errors: Aborting.";
216 return false;
217 }
218
219 if (nested_error_occured)
220 return false;
221
222 return ValidateRecommendedField(signature, result);
223 }
224
ValidateRecommendedField(const OncValueSignature & object_signature,base::DictionaryValue * result)225 bool Validator::ValidateRecommendedField(
226 const OncValueSignature& object_signature,
227 base::DictionaryValue* result) {
228 CHECK(result != NULL);
229
230 scoped_ptr<base::ListValue> recommended;
231 scoped_ptr<base::Value> recommended_value;
232 // This remove passes ownership to |recommended_value|.
233 if (!result->RemoveWithoutPathExpansion(::onc::kRecommended,
234 &recommended_value)) {
235 return true;
236 }
237 base::ListValue* recommended_list = NULL;
238 recommended_value.release()->GetAsList(&recommended_list);
239 CHECK(recommended_list);
240
241 recommended.reset(recommended_list);
242
243 if (!managed_onc_) {
244 error_or_warning_found_ = true;
245 LOG(WARNING) << MessageHeader() << "Found the field '"
246 << ::onc::kRecommended
247 << "' in an unmanaged ONC. Removing it.";
248 return true;
249 }
250
251 scoped_ptr<base::ListValue> repaired_recommended(new base::ListValue);
252 for (base::ListValue::iterator it = recommended->begin();
253 it != recommended->end(); ++it) {
254 std::string field_name;
255 if (!(*it)->GetAsString(&field_name)) {
256 NOTREACHED();
257 continue;
258 }
259
260 const OncFieldSignature* field_signature =
261 GetFieldSignature(object_signature, field_name);
262
263 bool found_error = false;
264 std::string error_cause;
265 if (field_signature == NULL) {
266 found_error = true;
267 error_cause = "unknown";
268 } else if (field_signature->value_signature->onc_type ==
269 base::Value::TYPE_DICTIONARY) {
270 found_error = true;
271 error_cause = "dictionary-typed";
272 }
273
274 if (found_error) {
275 error_or_warning_found_ = true;
276 path_.push_back(::onc::kRecommended);
277 std::string message = MessageHeader() + "The " + error_cause +
278 " field '" + field_name + "' cannot be recommended.";
279 path_.pop_back();
280 if (error_on_wrong_recommended_) {
281 LOG(ERROR) << message;
282 return false;
283 } else {
284 LOG(WARNING) << message;
285 continue;
286 }
287 }
288
289 repaired_recommended->Append((*it)->DeepCopy());
290 }
291
292 result->Set(::onc::kRecommended, repaired_recommended.release());
293 return true;
294 }
295
296 namespace {
297
JoinStringRange(const char ** range_begin,const char ** range_end,const std::string & separator)298 std::string JoinStringRange(const char** range_begin,
299 const char** range_end,
300 const std::string& separator) {
301 std::vector<std::string> string_vector;
302 std::copy(range_begin, range_end, std::back_inserter(string_vector));
303 return JoinString(string_vector, separator);
304 }
305
306 } // namespace
307
FieldExistsAndHasNoValidValue(const base::DictionaryValue & object,const std::string & field_name,const char ** valid_values)308 bool Validator::FieldExistsAndHasNoValidValue(
309 const base::DictionaryValue& object,
310 const std::string& field_name,
311 const char** valid_values) {
312 std::string actual_value;
313 if (!object.GetStringWithoutPathExpansion(field_name, &actual_value))
314 return false;
315
316 const char** it = valid_values;
317 for (; *it != NULL; ++it) {
318 if (actual_value == *it)
319 return false;
320 }
321 error_or_warning_found_ = true;
322 std::string valid_values_str =
323 "[" + JoinStringRange(valid_values, it, ", ") + "]";
324 path_.push_back(field_name);
325 LOG(ERROR) << MessageHeader() << "Found value '" << actual_value <<
326 "', but expected one of the values " << valid_values_str;
327 path_.pop_back();
328 return true;
329 }
330
FieldExistsAndIsNotInRange(const base::DictionaryValue & object,const std::string & field_name,int lower_bound,int upper_bound)331 bool Validator::FieldExistsAndIsNotInRange(const base::DictionaryValue& object,
332 const std::string& field_name,
333 int lower_bound,
334 int upper_bound) {
335 int actual_value;
336 if (!object.GetIntegerWithoutPathExpansion(field_name, &actual_value) ||
337 (lower_bound <= actual_value && actual_value <= upper_bound)) {
338 return false;
339 }
340 error_or_warning_found_ = true;
341 path_.push_back(field_name);
342 LOG(ERROR) << MessageHeader() << "Found value '" << actual_value
343 << "', but expected a value in the range [" << lower_bound
344 << ", " << upper_bound << "] (boundaries inclusive)";
345 path_.pop_back();
346 return true;
347 }
348
FieldExistsAndIsEmpty(const base::DictionaryValue & object,const std::string & field_name)349 bool Validator::FieldExistsAndIsEmpty(const base::DictionaryValue& object,
350 const std::string& field_name) {
351 const base::Value* value = NULL;
352 if (!object.GetWithoutPathExpansion(field_name, &value))
353 return false;
354
355 std::string str;
356 const base::ListValue* list = NULL;
357 if (value->GetAsString(&str)) {
358 if (!str.empty())
359 return false;
360 } else if (value->GetAsList(&list)) {
361 if (!list->empty())
362 return false;
363 } else {
364 NOTREACHED();
365 return false;
366 }
367
368 error_or_warning_found_ = true;
369 path_.push_back(field_name);
370 LOG(ERROR) << MessageHeader() << "Found an empty string, but expected a "
371 << "non-empty string.";
372 path_.pop_back();
373 return true;
374 }
375
RequireField(const base::DictionaryValue & dict,const std::string & field_name)376 bool Validator::RequireField(const base::DictionaryValue& dict,
377 const std::string& field_name) {
378 if (dict.HasKey(field_name))
379 return true;
380 error_or_warning_found_ = true;
381 std::string message = MessageHeader() + "The required field '" + field_name +
382 "' is missing.";
383 if (error_on_missing_field_)
384 LOG(ERROR) << message;
385 else
386 LOG(WARNING) << message;
387 return false;
388 }
389
CheckGuidIsUniqueAndAddToSet(const base::DictionaryValue & dict,const std::string & key_guid,std::set<std::string> * guids)390 bool Validator::CheckGuidIsUniqueAndAddToSet(const base::DictionaryValue& dict,
391 const std::string& key_guid,
392 std::set<std::string> *guids) {
393 std::string guid;
394 if (dict.GetStringWithoutPathExpansion(key_guid, &guid)) {
395 if (guids->count(guid) != 0) {
396 error_or_warning_found_ = true;
397 LOG(ERROR) << MessageHeader() << "Found a duplicate GUID " << guid << ".";
398 return false;
399 }
400 guids->insert(guid);
401 }
402 return true;
403 }
404
IsCertPatternInDevicePolicy(const std::string & cert_type)405 bool Validator::IsCertPatternInDevicePolicy(const std::string& cert_type) {
406 if (cert_type == ::onc::certificate::kPattern &&
407 onc_source_ == ::onc::ONC_SOURCE_DEVICE_POLICY) {
408 error_or_warning_found_ = true;
409 LOG(ERROR) << MessageHeader() << "Client certificate patterns are "
410 << "prohibited in ONC device policies.";
411 return true;
412 }
413 return false;
414 }
415
IsGlobalNetworkConfigInUserImport(const base::DictionaryValue & onc_object)416 bool Validator::IsGlobalNetworkConfigInUserImport(
417 const base::DictionaryValue& onc_object) {
418 if (onc_source_ == ::onc::ONC_SOURCE_USER_IMPORT &&
419 onc_object.HasKey(::onc::toplevel_config::kGlobalNetworkConfiguration)) {
420 error_or_warning_found_ = true;
421 LOG(ERROR) << MessageHeader() << "GlobalNetworkConfiguration is prohibited "
422 << "in ONC user imports";
423 return true;
424 }
425 return false;
426 }
427
ValidateToplevelConfiguration(base::DictionaryValue * result)428 bool Validator::ValidateToplevelConfiguration(base::DictionaryValue* result) {
429 using namespace ::onc::toplevel_config;
430
431 static const char* kValidTypes[] = { kUnencryptedConfiguration,
432 kEncryptedConfiguration,
433 NULL };
434 if (FieldExistsAndHasNoValidValue(*result, kType, kValidTypes))
435 return false;
436
437 // Not part of the ONC spec:
438 // - We don't require the type field (we assume that it's an
439 // UnencryptedConfiguration then).
440 // - We don't require specific toplevel objects to be present (e.g. at least
441 // one of NetworkConfiguration or Certificates).
442
443 if (IsGlobalNetworkConfigInUserImport(*result))
444 return false;
445
446 return true;
447 }
448
ValidateNetworkConfiguration(base::DictionaryValue * result)449 bool Validator::ValidateNetworkConfiguration(base::DictionaryValue* result) {
450 using namespace ::onc::network_config;
451
452 static const char* kValidTypes[] = { ::onc::network_type::kEthernet,
453 ::onc::network_type::kVPN,
454 ::onc::network_type::kWiFi,
455 ::onc::network_type::kCellular,
456 NULL };
457 if (FieldExistsAndHasNoValidValue(*result, kType, kValidTypes) ||
458 FieldExistsAndIsEmpty(*result, kGUID)) {
459 return false;
460 }
461
462 if (!CheckGuidIsUniqueAndAddToSet(*result, kGUID, &network_guids_))
463 return false;
464
465 bool all_required_exist = RequireField(*result, kGUID);
466
467 bool remove = false;
468 result->GetBooleanWithoutPathExpansion(::onc::kRemove, &remove);
469 if (!remove) {
470 all_required_exist &=
471 RequireField(*result, kName) && RequireField(*result, kType);
472
473 std::string type;
474 result->GetStringWithoutPathExpansion(kType, &type);
475
476 // Prohibit anything but WiFi and Ethernet for device-level policy (which
477 // corresponds to shared networks). See also http://crosbug.com/28741.
478 if (onc_source_ == ::onc::ONC_SOURCE_DEVICE_POLICY &&
479 type != ::onc::network_type::kWiFi &&
480 type != ::onc::network_type::kEthernet) {
481 error_or_warning_found_ = true;
482 LOG(ERROR) << MessageHeader() << "Networks of type '"
483 << type << "' are prohibited in ONC device policies.";
484 return false;
485 }
486
487 if (type == ::onc::network_type::kWiFi) {
488 all_required_exist &= RequireField(*result, ::onc::network_config::kWiFi);
489 } else if (type == ::onc::network_type::kEthernet) {
490 all_required_exist &=
491 RequireField(*result, ::onc::network_config::kEthernet);
492 } else if (type == ::onc::network_type::kCellular) {
493 all_required_exist &=
494 RequireField(*result, ::onc::network_config::kCellular);
495 } else if (type == ::onc::network_type::kVPN) {
496 all_required_exist &= RequireField(*result, ::onc::network_config::kVPN);
497 } else if (!type.empty()) {
498 NOTREACHED();
499 }
500 }
501
502 return !error_on_missing_field_ || all_required_exist;
503 }
504
ValidateEthernet(base::DictionaryValue * result)505 bool Validator::ValidateEthernet(base::DictionaryValue* result) {
506 using namespace ::onc::ethernet;
507
508 static const char* kValidAuthentications[] = { kNone, k8021X, NULL };
509 if (FieldExistsAndHasNoValidValue(
510 *result, kAuthentication, kValidAuthentications)) {
511 return false;
512 }
513
514 bool all_required_exist = true;
515 std::string auth;
516 result->GetStringWithoutPathExpansion(kAuthentication, &auth);
517 if (auth == k8021X)
518 all_required_exist &= RequireField(*result, kEAP);
519
520 return !error_on_missing_field_ || all_required_exist;
521 }
522
ValidateIPConfig(base::DictionaryValue * result)523 bool Validator::ValidateIPConfig(base::DictionaryValue* result) {
524 using namespace ::onc::ipconfig;
525
526 static const char* kValidTypes[] = { kIPv4, kIPv6, NULL };
527 if (FieldExistsAndHasNoValidValue(
528 *result, ::onc::ipconfig::kType, kValidTypes))
529 return false;
530
531 std::string type;
532 result->GetStringWithoutPathExpansion(::onc::ipconfig::kType, &type);
533 int lower_bound = 1;
534 // In case of missing type, choose higher upper_bound.
535 int upper_bound = (type == kIPv4) ? 32 : 128;
536 if (FieldExistsAndIsNotInRange(
537 *result, kRoutingPrefix, lower_bound, upper_bound)) {
538 return false;
539 }
540
541 bool all_required_exist = RequireField(*result, kIPAddress) &&
542 RequireField(*result, kRoutingPrefix) &&
543 RequireField(*result, ::onc::ipconfig::kType);
544
545 return !error_on_missing_field_ || all_required_exist;
546 }
547
ValidateWiFi(base::DictionaryValue * result)548 bool Validator::ValidateWiFi(base::DictionaryValue* result) {
549 using namespace ::onc::wifi;
550
551 static const char* kValidSecurities[] =
552 { kNone, kWEP_PSK, kWEP_8021X, kWPA_PSK, kWPA_EAP, NULL };
553 if (FieldExistsAndHasNoValidValue(*result, kSecurity, kValidSecurities))
554 return false;
555
556 bool all_required_exist =
557 RequireField(*result, kSecurity) && RequireField(*result, kSSID);
558
559 std::string security;
560 result->GetStringWithoutPathExpansion(kSecurity, &security);
561 if (security == kWEP_8021X || security == kWPA_EAP)
562 all_required_exist &= RequireField(*result, kEAP);
563 else if (security == kWEP_PSK || security == kWPA_PSK)
564 all_required_exist &= RequireField(*result, kPassphrase);
565
566 return !error_on_missing_field_ || all_required_exist;
567 }
568
ValidateVPN(base::DictionaryValue * result)569 bool Validator::ValidateVPN(base::DictionaryValue* result) {
570 using namespace ::onc::vpn;
571
572 static const char* kValidTypes[] =
573 { kIPsec, kTypeL2TP_IPsec, kOpenVPN, NULL };
574 if (FieldExistsAndHasNoValidValue(*result, ::onc::vpn::kType, kValidTypes))
575 return false;
576
577 bool all_required_exist = RequireField(*result, ::onc::vpn::kType);
578 std::string type;
579 result->GetStringWithoutPathExpansion(::onc::vpn::kType, &type);
580 if (type == kOpenVPN) {
581 all_required_exist &= RequireField(*result, kOpenVPN);
582 } else if (type == kIPsec) {
583 all_required_exist &= RequireField(*result, kIPsec);
584 } else if (type == kTypeL2TP_IPsec) {
585 all_required_exist &=
586 RequireField(*result, kIPsec) && RequireField(*result, kL2TP);
587 }
588
589 return !error_on_missing_field_ || all_required_exist;
590 }
591
ValidateIPsec(base::DictionaryValue * result)592 bool Validator::ValidateIPsec(base::DictionaryValue* result) {
593 using namespace ::onc::ipsec;
594 using namespace ::onc::certificate;
595
596 static const char* kValidAuthentications[] = { kPSK, kCert, NULL };
597 static const char* kValidCertTypes[] = { kRef, kPattern, NULL };
598 if (FieldExistsAndHasNoValidValue(
599 *result, kAuthenticationType, kValidAuthentications) ||
600 FieldExistsAndHasNoValidValue(
601 *result, ::onc::vpn::kClientCertType, kValidCertTypes) ||
602 FieldExistsAndIsEmpty(*result, kServerCARefs)) {
603 return false;
604 }
605
606 if (result->HasKey(kServerCARefs) && result->HasKey(kServerCARef)) {
607 error_or_warning_found_ = true;
608 LOG(ERROR) << MessageHeader() << "At most one of " << kServerCARefs
609 << " and " << kServerCARef << " can be set.";
610 return false;
611 }
612
613 bool all_required_exist = RequireField(*result, kAuthenticationType) &&
614 RequireField(*result, kIKEVersion);
615 std::string auth;
616 result->GetStringWithoutPathExpansion(kAuthenticationType, &auth);
617 bool has_server_ca_cert =
618 result->HasKey(kServerCARefs) || result->HasKey(kServerCARef);
619 if (auth == kCert) {
620 all_required_exist &= RequireField(*result, ::onc::vpn::kClientCertType);
621 if (!has_server_ca_cert) {
622 all_required_exist = false;
623 error_or_warning_found_ = true;
624 std::string message = MessageHeader() + "The required field '" +
625 kServerCARefs + "' is missing.";
626 if (error_on_missing_field_)
627 LOG(ERROR) << message;
628 else
629 LOG(WARNING) << message;
630 }
631 } else if (has_server_ca_cert) {
632 error_or_warning_found_ = true;
633 LOG(ERROR) << MessageHeader() << kServerCARefs << " (or " << kServerCARef
634 << ") can only be set if " << kAuthenticationType
635 << " is set to " << kCert << ".";
636 return false;
637 }
638
639 std::string cert_type;
640 result->GetStringWithoutPathExpansion(::onc::vpn::kClientCertType,
641 &cert_type);
642
643 if (IsCertPatternInDevicePolicy(cert_type))
644 return false;
645
646 if (cert_type == kPattern)
647 all_required_exist &= RequireField(*result, ::onc::vpn::kClientCertPattern);
648 else if (cert_type == kRef)
649 all_required_exist &= RequireField(*result, ::onc::vpn::kClientCertRef);
650
651 return !error_on_missing_field_ || all_required_exist;
652 }
653
ValidateOpenVPN(base::DictionaryValue * result)654 bool Validator::ValidateOpenVPN(base::DictionaryValue* result) {
655 using namespace ::onc::openvpn;
656 using namespace ::onc::certificate;
657
658 static const char* kValidAuthRetryValues[] =
659 { ::onc::openvpn::kNone, kInteract, kNoInteract, NULL };
660 static const char* kValidCertTypes[] =
661 { ::onc::certificate::kNone, kRef, kPattern, NULL };
662 static const char* kValidCertTlsValues[] =
663 { ::onc::openvpn::kNone, ::onc::openvpn::kServer, NULL };
664
665 if (FieldExistsAndHasNoValidValue(
666 *result, kAuthRetry, kValidAuthRetryValues) ||
667 FieldExistsAndHasNoValidValue(
668 *result, ::onc::vpn::kClientCertType, kValidCertTypes) ||
669 FieldExistsAndHasNoValidValue(
670 *result, kRemoteCertTLS, kValidCertTlsValues) ||
671 FieldExistsAndIsEmpty(*result, kServerCARefs)) {
672 return false;
673 }
674
675 if (result->HasKey(kServerCARefs) && result->HasKey(kServerCARef)) {
676 error_or_warning_found_ = true;
677 LOG(ERROR) << MessageHeader() << "At most one of " << kServerCARefs
678 << " and " << kServerCARef << " can be set.";
679 return false;
680 }
681
682 bool all_required_exist = RequireField(*result, ::onc::vpn::kClientCertType);
683 std::string cert_type;
684 result->GetStringWithoutPathExpansion(::onc::vpn::kClientCertType,
685 &cert_type);
686
687 if (IsCertPatternInDevicePolicy(cert_type))
688 return false;
689
690 if (cert_type == kPattern)
691 all_required_exist &= RequireField(*result, ::onc::vpn::kClientCertPattern);
692 else if (cert_type == kRef)
693 all_required_exist &= RequireField(*result, ::onc::vpn::kClientCertRef);
694
695 return !error_on_missing_field_ || all_required_exist;
696 }
697
ValidateVerifyX509(base::DictionaryValue * result)698 bool Validator::ValidateVerifyX509(base::DictionaryValue* result) {
699 using namespace ::onc::verify_x509;
700
701 static const char* kValidTypeValues[] =
702 {types::kName, types::kNamePrefix, types::kSubject, NULL};
703
704 if (FieldExistsAndHasNoValidValue(*result, kType, kValidTypeValues))
705 return false;
706
707 bool all_required_exist = RequireField(*result, kName);
708
709 return !error_on_missing_field_ || all_required_exist;
710 }
711
ValidateCertificatePattern(base::DictionaryValue * result)712 bool Validator::ValidateCertificatePattern(base::DictionaryValue* result) {
713 using namespace ::onc::certificate;
714
715 bool all_required_exist = true;
716 if (!result->HasKey(kSubject) && !result->HasKey(kIssuer) &&
717 !result->HasKey(kIssuerCARef)) {
718 error_or_warning_found_ = true;
719 all_required_exist = false;
720 std::string message = MessageHeader() + "None of the fields '" + kSubject +
721 "', '" + kIssuer + "', and '" + kIssuerCARef +
722 "' is present, but at least one is required.";
723 if (error_on_missing_field_)
724 LOG(ERROR) << message;
725 else
726 LOG(WARNING) << message;
727 }
728
729 return !error_on_missing_field_ || all_required_exist;
730 }
731
ValidateProxySettings(base::DictionaryValue * result)732 bool Validator::ValidateProxySettings(base::DictionaryValue* result) {
733 using namespace ::onc::proxy;
734
735 static const char* kValidTypes[] = { kDirect, kManual, kPAC, kWPAD, NULL };
736 if (FieldExistsAndHasNoValidValue(*result, ::onc::proxy::kType, kValidTypes))
737 return false;
738
739 bool all_required_exist = RequireField(*result, ::onc::proxy::kType);
740 std::string type;
741 result->GetStringWithoutPathExpansion(::onc::proxy::kType, &type);
742 if (type == kManual)
743 all_required_exist &= RequireField(*result, kManual);
744 else if (type == kPAC)
745 all_required_exist &= RequireField(*result, kPAC);
746
747 return !error_on_missing_field_ || all_required_exist;
748 }
749
ValidateProxyLocation(base::DictionaryValue * result)750 bool Validator::ValidateProxyLocation(base::DictionaryValue* result) {
751 using namespace ::onc::proxy;
752
753 bool all_required_exist =
754 RequireField(*result, kHost) && RequireField(*result, kPort);
755
756 return !error_on_missing_field_ || all_required_exist;
757 }
758
ValidateEAP(base::DictionaryValue * result)759 bool Validator::ValidateEAP(base::DictionaryValue* result) {
760 using namespace ::onc::eap;
761 using namespace ::onc::certificate;
762
763 static const char* kValidInnerValues[] =
764 { kAutomatic, kMD5, kMSCHAPv2, kPAP, NULL };
765 static const char* kValidOuterValues[] =
766 { kPEAP, kEAP_TLS, kEAP_TTLS, kLEAP, kEAP_SIM, kEAP_FAST, kEAP_AKA,
767 NULL };
768 static const char* kValidCertTypes[] = { kRef, kPattern, NULL };
769
770 if (FieldExistsAndHasNoValidValue(*result, kInner, kValidInnerValues) ||
771 FieldExistsAndHasNoValidValue(*result, kOuter, kValidOuterValues) ||
772 FieldExistsAndHasNoValidValue(
773 *result, kClientCertType, kValidCertTypes) ||
774 FieldExistsAndIsEmpty(*result, kServerCARefs)) {
775 return false;
776 }
777
778 if (result->HasKey(kServerCARefs) && result->HasKey(kServerCARef)) {
779 error_or_warning_found_ = true;
780 LOG(ERROR) << MessageHeader() << "At most one of " << kServerCARefs
781 << " and " << kServerCARef << " can be set.";
782 return false;
783 }
784
785 bool all_required_exist = RequireField(*result, kOuter);
786 std::string cert_type;
787 result->GetStringWithoutPathExpansion(kClientCertType, &cert_type);
788
789 if (IsCertPatternInDevicePolicy(cert_type))
790 return false;
791
792 if (cert_type == kPattern)
793 all_required_exist &= RequireField(*result, kClientCertPattern);
794 else if (cert_type == kRef)
795 all_required_exist &= RequireField(*result, kClientCertRef);
796
797 return !error_on_missing_field_ || all_required_exist;
798 }
799
ValidateCertificate(base::DictionaryValue * result)800 bool Validator::ValidateCertificate(base::DictionaryValue* result) {
801 using namespace ::onc::certificate;
802
803 static const char* kValidTypes[] = { kClient, kServer, kAuthority, NULL };
804 if (FieldExistsAndHasNoValidValue(*result, kType, kValidTypes) ||
805 FieldExistsAndIsEmpty(*result, kGUID)) {
806 return false;
807 }
808
809 std::string type;
810 result->GetStringWithoutPathExpansion(kType, &type);
811 if (onc_source_ == ::onc::ONC_SOURCE_DEVICE_POLICY &&
812 (type == kServer || type == kAuthority)) {
813 error_or_warning_found_ = true;
814 LOG(ERROR) << MessageHeader() << "Server and authority certificates are "
815 << "prohibited in ONC device policies.";
816 return false;
817 }
818
819 if (!CheckGuidIsUniqueAndAddToSet(*result, kGUID, &certificate_guids_))
820 return false;
821
822 bool all_required_exist = RequireField(*result, kGUID);
823
824 bool remove = false;
825 result->GetBooleanWithoutPathExpansion(::onc::kRemove, &remove);
826 if (!remove) {
827 all_required_exist &= RequireField(*result, kType);
828
829 if (type == kClient)
830 all_required_exist &= RequireField(*result, kPKCS12);
831 else if (type == kServer || type == kAuthority)
832 all_required_exist &= RequireField(*result, kX509);
833 }
834
835 return !error_on_missing_field_ || all_required_exist;
836 }
837
MessageHeader()838 std::string Validator::MessageHeader() {
839 std::string path = path_.empty() ? "toplevel" : JoinString(path_, ".");
840 std::string message = "At " + path + ": ";
841 return message;
842 }
843
844 } // namespace onc
845 } // namespace chromeos
846