onc_validator.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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
18namespace chromeos {
19namespace onc {
20
21namespace {
22
23template <typename T, size_t N>
24std::vector<T> toVector(T const (&array)[N]) {
25  return std::vector<T>(array, array + N);
26}
27
28// Copied from policy/configuration_policy_handler.cc.
29// TODO(pneubeck): move to a common place like base/.
30std::string ValueTypeToString(base::Value::Type type) {
31  const char* const strings[] = {"null",   "boolean", "integer",    "double",
32                                 "string", "binary",  "dictionary", "list"};
33  CHECK(static_cast<size_t>(type) < arraysize(strings));
34  return strings[type];
35}
36
37}  // namespace
38
39Validator::Validator(bool error_on_unknown_field,
40                     bool error_on_wrong_recommended,
41                     bool error_on_missing_field,
42                     bool managed_onc)
43    : error_on_unknown_field_(error_on_unknown_field),
44      error_on_wrong_recommended_(error_on_wrong_recommended),
45      error_on_missing_field_(error_on_missing_field),
46      managed_onc_(managed_onc),
47      onc_source_(::onc::ONC_SOURCE_NONE) {}
48
49Validator::~Validator() {}
50
51scoped_ptr<base::DictionaryValue> Validator::ValidateAndRepairObject(
52    const OncValueSignature* object_signature,
53    const base::DictionaryValue& onc_object,
54    Result* result) {
55  CHECK(object_signature);
56  *result = VALID;
57  error_or_warning_found_ = false;
58  bool error = false;
59  scoped_ptr<base::Value> result_value =
60      MapValue(*object_signature, onc_object, &error);
61  if (error) {
62    *result = INVALID;
63    result_value.reset();
64  } else if (error_or_warning_found_) {
65    *result = VALID_WITH_WARNINGS;
66  }
67  // The return value should be NULL if, and only if, |result| equals INVALID.
68  DCHECK_EQ(result_value.get() == NULL, *result == INVALID);
69
70  base::DictionaryValue* result_dict = NULL;
71  if (result_value) {
72    result_value.release()->GetAsDictionary(&result_dict);
73    CHECK(result_dict);
74  }
75
76  return make_scoped_ptr(result_dict);
77}
78
79scoped_ptr<base::Value> Validator::MapValue(const OncValueSignature& signature,
80                                            const base::Value& onc_value,
81                                            bool* error) {
82  if (onc_value.GetType() != signature.onc_type) {
83    LOG(ERROR) << MessageHeader() << "Found value '" << onc_value
84               << "' of type '" << ValueTypeToString(onc_value.GetType())
85               << "', but type '" << ValueTypeToString(signature.onc_type)
86               << "' is required.";
87    error_or_warning_found_ = *error = true;
88    return scoped_ptr<base::Value>();
89  }
90
91  scoped_ptr<base::Value> repaired =
92      Mapper::MapValue(signature, onc_value, error);
93  if (repaired)
94    CHECK_EQ(repaired->GetType(), signature.onc_type);
95  return repaired.Pass();
96}
97
98scoped_ptr<base::DictionaryValue> Validator::MapObject(
99    const OncValueSignature& signature,
100    const base::DictionaryValue& onc_object,
101    bool* error) {
102  scoped_ptr<base::DictionaryValue> repaired(new base::DictionaryValue);
103
104  bool valid = ValidateObjectDefault(signature, onc_object, repaired.get());
105  if (valid) {
106    if (&signature == &kToplevelConfigurationSignature) {
107      valid = ValidateToplevelConfiguration(repaired.get());
108    } else if (&signature == &kNetworkConfigurationSignature) {
109      valid = ValidateNetworkConfiguration(repaired.get());
110    } else if (&signature == &kEthernetSignature) {
111      valid = ValidateEthernet(repaired.get());
112    } else if (&signature == &kIPConfigSignature ||
113               &signature == &kSavedIPConfigSignature ||
114               &signature == &kStaticIPConfigSignature) {
115      valid = ValidateIPConfig(repaired.get());
116    } else if (&signature == &kWiFiSignature) {
117      valid = ValidateWiFi(repaired.get());
118    } else if (&signature == &kVPNSignature) {
119      valid = ValidateVPN(repaired.get());
120    } else if (&signature == &kIPsecSignature) {
121      valid = ValidateIPsec(repaired.get());
122    } else if (&signature == &kOpenVPNSignature) {
123      valid = ValidateOpenVPN(repaired.get());
124    } else if (&signature == &kVerifyX509Signature) {
125      valid = ValidateVerifyX509(repaired.get());
126    } else if (&signature == &kCertificatePatternSignature) {
127      valid = ValidateCertificatePattern(repaired.get());
128    } else if (&signature == &kProxySettingsSignature) {
129      valid = ValidateProxySettings(repaired.get());
130    } else if (&signature == &kProxyLocationSignature) {
131      valid = ValidateProxyLocation(repaired.get());
132    } else if (&signature == &kEAPSignature) {
133      valid = ValidateEAP(repaired.get());
134    } else if (&signature == &kCertificateSignature) {
135      valid = ValidateCertificate(repaired.get());
136    }
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
148scoped_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, &current_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
175scoped_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
193scoped_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
206bool 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
225bool Validator::ValidateRecommendedField(
226    const OncValueSignature& object_signature,
227    base::DictionaryValue* result) {
228  CHECK(result);
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) {
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
296bool Validator::ValidateClientCertFields(bool allow_cert_type_none,
297                                         base::DictionaryValue* result) {
298  using namespace ::onc::client_cert;
299  const char* const kValidCertTypes[] = {kRef, kPattern};
300  std::vector<const char*> valid_cert_types(toVector(kValidCertTypes));
301  if (allow_cert_type_none)
302    valid_cert_types.push_back(kClientCertTypeNone);
303  if (FieldExistsAndHasNoValidValue(*result, kClientCertType, valid_cert_types))
304    return false;
305
306  std::string cert_type;
307  result->GetStringWithoutPathExpansion(kClientCertType, &cert_type);
308
309  if (IsCertPatternInDevicePolicy(cert_type))
310    return false;
311
312  bool all_required_exist = true;
313
314  if (cert_type == kPattern)
315    all_required_exist &= RequireField(*result, kClientCertPattern);
316  else if (cert_type == kRef)
317    all_required_exist &= RequireField(*result, kClientCertRef);
318
319  return !error_on_missing_field_ || all_required_exist;
320}
321
322namespace {
323
324std::string JoinStringRange(const std::vector<const char*>& strings,
325                            const std::string& separator) {
326  std::vector<std::string> string_vector;
327  std::copy(strings.begin(), strings.end(), std::back_inserter(string_vector));
328  return JoinString(string_vector, separator);
329}
330
331}  // namespace
332
333bool Validator::FieldExistsAndHasNoValidValue(
334    const base::DictionaryValue& object,
335    const std::string& field_name,
336    const std::vector<const char*>& valid_values) {
337  std::string actual_value;
338  if (!object.GetStringWithoutPathExpansion(field_name, &actual_value))
339    return false;
340
341  for (std::vector<const char*>::const_iterator it = valid_values.begin();
342       it != valid_values.end();
343       ++it) {
344    if (actual_value == *it)
345      return false;
346  }
347  error_or_warning_found_ = true;
348  std::string valid_values_str =
349      "[" + JoinStringRange(valid_values, ", ") + "]";
350  path_.push_back(field_name);
351  LOG(ERROR) << MessageHeader() << "Found value '" << actual_value <<
352      "', but expected one of the values " << valid_values_str;
353  path_.pop_back();
354  return true;
355}
356
357bool Validator::FieldExistsAndIsNotInRange(const base::DictionaryValue& object,
358                                           const std::string& field_name,
359                                           int lower_bound,
360                                           int upper_bound) {
361  int actual_value;
362  if (!object.GetIntegerWithoutPathExpansion(field_name, &actual_value) ||
363      (lower_bound <= actual_value && actual_value <= upper_bound)) {
364    return false;
365  }
366  error_or_warning_found_ = true;
367  path_.push_back(field_name);
368  LOG(ERROR) << MessageHeader() << "Found value '" << actual_value
369             << "', but expected a value in the range [" << lower_bound
370             << ", " << upper_bound << "] (boundaries inclusive)";
371  path_.pop_back();
372  return true;
373}
374
375bool Validator::FieldExistsAndIsEmpty(const base::DictionaryValue& object,
376                                      const std::string& field_name) {
377  const base::Value* value = NULL;
378  if (!object.GetWithoutPathExpansion(field_name, &value))
379    return false;
380
381  std::string str;
382  const base::ListValue* list = NULL;
383  if (value->GetAsString(&str)) {
384    if (!str.empty())
385      return false;
386  } else if (value->GetAsList(&list)) {
387    if (!list->empty())
388      return false;
389  } else {
390    NOTREACHED();
391    return false;
392  }
393
394  error_or_warning_found_ = true;
395  path_.push_back(field_name);
396  LOG(ERROR) << MessageHeader() << "Found an empty string, but expected a "
397             << "non-empty string.";
398  path_.pop_back();
399  return true;
400}
401
402bool Validator::RequireField(const base::DictionaryValue& dict,
403                             const std::string& field_name) {
404  if (dict.HasKey(field_name))
405    return true;
406  error_or_warning_found_ = true;
407  std::string message = MessageHeader() + "The required field '" + field_name +
408      "' is missing.";
409  if (error_on_missing_field_)
410    LOG(ERROR) << message;
411  else
412    LOG(WARNING) << message;
413  return false;
414}
415
416bool Validator::CheckGuidIsUniqueAndAddToSet(const base::DictionaryValue& dict,
417                                             const std::string& key_guid,
418                                             std::set<std::string> *guids) {
419  std::string guid;
420  if (dict.GetStringWithoutPathExpansion(key_guid, &guid)) {
421    if (guids->count(guid) != 0) {
422      error_or_warning_found_ = true;
423      LOG(ERROR) << MessageHeader() << "Found a duplicate GUID " << guid << ".";
424      return false;
425    }
426    guids->insert(guid);
427  }
428  return true;
429}
430
431bool Validator::IsCertPatternInDevicePolicy(const std::string& cert_type) {
432  if (cert_type == ::onc::client_cert::kPattern &&
433      onc_source_ == ::onc::ONC_SOURCE_DEVICE_POLICY) {
434    error_or_warning_found_ = true;
435    LOG(ERROR) << MessageHeader() << "Client certificate patterns are "
436               << "prohibited in ONC device policies.";
437    return true;
438  }
439  return false;
440}
441
442bool Validator::IsGlobalNetworkConfigInUserImport(
443    const base::DictionaryValue& onc_object) {
444  if (onc_source_ == ::onc::ONC_SOURCE_USER_IMPORT &&
445      onc_object.HasKey(::onc::toplevel_config::kGlobalNetworkConfiguration)) {
446    error_or_warning_found_ = true;
447    LOG(ERROR) << MessageHeader() << "GlobalNetworkConfiguration is prohibited "
448               << "in ONC user imports";
449    return true;
450  }
451  return false;
452}
453
454bool Validator::ValidateToplevelConfiguration(base::DictionaryValue* result) {
455  using namespace ::onc::toplevel_config;
456
457  const char* const kValidTypes[] = {kUnencryptedConfiguration,
458                                     kEncryptedConfiguration};
459  const std::vector<const char*> valid_types(toVector(kValidTypes));
460  if (FieldExistsAndHasNoValidValue(*result, kType, valid_types))
461    return false;
462
463  if (IsGlobalNetworkConfigInUserImport(*result))
464    return false;
465
466  return true;
467}
468
469bool Validator::ValidateNetworkConfiguration(base::DictionaryValue* result) {
470  using namespace ::onc::network_config;
471
472  const char* const kValidTypes[] = {
473      ::onc::network_type::kEthernet, ::onc::network_type::kVPN,
474      ::onc::network_type::kWiFi, ::onc::network_type::kCellular};
475  const std::vector<const char*> valid_types(toVector(kValidTypes));
476  if (FieldExistsAndHasNoValidValue(*result, kType, valid_types) ||
477      FieldExistsAndIsEmpty(*result, kGUID)) {
478    return false;
479  }
480
481  if (!CheckGuidIsUniqueAndAddToSet(*result, kGUID, &network_guids_))
482    return false;
483
484  bool all_required_exist = RequireField(*result, kGUID);
485
486  bool remove = false;
487  result->GetBooleanWithoutPathExpansion(::onc::kRemove, &remove);
488  if (!remove) {
489    all_required_exist &=
490        RequireField(*result, kName) && RequireField(*result, kType);
491
492    std::string type;
493    result->GetStringWithoutPathExpansion(kType, &type);
494
495    // Prohibit anything but WiFi and Ethernet for device-level policy (which
496    // corresponds to shared networks). See also http://crosbug.com/28741.
497    if (onc_source_ == ::onc::ONC_SOURCE_DEVICE_POLICY &&
498        type != ::onc::network_type::kWiFi &&
499        type != ::onc::network_type::kEthernet) {
500      error_or_warning_found_ = true;
501      LOG(ERROR) << MessageHeader() << "Networks of type '"
502                 << type << "' are prohibited in ONC device policies.";
503      return false;
504    }
505
506    if (type == ::onc::network_type::kWiFi) {
507      all_required_exist &= RequireField(*result, ::onc::network_config::kWiFi);
508    } else if (type == ::onc::network_type::kEthernet) {
509      all_required_exist &=
510          RequireField(*result, ::onc::network_config::kEthernet);
511    } else if (type == ::onc::network_type::kCellular) {
512      all_required_exist &=
513          RequireField(*result, ::onc::network_config::kCellular);
514    } else if (type == ::onc::network_type::kVPN) {
515      all_required_exist &= RequireField(*result, ::onc::network_config::kVPN);
516    } else if (!type.empty()) {
517      NOTREACHED();
518    }
519  }
520
521  return !error_on_missing_field_ || all_required_exist;
522}
523
524bool Validator::ValidateEthernet(base::DictionaryValue* result) {
525  using namespace ::onc::ethernet;
526
527  const char* const kValidAuthentications[] = {kAuthenticationNone, k8021X};
528  const std::vector<const char*> valid_authentications(
529      toVector(kValidAuthentications));
530  if (FieldExistsAndHasNoValidValue(
531          *result, kAuthentication, valid_authentications)) {
532    return false;
533  }
534
535  bool all_required_exist = true;
536  std::string auth;
537  result->GetStringWithoutPathExpansion(kAuthentication, &auth);
538  if (auth == k8021X)
539    all_required_exist &= RequireField(*result, kEAP);
540
541  return !error_on_missing_field_ || all_required_exist;
542}
543
544bool Validator::ValidateIPConfig(base::DictionaryValue* result) {
545  using namespace ::onc::ipconfig;
546
547  const char* const kValidTypes[] = {kIPv4, kIPv6};
548  const std::vector<const char*> valid_types(toVector(kValidTypes));
549  if (FieldExistsAndHasNoValidValue(
550          *result, ::onc::ipconfig::kType, valid_types))
551    return false;
552
553  std::string type;
554  result->GetStringWithoutPathExpansion(::onc::ipconfig::kType, &type);
555  int lower_bound = 1;
556  // In case of missing type, choose higher upper_bound.
557  int upper_bound = (type == kIPv4) ? 32 : 128;
558  if (FieldExistsAndIsNotInRange(
559          *result, kRoutingPrefix, lower_bound, upper_bound)) {
560    return false;
561  }
562
563  bool all_required_exist = RequireField(*result, kIPAddress) &&
564                            RequireField(*result, kRoutingPrefix) &&
565                            RequireField(*result, ::onc::ipconfig::kType);
566
567  return !error_on_missing_field_ || all_required_exist;
568}
569
570bool Validator::ValidateWiFi(base::DictionaryValue* result) {
571  using namespace ::onc::wifi;
572
573  const char* const kValidSecurities[] = {kSecurityNone, kWEP_PSK, kWEP_8021X,
574                                          kWPA_PSK, kWPA_EAP};
575  const std::vector<const char*> valid_securities(toVector(kValidSecurities));
576  if (FieldExistsAndHasNoValidValue(*result, kSecurity, valid_securities))
577    return false;
578
579  bool all_required_exist =
580      RequireField(*result, kSecurity) && RequireField(*result, kSSID);
581
582  std::string security;
583  result->GetStringWithoutPathExpansion(kSecurity, &security);
584  if (security == kWEP_8021X || security == kWPA_EAP)
585    all_required_exist &= RequireField(*result, kEAP);
586  else if (security == kWEP_PSK || security == kWPA_PSK)
587    all_required_exist &= RequireField(*result, kPassphrase);
588
589  return !error_on_missing_field_ || all_required_exist;
590}
591
592bool Validator::ValidateVPN(base::DictionaryValue* result) {
593  using namespace ::onc::vpn;
594
595  const char* const kValidTypes[] = {kIPsec, kTypeL2TP_IPsec, kOpenVPN};
596  const std::vector<const char*> valid_types(toVector(kValidTypes));
597  if (FieldExistsAndHasNoValidValue(*result, ::onc::vpn::kType, valid_types))
598    return false;
599
600  bool all_required_exist = RequireField(*result, ::onc::vpn::kType);
601  std::string type;
602  result->GetStringWithoutPathExpansion(::onc::vpn::kType, &type);
603  if (type == kOpenVPN) {
604    all_required_exist &= RequireField(*result, kOpenVPN);
605  } else if (type == kIPsec) {
606    all_required_exist &= RequireField(*result, kIPsec);
607  } else if (type == kTypeL2TP_IPsec) {
608    all_required_exist &=
609        RequireField(*result, kIPsec) && RequireField(*result, kL2TP);
610  }
611
612  return !error_on_missing_field_ || all_required_exist;
613}
614
615bool Validator::ValidateIPsec(base::DictionaryValue* result) {
616  using namespace ::onc::ipsec;
617
618  const char* const kValidAuthentications[] = {kPSK, kCert};
619  const std::vector<const char*> valid_authentications(
620      toVector(kValidAuthentications));
621  if (FieldExistsAndHasNoValidValue(
622          *result, kAuthenticationType, valid_authentications) ||
623      FieldExistsAndIsEmpty(*result, kServerCARefs)) {
624    return false;
625  }
626
627  if (result->HasKey(kServerCARefs) && result->HasKey(kServerCARef)) {
628    error_or_warning_found_ = true;
629    LOG(ERROR) << MessageHeader() << "At most one of " << kServerCARefs
630               << " and " << kServerCARef << " can be set.";
631    return false;
632  }
633
634  if (!ValidateClientCertFields(false,  // don't allow ClientCertType None
635                                result)) {
636    return false;
637  }
638
639  bool all_required_exist = RequireField(*result, kAuthenticationType) &&
640                            RequireField(*result, kIKEVersion);
641  std::string auth;
642  result->GetStringWithoutPathExpansion(kAuthenticationType, &auth);
643  bool has_server_ca_cert =
644      result->HasKey(kServerCARefs) || result->HasKey(kServerCARef);
645  if (auth == kCert) {
646    all_required_exist &=
647        RequireField(*result, ::onc::client_cert::kClientCertType);
648    if (!has_server_ca_cert) {
649      all_required_exist = false;
650      error_or_warning_found_ = true;
651      std::string message = MessageHeader() + "The required field '" +
652                            kServerCARefs + "' is missing.";
653      if (error_on_missing_field_)
654        LOG(ERROR) << message;
655      else
656        LOG(WARNING) << message;
657    }
658  } else if (has_server_ca_cert) {
659    error_or_warning_found_ = true;
660    LOG(ERROR) << MessageHeader() << kServerCARefs << " (or " << kServerCARef
661               << ") can only be set if " << kAuthenticationType
662               << " is set to " << kCert << ".";
663    return false;
664  }
665
666  return !error_on_missing_field_ || all_required_exist;
667}
668
669bool Validator::ValidateOpenVPN(base::DictionaryValue* result) {
670  using namespace ::onc::openvpn;
671
672  const char* const kValidAuthRetryValues[] = {::onc::openvpn::kNone, kInteract,
673                                               kNoInteract};
674  const std::vector<const char*> valid_auth_retry_values(
675      toVector(kValidAuthRetryValues));
676  const char* const kValidCertTlsValues[] = {::onc::openvpn::kNone,
677                                             ::onc::openvpn::kServer};
678  const std::vector<const char*> valid_cert_tls_values(
679      toVector(kValidCertTlsValues));
680
681  if (FieldExistsAndHasNoValidValue(
682          *result, kAuthRetry, valid_auth_retry_values) ||
683      FieldExistsAndHasNoValidValue(
684          *result, kRemoteCertTLS, valid_cert_tls_values) ||
685      FieldExistsAndIsEmpty(*result, kServerCARefs)) {
686    return false;
687  }
688
689  if (result->HasKey(kServerCARefs) && result->HasKey(kServerCARef)) {
690    error_or_warning_found_ = true;
691    LOG(ERROR) << MessageHeader() << "At most one of " << kServerCARefs
692               << " and " << kServerCARef << " can be set.";
693    return false;
694  }
695
696  if (!ValidateClientCertFields(true /* allow ClientCertType None */, result))
697    return false;
698
699  bool all_required_exist =
700      RequireField(*result, ::onc::client_cert::kClientCertType);
701
702  return !error_on_missing_field_ || all_required_exist;
703}
704
705bool Validator::ValidateVerifyX509(base::DictionaryValue* result) {
706  using namespace ::onc::verify_x509;
707
708  const char* const kValidTypes[] = {types::kName, types::kNamePrefix,
709                                     types::kSubject};
710  const std::vector<const char*> valid_types(toVector(kValidTypes));
711
712  if (FieldExistsAndHasNoValidValue(*result, kType, valid_types))
713    return false;
714
715  bool all_required_exist = RequireField(*result, kName);
716
717  return !error_on_missing_field_ || all_required_exist;
718}
719
720bool Validator::ValidateCertificatePattern(base::DictionaryValue* result) {
721  using namespace ::onc::client_cert;
722
723  bool all_required_exist = true;
724  if (!result->HasKey(kSubject) && !result->HasKey(kIssuer) &&
725      !result->HasKey(kIssuerCARef)) {
726    error_or_warning_found_ = true;
727    all_required_exist = false;
728    std::string message = MessageHeader() + "None of the fields '" + kSubject +
729        "', '" + kIssuer + "', and '" + kIssuerCARef +
730        "' is present, but at least one is required.";
731    if (error_on_missing_field_)
732      LOG(ERROR) << message;
733    else
734      LOG(WARNING) << message;
735  }
736
737  return !error_on_missing_field_ || all_required_exist;
738}
739
740bool Validator::ValidateProxySettings(base::DictionaryValue* result) {
741  using namespace ::onc::proxy;
742
743  const char* const kValidTypes[] = {kDirect, kManual, kPAC, kWPAD};
744  const std::vector<const char*> valid_types(toVector(kValidTypes));
745  if (FieldExistsAndHasNoValidValue(*result, ::onc::proxy::kType, valid_types))
746    return false;
747
748  bool all_required_exist = RequireField(*result, ::onc::proxy::kType);
749  std::string type;
750  result->GetStringWithoutPathExpansion(::onc::proxy::kType, &type);
751  if (type == kManual)
752    all_required_exist &= RequireField(*result, kManual);
753  else if (type == kPAC)
754    all_required_exist &= RequireField(*result, kPAC);
755
756  return !error_on_missing_field_ || all_required_exist;
757}
758
759bool Validator::ValidateProxyLocation(base::DictionaryValue* result) {
760  using namespace ::onc::proxy;
761
762  bool all_required_exist =
763      RequireField(*result, kHost) && RequireField(*result, kPort);
764
765  return !error_on_missing_field_ || all_required_exist;
766}
767
768bool Validator::ValidateEAP(base::DictionaryValue* result) {
769  using namespace ::onc::eap;
770
771  const char* const kValidInnerValues[] = {kAutomatic, kMD5, kMSCHAPv2, kPAP};
772  const std::vector<const char*> valid_inner_values(
773      toVector(kValidInnerValues));
774  const char* const kValidOuterValues[] = {
775      kPEAP, kEAP_TLS, kEAP_TTLS, kLEAP, kEAP_SIM, kEAP_FAST, kEAP_AKA};
776  const std::vector<const char*> valid_outer_values(
777      toVector(kValidOuterValues));
778
779  if (FieldExistsAndHasNoValidValue(*result, kInner, valid_inner_values) ||
780      FieldExistsAndHasNoValidValue(*result, kOuter, valid_outer_values) ||
781      FieldExistsAndIsEmpty(*result, kServerCARefs)) {
782    return false;
783  }
784
785  if (result->HasKey(kServerCARefs) && result->HasKey(kServerCARef)) {
786    error_or_warning_found_ = true;
787    LOG(ERROR) << MessageHeader() << "At most one of " << kServerCARefs
788               << " and " << kServerCARef << " can be set.";
789    return false;
790  }
791
792  if (!ValidateClientCertFields(false,  // don't allow ClientCertType None
793                                result)) {
794    return false;
795  }
796
797  bool all_required_exist = RequireField(*result, kOuter);
798
799  return !error_on_missing_field_ || all_required_exist;
800}
801
802bool Validator::ValidateCertificate(base::DictionaryValue* result) {
803  using namespace ::onc::certificate;
804
805  const char* const kValidTypes[] = {kClient, kServer, kAuthority};
806  const std::vector<const char*> valid_types(toVector(kValidTypes));
807  if (FieldExistsAndHasNoValidValue(*result, kType, valid_types) ||
808      FieldExistsAndIsEmpty(*result, kGUID)) {
809    return false;
810  }
811
812  std::string type;
813  result->GetStringWithoutPathExpansion(kType, &type);
814  if (onc_source_ == ::onc::ONC_SOURCE_DEVICE_POLICY &&
815      (type == kServer || type == kAuthority)) {
816    error_or_warning_found_ = true;
817    LOG(ERROR) << MessageHeader() << "Server and authority certificates are "
818               << "prohibited in ONC device policies.";
819    return false;
820  }
821
822  if (!CheckGuidIsUniqueAndAddToSet(*result, kGUID, &certificate_guids_))
823    return false;
824
825  bool all_required_exist = RequireField(*result, kGUID);
826
827  bool remove = false;
828  result->GetBooleanWithoutPathExpansion(::onc::kRemove, &remove);
829  if (!remove) {
830    all_required_exist &= RequireField(*result, kType);
831
832    if (type == kClient)
833      all_required_exist &= RequireField(*result, kPKCS12);
834    else if (type == kServer || type == kAuthority)
835      all_required_exist &= RequireField(*result, kX509);
836  }
837
838  return !error_on_missing_field_ || all_required_exist;
839}
840
841std::string Validator::MessageHeader() {
842  std::string path = path_.empty() ? "toplevel" : JoinString(path_, ".");
843  std::string message = "At " + path + ": ";
844  return message;
845}
846
847}  // namespace onc
848}  // namespace chromeos
849