• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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