1 // Copyright 2015 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "buffet/buffet_config.h"
16 
17 #include <map>
18 #include <set>
19 
20 #include <base/files/file_util.h>
21 #include <base/files/important_file_writer.h>
22 #include <base/logging.h>
23 #include <base/message_loop/message_loop.h>
24 #include <base/strings/string_number_conversions.h>
25 #include <brillo/errors/error.h>
26 #include <brillo/errors/error_codes.h>
27 #include <brillo/osrelease_reader.h>
28 #include <brillo/strings/string_utils.h>
29 #include <weave/enum_to_string.h>
30 
31 namespace buffet {
32 
33 namespace {
34 
35 const char kErrorDomain[] = "buffet";
36 const char kFileReadError[] = "file_read_error";
37 const char kProductVersionKey[] = "product_version";
38 
39 class DefaultFileIO : public BuffetConfig::FileIO {
40  public:
ReadFile(const base::FilePath & path,std::string * content)41   bool ReadFile(const base::FilePath& path, std::string* content) override {
42     return base::ReadFileToString(path, content);
43   }
WriteFile(const base::FilePath & path,const std::string & content)44   bool WriteFile(const base::FilePath& path,
45                  const std::string& content) override {
46     return base::ImportantFileWriter::WriteFileAtomically(path, content);
47   }
48 };
49 
50 }  // namespace
51 
52 namespace config_keys {
53 
54 const char kClientId[] = "client_id";
55 const char kClientSecret[] = "client_secret";
56 const char kApiKey[] = "api_key";
57 const char kOAuthURL[] = "oauth_url";
58 const char kServiceURL[] = "service_url";
59 const char kName[] = "name";
60 const char kDescription[] = "description";
61 const char kLocation[] = "location";
62 const char kLocalAnonymousAccessRole[] = "local_anonymous_access_role";
63 const char kLocalDiscoveryEnabled[] = "local_discovery_enabled";
64 const char kLocalPairingEnabled[] = "local_pairing_enabled";
65 const char kOemName[] = "oem_name";
66 const char kModelName[] = "model_name";
67 const char kModelId[] = "model_id";
68 const char kWifiAutoSetupEnabled[] = "wifi_auto_setup_enabled";
69 const char kEmbeddedCode[] = "embedded_code";
70 const char kPairingModes[] = "pairing_modes";
71 
72 }  // namespace config_keys
73 
BuffetConfig(const Options & options)74 BuffetConfig::BuffetConfig(const Options& options)
75     : options_(options),
76       default_encryptor_(Encryptor::CreateDefaultEncryptor()),
77       encryptor_(default_encryptor_.get()),
78       default_file_io_(new DefaultFileIO),
79       file_io_(default_file_io_.get()) {}
80 
LoadDefaults(weave::Settings * settings)81 bool BuffetConfig::LoadDefaults(weave::Settings* settings) {
82   // Keep this hardcoded default for sometime. This previously was set by
83   // libweave. It should be set by overlay's buffet.conf.
84   // Keys owners: avakulenko, gene, vitalybuka.
85   settings->client_id =
86       "338428340000-vkb4p6h40c7kja1k3l70kke8t615cjit.apps.googleusercontent."
87       "com";
88   settings->client_secret = "LS_iPYo_WIOE0m2VnLdduhnx";
89   settings->api_key = "AIzaSyACK3oZtmIylUKXiTMqkZqfuRiCgQmQSAQ";
90 
91   settings->name = "Developer device";
92   settings->oem_name = "Chromium";
93   settings->model_name = "Brillo";
94   settings->model_id = "AAAAA";
95 
96   if (!base::PathExists(options_.defaults))
97     return true;  // Nothing to load.
98 
99   brillo::KeyValueStore store;
100   if (!store.Load(options_.defaults))
101     return false;
102   bool result = LoadDefaults(store, settings);
103   settings->test_privet_ssid = options_.test_privet_ssid;
104 
105   if (!options_.client_id.empty())
106     settings->client_id = options_.client_id;
107   if (!options_.client_secret.empty())
108     settings->client_secret = options_.client_secret;
109   if (!options_.api_key.empty())
110     settings->api_key = options_.api_key;
111   if (!options_.oauth_url.empty())
112     settings->oauth_url = options_.oauth_url;
113   if (!options_.service_url.empty())
114     settings->service_url = options_.service_url;
115 
116   return result;
117 }
118 
LoadDefaults(const brillo::KeyValueStore & store,weave::Settings * settings)119 bool BuffetConfig::LoadDefaults(const brillo::KeyValueStore& store,
120                                 weave::Settings* settings) {
121   store.GetString(config_keys::kClientId, &settings->client_id);
122   store.GetString(config_keys::kClientSecret, &settings->client_secret);
123   store.GetString(config_keys::kApiKey, &settings->api_key);
124   store.GetString(config_keys::kOAuthURL, &settings->oauth_url);
125   store.GetString(config_keys::kServiceURL, &settings->service_url);
126   store.GetString(config_keys::kOemName, &settings->oem_name);
127   store.GetString(config_keys::kModelName, &settings->model_name);
128   store.GetString(config_keys::kModelId, &settings->model_id);
129 
130   brillo::OsReleaseReader reader;
131   reader.Load();
132   if (!reader.GetString(kProductVersionKey, &settings->firmware_version)) {
133     LOG(ERROR) << "Could not read '" << kProductVersionKey << "' from OS";
134   }
135 
136   store.GetBoolean(config_keys::kWifiAutoSetupEnabled,
137                    &settings->wifi_auto_setup_enabled);
138   store.GetString(config_keys::kEmbeddedCode, &settings->embedded_code);
139 
140   std::string modes_str;
141   if (store.GetString(config_keys::kPairingModes, &modes_str)) {
142     std::set<weave::PairingType> pairing_modes;
143     for (const std::string& mode :
144          brillo::string_utils::Split(modes_str, ",", true, true)) {
145       weave::PairingType pairing_mode;
146       if (!StringToEnum(mode, &pairing_mode))
147         return false;
148       pairing_modes.insert(pairing_mode);
149     }
150     settings->pairing_modes = std::move(pairing_modes);
151   }
152 
153   store.GetString(config_keys::kName, &settings->name);
154   store.GetString(config_keys::kDescription, &settings->description);
155   store.GetString(config_keys::kLocation, &settings->location);
156 
157   std::string role_str;
158   if (store.GetString(config_keys::kLocalAnonymousAccessRole, &role_str)) {
159     if (!StringToEnum(role_str, &settings->local_anonymous_access_role))
160       return false;
161   }
162   store.GetBoolean(config_keys::kLocalDiscoveryEnabled,
163                    &settings->local_discovery_enabled);
164   store.GetBoolean(config_keys::kLocalPairingEnabled,
165                    &settings->local_pairing_enabled);
166   return true;
167 }
168 
LoadSettings(const std::string & name)169 std::string BuffetConfig::LoadSettings(const std::string& name) {
170   std::string settings_blob;
171   base::FilePath path = CreatePath(name);
172   if (!file_io_->ReadFile(path, &settings_blob)) {
173     LOG(WARNING) << "Failed to read \'" + path.value() +
174                         "\', proceeding with empty settings.";
175     return std::string();
176   }
177   std::string json_string;
178   if (!encryptor_->DecryptWithAuthentication(settings_blob, &json_string)) {
179     LOG(WARNING)
180         << "Failed to decrypt settings, proceeding with empty settings.";
181     SaveSettings(std::string(), name, {});
182     return std::string();
183   }
184   return json_string;
185 }
186 
LoadSettings()187 std::string BuffetConfig::LoadSettings() {
188   return LoadSettings("");
189 }
190 
SaveSettings(const std::string & name,const std::string & settings,const weave::DoneCallback & callback)191 void BuffetConfig::SaveSettings(const std::string& name,
192                                 const std::string& settings,
193                                 const weave::DoneCallback& callback) {
194   std::string encrypted_settings;
195   weave::ErrorPtr error;
196   base::FilePath path = CreatePath(name);
197   if (!encryptor_->EncryptWithAuthentication(settings, &encrypted_settings)) {
198     weave::Error::AddTo(&error, FROM_HERE, "file_write_error",
199                         "Failed to encrypt settings.");
200     encrypted_settings.clear();
201   }
202   if (!file_io_->WriteFile(path, encrypted_settings)) {
203     weave::Error::AddTo(&error, FROM_HERE, "file_write_error",
204                         "Failed to write \'" + path.value() +
205                             "\', proceeding with empty settings.");
206   }
207   if (!callback.is_null()) {
208     base::MessageLoop::current()->PostTask(
209         FROM_HERE, base::Bind(callback, base::Passed(&error)));
210   }
211 }
212 
CreatePath(const std::string & name) const213 base::FilePath BuffetConfig::CreatePath(const std::string& name) const {
214   return name.empty() ? options_.settings
215                       : options_.settings.InsertBeforeExtension(
216                             base::FilePath::kExtensionSeparator + name);
217 }
218 
LoadFile(const base::FilePath & file_path,std::string * data,brillo::ErrorPtr * error)219 bool BuffetConfig::LoadFile(const base::FilePath& file_path,
220                             std::string* data,
221                             brillo::ErrorPtr* error) {
222   if (!file_io_->ReadFile(file_path, data)) {
223     brillo::errors::system::AddSystemError(error, FROM_HERE, errno);
224     brillo::Error::AddToPrintf(error, FROM_HERE, kErrorDomain, kFileReadError,
225                                  "Failed to read file '%s'",
226                                  file_path.value().c_str());
227     return false;
228   }
229   return true;
230 }
231 
232 }  // namespace buffet
233