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