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