1/*
2 * hidl interface for wpa_hostapd daemon
3 * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi>
4 * Copyright (c) 2004-2018, Roshan Pius <rpius@google.com>
5 *
6 * This software may be distributed under the terms of the BSD license.
7 * See README for more details.
8 */
9#include <iomanip>
10#include <sstream>
11#include <string>
12#include <vector>
13
14#include <android-base/file.h>
15#include <android-base/stringprintf.h>
16
17#include "hostapd.h"
18#include "hidl_return_util.h"
19
20extern "C"
21{
22#include "utils/eloop.h"
23}
24
25// The HIDL implementation for hostapd creates a hostapd.conf dynamically for
26// each interface. This file can then be used to hook onto the normal config
27// file parsing logic in hostapd code.  Helps us to avoid duplication of code
28// in the HIDL interface.
29// TOOD(b/71872409): Add unit tests for this.
30namespace {
31constexpr char kConfFileNameFmt[] = "/data/vendor/wifi/hostapd/hostapd_%s.conf";
32
33using android::base::RemoveFileIfExists;
34using android::base::StringPrintf;
35using android::base::WriteStringToFile;
36using android::hardware::wifi::hostapd::V1_0::IHostapd;
37
38std::string WriteHostapdConfig(
39    const std::string& interface_name, const std::string& config)
40{
41	const std::string file_path =
42	    StringPrintf(kConfFileNameFmt, interface_name.c_str());
43	if (WriteStringToFile(
44		config, file_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
45		getuid(), getgid())) {
46		return file_path;
47	}
48	// Diagnose failure
49	int error = errno;
50	wpa_printf(
51	    MSG_ERROR, "Cannot write hostapd config to %s, error: %s",
52	    file_path.c_str(), strerror(error));
53	struct stat st;
54	int result = stat(file_path.c_str(), &st);
55	if (result == 0) {
56		wpa_printf(
57		    MSG_ERROR, "hostapd config file uid: %d, gid: %d, mode: %d",
58		    st.st_uid, st.st_gid, st.st_mode);
59	} else {
60		wpa_printf(
61		    MSG_ERROR,
62		    "Error calling stat() on hostapd config file: %s",
63		    strerror(errno));
64	}
65	return "";
66}
67
68std::string CreateHostapdConfig(
69    const IHostapd::IfaceParams& iface_params,
70    const IHostapd::NetworkParams& nw_params)
71{
72	if (nw_params.ssid.size() >
73	    static_cast<uint32_t>(
74		IHostapd::ParamSizeLimits::SSID_MAX_LEN_IN_BYTES)) {
75		wpa_printf(
76		    MSG_ERROR, "Invalid SSID size: %zu", nw_params.ssid.size());
77		return "";
78	}
79	if ((nw_params.encryptionType != IHostapd::EncryptionType::NONE) &&
80	    (nw_params.pskPassphrase.size() <
81		 static_cast<uint32_t>(
82		     IHostapd::ParamSizeLimits::
83			 WPA2_PSK_PASSPHRASE_MIN_LEN_IN_BYTES) ||
84	     nw_params.pskPassphrase.size() >
85		 static_cast<uint32_t>(
86		     IHostapd::ParamSizeLimits::
87			 WPA2_PSK_PASSPHRASE_MAX_LEN_IN_BYTES))) {
88		wpa_printf(
89		    MSG_ERROR, "Invalid psk passphrase size: %zu",
90		    nw_params.pskPassphrase.size());
91		return "";
92	}
93
94	// SSID string
95	std::stringstream ss;
96	ss << std::hex;
97	ss << std::setfill('0');
98	for (uint8_t b : nw_params.ssid) {
99		ss << std::setw(2) << static_cast<unsigned int>(b);
100	}
101	const std::string ssid_as_string = ss.str();
102
103	// Encryption config string
104	std::string encryption_config_as_string;
105	switch (nw_params.encryptionType) {
106	case IHostapd::EncryptionType::NONE:
107		// no security params
108		break;
109	case IHostapd::EncryptionType::WPA:
110		encryption_config_as_string = StringPrintf(
111		    "wpa=3\n"
112		    "wpa_pairwise=TKIP CCMP\n"
113		    "wpa_passphrase=%s",
114		    nw_params.pskPassphrase.c_str());
115		break;
116	case IHostapd::EncryptionType::WPA2:
117		encryption_config_as_string = StringPrintf(
118		    "wpa=2\n"
119		    "rsn_pairwise=CCMP\n"
120		    "wpa_passphrase=%s",
121		    nw_params.pskPassphrase.c_str());
122		break;
123	default:
124		wpa_printf(MSG_ERROR, "Unknown encryption type");
125		return "";
126	}
127
128	std::string channel_config_as_string;
129	if (iface_params.channelParams.enableAcs) {
130		channel_config_as_string = StringPrintf(
131		    "channel=0\n"
132		    "acs_exclude_dfs=%d",
133		    iface_params.channelParams.acsShouldExcludeDfs);
134	} else {
135		channel_config_as_string = StringPrintf(
136		    "channel=%d", iface_params.channelParams.channel);
137	}
138
139	// Hw Mode String
140	std::string hw_mode_as_string;
141	std::string ht_cap_vht_oper_chwidth_as_string;
142	switch (iface_params.channelParams.band) {
143	case IHostapd::Band::BAND_2_4_GHZ:
144		hw_mode_as_string = "hw_mode=g";
145		break;
146	case IHostapd::Band::BAND_5_GHZ:
147		hw_mode_as_string = "hw_mode=a";
148		if (iface_params.channelParams.enableAcs) {
149			ht_cap_vht_oper_chwidth_as_string =
150			    "ht_capab=[HT40+]\n"
151			    "vht_oper_chwidth=1";
152		}
153		break;
154	case IHostapd::Band::BAND_ANY:
155		hw_mode_as_string = "hw_mode=any";
156		if (iface_params.channelParams.enableAcs) {
157			ht_cap_vht_oper_chwidth_as_string =
158			    "ht_capab=[HT40+]\n"
159			    "vht_oper_chwidth=1";
160		}
161		break;
162	default:
163		wpa_printf(MSG_ERROR, "Invalid band");
164		return "";
165	}
166
167	return StringPrintf(
168	    "interface=%s\n"
169	    "driver=nl80211\n"
170	    "ctrl_interface=/data/vendor/wifi/hostapd/ctrl\n"
171	    // ssid2 signals to hostapd that the value is not a literal value
172	    // for use as a SSID.  In this case, we're giving it a hex
173	    // std::string and hostapd needs to expect that.
174	    "ssid2=%s\n"
175	    "%s\n"
176	    "ieee80211n=%d\n"
177	    "ieee80211ac=%d\n"
178	    "%s\n"
179	    "%s\n"
180	    "ignore_broadcast_ssid=%d\n"
181	    "wowlan_triggers=any\n"
182	    "%s\n",
183	    iface_params.ifaceName.c_str(), ssid_as_string.c_str(),
184	    channel_config_as_string.c_str(),
185	    iface_params.hwModeParams.enable80211N ? 1 : 0,
186	    iface_params.hwModeParams.enable80211AC ? 1 : 0,
187	    hw_mode_as_string.c_str(), ht_cap_vht_oper_chwidth_as_string.c_str(),
188	    nw_params.isHidden ? 1 : 0, encryption_config_as_string.c_str());
189}
190}  // namespace
191
192namespace android {
193namespace hardware {
194namespace wifi {
195namespace hostapd {
196namespace V1_0 {
197namespace implementation {
198using hidl_return_util::call;
199
200Hostapd::Hostapd(struct hapd_interfaces* interfaces) : interfaces_(interfaces)
201{}
202
203Return<void> Hostapd::addAccessPoint(
204    const IfaceParams& iface_params, const NetworkParams& nw_params,
205    addAccessPoint_cb _hidl_cb)
206{
207	return call(
208	    this, &Hostapd::addAccessPointInternal, _hidl_cb, iface_params,
209	    nw_params);
210}
211
212Return<void> Hostapd::removeAccessPoint(
213    const hidl_string& iface_name, removeAccessPoint_cb _hidl_cb)
214{
215	return call(
216	    this, &Hostapd::removeAccessPointInternal, _hidl_cb, iface_name);
217}
218
219Return<void> Hostapd::terminate() {
220	wpa_printf(MSG_INFO, "Terminating...");
221	eloop_terminate();
222	return Void();
223}
224
225HostapdStatus Hostapd::addAccessPointInternal(
226    const IfaceParams& iface_params, const NetworkParams& nw_params)
227{
228	if (hostapd_get_iface(interfaces_, iface_params.ifaceName.c_str())) {
229		wpa_printf(
230		    MSG_ERROR, "Interface %s already present",
231		    iface_params.ifaceName.c_str());
232		return {HostapdStatusCode::FAILURE_IFACE_EXISTS, ""};
233	}
234	const auto conf_params = CreateHostapdConfig(iface_params, nw_params);
235	if (conf_params.empty()) {
236		wpa_printf(MSG_ERROR, "Failed to create config params");
237		return {HostapdStatusCode::FAILURE_ARGS_INVALID, ""};
238	}
239	const auto conf_file_path =
240	    WriteHostapdConfig(iface_params.ifaceName, conf_params);
241	if (conf_file_path.empty()) {
242		wpa_printf(MSG_ERROR, "Failed to write config file");
243		return {HostapdStatusCode::FAILURE_UNKNOWN, ""};
244	}
245	std::string add_iface_param_str = StringPrintf(
246	    "%s config=%s", iface_params.ifaceName.c_str(),
247	    conf_file_path.c_str());
248	std::vector<char> add_iface_param_vec(
249	    add_iface_param_str.begin(), add_iface_param_str.end() + 1);
250	if (hostapd_add_iface(interfaces_, add_iface_param_vec.data()) < 0) {
251		wpa_printf(
252		    MSG_ERROR, "Adding interface %s failed",
253		    add_iface_param_str.c_str());
254		return {HostapdStatusCode::FAILURE_UNKNOWN, ""};
255	}
256	struct hostapd_data* iface_hapd =
257	    hostapd_get_iface(interfaces_, iface_params.ifaceName.c_str());
258	WPA_ASSERT(iface_hapd != nullptr && iface_hapd->iface != nullptr);
259	if (hostapd_enable_iface(iface_hapd->iface) < 0) {
260		wpa_printf(
261		    MSG_ERROR, "Enabling interface %s failed",
262		    iface_params.ifaceName.c_str());
263		return {HostapdStatusCode::FAILURE_UNKNOWN, ""};
264	}
265	return {HostapdStatusCode::SUCCESS, ""};
266}
267
268HostapdStatus Hostapd::removeAccessPointInternal(const std::string& iface_name)
269{
270	std::vector<char> remove_iface_param_vec(
271	    iface_name.begin(), iface_name.end() + 1);
272	if (hostapd_remove_iface(interfaces_, remove_iface_param_vec.data()) <
273	    0) {
274		wpa_printf(
275		    MSG_ERROR, "Removing interface %s failed",
276		    iface_name.c_str());
277		return {HostapdStatusCode::FAILURE_UNKNOWN, ""};
278	}
279	return {HostapdStatusCode::SUCCESS, ""};
280}
281}  // namespace implementation
282}  // namespace V1_0
283}  // namespace hostapd
284}  // namespace wifi
285}  // namespace hardware
286}  // namespace android
287