onc_translator_onc_to_shill.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// The implementation of TranslateONCObjectToShill is structured in two parts:
6// - The recursion through the existing ONC hierarchy
7//     see TranslateONCHierarchy
8// - The local translation of an object depending on the associated signature
9//     see LocalTranslator::TranslateFields
10
11#include "chromeos/network/onc/onc_translator.h"
12
13#include <string>
14
15#include "base/json/json_reader.h"
16#include "base/json/json_writer.h"
17#include "base/logging.h"
18#include "base/strings/string_util.h"
19#include "base/values.h"
20#include "chromeos/network/onc/onc_signature.h"
21#include "chromeos/network/onc/onc_translation_tables.h"
22#include "chromeos/network/shill_property_util.h"
23#include "components/onc/onc_constants.h"
24#include "third_party/cros_system_api/dbus/service_constants.h"
25
26namespace chromeos {
27namespace onc {
28
29namespace {
30
31bool ConvertListValueToStringVector(const base::ListValue& string_list,
32                                    std::vector<std::string>* result) {
33  for (size_t i = 0; i < string_list.GetSize(); ++i) {
34    std::string str;
35    if (!string_list.GetString(i, &str))
36      return false;
37    result->push_back(str);
38  }
39  return true;
40}
41
42scoped_ptr<base::StringValue> ConvertValueToString(const base::Value& value) {
43  std::string str;
44  if (!value.GetAsString(&str))
45    base::JSONWriter::Write(&value, &str);
46  return make_scoped_ptr(new base::StringValue(str));
47}
48
49// This class is responsible to translate the local fields of the given
50// |onc_object| according to |onc_signature| into |shill_dictionary|. This
51// translation should consider (if possible) only fields of this ONC object and
52// not nested objects because recursion is handled by the calling function
53// TranslateONCHierarchy.
54class LocalTranslator {
55 public:
56  LocalTranslator(const OncValueSignature& onc_signature,
57                  const base::DictionaryValue& onc_object,
58                  base::DictionaryValue* shill_dictionary)
59      : onc_signature_(&onc_signature),
60        onc_object_(&onc_object),
61        shill_dictionary_(shill_dictionary) {
62    field_translation_table_ = GetFieldTranslationTable(onc_signature);
63  }
64
65  void TranslateFields();
66
67 private:
68  void TranslateEthernet();
69  void TranslateOpenVPN();
70  void TranslateIPsec();
71  void TranslateVPN();
72  void TranslateWiFi();
73  void TranslateEAP();
74  void TranslateStaticIPConfig();
75  void TranslateNetworkConfiguration();
76
77  // Copies all entries from |onc_object_| to |shill_dictionary_| for which a
78  // translation (shill_property_name) is defined by |onc_signature_|.
79  void CopyFieldsAccordingToSignature();
80
81  // Adds |value| to |shill_dictionary| at the field shill_property_name given
82  // by the associated signature. Takes ownership of |value|. Does nothing if
83  // |value| is NULL or the property name cannot be read from the signature.
84  void AddValueAccordingToSignature(const std::string& onc_field_name,
85                                    scoped_ptr<base::Value> value);
86
87  // If existent, translates the entry at |onc_field_name| in |onc_object_|
88  // using |table|. It is an error if no matching table entry is found. Writes
89  // the result as entry at |shill_property_name| in |shill_dictionary_|.
90  void TranslateWithTableAndSet(const std::string& onc_field_name,
91                                const StringTranslationEntry table[],
92                                const std::string& shill_property_name);
93
94  const OncValueSignature* onc_signature_;
95  const FieldTranslationEntry* field_translation_table_;
96  const base::DictionaryValue* onc_object_;
97  base::DictionaryValue* shill_dictionary_;
98
99  DISALLOW_COPY_AND_ASSIGN(LocalTranslator);
100};
101
102void LocalTranslator::TranslateFields() {
103  if (onc_signature_ == &kNetworkConfigurationSignature)
104    TranslateNetworkConfiguration();
105  else if (onc_signature_ == &kStaticIPConfigSignature)
106    TranslateStaticIPConfig();
107  else if (onc_signature_ == &kEthernetSignature)
108    TranslateEthernet();
109  else if (onc_signature_ == &kVPNSignature)
110    TranslateVPN();
111  else if (onc_signature_ == &kOpenVPNSignature)
112    TranslateOpenVPN();
113  else if (onc_signature_ == &kIPsecSignature)
114    TranslateIPsec();
115  else if (onc_signature_ == &kWiFiSignature)
116    TranslateWiFi();
117  else if (onc_signature_ == &kEAPSignature)
118    TranslateEAP();
119  else
120    CopyFieldsAccordingToSignature();
121}
122
123void LocalTranslator::TranslateEthernet() {
124  std::string authentication;
125  onc_object_->GetStringWithoutPathExpansion(::onc::ethernet::kAuthentication,
126                                             &authentication);
127
128  const char* shill_type = shill::kTypeEthernet;
129  if (authentication == ::onc::ethernet::k8021X)
130    shill_type = shill::kTypeEthernetEap;
131  shill_dictionary_->SetStringWithoutPathExpansion(shill::kTypeProperty,
132                                                   shill_type);
133
134  CopyFieldsAccordingToSignature();
135}
136
137
138void LocalTranslator::TranslateStaticIPConfig() {
139  const base::ListValue* onc_nameservers = NULL;
140  if (onc_object_->GetListWithoutPathExpansion(::onc::ipconfig::kNameServers,
141                                               &onc_nameservers)) {
142    std::vector<std::string> onc_nameservers_vector;
143    ConvertListValueToStringVector(*onc_nameservers, &onc_nameservers_vector);
144    std::string shill_nameservers = JoinString(onc_nameservers_vector, ',');
145    shill_dictionary_->SetStringWithoutPathExpansion(
146        shill::kStaticIPNameServersProperty, shill_nameservers);
147  }
148
149  CopyFieldsAccordingToSignature();
150}
151
152void LocalTranslator::TranslateOpenVPN() {
153  // SaveCredentials needs special handling when translating from Shill -> ONC
154  // so handle it explicitly here.
155  bool save_credentials;
156  if (onc_object_->GetBooleanWithoutPathExpansion(
157          ::onc::vpn::kSaveCredentials, &save_credentials)) {
158    shill_dictionary_->SetBooleanWithoutPathExpansion(
159        shill::kSaveCredentialsProperty, save_credentials);
160  }
161  // Shill supports only one RemoteCertKU but ONC a list.
162  // Copy only the first entry if existing.
163  const base::ListValue* certKUs = NULL;
164  std::string certKU;
165  if (onc_object_->GetListWithoutPathExpansion(::onc::openvpn::kRemoteCertKU,
166                                               &certKUs) &&
167      certKUs->GetString(0, &certKU)) {
168    shill_dictionary_->SetStringWithoutPathExpansion(
169        shill::kOpenVPNRemoteCertKUProperty, certKU);
170  }
171
172  for (base::DictionaryValue::Iterator it(*onc_object_); !it.IsAtEnd();
173       it.Advance()) {
174    scoped_ptr<base::Value> translated;
175    if (it.key() == ::onc::openvpn::kRemoteCertKU ||
176        it.key() == ::onc::openvpn::kServerCAPEMs) {
177      translated.reset(it.value().DeepCopy());
178    } else {
179      // Shill wants all Provider/VPN fields to be strings.
180      translated = ConvertValueToString(it.value());
181    }
182    AddValueAccordingToSignature(it.key(), translated.Pass());
183  }
184}
185
186void LocalTranslator::TranslateIPsec() {
187  CopyFieldsAccordingToSignature();
188
189  // SaveCredentials needs special handling when translating from Shill -> ONC
190  // so handle it explicitly here.
191  bool save_credentials;
192  if (onc_object_->GetBooleanWithoutPathExpansion(
193          ::onc::vpn::kSaveCredentials, &save_credentials)) {
194    shill_dictionary_->SetBooleanWithoutPathExpansion(
195        shill::kSaveCredentialsProperty, save_credentials);
196  }
197}
198
199void LocalTranslator::TranslateVPN() {
200  std::string host;
201  if (onc_object_->GetStringWithoutPathExpansion(::onc::vpn::kHost, &host)) {
202    shill_dictionary_->SetStringWithoutPathExpansion(
203        shill::kProviderHostProperty, host);
204  }
205  std::string type;
206  onc_object_->GetStringWithoutPathExpansion(::onc::vpn::kType, &type);
207  TranslateWithTableAndSet(type, kVPNTypeTable, shill::kProviderTypeProperty);
208
209  CopyFieldsAccordingToSignature();
210}
211
212void LocalTranslator::TranslateWiFi() {
213  std::string security;
214  onc_object_->GetStringWithoutPathExpansion(::onc::wifi::kSecurity, &security);
215  TranslateWithTableAndSet(security, kWiFiSecurityTable,
216                           shill::kSecurityProperty);
217
218  std::string ssid;
219  onc_object_->GetStringWithoutPathExpansion(::onc::wifi::kSSID, &ssid);
220  shill_property_util::SetSSID(ssid, shill_dictionary_);
221
222  // We currently only support managed and no adhoc networks.
223  shill_dictionary_->SetStringWithoutPathExpansion(shill::kModeProperty,
224                                                   shill::kModeManaged);
225  CopyFieldsAccordingToSignature();
226}
227
228void LocalTranslator::TranslateEAP() {
229  std::string outer;
230  onc_object_->GetStringWithoutPathExpansion(::onc::eap::kOuter, &outer);
231  TranslateWithTableAndSet(outer, kEAPOuterTable, shill::kEapMethodProperty);
232
233  // Translate the inner protocol only for outer tunneling protocols.
234  if (outer == ::onc::eap::kPEAP || outer == ::onc::eap::kEAP_TTLS) {
235    // In ONC the Inner protocol defaults to "Automatic".
236    std::string inner = ::onc::eap::kAutomatic;
237    // ONC's Inner == "Automatic" translates to omitting the Phase2 property in
238    // Shill.
239    onc_object_->GetStringWithoutPathExpansion(::onc::eap::kInner, &inner);
240    if (inner != ::onc::eap::kAutomatic) {
241      const StringTranslationEntry* table =
242          outer == ::onc::eap::kPEAP ? kEAP_PEAP_InnerTable :
243                                       kEAP_TTLS_InnerTable;
244      TranslateWithTableAndSet(inner, table, shill::kEapPhase2AuthProperty);
245    }
246  }
247
248  CopyFieldsAccordingToSignature();
249}
250
251void LocalTranslator::TranslateNetworkConfiguration() {
252  std::string type;
253  onc_object_->GetStringWithoutPathExpansion(::onc::network_config::kType,
254                                             &type);
255
256  // Set the type except for Ethernet which is set in TranslateEthernet.
257  if (type != ::onc::network_type::kEthernet)
258    TranslateWithTableAndSet(type, kNetworkTypeTable, shill::kTypeProperty);
259
260  // Shill doesn't allow setting the name for non-VPN networks.
261  if (type == ::onc::network_type::kVPN) {
262    std::string name;
263    onc_object_->GetStringWithoutPathExpansion(::onc::network_config::kName,
264                                               &name);
265    shill_dictionary_->SetStringWithoutPathExpansion(shill::kNameProperty,
266                                                     name);
267  }
268
269  CopyFieldsAccordingToSignature();
270}
271
272void LocalTranslator::CopyFieldsAccordingToSignature() {
273  for (base::DictionaryValue::Iterator it(*onc_object_); !it.IsAtEnd();
274       it.Advance()) {
275    AddValueAccordingToSignature(it.key(),
276                                 make_scoped_ptr(it.value().DeepCopy()));
277  }
278}
279
280void LocalTranslator::AddValueAccordingToSignature(
281    const std::string& onc_name,
282    scoped_ptr<base::Value> value) {
283  if (!value || !field_translation_table_)
284    return;
285  std::string shill_property_name;
286  if (!GetShillPropertyName(onc_name,
287                            field_translation_table_,
288                            &shill_property_name))
289    return;
290
291  shill_dictionary_->SetWithoutPathExpansion(shill_property_name,
292                                             value.release());
293}
294
295void LocalTranslator::TranslateWithTableAndSet(
296    const std::string& onc_value,
297    const StringTranslationEntry table[],
298    const std::string& shill_property_name) {
299  std::string shill_value;
300  if (TranslateStringToShill(table, onc_value, &shill_value)) {
301    shill_dictionary_->SetStringWithoutPathExpansion(shill_property_name,
302                                                     shill_value);
303    return;
304  }
305  // As we previously validate ONC, this case should never occur. If it still
306  // occurs, we should check here. Otherwise the failure will only show up much
307  // later in Shill.
308  LOG(ERROR) << "Value '" << onc_value
309             << "' cannot be translated to Shill property "
310             << shill_property_name;
311}
312
313// Iterates recursively over |onc_object| and its |signature|. At each object
314// applies the local translation using LocalTranslator::TranslateFields. The
315// results are written to |shill_dictionary|.
316void TranslateONCHierarchy(const OncValueSignature& signature,
317                           const base::DictionaryValue& onc_object,
318                           base::DictionaryValue* shill_dictionary) {
319  base::DictionaryValue* target_shill_dictionary = shill_dictionary;
320  std::vector<std::string> path_to_shill_dictionary =
321      GetPathToNestedShillDictionary(signature);
322  for (std::vector<std::string>::const_iterator it =
323           path_to_shill_dictionary.begin();
324       it != path_to_shill_dictionary.end();
325       ++it) {
326    base::DictionaryValue* nested_shill_dict = NULL;
327    target_shill_dictionary->GetDictionaryWithoutPathExpansion(
328        *it, &nested_shill_dict);
329    if (!nested_shill_dict)
330      nested_shill_dict = new base::DictionaryValue;
331    target_shill_dictionary->SetWithoutPathExpansion(*it, nested_shill_dict);
332    target_shill_dictionary = nested_shill_dict;
333  }
334  // Translates fields of |onc_object| and writes them to
335  // |target_shill_dictionary_| nested in |shill_dictionary|.
336  LocalTranslator translator(signature, onc_object, target_shill_dictionary);
337  translator.TranslateFields();
338
339  // Recurse into nested objects.
340  for (base::DictionaryValue::Iterator it(onc_object); !it.IsAtEnd();
341       it.Advance()) {
342    const base::DictionaryValue* inner_object = NULL;
343    if (!it.value().GetAsDictionary(&inner_object))
344      continue;
345
346    const OncFieldSignature* field_signature =
347        GetFieldSignature(signature, it.key());
348
349    TranslateONCHierarchy(*field_signature->value_signature, *inner_object,
350                          shill_dictionary);
351  }
352}
353
354}  // namespace
355
356scoped_ptr<base::DictionaryValue> TranslateONCObjectToShill(
357    const OncValueSignature* onc_signature,
358    const base::DictionaryValue& onc_object) {
359  CHECK(onc_signature != NULL);
360  scoped_ptr<base::DictionaryValue> shill_dictionary(new base::DictionaryValue);
361  TranslateONCHierarchy(*onc_signature, onc_object, shill_dictionary.get());
362  return shill_dictionary.Pass();
363}
364
365}  // namespace onc
366}  // namespace chromeos
367