1 //
2 // Copyright (C) 2012 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 #include "shill/profile.h"
18 
19 #include <set>
20 #include <string>
21 #include <vector>
22 
23 #include <base/files/file_util.h>
24 #include <base/stl_util.h>
25 #include <base/strings/string_split.h>
26 #include <base/strings/string_util.h>
27 #include <base/strings/stringprintf.h>
28 #if defined(__ANDROID__)
29 #include <dbus/service_constants.h>
30 #else
31 #include <chromeos/dbus/service_constants.h>
32 #endif  // __ANDROID__
33 
34 #include "shill/adaptor_interfaces.h"
35 #include "shill/control_interface.h"
36 #include "shill/logging.h"
37 #include "shill/manager.h"
38 #include "shill/property_accessor.h"
39 #include "shill/service.h"
40 #include "shill/store_factory.h"
41 #include "shill/store_interface.h"
42 #include "shill/stub_storage.h"
43 
44 using base::FilePath;
45 using std::set;
46 using std::string;
47 using std::vector;
48 
49 namespace shill {
50 
51 #if defined(ENABLE_JSON_STORE)
52 namespace {
53 const char kFileExtensionJson[] = "json";
54 }
55 #endif
56 
57 // static
58 const char Profile::kUserProfileListPathname[] =
59     RUNDIR "/loaded_profile_list";
60 
Profile(ControlInterface * control_interface,Metrics * metrics,Manager * manager,const Identifier & name,const base::FilePath & storage_directory,bool connect_to_rpc)61 Profile::Profile(ControlInterface* control_interface,
62                  Metrics* metrics,
63                  Manager* manager,
64                  const Identifier& name,
65                  const base::FilePath& storage_directory,
66                  bool connect_to_rpc)
67     : metrics_(metrics),
68       manager_(manager),
69       control_interface_(control_interface),
70       name_(name) {
71   if (connect_to_rpc)
72     adaptor_.reset(control_interface->CreateProfileAdaptor(this));
73 
74   // kCheckPortalListProperty: Registered in DefaultProfile
75   // kCountryProperty: Registered in DefaultProfile
76   store_.RegisterConstString(kNameProperty, &name_.identifier);
77   store_.RegisterConstString(kUserHashProperty, &name_.user_hash);
78 
79   // kOfflineModeProperty: Registered in DefaultProfile
80   // kPortalURLProperty: Registered in DefaultProfile
81 
82   HelpRegisterConstDerivedStrings(kServicesProperty,
83                                   &Profile::EnumerateAvailableServices);
84   HelpRegisterConstDerivedStrings(kEntriesProperty, &Profile::EnumerateEntries);
85 
86   if (name.user.empty()) {
87     // Subtle: Profile is only directly instantiated for user
88     // profiles. And user profiles must have a non-empty
89     // |name.user|. So we want to CHECK here. But Profile is also the
90     // base class for DefaultProfile. So a CHECK here would cause us
91     // to abort whenever we attempt to instantiate a DefaultProfile.
92     //
93     // Instead, we leave |persistent_profile_path_| unintialized. One
94     // of two things will happen: a) we become a DefaultProfile, and
95     // the DefaultProfile ctor sets |persistent_profile_path_|, or b)
96     // we really are destined to be a user Profile. In the latter
97     // case, our |name| argument was invalid,
98     // |persistent_profile_path_| is never set, and we CHECK for an
99     // empty |persistent_profile_path_| in InitStorage().
100     //
101     // TODO(quiche): Clean this up. crbug.com/527553
102   } else {
103     persistent_profile_path_ = GetFinalStoragePath(storage_directory, name);
104   }
105 }
106 
~Profile()107 Profile::~Profile() {}
108 
InitStorage(InitStorageOption storage_option,Error * error)109 bool Profile::InitStorage(InitStorageOption storage_option, Error* error) {
110   CHECK(!persistent_profile_path_.empty());
111   std::unique_ptr<StoreInterface> storage(
112       StoreFactory::GetInstance()->CreateStore(persistent_profile_path_));
113   bool already_exists = storage->IsNonEmpty();
114   if (!already_exists && storage_option != kCreateNew &&
115       storage_option != kCreateOrOpenExisting) {
116     Error::PopulateAndLog(
117         FROM_HERE, error, Error::kNotFound,
118         base::StringPrintf("Profile storage for %s:%s does not already exist",
119                            name_.user.c_str(), name_.identifier.c_str()));
120     return false;
121   } else if (already_exists && storage_option != kOpenExisting &&
122              storage_option != kCreateOrOpenExisting) {
123     Error::PopulateAndLog(
124         FROM_HERE, error, Error::kAlreadyExists,
125         base::StringPrintf("Profile storage for %s:%s already exists",
126                            name_.user.c_str(), name_.identifier.c_str()));
127     return false;
128   }
129   if (!storage->Open()) {
130     Error::PopulateAndLog(
131         FROM_HERE, error, Error::kInternalError,
132         base::StringPrintf("Could not open profile storage for %s:%s",
133                            name_.user.c_str(), name_.identifier.c_str()));
134     if (already_exists) {
135       // The profile contents are corrupt, or we do not have access to
136       // this file.  Move this file out of the way so a future open attempt
137       // will succeed, assuming the failure reason was the former.
138       storage->MarkAsCorrupted();
139       metrics_->NotifyCorruptedProfile();
140     }
141     return false;
142   }
143   if (!already_exists) {
144     // Add a descriptive header to the profile so even if nothing is stored
145     // to it, it still has some content.  Completely empty keyfiles are not
146     // valid for reading.
147     storage->SetHeader(
148         base::StringPrintf("Profile %s:%s", name_.user.c_str(),
149                            name_.identifier.c_str()));
150   }
151   set_storage(storage.release());
152   manager_->OnProfileStorageInitialized(this);
153   return true;
154 }
155 
InitStubStorage()156 void Profile::InitStubStorage() {
157   set_storage(new StubStorage());
158 }
159 
RemoveStorage(Error * error)160 bool Profile::RemoveStorage(Error* error) {
161   CHECK(!storage_.get());
162   CHECK(!persistent_profile_path_.empty());
163 
164   if (!base::DeleteFile(persistent_profile_path_, false)) {
165     Error::PopulateAndLog(
166         FROM_HERE, error, Error::kOperationFailed,
167         base::StringPrintf("Could not remove path %s",
168                            persistent_profile_path_.value().c_str()));
169     return false;
170   }
171 
172   return true;
173 }
174 
GetFriendlyName()175 string Profile::GetFriendlyName() {
176   return (name_.user.empty() ? "" : name_.user + "/") + name_.identifier;
177 }
178 
GetRpcIdentifier()179 string Profile::GetRpcIdentifier() {
180   if (!adaptor_.get()) {
181     return string();
182   }
183   return adaptor_->GetRpcIdentifier();
184 }
185 
set_storage(StoreInterface * storage)186 void Profile::set_storage(StoreInterface* storage) {
187   storage_.reset(storage);
188 }
189 
AdoptService(const ServiceRefPtr & service)190 bool Profile::AdoptService(const ServiceRefPtr& service) {
191   if (service->profile() == this) {
192     return false;
193   }
194   service->SetProfile(this);
195   return service->Save(storage_.get()) && storage_->Flush();
196 }
197 
AbandonService(const ServiceRefPtr & service)198 bool Profile::AbandonService(const ServiceRefPtr& service) {
199   if (service->profile() == this)
200     service->SetProfile(nullptr);
201   return storage_->DeleteGroup(service->GetStorageIdentifier()) &&
202       storage_->Flush();
203 }
204 
UpdateService(const ServiceRefPtr & service)205 bool Profile::UpdateService(const ServiceRefPtr& service) {
206   return service->Save(storage_.get()) && storage_->Flush();
207 }
208 
LoadService(const ServiceRefPtr & service)209 bool Profile::LoadService(const ServiceRefPtr& service) {
210   if (!ContainsService(service))
211     return false;
212   return service->Load(storage_.get());
213 }
214 
ConfigureService(const ServiceRefPtr & service)215 bool Profile::ConfigureService(const ServiceRefPtr& service) {
216   if (!LoadService(service))
217     return false;
218   service->SetProfile(this);
219   return true;
220 }
221 
ConfigureDevice(const DeviceRefPtr & device)222 bool Profile::ConfigureDevice(const DeviceRefPtr& device) {
223   return device->Load(storage_.get());
224 }
225 
ContainsService(const ServiceConstRefPtr & service)226 bool Profile::ContainsService(const ServiceConstRefPtr& service) {
227   return service->IsLoadableFrom(*storage_.get());
228 }
229 
DeleteEntry(const std::string & entry_name,Error * error)230 void Profile::DeleteEntry(const std::string& entry_name, Error* error) {
231   if (!storage_->ContainsGroup(entry_name)) {
232     Error::PopulateAndLog(
233         FROM_HERE, error, Error::kNotFound,
234         base::StringPrintf("Entry %s does not exist in profile",
235                            entry_name.c_str()));
236     return;
237   }
238   if (!manager_->HandleProfileEntryDeletion(this, entry_name)) {
239     // If HandleProfileEntryDeletion() returns succeeds, DeleteGroup()
240     // has already been called when AbandonService was called.
241     // Otherwise, we need to delete the group ourselves.
242     storage_->DeleteGroup(entry_name);
243   }
244   Save();
245 }
246 
GetServiceFromEntry(const std::string & entry_name,Error * error)247 ServiceRefPtr Profile::GetServiceFromEntry(const std::string& entry_name,
248                                            Error* error) {
249   if (!storage_->ContainsGroup(entry_name)) {
250     Error::PopulateAndLog(
251         FROM_HERE, error, Error::kNotFound,
252         base::StringPrintf("Entry %s does not exist in profile",
253                            entry_name.c_str()));
254     return nullptr;
255   }
256 
257   // Lookup the service entry from the registered services.
258   ServiceRefPtr service =
259       manager_->GetServiceWithStorageIdentifier(this, entry_name, error);
260   if (service) {
261     return service;
262   }
263 
264   // Load the service entry to a temporary service.
265   return manager_->CreateTemporaryServiceFromProfile(this, entry_name, error);
266 }
267 
IsValidIdentifierToken(const string & token)268 bool Profile::IsValidIdentifierToken(const string& token) {
269   if (token.empty()) {
270     return false;
271   }
272   for (auto chr : token) {
273     if (!base::IsAsciiAlpha(chr) && !base::IsAsciiDigit(chr)) {
274       return false;
275     }
276   }
277   return true;
278 }
279 
280 // static
ParseIdentifier(const string & raw,Identifier * parsed)281 bool Profile::ParseIdentifier(const string& raw, Identifier* parsed) {
282   if (raw.empty()) {
283     return false;
284   }
285   if (raw[0] == '~') {
286     // Format: "~user/identifier".
287     size_t slash = raw.find('/');
288     if (slash == string::npos) {
289       return false;
290     }
291     string user(raw.begin() + 1, raw.begin() + slash);
292     string identifier(raw.begin() + slash + 1, raw.end());
293     if (!IsValidIdentifierToken(user) || !IsValidIdentifierToken(identifier)) {
294       return false;
295     }
296     parsed->user = user;
297     parsed->identifier = identifier;
298     return true;
299   }
300 
301   // Format: "identifier".
302   if (!IsValidIdentifierToken(raw)) {
303     return false;
304   }
305   parsed->user = "";
306   parsed->identifier = raw;
307   return true;
308 }
309 
310 // static
IdentifierToString(const Identifier & name)311 string Profile::IdentifierToString(const Identifier& name) {
312   if (name.user.empty()) {
313     // Format: "identifier".
314     return name.identifier;
315   }
316 
317   // Format: "~user/identifier".
318   return base::StringPrintf(
319       "~%s/%s", name.user.c_str(), name.identifier.c_str());
320 }
321 
322 // static
LoadUserProfileList(const FilePath & path)323 vector<Profile::Identifier> Profile::LoadUserProfileList(const FilePath& path) {
324   vector<Identifier> profile_identifiers;
325   string profile_data;
326   if (!base::ReadFileToString(path, &profile_data)) {
327     return profile_identifiers;
328   }
329 
330   vector<string> profile_lines =
331       base::SplitString(profile_data, "\n", base::KEEP_WHITESPACE,
332                         base::SPLIT_WANT_ALL);
333   for (const auto& line : profile_lines) {
334     if (line.empty()) {
335       // This will be the case on the last line, so let's not complain about it.
336       continue;
337     }
338     size_t space = line.find(' ');
339     if (space == string::npos || space == 0) {
340       LOG(ERROR) << "Invalid line found in " << path.value()
341                  << ": " << line;
342       continue;
343     }
344     string name(line.begin(), line.begin() + space);
345     Identifier identifier;
346     if (!ParseIdentifier(name, &identifier) || identifier.user.empty()) {
347       LOG(ERROR) << "Invalid profile name found in " << path.value()
348                  << ": " << name;
349       continue;
350     }
351     identifier.user_hash = string(line.begin() + space + 1, line.end());
352     profile_identifiers.push_back(identifier);
353   }
354 
355   return profile_identifiers;
356 }
357 
358 // static
SaveUserProfileList(const FilePath & path,const vector<ProfileRefPtr> & profiles)359 bool Profile::SaveUserProfileList(const FilePath& path,
360                                   const vector<ProfileRefPtr>& profiles) {
361   vector<string> lines;
362   for (const auto& profile : profiles) {
363     Identifier& id = profile->name_;
364     if (id.user.empty()) {
365       continue;
366     }
367     lines.push_back(base::StringPrintf("%s %s\n",
368                                        IdentifierToString(id).c_str(),
369                                        id.user_hash.c_str()));
370   }
371   string content = base::JoinString(lines, "");
372   size_t ret = base::WriteFile(path, content.c_str(), content.length());
373   return ret == content.length();
374 }
375 
MatchesIdentifier(const Identifier & name) const376 bool Profile::MatchesIdentifier(const Identifier& name) const {
377   return name.user == name_.user && name.identifier == name_.identifier;
378 }
379 
Save()380 bool Profile::Save() {
381   return storage_->Flush();
382 }
383 
EnumerateAvailableServices(Error * error)384 vector<string> Profile::EnumerateAvailableServices(Error* error) {
385   // We should return the Manager's service list if this is the active profile.
386   if (manager_->IsActiveProfile(this)) {
387     return manager_->EnumerateAvailableServices(error);
388   } else {
389     return vector<string>();
390   }
391 }
392 
EnumerateEntries(Error *)393 vector<string> Profile::EnumerateEntries(Error* /*error*/) {
394   vector<string> service_groups;
395 
396   // Filter this list down to only entries that correspond
397   // to a technology.  (wifi_*, etc)
398   for (const auto& group : storage_->GetGroups()) {
399     if (Technology::IdentifierFromStorageGroup(group) != Technology::kUnknown)
400       service_groups.push_back(group);
401   }
402 
403   return service_groups;
404 }
405 
UpdateDevice(const DeviceRefPtr & device)406 bool Profile::UpdateDevice(const DeviceRefPtr& device) {
407   return false;
408 }
409 
410 #if !defined(DISABLE_WIFI)
UpdateWiFiProvider(const WiFiProvider & wifi_provider)411 bool Profile::UpdateWiFiProvider(const WiFiProvider& wifi_provider) {
412   return false;
413 }
414 #endif  // DISABLE_WIFI
415 
HelpRegisterConstDerivedStrings(const string & name,Strings (Profile::* get)(Error *))416 void Profile::HelpRegisterConstDerivedStrings(
417     const string& name,
418     Strings(Profile::*get)(Error*)) {
419   store_.RegisterDerivedStrings(
420       name, StringsAccessor(
421                 new CustomAccessor<Profile, Strings>(this, get, nullptr)));
422 }
423 
424 // static
GetFinalStoragePath(const FilePath & storage_dir,const Identifier & profile_name)425 FilePath Profile::GetFinalStoragePath(
426     const FilePath& storage_dir,
427     const Identifier& profile_name) {
428   FilePath base_path;
429   if (profile_name.user.empty()) {  // True for DefaultProfiles.
430     base_path = storage_dir.Append(
431         base::StringPrintf("%s.profile", profile_name.identifier.c_str()));
432   } else {
433     base_path = storage_dir.Append(
434         base::StringPrintf("%s/%s.profile",
435                            profile_name.user.c_str(),
436                            profile_name.identifier.c_str()));
437   }
438 
439   // TODO(petkov): Validate the directory permissions, etc.
440 
441 #if defined(ENABLE_JSON_STORE)
442   return base_path.AddExtension(kFileExtensionJson);
443 #else
444   return base_path;
445 #endif
446 }
447 
448 }  // namespace shill
449