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/supplicant_manager.h"
18
19 #include <android-base/logging.h>
20 #include <cutils/properties.h>
21 #include <fcntl.h>
22 #include <string.h>
23 #include <sys/stat.h>
24 #include <unistd.h>
25
26 // This ugliness is necessary to access internal implementation details
27 // of the property subsystem.
28 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
29 #include <sys/_system_properties.h>
30
31 namespace android {
32 namespace wifi_system {
33 namespace {
34
35 const char kSupplicantInitProperty[] = "init.svc.wpa_supplicant";
36 const char kSupplicantConfigTemplatePath[] =
37 "/etc/wifi/wpa_supplicant.conf";
38 const char kSupplicantConfigFile[] = "/data/misc/wifi/wpa_supplicant.conf";
39 const char kP2pConfigFile[] = "/data/misc/wifi/p2p_supplicant.conf";
40 const char kSupplicantServiceName[] = "wpa_supplicant";
41 constexpr mode_t kConfigFileMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
42
43 const char kWiFiEntropyFile[] = "/data/misc/wifi/entropy.bin";
44
45 const unsigned char kDummyKey[21] = {0x02, 0x11, 0xbe, 0x33, 0x43, 0x35, 0x68,
46 0x47, 0x84, 0x99, 0xa9, 0x2b, 0x1c, 0xd3,
47 0xee, 0xff, 0xf1, 0xe2, 0xf3, 0xf4, 0xf5};
48
ensure_config_file_exists(const char * config_file)49 int ensure_config_file_exists(const char* config_file) {
50 char buf[2048];
51 int srcfd, destfd;
52 int nread;
53 int ret;
54 std::string templatePath;
55
56 ret = access(config_file, R_OK | W_OK);
57 if ((ret == 0) || (errno == EACCES)) {
58 if ((ret != 0) && (chmod(config_file, kConfigFileMode) != 0)) {
59 LOG(ERROR) << "Cannot set RW to \"" << config_file << "\": "
60 << strerror(errno);
61 return false;
62 }
63 return true;
64 } else if (errno != ENOENT) {
65 LOG(ERROR) << "Cannot access \"" << config_file << "\": "
66 << strerror(errno);
67 return false;
68 }
69
70 std::string configPathSystem =
71 std::string("/system") + std::string(kSupplicantConfigTemplatePath);
72 std::string configPathVendor =
73 std::string("/vendor") + std::string(kSupplicantConfigTemplatePath);
74 srcfd = TEMP_FAILURE_RETRY(open(configPathSystem.c_str(), O_RDONLY));
75 templatePath = configPathSystem;
76 if (srcfd < 0) {
77 int errnoSystem = errno;
78 srcfd = TEMP_FAILURE_RETRY(open(configPathVendor.c_str(), O_RDONLY));
79 templatePath = configPathVendor;
80 if (srcfd < 0) {
81 int errnoVendor = errno;
82 LOG(ERROR) << "Cannot open \"" << configPathSystem << "\": "
83 << strerror(errnoSystem);
84 LOG(ERROR) << "Cannot open \"" << configPathVendor << "\": "
85 << strerror(errnoVendor);
86 return false;
87 }
88 }
89
90 destfd = TEMP_FAILURE_RETRY(open(config_file,
91 O_CREAT | O_RDWR,
92 kConfigFileMode));
93 if (destfd < 0) {
94 close(srcfd);
95 LOG(ERROR) << "Cannot create \"" << config_file << "\": "
96 << strerror(errno);
97 return false;
98 }
99
100 while ((nread = TEMP_FAILURE_RETRY(read(srcfd, buf, sizeof(buf)))) != 0) {
101 if (nread < 0) {
102 LOG(ERROR) << "Error reading \"" << templatePath
103 << "\": " << strerror(errno);
104 close(srcfd);
105 close(destfd);
106 unlink(config_file);
107 return false;
108 }
109 TEMP_FAILURE_RETRY(write(destfd, buf, nread));
110 }
111
112 close(destfd);
113 close(srcfd);
114
115 /* chmod is needed because open() didn't set permisions properly */
116 if (chmod(config_file, kConfigFileMode) < 0) {
117 LOG(ERROR) << "Error changing permissions of " << config_file
118 << " to 0660: " << strerror(errno);
119 unlink(config_file);
120 return false;
121 }
122
123 return true;
124 }
125
126 } // namespace
127
StartSupplicant()128 bool SupplicantManager::StartSupplicant() {
129 char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
130 int count = 200; /* wait at most 20 seconds for completion */
131 const prop_info* pi;
132 unsigned serial = 0;
133
134 /* Check whether already running */
135 if (property_get(kSupplicantInitProperty, supp_status, NULL) &&
136 strcmp(supp_status, "running") == 0) {
137 return true;
138 }
139
140 /* Before starting the daemon, make sure its config file exists */
141 if (ensure_config_file_exists(kSupplicantConfigFile) < 0) {
142 LOG(ERROR) << "Wi-Fi will not be enabled";
143 return false;
144 }
145
146 /*
147 * Some devices have another configuration file for the p2p interface.
148 * However, not all devices have this, and we'll let it slide if it
149 * is missing. For devices that do expect this file to exist,
150 * supplicant will refuse to start and emit a good error message.
151 * No need to check for it here.
152 */
153 (void)ensure_config_file_exists(kP2pConfigFile);
154
155 if (!EnsureEntropyFileExists()) {
156 LOG(ERROR) << "Wi-Fi entropy file was not created";
157 }
158
159 /*
160 * Get a reference to the status property, so we can distinguish
161 * the case where it goes stopped => running => stopped (i.e.,
162 * it start up, but fails right away) from the case in which
163 * it starts in the stopped state and never manages to start
164 * running at all.
165 */
166 pi = __system_property_find(kSupplicantInitProperty);
167 if (pi != NULL) {
168 serial = __system_property_serial(pi);
169 }
170
171 property_set("ctl.start", kSupplicantServiceName);
172 sched_yield();
173
174 while (count-- > 0) {
175 if (pi == NULL) {
176 pi = __system_property_find(kSupplicantInitProperty);
177 }
178 if (pi != NULL) {
179 /*
180 * property serial updated means that init process is scheduled
181 * after we sched_yield, further property status checking is based on this
182 */
183 if (__system_property_serial(pi) != serial) {
184 __system_property_read(pi, NULL, supp_status);
185 if (strcmp(supp_status, "running") == 0) {
186 return true;
187 } else if (strcmp(supp_status, "stopped") == 0) {
188 return false;
189 }
190 }
191 }
192 usleep(100000);
193 }
194 return false;
195 }
196
StopSupplicant()197 bool SupplicantManager::StopSupplicant() {
198 char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
199 int count = 50; /* wait at most 5 seconds for completion */
200
201 /* Check whether supplicant already stopped */
202 if (property_get(kSupplicantInitProperty, supp_status, NULL) &&
203 strcmp(supp_status, "stopped") == 0) {
204 return true;
205 }
206
207 property_set("ctl.stop", kSupplicantServiceName);
208 sched_yield();
209
210 while (count-- > 0) {
211 if (property_get(kSupplicantInitProperty, supp_status, NULL)) {
212 if (strcmp(supp_status, "stopped") == 0) return true;
213 }
214 usleep(100000);
215 }
216 LOG(ERROR) << "Failed to stop supplicant";
217 return false;
218 }
219
IsSupplicantRunning()220 bool SupplicantManager::IsSupplicantRunning() {
221 char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
222 if (property_get(kSupplicantInitProperty, supp_status, NULL)) {
223 return strcmp(supp_status, "running") == 0;
224 }
225 return false; // Failed to read service status from init.
226 }
227
EnsureEntropyFileExists()228 bool SupplicantManager::EnsureEntropyFileExists() {
229 int ret;
230 int destfd;
231
232 ret = access(kWiFiEntropyFile, R_OK | W_OK);
233 if ((ret == 0) || (errno == EACCES)) {
234 if ((ret != 0) &&
235 (chmod(kWiFiEntropyFile, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) != 0)) {
236 PLOG(ERROR) << "Cannot set RW to " << kWiFiEntropyFile;
237 return false;
238 }
239 return true;
240 }
241 destfd = TEMP_FAILURE_RETRY(open(kWiFiEntropyFile, O_CREAT | O_RDWR, 0660));
242 if (destfd < 0) {
243 PLOG(ERROR) << "Cannot create " << kWiFiEntropyFile;
244 return false;
245 }
246
247 if (TEMP_FAILURE_RETRY(write(destfd, kDummyKey, sizeof(kDummyKey))) !=
248 sizeof(kDummyKey)) {
249 PLOG(ERROR) << "Error writing " << kWiFiEntropyFile;
250 close(destfd);
251 return false;
252 }
253 close(destfd);
254
255 /* chmod is needed because open() didn't set permisions properly */
256 if (chmod(kWiFiEntropyFile, 0660) < 0) {
257 PLOG(ERROR) << "Error changing permissions of " << kWiFiEntropyFile
258 << " to 0600 ";
259 unlink(kWiFiEntropyFile);
260 return false;
261 }
262
263 return true;
264 }
265
266 } // namespace wifi_system
267 } // namespace android
268