blob: 338d6dc4098beebeac9fed405b7d03f068873ce6 [file] [log] [blame]
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
mukesh agrawalb54601c2011-06-07 17:39:22 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "shill/wifi_service.h"
6
mukesh agrawal43970a22013-02-15 16:00:07 -08007#include <algorithm>
mukesh agrawalb54601c2011-06-07 17:39:22 -07008#include <string>
Gaurav Shah10109f22011-11-11 20:16:22 -08009#include <utility>
mukesh agrawalb54601c2011-06-07 17:39:22 -070010
Chris Masone34af2182011-08-22 11:59:36 -070011#include <base/stringprintf.h>
12#include <base/string_number_conversions.h>
Paul Stewarta41e38d2011-11-11 07:47:29 -080013#include <base/string_split.h>
Chris Masone34af2182011-08-22 11:59:36 -070014#include <base/string_util.h>
Chris Masone3bd3c8c2011-06-13 08:20:26 -070015#include <chromeos/dbus/service_constants.h>
mukesh agrawal6e277772011-09-29 15:04:23 -070016#include <dbus/dbus.h>
mukesh agrawalb54601c2011-06-07 17:39:22 -070017
mukesh agrawale1d90e92012-02-15 17:36:08 -080018#include "shill/adaptor_interfaces.h"
Paul Stewart5baebb72013-03-14 11:43:29 -070019#include "shill/certificate_file.h"
mukesh agrawalb54601c2011-06-07 17:39:22 -070020#include "shill/control_interface.h"
mukesh agrawalcbfb34e2013-04-17 19:33:25 -070021#include "shill/dbus_adaptor.h"
mukesh agrawalb54601c2011-06-07 17:39:22 -070022#include "shill/device.h"
Paul Stewartc43cbbe2013-04-11 06:29:30 -070023#include "shill/eap_credentials.h"
mukesh agrawal1a056262011-10-05 14:36:54 -070024#include "shill/error.h"
Paul Stewart26b327e2011-10-19 11:38:09 -070025#include "shill/event_dispatcher.h"
mukesh agrawal1a056262011-10-05 14:36:54 -070026#include "shill/ieee80211.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070027#include "shill/logging.h"
Paul Stewart4357f4e2012-04-26 17:39:26 -070028#include "shill/manager.h"
Thieu Le48e6d6d2011-12-06 00:40:27 +000029#include "shill/metrics.h"
Paul Stewartecf4cd12012-04-17 11:08:39 -070030#include "shill/nss.h"
Thieu Lef7709452011-11-15 01:13:19 +000031#include "shill/property_accessor.h"
Paul Stewartd08f4432011-11-04 07:48:20 -070032#include "shill/store_interface.h"
mukesh agrawalb54601c2011-06-07 17:39:22 -070033#include "shill/wifi.h"
mukesh agrawal6e277772011-09-29 15:04:23 -070034#include "shill/wifi_endpoint.h"
Paul Stewart3c504012013-01-17 17:49:58 -080035#include "shill/wifi_provider.h"
mukesh agrawal6e277772011-09-29 15:04:23 -070036#include "shill/wpa_supplicant.h"
mukesh agrawalb54601c2011-06-07 17:39:22 -070037
mukesh agrawal261daca2011-12-02 18:56:56 +000038using std::set;
mukesh agrawalb54601c2011-06-07 17:39:22 -070039using std::string;
mukesh agrawal1a056262011-10-05 14:36:54 -070040using std::vector;
mukesh agrawalb54601c2011-06-07 17:39:22 -070041
42namespace shill {
mukesh agrawalb54601c2011-06-07 17:39:22 -070043
mukesh agrawalbf14e942012-03-02 14:36:34 -080044const char WiFiService::kAutoConnNoEndpoint[] = "no endpoints";
Paul Stewart3c504012013-01-17 17:49:58 -080045const char WiFiService::kAnyDeviceAddress[] = "any";
Paul Stewartbca08f82013-07-09 16:32:37 -070046const int WiFiService::kSuspectedCredentialFailureThreshold = 3;
mukesh agrawalbf14e942012-03-02 14:36:34 -080047
Paul Stewartd08f4432011-11-04 07:48:20 -070048const char WiFiService::kStorageHiddenSSID[] = "WiFi.HiddenSSID";
Paul Stewart2706aaf2011-12-14 16:44:04 -080049const char WiFiService::kStorageMode[] = "WiFi.Mode";
50const char WiFiService::kStoragePassphrase[] = "Passphrase";
51const char WiFiService::kStorageSecurity[] = "WiFi.Security";
Paul Stewart71a4d3b2013-01-18 18:12:56 -080052const char WiFiService::kStorageSecurityClass[] = "WiFi.SecurityClass";
Paul Stewart2706aaf2011-12-14 16:44:04 -080053const char WiFiService::kStorageSSID[] = "SSID";
mukesh agrawale1d90e92012-02-15 17:36:08 -080054bool WiFiService::logged_signal_warning = false;
Paul Stewartd08f4432011-11-04 07:48:20 -070055
mukesh agrawalb54601c2011-06-07 17:39:22 -070056WiFiService::WiFiService(ControlInterface *control_interface,
57 EventDispatcher *dispatcher,
Thieu Le3426c8f2012-01-11 17:35:11 -080058 Metrics *metrics,
Chris Masone6791a432011-07-12 13:23:19 -070059 Manager *manager,
Paul Stewart3c504012013-01-17 17:49:58 -080060 WiFiProvider *provider,
Paul Stewarta41e38d2011-11-11 07:47:29 -080061 const vector<uint8_t> &ssid,
62 const string &mode,
63 const string &security,
Paul Stewartced6a0b2011-11-08 15:32:04 -080064 bool hidden_ssid)
Thieu Le3426c8f2012-01-11 17:35:11 -080065 : Service(control_interface, dispatcher, metrics, manager,
66 Technology::kWifi),
Chris Masone75612302011-10-12 16:31:21 -070067 need_passphrase_(false),
mukesh agrawal6e277772011-09-29 15:04:23 -070068 security_(security),
Chris Masone092df3e2011-08-22 09:41:39 -070069 mode_(mode),
Paul Stewartced6a0b2011-11-08 15:32:04 -080070 hidden_ssid_(hidden_ssid),
Thieu Lee41a72d2012-02-06 20:46:51 +000071 frequency_(0),
mukesh agrawalf6b32092013-04-10 15:49:55 -070072 physical_mode_(Metrics::kWiFiNetworkPhyModeUndef),
Paul Stewart23b393a2012-09-25 21:21:06 -070073 raw_signal_strength_(0),
mukesh agrawal43970a22013-02-15 16:00:07 -080074 cipher_8021x_(kCryptoNone),
Paul Stewartbca08f82013-07-09 16:32:37 -070075 suspected_credential_failures_(0),
Paul Stewartecf4cd12012-04-17 11:08:39 -070076 ssid_(ssid),
Paul Stewarta5e7d5f2013-01-09 18:06:15 -080077 ieee80211w_required_(false),
Paul Stewart3c504012013-01-17 17:49:58 -080078 nss_(NSS::GetInstance()),
Paul Stewarteb713e82013-06-28 14:51:54 -070079 certificate_file_(new CertificateFile()),
Paul Stewart3c504012013-01-17 17:49:58 -080080 provider_(provider) {
mukesh agrawalde29fa82011-09-16 16:16:36 -070081 PropertyStore *store = this->mutable_store();
Paul Stewartac4ac002011-08-26 12:04:26 -070082 store->RegisterConstString(flimflam::kModeProperty, &mode_);
mukesh agrawal292dc0f2012-01-26 18:02:46 -080083 HelpRegisterWriteOnlyDerivedString(flimflam::kPassphraseProperty,
84 &WiFiService::SetPassphrase,
85 &WiFiService::ClearPassphrase,
86 NULL);
Paul Stewartac4ac002011-08-26 12:04:26 -070087 store->RegisterBool(flimflam::kPassphraseRequiredProperty, &need_passphrase_);
Paul Stewart6df20bd2013-03-13 19:31:25 -070088 HelpRegisterDerivedString(flimflam::kSecurityProperty,
89 &WiFiService::GetSecurity,
90 NULL);
Chris Masone3bd3c8c2011-06-13 08:20:26 -070091
Paul Stewartac4ac002011-08-26 12:04:26 -070092 store->RegisterConstString(flimflam::kWifiAuthMode, &auth_mode_);
Paul Stewart0cab5682012-09-13 18:50:34 -070093 store->RegisterBool(flimflam::kWifiHiddenSsid, &hidden_ssid_);
Paul Stewartac4ac002011-08-26 12:04:26 -070094 store->RegisterConstUint16(flimflam::kWifiFrequency, &frequency_);
mukesh agrawale7c7e652013-06-18 17:19:39 -070095 store->RegisterConstUint16s(kWifiFrequencyListProperty, &frequency_list_);
Paul Stewartac4ac002011-08-26 12:04:26 -070096 store->RegisterConstUint16(flimflam::kWifiPhyMode, &physical_mode_);
mukesh agrawal923f14f2012-06-04 16:46:08 -070097 store->RegisterConstString(flimflam::kWifiBSsid, &bssid_);
Paul Stewartbdbd3c32013-04-17 09:47:21 -070098 store->RegisterConstString(flimflam::kCountryProperty, &country_code_);
Paul Stewart72b2fdc2012-06-02 08:58:51 -070099 store->RegisterConstStringmap(kWifiVendorInformationProperty,
100 &vendor_information_);
Paul Stewarta5e7d5f2013-01-09 18:06:15 -0800101 store->RegisterConstBool(kWifiProtectedManagementFrameRequiredProperty,
102 &ieee80211w_required_);
mukesh agrawal32399322011-09-01 10:53:43 -0700103
mukesh agrawald835b202011-10-07 15:26:47 -0700104 hex_ssid_ = base::HexEncode(ssid_.data(), ssid_.size());
105 string ssid_string(
106 reinterpret_cast<const char *>(ssid_.data()), ssid_.size());
mukesh agrawal16bc1b82012-02-09 18:38:26 -0800107 if (WiFi::SanitizeSSID(&ssid_string)) {
mukesh agrawald835b202011-10-07 15:26:47 -0700108 // WifiHexSsid property should only be present if Name property
109 // has been munged.
110 store->RegisterConstString(flimflam::kWifiHexSsid, &hex_ssid_);
111 }
112 set_friendly_name(ssid_string);
Chris Masone9d779932011-08-25 16:33:41 -0700113
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700114 SetEapCredentials(new EapCredentials());
115
mukesh agrawal6e277772011-09-29 15:04:23 -0700116 // TODO(quiche): determine if it is okay to set EAP.KeyManagement for
117 // a service that is not 802.1x.
Gaurav Shah29d68882012-01-30 19:06:42 -0800118 if (Is8021x()) {
Gaurav Shah10109f22011-11-11 20:16:22 -0800119 // Passphrases are not mandatory for 802.1X.
120 need_passphrase_ = false;
mukesh agrawal6e277772011-09-29 15:04:23 -0700121 } else if (security_ == flimflam::kSecurityPsk) {
122 SetEAPKeyManagement("WPA-PSK");
mukesh agrawal6e277772011-09-29 15:04:23 -0700123 } else if (security_ == flimflam::kSecurityRsn) {
124 SetEAPKeyManagement("WPA-PSK");
mukesh agrawal6e277772011-09-29 15:04:23 -0700125 } else if (security_ == flimflam::kSecurityWpa) {
126 SetEAPKeyManagement("WPA-PSK");
mukesh agrawal6e277772011-09-29 15:04:23 -0700127 } else if (security_ == flimflam::kSecurityWep) {
128 SetEAPKeyManagement("NONE");
mukesh agrawal6e277772011-09-29 15:04:23 -0700129 } else if (security_ == flimflam::kSecurityNone) {
130 SetEAPKeyManagement("NONE");
mukesh agrawal6e277772011-09-29 15:04:23 -0700131 } else {
Gaurav Shah10109f22011-11-11 20:16:22 -0800132 LOG(ERROR) << "Unsupported security method " << security_;
mukesh agrawal6e277772011-09-29 15:04:23 -0700133 }
134
Paul Stewartd08f4432011-11-04 07:48:20 -0700135 // Until we know better (at Profile load time), use the generic name.
Paul Stewart71a4d3b2013-01-18 18:12:56 -0800136 storage_identifier_ = GetDefaultStorageIdentifier();
mukesh agrawal29c13a12011-11-24 00:09:19 +0000137 UpdateConnectable();
mukesh agrawal43970a22013-02-15 16:00:07 -0800138 UpdateSecurity();
Paul Stewartcb59fed2012-03-21 21:14:46 -0700139
140 IgnoreParameterForConfigure(flimflam::kModeProperty);
141 IgnoreParameterForConfigure(flimflam::kSSIDProperty);
142 IgnoreParameterForConfigure(flimflam::kSecurityProperty);
Darin Petkov457728b2013-01-09 09:49:08 +0100143
mukesh agrawaldc7b8442012-09-27 13:48:14 -0700144 // Log the |unique_name| to |friendly_name| mapping for debugging purposes.
145 // The latter will be tagged for scrubbing.
146 LOG(INFO) << "Constructed WiFi service " << unique_name()
147 << " name: " << WiFi::LogSSID(friendly_name());
mukesh agrawalb54601c2011-06-07 17:39:22 -0700148}
149
Darin Petkov9cd7ca12012-07-03 11:06:40 +0200150WiFiService::~WiFiService() {}
mukesh agrawalb54601c2011-06-07 17:39:22 -0700151
mukesh agrawalbf14e942012-03-02 14:36:34 -0800152bool WiFiService::IsAutoConnectable(const char **reason) const {
153 if (!Service::IsAutoConnectable(reason)) {
154 return false;
155 }
156
157 // Only auto-connect to Services which have visible Endpoints.
158 // (Needed because hidden Services may remain registered with
159 // Manager even without visible Endpoints.)
160 if (!HasEndpoints()) {
161 *reason = kAutoConnNoEndpoint;
162 return false;
163 }
164
Paul Stewart3c504012013-01-17 17:49:58 -0800165 CHECK(wifi_) << "We have endpoints but no WiFi device is selected?";
166
mukesh agrawalbf14e942012-03-02 14:36:34 -0800167 // Do not preempt an existing connection (whether pending, or
168 // connected, and whether to this service, or another).
169 if (!wifi_->IsIdle()) {
170 *reason = kAutoConnBusy;
171 return false;
172 }
173
174 return true;
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000175}
176
mukesh agrawal43970a22013-02-15 16:00:07 -0800177void WiFiService::SetEAPKeyManagement(const string &key_management) {
178 Service::SetEAPKeyManagement(key_management);
179 UpdateSecurity();
180}
181
Darin Petkov4a66cc52012-06-15 10:08:29 +0200182void WiFiService::AddEndpoint(const WiFiEndpointConstRefPtr &endpoint) {
mukesh agrawal261daca2011-12-02 18:56:56 +0000183 DCHECK(endpoint->ssid() == ssid());
184 endpoints_.insert(endpoint);
mukesh agrawale1d90e92012-02-15 17:36:08 -0800185 UpdateFromEndpoints();
mukesh agrawal261daca2011-12-02 18:56:56 +0000186}
187
Darin Petkov4a66cc52012-06-15 10:08:29 +0200188void WiFiService::RemoveEndpoint(const WiFiEndpointConstRefPtr &endpoint) {
mukesh agrawal261daca2011-12-02 18:56:56 +0000189 set<WiFiEndpointConstRefPtr>::iterator i = endpoints_.find(endpoint);
190 DCHECK(i != endpoints_.end());
191 if (i == endpoints_.end()) {
192 LOG(WARNING) << "In " << __func__ << "(): "
Darin Petkov457728b2013-01-09 09:49:08 +0100193 << "ignoring non-existent endpoint "
mukesh agrawal261daca2011-12-02 18:56:56 +0000194 << endpoint->bssid_string();
195 return;
196 }
197 endpoints_.erase(i);
mukesh agrawale1d90e92012-02-15 17:36:08 -0800198 if (current_endpoint_ == endpoint) {
199 current_endpoint_ = NULL;
200 }
201 UpdateFromEndpoints();
mukesh agrawal261daca2011-12-02 18:56:56 +0000202}
203
Paul Stewart3c504012013-01-17 17:49:58 -0800204void WiFiService::NotifyCurrentEndpoint(
205 const WiFiEndpointConstRefPtr &endpoint) {
mukesh agrawale1d90e92012-02-15 17:36:08 -0800206 DCHECK(!endpoint || (endpoints_.find(endpoint) != endpoints_.end()));
207 current_endpoint_ = endpoint;
208 UpdateFromEndpoints();
Thieu Lee41a72d2012-02-06 20:46:51 +0000209}
210
Paul Stewart3c504012013-01-17 17:49:58 -0800211void WiFiService::NotifyEndpointUpdated(
212 const WiFiEndpointConstRefPtr &endpoint) {
213 DCHECK(endpoints_.find(endpoint) != endpoints_.end());
mukesh agrawale1d90e92012-02-15 17:36:08 -0800214 UpdateFromEndpoints();
mukesh agrawalb20776f2012-02-10 16:00:36 -0800215}
216
Chris Masone6515aab2011-10-12 16:19:09 -0700217string WiFiService::GetStorageIdentifier() const {
Paul Stewartd08f4432011-11-04 07:48:20 -0700218 return storage_identifier_;
Chris Masone34af2182011-08-22 11:59:36 -0700219}
mukesh agrawal445e72c2011-06-22 11:13:50 -0700220
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700221bool WiFiService::SetPassphrase(const string &passphrase, Error *error) {
mukesh agrawal1a056262011-10-05 14:36:54 -0700222 if (security_ == flimflam::kSecurityWep) {
Thieu Lef4cbda92011-11-10 23:41:24 +0000223 ValidateWEPPassphrase(passphrase, error);
mukesh agrawal1a056262011-10-05 14:36:54 -0700224 } else if (security_ == flimflam::kSecurityPsk ||
225 security_ == flimflam::kSecurityWpa ||
226 security_ == flimflam::kSecurityRsn) {
Thieu Lef4cbda92011-11-10 23:41:24 +0000227 ValidateWPAPassphrase(passphrase, error);
228 } else {
229 error->Populate(Error::kNotSupported);
mukesh agrawal1a056262011-10-05 14:36:54 -0700230 }
Thieu Lef4cbda92011-11-10 23:41:24 +0000231
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700232 if (!error->IsSuccess()) {
233 return false;
234 }
235 if (passphrase_ == passphrase) {
236 // After a user logs in, Chrome may reconfigure a Service with the
237 // same credentials as before login. When that occurs, we don't
238 // want to bump the user off the network. Hence, we MUST return
239 // early. (See crbug.com/231456#c17)
240 return false;
Paul Stewart2706aaf2011-12-14 16:44:04 -0800241 }
mukesh agrawal29c13a12011-11-24 00:09:19 +0000242
Paul Stewart835934a2012-12-06 19:27:09 -0800243 passphrase_ = passphrase;
244 ClearCachedCredentials();
mukesh agrawal29c13a12011-11-24 00:09:19 +0000245 UpdateConnectable();
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700246 return true;
mukesh agrawal1a056262011-10-05 14:36:54 -0700247}
248
mukesh agrawal292dc0f2012-01-26 18:02:46 -0800249// ClearPassphrase is separate from SetPassphrase, because the default
250// value for |passphrase_| would not pass validation.
251void WiFiService::ClearPassphrase(Error */*error*/) {
252 passphrase_.clear();
Paul Stewart835934a2012-12-06 19:27:09 -0800253 ClearCachedCredentials();
mukesh agrawal292dc0f2012-01-26 18:02:46 -0800254 UpdateConnectable();
255}
256
Paul Stewarte7de2942013-04-25 17:07:31 -0700257string WiFiService::GetLoadableStorageIdentifier(
258 const StoreInterface &storage) const {
259 set<string> groups = storage.GetGroupsWithProperties(GetStorageProperties());
260 if (groups.empty()) {
261 LOG(WARNING) << "Configuration for service "
262 << unique_name()
263 << " is not available in the persistent store";
264 return "";
265 }
266 if (groups.size() > 1) {
267 LOG(WARNING) << "More than one configuration for service "
268 << unique_name()
269 << " is available; choosing the first.";
270 }
271 return *groups.begin();
272}
273
274bool WiFiService::IsLoadableFrom(const StoreInterface &storage) const {
275 return !storage.GetGroupsWithProperties(GetStorageProperties()).empty();
Paul Stewartd08f4432011-11-04 07:48:20 -0700276}
277
Paul Stewarta41e38d2011-11-11 07:47:29 -0800278bool WiFiService::IsVisible() const {
Paul Stewarta41e38d2011-11-11 07:47:29 -0800279 // WiFi Services should be displayed only if they are in range (have
280 // endpoints that have shown up in a scan) or if the service is actively
281 // being connected.
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000282 return HasEndpoints() || IsConnected() || IsConnecting();
Paul Stewarta41e38d2011-11-11 07:47:29 -0800283}
284
Paul Stewartd08f4432011-11-04 07:48:20 -0700285bool WiFiService::Load(StoreInterface *storage) {
Paul Stewarte7de2942013-04-25 17:07:31 -0700286 string id = GetLoadableStorageIdentifier(*storage);
287 if (id.empty()) {
Paul Stewart71a4d3b2013-01-18 18:12:56 -0800288 return false;
Paul Stewartd08f4432011-11-04 07:48:20 -0700289 }
290
291 // Set our storage identifier to match the storage name in the Profile.
292 storage_identifier_ = id;
293
294 // Load properties common to all Services.
295 if (!Service::Load(storage)) {
296 return false;
297 }
298
299 // Load properties specific to WiFi services.
300 storage->GetBool(id, kStorageHiddenSSID, &hidden_ssid_);
mukesh agrawal29c13a12011-11-24 00:09:19 +0000301
Paul Stewart2706aaf2011-12-14 16:44:04 -0800302 // NB: mode, security and ssid parameters are never read in from
303 // Load() as they are provided from the scan.
304
305 string passphrase;
306 if (storage->GetCryptedString(id, kStoragePassphrase, &passphrase)) {
307 Error error;
308 SetPassphrase(passphrase, &error);
Paul Stewartfa013ab2013-04-11 07:12:03 -0700309 if (!error.IsSuccess() &&
310 !(passphrase.empty() && error.type() == Error::kNotSupported)) {
Paul Stewart2706aaf2011-12-14 16:44:04 -0800311 LOG(ERROR) << "Passphrase could not be set: "
312 << Error::GetName(error.type());
313 }
314 }
315
Paul Stewartd08f4432011-11-04 07:48:20 -0700316 return true;
317}
318
319bool WiFiService::Save(StoreInterface *storage) {
320 // Save properties common to all Services.
321 if (!Service::Save(storage)) {
322 return false;
323 }
324
325 // Save properties specific to WiFi services.
326 const string id = GetStorageIdentifier();
Paul Stewart2706aaf2011-12-14 16:44:04 -0800327 storage->SetBool(id, kStorageHiddenSSID, hidden_ssid_);
328 storage->SetString(id, kStorageMode, mode_);
329 storage->SetCryptedString(id, kStoragePassphrase, passphrase_);
330 storage->SetString(id, kStorageSecurity, security_);
Paul Stewart71a4d3b2013-01-18 18:12:56 -0800331 storage->SetString(id, kStorageSecurityClass, GetSecurityClass(security_));
Paul Stewart2706aaf2011-12-14 16:44:04 -0800332 storage->SetString(id, kStorageSSID, hex_ssid_);
mukesh agrawal29c13a12011-11-24 00:09:19 +0000333
Paul Stewartd08f4432011-11-04 07:48:20 -0700334 return true;
335}
336
Paul Stewart65512e12012-03-26 18:01:08 -0700337bool WiFiService::Unload() {
Paul Stewartd8ad3c42012-01-09 12:39:38 -0800338 Service::Unload();
Albert Chaulk0e1cdea2013-02-27 15:32:55 -0800339 if (wifi_) {
340 wifi_->DestroyServiceLease(*this);
341 }
Paul Stewartd8ad3c42012-01-09 12:39:38 -0800342 hidden_ssid_ = false;
Paul Stewartbca08f82013-07-09 16:32:37 -0700343 ResetSuspectedCredentialFailures();
Wade Guthrie005bd342012-05-02 09:37:07 -0700344 Error unused_error;
345 ClearPassphrase(&unused_error);
Paul Stewart3c504012013-01-17 17:49:58 -0800346 return provider_->OnServiceUnloaded(this);
Paul Stewartd8ad3c42012-01-09 12:39:38 -0800347}
348
Paul Stewart6ab23a92011-11-09 17:17:47 -0800349bool WiFiService::IsSecurityMatch(const string &security) const {
350 return GetSecurityClass(security) == GetSecurityClass(security_);
351}
352
Paul Stewartbca08f82013-07-09 16:32:37 -0700353bool WiFiService::AddSuspectedCredentialFailure() {
354 if (!has_ever_connected()) {
355 return true;
356 }
357 ++suspected_credential_failures_;
358 return suspected_credential_failures_ >= kSuspectedCredentialFailureThreshold;
359}
360
361void WiFiService::ResetSuspectedCredentialFailures() {
362 suspected_credential_failures_ = 0;
363}
364
Thieu Le48e6d6d2011-12-06 00:40:27 +0000365void WiFiService::InitializeCustomMetrics() const {
366 string histogram = metrics()->GetFullMetricName(
367 Metrics::kMetricTimeToJoinMilliseconds,
368 technology());
369 metrics()->AddServiceStateTransitionTimer(this,
370 histogram,
371 Service::kStateAssociating,
372 Service::kStateConfiguring);
373}
374
Thieu Leb84ba342012-03-02 15:15:19 -0800375void WiFiService::SendPostReadyStateMetrics(
376 int64 time_resume_to_ready_milliseconds) const {
Thieu Le48e6d6d2011-12-06 00:40:27 +0000377 metrics()->SendEnumToUMA(
378 metrics()->GetFullMetricName(Metrics::kMetricNetworkChannel,
379 technology()),
380 Metrics::WiFiFrequencyToChannel(frequency_),
381 Metrics::kMetricNetworkChannelMax);
Thieu Lead1ec2c2012-01-05 23:39:48 +0000382
383 DCHECK(physical_mode_ < Metrics::kWiFiNetworkPhyModeMax);
384 metrics()->SendEnumToUMA(
385 metrics()->GetFullMetricName(Metrics::kMetricNetworkPhyMode,
386 technology()),
387 static_cast<Metrics::WiFiNetworkPhyMode>(physical_mode_),
388 Metrics::kWiFiNetworkPhyModeMax);
389
Paul Stewart4108db92013-03-11 12:13:24 -0700390 string security_mode = security_;
391 if (current_endpoint_) {
392 security_mode = current_endpoint_->security_mode();
393 }
Thieu Lead1ec2c2012-01-05 23:39:48 +0000394 Metrics::WiFiSecurity security_uma =
Paul Stewart4108db92013-03-11 12:13:24 -0700395 Metrics::WiFiSecurityStringToEnum(security_mode);
Thieu Lead1ec2c2012-01-05 23:39:48 +0000396 DCHECK(security_uma != Metrics::kWiFiSecurityUnknown);
397 metrics()->SendEnumToUMA(
398 metrics()->GetFullMetricName(Metrics::kMetricNetworkSecurity,
399 technology()),
400 security_uma,
401 Metrics::kMetricNetworkSecurityMax);
Thieu Leb84ba342012-03-02 15:15:19 -0800402
Paul Stewart21f40962013-03-01 14:27:28 -0800403 if (Is8021x()) {
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700404 eap()->OutputConnectionMetrics(metrics(), technology());
Paul Stewart21f40962013-03-01 14:27:28 -0800405 }
406
Paul Stewart23b393a2012-09-25 21:21:06 -0700407 // We invert the sign of the signal strength value, since UMA histograms
408 // cannot represent negative numbers (it stores them but cannot display
409 // them), and dBm values of interest start at 0 and go negative from there.
410 metrics()->SendToUMA(
411 metrics()->GetFullMetricName(Metrics::kMetricNetworkSignalStrength,
412 technology()),
413 -raw_signal_strength_,
414 Metrics::kMetricNetworkSignalStrengthMin,
415 Metrics::kMetricNetworkSignalStrengthMax,
416 Metrics::kMetricNetworkSignalStrengthNumBuckets);
417
Thieu Leb84ba342012-03-02 15:15:19 -0800418 if (time_resume_to_ready_milliseconds > 0) {
419 metrics()->SendToUMA(
420 metrics()->GetFullMetricName(
421 Metrics::kMetricTimeResumeToReadyMilliseconds, technology()),
422 time_resume_to_ready_milliseconds,
423 Metrics::kTimerHistogramMillisecondsMin,
424 Metrics::kTimerHistogramMillisecondsMax,
425 Metrics::kTimerHistogramNumBuckets);
426 }
Paul Stewarte4cedde2013-07-17 08:56:44 -0700427
428 Metrics::WiFiApMode ap_mode_uma = Metrics::WiFiApModeStringToEnum(mode_);
429 metrics()->SendEnumToUMA(
430 metrics()->GetFullMetricName(Metrics::kMetricNetworkApMode, technology()),
431 ap_mode_uma,
432 Metrics::kWiFiApModeMax);
Thieu Le48e6d6d2011-12-06 00:40:27 +0000433}
434
mukesh agrawal32399322011-09-01 10:53:43 -0700435// private methods
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700436void WiFiService::HelpRegisterConstDerivedString(
437 const string &name,
438 string(WiFiService::*get)(Error *)) {
439 mutable_store()->RegisterDerivedString(
440 name,
441 StringAccessor(
442 new CustomAccessor<WiFiService, string>(this, get, NULL)));
443}
444
Paul Stewart6df20bd2013-03-13 19:31:25 -0700445void WiFiService::HelpRegisterDerivedString(
446 const string &name,
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700447 string(WiFiService::*get)(Error *error),
448 bool(WiFiService::*set)(const string &, Error *)) {
Paul Stewart6df20bd2013-03-13 19:31:25 -0700449 mutable_store()->RegisterDerivedString(
450 name,
451 StringAccessor(new CustomAccessor<WiFiService, string>(this, get, set)));
452}
453
mukesh agrawal292dc0f2012-01-26 18:02:46 -0800454void WiFiService::HelpRegisterWriteOnlyDerivedString(
455 const string &name,
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700456 bool(WiFiService::*set)(const string &, Error *),
mukesh agrawal292dc0f2012-01-26 18:02:46 -0800457 void(WiFiService::*clear)(Error *),
458 const string *default_value) {
459 mutable_store()->RegisterDerivedString(
Thieu Lef7709452011-11-15 01:13:19 +0000460 name,
mukesh agrawal292dc0f2012-01-26 18:02:46 -0800461 StringAccessor(
462 new CustomWriteOnlyAccessor<WiFiService, string>(
463 this, set, clear, default_value)));
Thieu Lef7709452011-11-15 01:13:19 +0000464}
465
mukesh agrawaldc7b8442012-09-27 13:48:14 -0700466void WiFiService::Connect(Error *error, const char *reason) {
mukesh agrawal6e277772011-09-29 15:04:23 -0700467 std::map<string, DBus::Variant> params;
468 DBus::MessageIter writer;
469
Wade Guthrie005bd342012-05-02 09:37:07 -0700470 if (!connectable()) {
Darin Petkov457728b2013-01-09 09:49:08 +0100471 LOG(ERROR) << "Can't connect. Service " << unique_name()
472 << " is not connectable.";
Christopher Wiley1ce658d2012-10-10 10:02:03 -0700473 Error::PopulateAndLog(error,
474 Error::kOperationFailed,
475 Error::GetDefaultMessage(Error::kOperationFailed));
476 return;
477 }
478 if (IsConnecting() || IsConnected()) {
Darin Petkov457728b2013-01-09 09:49:08 +0100479 LOG(WARNING) << "Can't connect. Service " << unique_name()
Christopher Wiley1ce658d2012-10-10 10:02:03 -0700480 << " is already connecting or connected.";
481 Error::PopulateAndLog(error,
482 Error::kAlreadyConnected,
483 Error::GetDefaultMessage(Error::kAlreadyConnected));
Wade Guthrie005bd342012-05-02 09:37:07 -0700484 return;
485 }
Paul Stewart3c504012013-01-17 17:49:58 -0800486
487 WiFiRefPtr wifi = wifi_;
488 if (!wifi) {
489 // If this is a hidden service before it has been found in a scan, we
490 // may need to late-bind to any available WiFi Device. We don't actually
491 // set |wifi_| in this case snce we do not yet see any endpoints. This
492 // will mean this service is not disconnectable until an endpoint is
493 // found.
494 wifi = ChooseDevice();
495 if (!wifi) {
496 LOG(ERROR) << "Can't connect. Service " << unique_name()
497 << " cannot find a WiFi device.";
498 Error::PopulateAndLog(error,
499 Error::kOperationFailed,
500 Error::GetDefaultMessage(Error::kOperationFailed));
501 return;
502 }
503 }
504
505 if (wifi->IsCurrentService(this)) {
Darin Petkov457728b2013-01-09 09:49:08 +0100506 LOG(WARNING) << "Can't connect. Service " << unique_name()
Wade Guthrie8bc50882012-10-31 16:23:20 -0700507 << " is the current service (but, in " << GetStateString()
Paul Stewart3c504012013-01-17 17:49:58 -0800508 << " state, not connected).";
Wade Guthrie8bc50882012-10-31 16:23:20 -0700509 Error::PopulateAndLog(error,
510 Error::kInProgress,
511 Error::GetDefaultMessage(Error::kInProgress));
512 return;
513 }
Wade Guthrie005bd342012-05-02 09:37:07 -0700514
Paul Stewart0654ece2013-03-26 15:21:26 -0700515 params[WPASupplicant::kNetworkPropertyMode].writer().
mukesh agrawal6e277772011-09-29 15:04:23 -0700516 append_uint32(WiFiEndpoint::ModeStringToUint(mode_));
517
Paul Stewarte2d7c502012-07-16 16:35:10 -0700518 if (mode_ == flimflam::kModeAdhoc && frequency_ != 0) {
519 // Frequency is required in order to successfully conntect to an IBSS
520 // with wpa_supplicant. If we have one from our endpoint, insert it
521 // here.
Paul Stewart0654ece2013-03-26 15:21:26 -0700522 params[WPASupplicant::kNetworkPropertyFrequency].writer().
Paul Stewarte2d7c502012-07-16 16:35:10 -0700523 append_int32(frequency_);
524 }
525
Gaurav Shah29d68882012-01-30 19:06:42 -0800526 if (Is8021x()) {
527 // Is EAP key management is not set, set to a default.
Gaurav Shah10109f22011-11-11 20:16:22 -0800528 if (GetEAPKeyManagement().empty())
529 SetEAPKeyManagement("WPA-EAP");
Paul Stewart0654ece2013-03-26 15:21:26 -0700530 vector<char> nss_identifier(ssid_.begin(), ssid_.end());
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700531 eap()->PopulateSupplicantProperties(
532 certificate_file_.get(), nss_, nss_identifier, &params);
Paul Stewartbc6e7392012-05-24 07:07:48 -0700533 ClearEAPCertification();
Paul Stewart4108db92013-03-11 12:13:24 -0700534 } else if (security_ == flimflam::kSecurityPsk ||
535 security_ == flimflam::kSecurityRsn ||
536 security_ == flimflam::kSecurityWpa) {
Gaurav Shahf8721ee2011-11-07 09:12:46 -0800537 const string psk_proto = StringPrintf("%s %s",
Paul Stewart0654ece2013-03-26 15:21:26 -0700538 WPASupplicant::kSecurityModeWPA,
539 WPASupplicant::kSecurityModeRSN);
540 params[WPASupplicant::kPropertySecurityProtocol].writer().
Gaurav Shahf8721ee2011-11-07 09:12:46 -0800541 append_string(psk_proto.c_str());
Paul Stewart0654ece2013-03-26 15:21:26 -0700542 params[WPASupplicant::kPropertyPreSharedKey].writer().
Gaurav Shahf8721ee2011-11-07 09:12:46 -0800543 append_string(passphrase_.c_str());
mukesh agrawal6e277772011-09-29 15:04:23 -0700544 } else if (security_ == flimflam::kSecurityWep) {
Paul Stewart0654ece2013-03-26 15:21:26 -0700545 params[WPASupplicant::kPropertyAuthAlg].writer().
546 append_string(WPASupplicant::kSecurityAuthAlg);
Thieu Lef4cbda92011-11-10 23:41:24 +0000547 Error error;
548 int key_index;
549 std::vector<uint8> password_bytes;
550 ParseWEPPassphrase(passphrase_, &key_index, &password_bytes, &error);
Paul Stewart0654ece2013-03-26 15:21:26 -0700551 writer = params[WPASupplicant::kPropertyWEPKey +
Thieu Lef4cbda92011-11-10 23:41:24 +0000552 base::IntToString(key_index)].writer();
553 writer << password_bytes;
Paul Stewart0654ece2013-03-26 15:21:26 -0700554 params[WPASupplicant::kPropertyWEPTxKeyIndex].writer().
Thieu Lef4cbda92011-11-10 23:41:24 +0000555 append_uint32(key_index);
mukesh agrawal6e277772011-09-29 15:04:23 -0700556 } else if (security_ == flimflam::kSecurityNone) {
Gaurav Shahf8721ee2011-11-07 09:12:46 -0800557 // Nothing special to do here.
mukesh agrawal6e277772011-09-29 15:04:23 -0700558 } else {
Gaurav Shahf8721ee2011-11-07 09:12:46 -0800559 LOG(ERROR) << "Can't connect. Unsupported security method " << security_;
mukesh agrawaldc7b8442012-09-27 13:48:14 -0700560 Error::PopulateAndLog(error,
561 Error::kInvalidArguments,
562 Error::GetDefaultMessage(Error::kInvalidArguments));
563 return;
mukesh agrawal6e277772011-09-29 15:04:23 -0700564 }
565
Paul Stewart0654ece2013-03-26 15:21:26 -0700566 params[WPASupplicant::kNetworkPropertyEapKeyManagement].writer().
mukesh agrawal6e277772011-09-29 15:04:23 -0700567 append_string(key_management().c_str());
Gaurav Shah7ad8e532011-11-11 17:14:49 -0800568
Paul Stewarta5e7d5f2013-01-09 18:06:15 -0800569 if (ieee80211w_required_) {
570 // TODO(pstew): We should also enable IEEE 802.11w if the user
571 // explicitly enables support for this through a service / device
Paul Stewartee6b3d72013-07-12 16:07:51 -0700572 // property. crbug.com/219950
Paul Stewart0654ece2013-03-26 15:21:26 -0700573 params[WPASupplicant::kNetworkPropertyIeee80211w].writer().
574 append_uint32(WPASupplicant::kNetworkIeee80211wEnabled);
Paul Stewarta5e7d5f2013-01-09 18:06:15 -0800575 }
576
Gaurav Shah7ad8e532011-11-11 17:14:49 -0800577 // See note in dbus_adaptor.cc on why we need to use a local.
Paul Stewart0654ece2013-03-26 15:21:26 -0700578 writer = params[WPASupplicant::kNetworkPropertySSID].writer();
mukesh agrawal6e277772011-09-29 15:04:23 -0700579 writer << ssid_;
580
mukesh agrawaldc7b8442012-09-27 13:48:14 -0700581 Service::Connect(error, reason);
Paul Stewart3c504012013-01-17 17:49:58 -0800582 wifi->ConnectTo(this, params);
mukesh agrawalb54601c2011-06-07 17:39:22 -0700583}
584
Eric Shienbrood9a245532012-03-07 14:20:39 -0500585void WiFiService::Disconnect(Error *error) {
Eric Shienbrood9a245532012-03-07 14:20:39 -0500586 Service::Disconnect(error);
Paul Stewart3c504012013-01-17 17:49:58 -0800587 if (!wifi_) {
588 // If we are connecting to a hidden service, but have not yet found
589 // any endpoints, we could end up with a disconnect request without
590 // a wifi_ reference. This is not a fatal error.
591 LOG_IF(ERROR, IsConnecting())
592 << "WiFi endpoints do not (yet) exist. Cannot disconnect service "
593 << unique_name();
594 LOG_IF(FATAL, IsConnected())
595 << "WiFi device does not exist. Cannot disconnect service "
596 << unique_name();
597 error->Populate(Error::kOperationFailed);
598 return;
599 }
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000600 wifi_->DisconnectFrom(this);
601}
602
Paul Stewart3c504012013-01-17 17:49:58 -0800603string WiFiService::GetDeviceRpcId(Error *error) {
604 if (!wifi_) {
605 error->Populate(Error::kNotFound, "Not associated with a device");
mukesh agrawalcbfb34e2013-04-17 19:33:25 -0700606 return DBusAdaptor::kNullPath;
Paul Stewart3c504012013-01-17 17:49:58 -0800607 }
Chris Masone95207da2011-06-29 16:50:49 -0700608 return wifi_->GetRpcIdentifier();
609}
610
mukesh agrawal29c13a12011-11-24 00:09:19 +0000611void WiFiService::UpdateConnectable() {
Gaurav Shah10109f22011-11-11 20:16:22 -0800612 bool is_connectable = false;
mukesh agrawal29c13a12011-11-24 00:09:19 +0000613 if (security_ == flimflam::kSecurityNone) {
614 DCHECK(passphrase_.empty());
Paul Stewartd8ad3c42012-01-09 12:39:38 -0800615 need_passphrase_ = false;
Gaurav Shah10109f22011-11-11 20:16:22 -0800616 is_connectable = true;
Gaurav Shah29d68882012-01-30 19:06:42 -0800617 } else if (Is8021x()) {
618 is_connectable = Is8021xConnectable();
mukesh agrawal29c13a12011-11-24 00:09:19 +0000619 } else if (security_ == flimflam::kSecurityWep ||
620 security_ == flimflam::kSecurityWpa ||
621 security_ == flimflam::kSecurityPsk ||
622 security_ == flimflam::kSecurityRsn) {
Paul Stewartd8ad3c42012-01-09 12:39:38 -0800623 need_passphrase_ = passphrase_.empty();
Gaurav Shah10109f22011-11-11 20:16:22 -0800624 is_connectable = !need_passphrase_;
mukesh agrawal29c13a12011-11-24 00:09:19 +0000625 }
mukesh agrawalcbfb34e2013-04-17 19:33:25 -0700626 SetConnectable(is_connectable);
mukesh agrawal29c13a12011-11-24 00:09:19 +0000627}
628
mukesh agrawale1d90e92012-02-15 17:36:08 -0800629void WiFiService::UpdateFromEndpoints() {
630 const WiFiEndpoint *representative_endpoint = NULL;
631
632 if (current_endpoint_) {
mukesh agrawale1d90e92012-02-15 17:36:08 -0800633 representative_endpoint = current_endpoint_;
634 } else {
635 int16 best_signal = std::numeric_limits<int16>::min();
636 for (set<WiFiEndpointConstRefPtr>::iterator i = endpoints_.begin();
637 i != endpoints_.end(); ++i) {
638 if ((*i)->signal_strength() >= best_signal) {
639 best_signal = (*i)->signal_strength();
640 representative_endpoint = *i;
641 }
642 }
643 }
644
Paul Stewart3c504012013-01-17 17:49:58 -0800645 WiFiRefPtr wifi;
646 if (representative_endpoint) {
647 wifi = representative_endpoint->device();
Paul Stewart8653f462013-02-06 12:21:05 -0800648 } else if (IsConnected() || IsConnecting()) {
649 LOG(WARNING) << "Service " << unique_name()
650 << " will disconnect due to no remaining endpoints.";
Paul Stewart3c504012013-01-17 17:49:58 -0800651 }
652
653 SetWiFi(wifi);
654
Paul Stewarta5e7d5f2013-01-09 18:06:15 -0800655 for (set<WiFiEndpointConstRefPtr>::iterator i = endpoints_.begin();
656 i != endpoints_.end(); ++i) {
657 if ((*i)->ieee80211w_required()) {
658 // Never reset ieee80211w_required_ to false, so we track whether we have
659 // ever seen an AP that requires 802.11w.
660 ieee80211w_required_ = true;
661 }
662 }
663
mukesh agrawale7c7e652013-06-18 17:19:39 -0700664 set<uint16> frequency_set;
665 for (const auto &endpoint : endpoints_) {
666 frequency_set.insert(endpoint->frequency());
667 }
668 frequency_list_.assign(frequency_set.begin(), frequency_set.end());
669
mukesh agrawal43970a22013-02-15 16:00:07 -0800670 if (Is8021x())
671 cipher_8021x_ = ComputeCipher8021x(endpoints_);
672
Paul Stewart72b2fdc2012-06-02 08:58:51 -0700673 uint16 frequency = 0;
674 int16 signal = std::numeric_limits<int16>::min();
mukesh agrawal923f14f2012-06-04 16:46:08 -0700675 string bssid;
Paul Stewartbdbd3c32013-04-17 09:47:21 -0700676 string country_code;
Paul Stewart72b2fdc2012-06-02 08:58:51 -0700677 Stringmap vendor_information;
mukesh agrawalf6b32092013-04-10 15:49:55 -0700678 uint16 physical_mode = Metrics::kWiFiNetworkPhyModeUndef;
Paul Stewart23b393a2012-09-25 21:21:06 -0700679 // Represent "unknown raw signal strength" as 0.
680 raw_signal_strength_ = 0;
Paul Stewart72b2fdc2012-06-02 08:58:51 -0700681 if (representative_endpoint) {
mukesh agrawale1d90e92012-02-15 17:36:08 -0800682 frequency = representative_endpoint->frequency();
683 signal = representative_endpoint->signal_strength();
Paul Stewart23b393a2012-09-25 21:21:06 -0700684 raw_signal_strength_ = signal;
mukesh agrawal923f14f2012-06-04 16:46:08 -0700685 bssid = representative_endpoint->bssid_string();
Paul Stewartbdbd3c32013-04-17 09:47:21 -0700686 country_code = representative_endpoint->country_code();
Paul Stewart72b2fdc2012-06-02 08:58:51 -0700687 vendor_information = representative_endpoint->GetVendorInformation();
mukesh agrawalf6b32092013-04-10 15:49:55 -0700688 physical_mode = representative_endpoint->physical_mode();
mukesh agrawale1d90e92012-02-15 17:36:08 -0800689 }
690
691 if (frequency_ != frequency) {
692 frequency_ = frequency;
693 adaptor()->EmitUint16Changed(flimflam::kWifiFrequency, frequency_);
694 }
mukesh agrawal923f14f2012-06-04 16:46:08 -0700695 if (bssid_ != bssid) {
696 bssid_ = bssid;
697 adaptor()->EmitStringChanged(flimflam::kWifiBSsid, bssid_);
698 }
Paul Stewartbdbd3c32013-04-17 09:47:21 -0700699 if (country_code_ != country_code) {
700 country_code_ = country_code;
701 adaptor()->EmitStringChanged(flimflam::kCountryProperty, country_code_);
702 }
Paul Stewart72b2fdc2012-06-02 08:58:51 -0700703 if (vendor_information_ != vendor_information) {
704 vendor_information_ = vendor_information;
705 adaptor()->EmitStringmapChanged(kWifiVendorInformationProperty,
706 vendor_information_);
707 }
mukesh agrawalf6b32092013-04-10 15:49:55 -0700708 if (physical_mode_ != physical_mode) {
709 physical_mode_ = physical_mode;
710 adaptor()->EmitUint16Changed(flimflam::kWifiPhyMode, physical_mode_);
711 }
mukesh agrawale7c7e652013-06-18 17:19:39 -0700712 adaptor()->EmitUint16sChanged(kWifiFrequencyListProperty, frequency_list_);
mukesh agrawale1d90e92012-02-15 17:36:08 -0800713 SetStrength(SignalToStrength(signal));
mukesh agrawal43970a22013-02-15 16:00:07 -0800714 UpdateSecurity();
715}
716
717void WiFiService::UpdateSecurity() {
718 CryptoAlgorithm algorithm = kCryptoNone;
719 bool key_rotation = false;
720 bool endpoint_auth = false;
721
722 if (security_ == flimflam::kSecurityNone) {
723 // initial values apply
724 } else if (security_ == flimflam::kSecurityWep) {
725 algorithm = kCryptoRc4;
726 key_rotation = Is8021x();
727 endpoint_auth = Is8021x();
728 } else if (security_ == flimflam::kSecurityPsk ||
729 security_ == flimflam::kSecurityWpa) {
730 algorithm = kCryptoRc4;
731 key_rotation = true;
732 endpoint_auth = false;
733 } else if (security_ == flimflam::kSecurityRsn) {
734 algorithm = kCryptoAes;
735 key_rotation = true;
736 endpoint_auth = false;
737 } else if (security_ == flimflam::kSecurity8021x) {
738 algorithm = cipher_8021x_;
739 key_rotation = true;
740 endpoint_auth = true;
741 }
742 SetSecurity(algorithm, key_rotation, endpoint_auth);
743}
744
745// static
746Service::CryptoAlgorithm WiFiService::ComputeCipher8021x(
747 const set<WiFiEndpointConstRefPtr> &endpoints) {
748
749 if (endpoints.empty())
750 return kCryptoNone; // Will update after scan results.
751
752 // Find weakest cipher (across endpoints) of the strongest ciphers
753 // (per endpoint).
754 Service::CryptoAlgorithm cipher = Service::kCryptoAes;
755 for (set<WiFiEndpointConstRefPtr>::iterator i = endpoints.begin();
756 i != endpoints.end(); ++i) {
757 Service::CryptoAlgorithm endpoint_cipher;
758 if ((*i)->has_rsn_property()) {
759 endpoint_cipher = Service::kCryptoAes;
760 } else if ((*i)->has_wpa_property()) {
761 endpoint_cipher = Service::kCryptoRc4;
762 } else {
763 // We could be in the Dynamic WEP case here. But that's okay,
764 // because |cipher_8021x_| is not defined in that case.
765 endpoint_cipher = Service::kCryptoNone;
766 }
767 cipher = std::min(cipher, endpoint_cipher);
768 }
769 return cipher;
mukesh agrawale1d90e92012-02-15 17:36:08 -0800770}
771
mukesh agrawal1a056262011-10-05 14:36:54 -0700772// static
Thieu Lef4cbda92011-11-10 23:41:24 +0000773void WiFiService::ValidateWEPPassphrase(const std::string &passphrase,
774 Error *error) {
775 ParseWEPPassphrase(passphrase, NULL, NULL, error);
mukesh agrawal1a056262011-10-05 14:36:54 -0700776}
777
778// static
Thieu Lef4cbda92011-11-10 23:41:24 +0000779void WiFiService::ValidateWPAPassphrase(const std::string &passphrase,
780 Error *error) {
mukesh agrawal1a056262011-10-05 14:36:54 -0700781 unsigned int length = passphrase.length();
782 vector<uint8> passphrase_bytes;
783
784 if (base::HexStringToBytes(passphrase, &passphrase_bytes)) {
785 if (length != IEEE_80211::kWPAHexLen &&
786 (length < IEEE_80211::kWPAAsciiMinLen ||
787 length > IEEE_80211::kWPAAsciiMaxLen)) {
788 error->Populate(Error::kInvalidPassphrase);
789 }
790 } else {
791 if (length < IEEE_80211::kWPAAsciiMinLen ||
792 length > IEEE_80211::kWPAAsciiMaxLen) {
793 error->Populate(Error::kInvalidPassphrase);
794 }
795 }
Thieu Lef4cbda92011-11-10 23:41:24 +0000796}
mukesh agrawal1a056262011-10-05 14:36:54 -0700797
Thieu Lef4cbda92011-11-10 23:41:24 +0000798// static
799void WiFiService::ParseWEPPassphrase(const string &passphrase,
800 int *key_index,
801 std::vector<uint8> *password_bytes,
802 Error *error) {
803 unsigned int length = passphrase.length();
804 int key_index_local;
805 std::string password_text;
806 bool is_hex = false;
807
808 switch (length) {
809 case IEEE_80211::kWEP40AsciiLen:
810 case IEEE_80211::kWEP104AsciiLen:
811 key_index_local = 0;
812 password_text = passphrase;
813 break;
814 case IEEE_80211::kWEP40AsciiLen + 2:
815 case IEEE_80211::kWEP104AsciiLen + 2:
816 if (CheckWEPKeyIndex(passphrase, error)) {
817 base::StringToInt(passphrase.substr(0,1), &key_index_local);
818 password_text = passphrase.substr(2);
819 }
820 break;
821 case IEEE_80211::kWEP40HexLen:
822 case IEEE_80211::kWEP104HexLen:
823 if (CheckWEPIsHex(passphrase, error)) {
824 key_index_local = 0;
825 password_text = passphrase;
826 is_hex = true;
827 }
828 break;
829 case IEEE_80211::kWEP40HexLen + 2:
830 case IEEE_80211::kWEP104HexLen + 2:
831 if(CheckWEPKeyIndex(passphrase, error) &&
832 CheckWEPIsHex(passphrase.substr(2), error)) {
833 base::StringToInt(passphrase.substr(0,1), &key_index_local);
834 password_text = passphrase.substr(2);
835 is_hex = true;
836 } else if (CheckWEPPrefix(passphrase, error) &&
837 CheckWEPIsHex(passphrase.substr(2), error)) {
838 key_index_local = 0;
839 password_text = passphrase.substr(2);
840 is_hex = true;
841 }
842 break;
843 case IEEE_80211::kWEP40HexLen + 4:
844 case IEEE_80211::kWEP104HexLen + 4:
845 if (CheckWEPKeyIndex(passphrase, error) &&
846 CheckWEPPrefix(passphrase.substr(2), error) &&
847 CheckWEPIsHex(passphrase.substr(4), error)) {
848 base::StringToInt(passphrase.substr(0,1), &key_index_local);
849 password_text = passphrase.substr(4);
850 is_hex = true;
851 }
852 break;
853 default:
854 error->Populate(Error::kInvalidPassphrase);
855 break;
856 }
857
mukesh agrawal1a056262011-10-05 14:36:54 -0700858 if (error->IsSuccess()) {
Thieu Lef4cbda92011-11-10 23:41:24 +0000859 if (key_index)
860 *key_index = key_index_local;
861 if (password_bytes) {
862 if (is_hex)
863 base::HexStringToBytes(password_text, password_bytes);
864 else
865 password_bytes->insert(password_bytes->end(),
866 password_text.begin(),
867 password_text.end());
868 }
mukesh agrawal1a056262011-10-05 14:36:54 -0700869 }
870}
871
872// static
873bool WiFiService::CheckWEPIsHex(const string &passphrase, Error *error) {
874 vector<uint8> passphrase_bytes;
875 if (base::HexStringToBytes(passphrase, &passphrase_bytes)) {
876 return true;
877 } else {
878 error->Populate(Error::kInvalidPassphrase);
879 return false;
880 }
881}
882
883// static
884bool WiFiService::CheckWEPKeyIndex(const string &passphrase, Error *error) {
885 if (StartsWithASCII(passphrase, "0:", false) ||
886 StartsWithASCII(passphrase, "1:", false) ||
887 StartsWithASCII(passphrase, "2:", false) ||
888 StartsWithASCII(passphrase, "3:", false)) {
889 return true;
890 } else {
891 error->Populate(Error::kInvalidPassphrase);
892 return false;
893 }
894}
895
896// static
897bool WiFiService::CheckWEPPrefix(const string &passphrase, Error *error) {
898 if (StartsWithASCII(passphrase, "0x", false)) {
899 return true;
900 } else {
901 error->Populate(Error::kInvalidPassphrase);
902 return false;
903 }
904}
905
Paul Stewart6ab23a92011-11-09 17:17:47 -0800906// static
Paul Stewart6ab23a92011-11-09 17:17:47 -0800907string WiFiService::GetSecurityClass(const string &security) {
908 if (security == flimflam::kSecurityRsn ||
909 security == flimflam::kSecurityWpa) {
910 return flimflam::kSecurityPsk;
Paul Stewartd08f4432011-11-04 07:48:20 -0700911 } else {
Paul Stewart6ab23a92011-11-09 17:17:47 -0800912 return security;
Paul Stewartd08f4432011-11-04 07:48:20 -0700913 }
914}
915
Paul Stewarta41e38d2011-11-11 07:47:29 -0800916// static
917bool WiFiService::ParseStorageIdentifier(const string &storage_name,
918 string *address,
919 string *mode,
920 string *security) {
921 vector<string> wifi_parts;
922 base::SplitString(storage_name, '_', &wifi_parts);
Paul Stewart0756db92012-01-27 08:34:47 -0800923 if ((wifi_parts.size() != 5 && wifi_parts.size() != 6) ||
924 wifi_parts[0] != flimflam::kTypeWifi) {
Paul Stewarta41e38d2011-11-11 07:47:29 -0800925 return false;
926 }
927 *address = wifi_parts[1];
928 *mode = wifi_parts[3];
Paul Stewart0756db92012-01-27 08:34:47 -0800929 if (wifi_parts.size() == 5) {
930 *security = wifi_parts[4];
931 } else {
932 // Account for security type "802_1x" which got split up above.
933 *security = wifi_parts[4] + "_" + wifi_parts[5];
934 }
Paul Stewarta41e38d2011-11-11 07:47:29 -0800935 return true;
936}
937
mukesh agrawale1d90e92012-02-15 17:36:08 -0800938// static
Paul Stewart85aea152013-01-22 09:31:56 -0800939bool WiFiService::FixupServiceEntries(StoreInterface *storage) {
940 bool fixed_entry = false;
941 set<string> groups = storage->GetGroups();
942 for (set<string>::const_iterator it = groups.begin(); it != groups.end();
943 ++it) {
944 const string &id = *it;
945 string device_address, network_mode, security;
946 if (!ParseStorageIdentifier(id, &device_address,
947 &network_mode, &security)) {
948 continue;
949 }
950 if (!storage->GetString(id, kStorageType, NULL)) {
951 storage->SetString(id, kStorageType, flimflam::kTypeWifi);
952 fixed_entry = true;
953 }
954 if (!storage->GetString(id, kStorageMode, NULL)) {
955 storage->SetString(id, kStorageMode, network_mode);
956 fixed_entry = true;
957 }
958 if (!storage->GetString(id, kStorageSecurity, NULL)) {
959 storage->SetString(id, kStorageSecurity, security);
960 fixed_entry = true;
961 }
Paul Stewart71a4d3b2013-01-18 18:12:56 -0800962 if (!storage->GetString(id, kStorageSecurityClass, NULL)) {
963 storage->SetString(id, kStorageSecurityClass, GetSecurityClass(security));
964 fixed_entry = true;
965 }
Paul Stewart85aea152013-01-22 09:31:56 -0800966 }
967 return fixed_entry;
968}
969
970// static
Paul Stewartd2e1c362013-03-03 19:06:07 -0800971bool WiFiService::IsValidMode(const string &mode) {
972 return mode == flimflam::kModeManaged ||
973 mode == flimflam::kModeAdhoc;
974}
975
976// static
Paul Stewart3c504012013-01-17 17:49:58 -0800977bool WiFiService::IsValidSecurityMethod(const string &method) {
978 return method == flimflam::kSecurityNone ||
979 method == flimflam::kSecurityWep ||
980 method == flimflam::kSecurityPsk ||
981 method == flimflam::kSecurityWpa ||
982 method == flimflam::kSecurityRsn ||
983 method == flimflam::kSecurity8021x;
984}
985
986// static
mukesh agrawale1d90e92012-02-15 17:36:08 -0800987uint8 WiFiService::SignalToStrength(int16 signal_dbm) {
988 int16 strength;
989 if (signal_dbm > 0) {
990 if (!logged_signal_warning) {
991 LOG(WARNING) << "Signal strength is suspiciously high. "
992 << "Assuming value " << signal_dbm << " is not in dBm.";
993 logged_signal_warning = true;
994 }
995 strength = signal_dbm;
996 } else {
997 strength = 120 + signal_dbm; // Call -20dBm "perfect".
998 }
999
mukesh agrawal8f3f7752012-02-17 19:42:09 -08001000 if (strength > kStrengthMax) {
1001 strength = kStrengthMax;
1002 } else if (strength < kStrengthMin) {
1003 strength = kStrengthMin;
mukesh agrawale1d90e92012-02-15 17:36:08 -08001004 }
1005 return strength;
1006}
1007
Paul Stewart71a4d3b2013-01-18 18:12:56 -08001008KeyValueStore WiFiService::GetStorageProperties() const {
1009 KeyValueStore args;
1010 args.SetString(kStorageType, flimflam::kTypeWifi);
1011 args.SetString(kStorageSSID, hex_ssid_);
1012 args.SetString(kStorageMode, mode_);
1013 args.SetString(kStorageSecurityClass, GetSecurityClass(security_));
1014 return args;
Paul Stewart6ab23a92011-11-09 17:17:47 -08001015}
1016
Paul Stewart71a4d3b2013-01-18 18:12:56 -08001017string WiFiService::GetDefaultStorageIdentifier() const {
1018 string security = GetSecurityClass(security_);
1019 return StringToLowerASCII(base::StringPrintf("%s_%s_%s_%s_%s",
Paul Stewartd08f4432011-11-04 07:48:20 -07001020 flimflam::kTypeWifi,
Paul Stewart3c504012013-01-17 17:49:58 -08001021 kAnyDeviceAddress,
Paul Stewartd08f4432011-11-04 07:48:20 -07001022 hex_ssid_.c_str(),
1023 mode_.c_str(),
1024 security.c_str()));
1025}
1026
Paul Stewart6df20bd2013-03-13 19:31:25 -07001027string WiFiService::GetSecurity(Error */*error*/) {
1028 if (current_endpoint_) {
1029 return current_endpoint_->security_mode();
1030 }
1031 return security_;
1032}
1033
Paul Stewart835934a2012-12-06 19:27:09 -08001034void WiFiService::ClearCachedCredentials() {
Paul Stewart3c504012013-01-17 17:49:58 -08001035 if (wifi_) {
1036 wifi_->ClearCachedCredentials(this);
1037 }
Paul Stewart835934a2012-12-06 19:27:09 -08001038}
1039
Paul Stewartc43cbbe2013-04-11 06:29:30 -07001040void WiFiService::OnEapCredentialsChanged() {
Paul Stewart835934a2012-12-06 19:27:09 -08001041 ClearCachedCredentials();
Gaurav Shah10109f22011-11-11 20:16:22 -08001042 UpdateConnectable();
1043}
1044
Paul Stewart4357f4e2012-04-26 17:39:26 -07001045void WiFiService::OnProfileConfigured() {
1046 if (profile() || !hidden_ssid()) {
1047 return;
1048 }
1049 // This situation occurs when a hidden WiFi service created via GetService
1050 // has been persisted to a profile in Manager::ConfigureService(). Now
1051 // that configuration is saved, we must join the service with its profile,
1052 // which will make this SSID eligible for directed probes during scans.
1053 manager()->RegisterService(this);
1054}
1055
Gaurav Shah29d68882012-01-30 19:06:42 -08001056bool WiFiService::Is8021x() const {
1057 if (security_ == flimflam::kSecurity8021x)
1058 return true;
1059
1060 // Dynamic WEP + 802.1x.
1061 if (security_ == flimflam::kSecurityWep &&
Paul Stewart9413bcc2013-04-04 16:12:43 -07001062 GetEAPKeyManagement() == WPASupplicant::kKeyManagementIeee8021X)
Gaurav Shah29d68882012-01-30 19:06:42 -08001063 return true;
1064 return false;
1065}
1066
Paul Stewart3c504012013-01-17 17:49:58 -08001067WiFiRefPtr WiFiService::ChooseDevice() {
Paul Stewartee6b3d72013-07-12 16:07:51 -07001068 // TODO(pstew): Style frowns on dynamic_cast. crbug.com/220387
Paul Stewart3c504012013-01-17 17:49:58 -08001069 DeviceRefPtr device =
1070 manager()->GetEnabledDeviceWithTechnology(Technology::kWifi);
1071 return dynamic_cast<WiFi *>(device.get());
1072}
1073
1074void WiFiService::ResetWiFi() {
1075 SetWiFi(NULL);
1076}
1077
mukesh agrawalcbfb34e2013-04-17 19:33:25 -07001078void WiFiService::SetWiFi(const WiFiRefPtr &new_wifi) {
1079 if (wifi_ == new_wifi) {
Paul Stewart3c504012013-01-17 17:49:58 -08001080 return;
1081 }
1082 ClearCachedCredentials();
1083 if (wifi_) {
1084 wifi_->DisassociateFromService(this);
1085 }
mukesh agrawalcbfb34e2013-04-17 19:33:25 -07001086 if (new_wifi) {
1087 adaptor()->EmitRpcIdentifierChanged(flimflam::kDeviceProperty,
1088 new_wifi->GetRpcIdentifier());
1089 } else {
1090 adaptor()->EmitRpcIdentifierChanged(flimflam::kDeviceProperty,
1091 DBusAdaptor::kNullPath);
1092 }
1093 wifi_ = new_wifi;
Paul Stewart3c504012013-01-17 17:49:58 -08001094}
1095
mukesh agrawalb54601c2011-06-07 17:39:22 -07001096} // namespace shill