1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "services/device/geolocation/wifi_data_provider_win.h"
6
7 #include <windows.h>
8 #include <winioctl.h>
9 #include <wlanapi.h>
10
11 #include "base/logging.h"
12 #include "base/memory/free_deleter.h"
13 #include "base/memory/ptr_util.h"
14 #include "base/metrics/histogram_macros.h"
15 #include "base/stl_util.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/win/windows_version.h"
18 #include "services/device/geolocation/wifi_data_provider_common.h"
19 #include "services/device/geolocation/wifi_data_provider_common_win.h"
20 #include "services/device/geolocation/wifi_data_provider_manager.h"
21
22 namespace device {
23
24 namespace {
25
26 static const int kDefaultPollingIntervalMs = 10 * 1000; // 10s
27 static const int kNoChangePollingIntervalMs = 2 * 60 * 1000; // 2 mins
28 static const int kTwoNoChangePollingIntervalMs = 10 * 60 * 1000; // 10 mins
29 static const int kNoWifiPollingIntervalMs = 20 * 1000; // 20s
30
31 // WlanOpenHandle
32 typedef DWORD(WINAPI* WlanOpenHandleFunction)(DWORD dwClientVersion,
33 PVOID pReserved,
34 PDWORD pdwNegotiatedVersion,
35 PHANDLE phClientHandle);
36
37 // WlanEnumInterfaces
38 typedef DWORD(WINAPI* WlanEnumInterfacesFunction)(
39 HANDLE hClientHandle,
40 PVOID pReserved,
41 PWLAN_INTERFACE_INFO_LIST* ppInterfaceList);
42
43 // WlanGetNetworkBssList
44 typedef DWORD(WINAPI* WlanGetNetworkBssListFunction)(
45 HANDLE hClientHandle,
46 const GUID* pInterfaceGuid,
47 const PDOT11_SSID pDot11Ssid,
48 DOT11_BSS_TYPE dot11BssType,
49 BOOL bSecurityEnabled,
50 PVOID pReserved,
51 PWLAN_BSS_LIST* ppWlanBssList);
52
53 // WlanFreeMemory
54 typedef VOID(WINAPI* WlanFreeMemoryFunction)(PVOID pMemory);
55
56 // WlanCloseHandle
57 typedef DWORD(WINAPI* WlanCloseHandleFunction)(HANDLE hClientHandle,
58 PVOID pReserved);
59
60 // Extracts data for an access point and converts to AccessPointData.
GetNetworkData(const WLAN_BSS_ENTRY & bss_entry)61 AccessPointData GetNetworkData(const WLAN_BSS_ENTRY& bss_entry) {
62 AccessPointData access_point_data;
63 // Currently we get only MAC address, signal strength and SSID.
64 access_point_data.mac_address = MacAddressAsString16(bss_entry.dot11Bssid);
65 access_point_data.radio_signal_strength = bss_entry.lRssi;
66 // bss_entry.dot11Ssid.ucSSID is not null-terminated.
67 base::UTF8ToUTF16(reinterpret_cast<const char*>(bss_entry.dot11Ssid.ucSSID),
68 static_cast<ULONG>(bss_entry.dot11Ssid.uSSIDLength),
69 &access_point_data.ssid);
70
71 // TODO(steveblock): Is it possible to get the following?
72 // access_point_data.signal_to_noise
73 // access_point_data.age
74 // access_point_data.channel
75 return access_point_data;
76 }
77
78 // This class encapsulates loading and interacting with wlan_api.dll, which can
79 // not be loaded statically because it's not available in Server 2008 R2, where
80 // it must be installed explicitly by the user if and when they wants to use the
81 // Wireless interface.
82 // https://www.bonusbits.com/wiki/KB:Wlanapi.dll_missing_on_Windows_Server_2008_R2
83 class WindowsWlanApi : public WifiDataProviderCommon::WlanApiInterface {
84 public:
85 static std::unique_ptr<WindowsWlanApi> Create();
86
87 // Takes ownership of the library handle.
88 explicit WindowsWlanApi(HINSTANCE library);
89 ~WindowsWlanApi() override;
90
91 // WlanApiInterface implementation
92 bool GetAccessPointData(WifiData::AccessPointDataSet* data) override;
93
94 private:
95 bool GetInterfaceDataWLAN(HANDLE wlan_handle,
96 const GUID& interface_id,
97 WifiData::AccessPointDataSet* data);
98 // Handle to the wlanapi.dll library.
99 HINSTANCE library_;
100
101 // Function pointers for WLAN
102 WlanOpenHandleFunction WlanOpenHandle_function_;
103 WlanEnumInterfacesFunction WlanEnumInterfaces_function_;
104 WlanGetNetworkBssListFunction WlanGetNetworkBssList_function_;
105 WlanFreeMemoryFunction WlanFreeMemory_function_;
106 WlanCloseHandleFunction WlanCloseHandle_function_;
107 };
108
109 // static
Create()110 std::unique_ptr<WindowsWlanApi> WindowsWlanApi::Create() {
111 // Use an absolute path to load the DLL to avoid DLL preloading attacks.
112 static const wchar_t* const kDLL = L"%WINDIR%\\system32\\wlanapi.dll";
113 wchar_t path[MAX_PATH] = {0};
114 ExpandEnvironmentStrings(kDLL, path, base::size(path));
115 HINSTANCE library = LoadLibraryEx(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
116 if (!library)
117 return nullptr;
118 return std::make_unique<WindowsWlanApi>(library);
119 }
120
WindowsWlanApi(HINSTANCE library)121 WindowsWlanApi::WindowsWlanApi(HINSTANCE library) : library_(library) {
122 DCHECK(library_);
123 // Extract all methods from |library_|.
124 WlanOpenHandle_function_ = reinterpret_cast<WlanOpenHandleFunction>(
125 GetProcAddress(library_, "WlanOpenHandle"));
126 WlanEnumInterfaces_function_ = reinterpret_cast<WlanEnumInterfacesFunction>(
127 GetProcAddress(library_, "WlanEnumInterfaces"));
128 WlanGetNetworkBssList_function_ =
129 reinterpret_cast<WlanGetNetworkBssListFunction>(
130 GetProcAddress(library_, "WlanGetNetworkBssList"));
131 WlanFreeMemory_function_ = reinterpret_cast<WlanFreeMemoryFunction>(
132 GetProcAddress(library_, "WlanFreeMemory"));
133 WlanCloseHandle_function_ = reinterpret_cast<WlanCloseHandleFunction>(
134 GetProcAddress(library_, "WlanCloseHandle"));
135
136 DCHECK(WlanOpenHandle_function_ && WlanEnumInterfaces_function_ &&
137 WlanGetNetworkBssList_function_ && WlanFreeMemory_function_ &&
138 WlanCloseHandle_function_);
139 }
140
~WindowsWlanApi()141 WindowsWlanApi::~WindowsWlanApi() {
142 FreeLibrary(library_);
143 }
144
GetAccessPointData(WifiData::AccessPointDataSet * data)145 bool WindowsWlanApi::GetAccessPointData(WifiData::AccessPointDataSet* data) {
146 DCHECK(data);
147
148 DWORD negotiated_version;
149 HANDLE wlan_handle = nullptr;
150 // Highest WLAN API version supported by the client; pass the lowest. It seems
151 // that the negotiated version is the Vista version (the highest) irrespective
152 // of what we pass!
153 static const int kXpWlanClientVersion = 1;
154 if ((*WlanOpenHandle_function_)(kXpWlanClientVersion, NULL,
155 &negotiated_version,
156 &wlan_handle) != ERROR_SUCCESS) {
157 return false;
158 }
159 DCHECK(wlan_handle);
160
161 // Get the list of interfaces. WlanEnumInterfaces allocates |interface_list|.
162 WLAN_INTERFACE_INFO_LIST* interface_list = nullptr;
163 if ((*WlanEnumInterfaces_function_)(wlan_handle, NULL, &interface_list) !=
164 ERROR_SUCCESS) {
165 return false;
166 }
167 DCHECK(interface_list);
168
169 // Go through the list of interfaces and get the data for each.
170 for (size_t i = 0; i < interface_list->dwNumberOfItems; ++i) {
171 const WLAN_INTERFACE_INFO interface_info = interface_list->InterfaceInfo[i];
172
173 // Skip any interface that is midway through association; the
174 // WlanGetNetworkBssList function call is known to hang indefinitely
175 // when it's in this state. https://crbug.com/39300
176 if (interface_info.isState == wlan_interface_state_associating) {
177 DLOG(WARNING) << "Skipping wifi scan on adapter " << i << " ("
178 << interface_info.strInterfaceDescription
179 << ") in 'associating' state. Repeated occurrences "
180 "indicates a non-responding adapter.";
181 continue;
182 }
183 GetInterfaceDataWLAN(wlan_handle, interface_info.InterfaceGuid, data);
184 }
185
186 (*WlanFreeMemory_function_)(interface_list);
187
188 return (*WlanCloseHandle_function_)(wlan_handle, NULL) == ERROR_SUCCESS;
189 }
190
191 // Appends the data for a single interface to |data|. Returns false for error.
GetInterfaceDataWLAN(const HANDLE wlan_handle,const GUID & interface_id,WifiData::AccessPointDataSet * data)192 bool WindowsWlanApi::GetInterfaceDataWLAN(const HANDLE wlan_handle,
193 const GUID& interface_id,
194 WifiData::AccessPointDataSet* data) {
195 // WlanGetNetworkBssList allocates |bss_list|.
196 WLAN_BSS_LIST* bss_list = nullptr;
197 if ((*WlanGetNetworkBssList_function_)(wlan_handle, &interface_id,
198 NULL, // Use all SSIDs.
199 dot11_BSS_type_any,
200 false, // bSecurityEnabled - unused
201 NULL, // reserved
202 &bss_list) != ERROR_SUCCESS) {
203 return false;
204 }
205 // WlanGetNetworkBssList() can return success without filling |bss_list|.
206 if (!bss_list)
207 return false;
208
209 for (size_t i = 0; i < bss_list->dwNumberOfItems; ++i)
210 data->insert(GetNetworkData(bss_list->wlanBssEntries[i]));
211
212 (*WlanFreeMemory_function_)(bss_list);
213
214 return true;
215 }
216
217 } // anonymous namespace
218
DefaultFactoryFunction()219 WifiDataProvider* WifiDataProviderManager::DefaultFactoryFunction() {
220 return new WifiDataProviderWin();
221 }
222
223 WifiDataProviderWin::WifiDataProviderWin() = default;
224
225 WifiDataProviderWin::~WifiDataProviderWin() = default;
226
227 std::unique_ptr<WifiDataProviderCommon::WlanApiInterface>
CreateWlanApi()228 WifiDataProviderWin::CreateWlanApi() {
229 return WindowsWlanApi::Create();
230 }
231
CreatePollingPolicy()232 std::unique_ptr<WifiPollingPolicy> WifiDataProviderWin::CreatePollingPolicy() {
233 return std::make_unique<GenericWifiPollingPolicy<
234 kDefaultPollingIntervalMs, kNoChangePollingIntervalMs,
235 kTwoNoChangePollingIntervalMs, kNoWifiPollingIntervalMs>>();
236 }
237
238 } // namespace device
239