1/*
2 * Copyright (C) 2016 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 "wifi_system/hostapd_manager.h"
18
19#include <iomanip>
20#include <sstream>
21#include <string>
22#include <vector>
23
24#include <android-base/file.h>
25#include <android-base/logging.h>
26#include <android-base/parseint.h>
27#include <android-base/stringprintf.h>
28#include <cutils/properties.h>
29#include <openssl/evp.h>
30#include <openssl/sha.h>
31#include <private/android_filesystem_config.h>
32
33#include "wifi_system/supplicant_manager.h"
34
35using android::base::ParseInt;
36using android::base::ReadFileToString;
37using android::base::StringPrintf;
38using android::base::WriteStringToFile;
39using std::string;
40using std::vector;
41using std::stringstream;
42
43namespace android {
44namespace wifi_system {
45namespace {
46
47const int kDefaultApChannel = 6;
48const char kHostapdServiceName[] = "hostapd";
49const char kHostapdConfigFilePath[] = "/data/misc/wifi/hostapd.conf";
50
51
52string GeneratePsk(const vector<uint8_t>& ssid,
53                   const vector<uint8_t>& passphrase) {
54  string result;
55  unsigned char psk[SHA256_DIGEST_LENGTH];
56
57  // Use the PKCS#5 PBKDF2 with 4096 iterations
58  if (PKCS5_PBKDF2_HMAC_SHA1(reinterpret_cast<const char*>(passphrase.data()),
59                             passphrase.size(),
60                             ssid.data(), ssid.size(),
61                             4096, sizeof(psk), psk) != 1) {
62    LOG(ERROR) << "Cannot generate PSK using PKCS#5 PBKDF2";
63    return result;
64  }
65
66  stringstream ss;
67  ss << std::hex;
68  ss << std::setfill('0');
69  for (int j = 0; j < SHA256_DIGEST_LENGTH; j++) {
70    ss << std::setw(2) << static_cast<unsigned int>(psk[j]);
71  }
72  result = ss.str();
73
74  return result;
75}
76
77}  // namespace
78
79bool HostapdManager::StartHostapd() {
80  if (!SupplicantManager::EnsureEntropyFileExists()) {
81    LOG(WARNING) << "Wi-Fi entropy file was not created";
82  }
83
84  if (property_set("ctl.start", kHostapdServiceName) != 0) {
85    LOG(ERROR) << "Failed to start SoftAP";
86    return false;
87  }
88
89  LOG(DEBUG) << "SoftAP started successfully";
90  return true;
91}
92
93bool HostapdManager::StopHostapd() {
94  LOG(DEBUG) << "Stopping the SoftAP service...";
95
96  if (property_set("ctl.stop", kHostapdServiceName) < 0) {
97    LOG(ERROR) << "Failed to stop hostapd service!";
98    return false;
99  }
100
101  LOG(DEBUG) << "SoftAP stopped successfully";
102  return true;
103}
104
105bool HostapdManager::WriteHostapdConfig(const string& config) {
106  if (!WriteStringToFile(config, kHostapdConfigFilePath,
107                         S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
108                         AID_WIFI, AID_WIFI)) {
109    int error = errno;
110    LOG(ERROR) << "Cannot write hostapd config to \""
111               << kHostapdConfigFilePath << "\": " << strerror(error);
112    struct stat st;
113    int result = stat(kHostapdConfigFilePath, &st);
114    if (result == 0) {
115      LOG(ERROR) << "hostapd config file uid: "<< st.st_uid << ", gid: " << st.st_gid
116                 << ", mode: " << st.st_mode;
117    } else {
118      LOG(ERROR) << "Error calling stat() on hostapd config file: " << strerror(errno);
119    }
120    return false;
121  }
122  return true;
123}
124
125string HostapdManager::CreateHostapdConfig(
126    const string& interface_name,
127    const vector<uint8_t> ssid,
128    bool is_hidden,
129    int channel,
130    EncryptionType encryption_type,
131    const vector<uint8_t> passphrase) {
132  string result;
133
134  if (channel < 0) {
135    channel = kDefaultApChannel;
136  }
137
138  if (ssid.size() > 32) {
139    LOG(ERROR) << "SSIDs must be <= 32 bytes long";
140    return result;
141  }
142
143  stringstream ss;
144  ss << std::hex;
145  ss << std::setfill('0');
146  for (uint8_t b : ssid) {
147    ss << std::setw(2) << static_cast<unsigned int>(b);
148  }
149  const string ssid_as_string  = ss.str();
150
151  string encryption_config;
152  if (encryption_type != EncryptionType::kOpen) {
153    string psk = GeneratePsk(ssid, passphrase);
154    if (psk.empty()) {
155      return result;
156    }
157    if (encryption_type == EncryptionType::kWpa) {
158      encryption_config = StringPrintf("wpa=3\n"
159                                       "wpa_pairwise=TKIP CCMP\n"
160                                       "wpa_psk=%s\n", psk.c_str());
161    } else if (encryption_type == EncryptionType::kWpa2) {
162      encryption_config = StringPrintf("wpa=2\n"
163                                       "rsn_pairwise=CCMP\n"
164                                       "wpa_psk=%s\n", psk.c_str());
165    } else {
166      using encryption_t = std::underlying_type<EncryptionType>::type;
167      LOG(ERROR) << "Unknown encryption type ("
168                 << static_cast<encryption_t>(encryption_type)
169                 << ")";
170      return result;
171    }
172  }
173
174  result = StringPrintf(
175      "interface=%s\n"
176      "driver=nl80211\n"
177      "ctrl_interface=/data/misc/wifi/hostapd/ctrl\n"
178      // ssid2 signals to hostapd that the value is not a literal value
179      // for use as a SSID.  In this case, we're giving it a hex string
180      // and hostapd needs to expect that.
181      "ssid2=%s\n"
182      "channel=%d\n"
183      "ieee80211n=1\n"
184      "hw_mode=%c\n"
185      "ignore_broadcast_ssid=%d\n"
186      "wowlan_triggers=any\n"
187      "%s",
188      interface_name.c_str(),
189      ssid_as_string.c_str(),
190      channel,
191      (channel <= 14) ? 'g' : 'a',
192      (is_hidden) ? 1 : 0,
193      encryption_config.c_str());
194  return result;
195}
196
197}  // namespace wifi_system
198}  // namespace android
199