Add new dbus method to get SoftAP Config options
[platform/core/connectivity/wifi-mesh-manager.git] / src / wmesh-softap.c
1 /*
2  * Network Configuration Module
3  *
4  * Copyright (c) 2017 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  */
19 #include <glib.h>
20 #include <tzplatform_config.h>
21 #include <openssl/evp.h>
22 #include <openssl/sha.h>
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <netinet/in.h>
31 #include <sys/socket.h>
32 #include <sys/un.h>
33 #include <sys/wait.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36
37 #include "wmesh.h"
38 #include "wmesh-log.h"
39 #include "wmesh-util.h"
40 #include "wmesh-service.h"
41 #include "wmesh-softap.h"
42
43 #define MOBILE_AP_WIFI_KEY_MAX_LEN      64      /**< Maximum length of wifi hash key */
44
45 #define HOSTAPD_VENDOR_ELEMENTS_WIFI_AP "DD050016321000"        /* Specific application mode AP (e.g. GroupPlay) */
46 #define HOSTAPD_CONF            "interface=%s\n" \
47                                 "driver=nl80211\n" \
48                                 "ctrl_interface=%s\n" \
49                                 "ssid=%s\n" \
50                                 "channel=%d\n" \
51                                 "ignore_broadcast_ssid=%d\n" \
52                                 "hw_mode=%s\n" \
53                                 "max_num_sta=%d\n" \
54                                 "macaddr_acl=%d\n" \
55                                 "accept_mac_file=%s\n" \
56                                 "deny_mac_file=%s\n" \
57                                 "ieee80211n=1\n"
58 #define HOSTAPD_CONF_LEN        1024
59 #define HOSTAPD_BIN             "/usr/sbin/hostapd"
60 #define HOSTAPD_DEBUG_FILE      "/var/log/mesh_hostapd.log"
61 #define HOSTAPD_ENTROPY_FILE    tzplatform_mkpath(TZ_SYS_VAR, "/lib/misc/hostapd.bin")
62 #define HOSTAPD_MESH_CONF_FILE  tzplatform_mkpath(TZ_SYS_VAR, "/lib/wmesh/mesh_hostapd.conf")
63 #define HOSTAPD_CTRL_INTF_DIR   tzplatform_mkpath(TZ_SYS_RUN, "/hostapd")
64 #define HOSTAPD_PID_FILE                tzplatform_mkpath(TZ_SYS_RUN, "/.mesh_hostapd.pid")
65 #define HOSTAPD_ALLOWED_LIST    tzplatform_mkpath(TZ_SYS_VAR, "/lib/hostapd/hostapd.accept")
66 #define HOSTAPD_BLOCKED_LIST    tzplatform_mkpath(TZ_SYS_VAR, "/lib/hostapd/hostapd.deny")
67 #define HOSTAPD_RETRY_MAX       5
68 #define HOSTAPD_RETRY_DELAY     500000  /* us */
69
70 #define MH_CTRL_INTF            "/tmp/mesh_hostapd_wpa_ctrl"
71
72 #define PSK_ITERATION_COUNT     4096
73 #define MAX_BUF_SIZE            (256u)
74
75 static int hostapd_ctrl_fd = 0;
76 static char *g_passphrase = NULL;
77
78 static int __get_psk_hexascii(const char *pass, const unsigned char *salt,
79                 char *psk, unsigned int psk_len)
80 {
81         int i = 0;
82         int d_16 = 0;
83         int r_16 = 0;
84         unsigned char buf[SHA256_DIGEST_LENGTH] = {0, };
85
86         if (pass == NULL || salt == NULL || psk == NULL
87                         || psk_len < (SHA256_DIGEST_LENGTH * 2 + 1)) {
88                 WMESH_LOGE("Invalid parameter");
89                 return WMESHD_ERROR_INVALID_PARAMETER;
90         }
91
92         if (!PKCS5_PBKDF2_HMAC_SHA1(pass, strlen(pass),
93                                 salt, strlen((const char *)salt),
94                                 PSK_ITERATION_COUNT, sizeof(buf), buf)) {
95                 WMESH_LOGE("Getting psk is failed");
96                 return WMESHD_ERROR_OPERATION_FAILED;
97         }
98
99         for (i = 0; i < SHA256_DIGEST_LENGTH; i++) {
100                 d_16 = buf[i] >> 4;
101                 r_16 = buf[i] & 0xf;
102
103                 psk[i << 1] = d_16 < 10 ? d_16 + '0' : d_16 - 10 + 'a';
104                 psk[(i << 1) + 1] = r_16 < 10 ? r_16 + '0' : r_16 - 10 + 'a';
105         }
106         psk[i << 1] = '\0';
107
108         return WMESHD_ERROR_NONE;
109 }
110
111 static int __config_hostapd(const char *softap_interface, const char *ssid,
112                 const char *security, const char *passphrase, const char* mode,
113                 int channel, int visibility, int mac_filter, int max_sta)
114 {
115         char *conf = NULL;
116         char *old_conf;
117         char buf[HOSTAPD_CONF_LEN] = "";
118         FILE *fp = NULL;
119         int ret;
120         char key[MOBILE_AP_WIFI_KEY_MAX_LEN + 1];
121         char *hw_mode = NULL;
122
123         if (mode == NULL)
124                 hw_mode = g_strdup("g");
125         else
126                 hw_mode = g_strdup(mode);
127
128         /* Default conf. */
129         snprintf(buf, sizeof(buf), HOSTAPD_CONF,
130                         softap_interface,
131                         HOSTAPD_CTRL_INTF_DIR,
132                         ssid,
133                         channel,
134                         (visibility ? 0 : 2),
135                         hw_mode,
136                         max_sta,
137                         mac_filter,
138                         HOSTAPD_ALLOWED_LIST,
139                         HOSTAPD_BLOCKED_LIST);
140         conf = g_strdup(buf);
141
142         free(hw_mode);
143
144         /* Vendor elements conf. */
145         snprintf(buf, sizeof(buf),
146                         "vendor_elements=%s\n", HOSTAPD_VENDOR_ELEMENTS_WIFI_AP);
147         old_conf = conf;
148         conf = g_strconcat(old_conf, buf, NULL);
149         g_free(old_conf);
150
151         /* Security conf. */
152         if (security != NULL && g_strcmp0(security, "wpa2-psk") == 0) {
153                 ret = __get_psk_hexascii(passphrase,
154                                 (const unsigned char *)ssid, key, sizeof(key));
155                 if (ret != WMESHD_ERROR_NONE) {
156                         g_free(conf);
157                         WMESH_LOGE("hex conversion failed");
158                         return WMESHD_ERROR_OPERATION_FAILED;
159                 }
160                 snprintf(buf, sizeof(buf),
161                                 "wpa=2\nrsn_pairwise=CCMP\nwpa_psk=%s\n", key);
162
163                 old_conf = conf;
164                 conf = g_strconcat(old_conf, buf, NULL);
165                 g_free(old_conf);
166         } else {
167                 WMESH_LOGD("Open connection [%s]", security);
168         }
169
170         fp = fopen(HOSTAPD_MESH_CONF_FILE, "w");
171         if (NULL == fp) {
172                 WMESH_LOGE("Could not create the file [%s].", HOSTAPD_MESH_CONF_FILE);
173                 g_free(conf);
174                 return WMESHD_ERROR_IO_ERROR;
175         }
176
177         if (conf) {
178                 fputs(conf, fp);
179                 g_free(conf);
180         }
181         fclose(fp);
182
183         return WMESHD_ERROR_NONE;
184 }
185
186 static int __read_hostapd_config(char **softap_interface, char **ssid,
187                 char **mode, int *channel, int *visibility, int *max_sta, int *security,
188                 char **passphrase)
189 {
190         FILE *fp = NULL;
191         char buf[256];
192
193         fp = fopen(HOSTAPD_MESH_CONF_FILE, "r");
194         if (fp == NULL) {
195                 WMESH_LOGE("Failed to read file");
196                 return WMESHD_ERROR_IO_ERROR;
197         }
198
199         if (!softap_interface || !ssid || !security || !passphrase || !mode ||
200                 !channel || !visibility || !max_sta)
201                 return WMESHD_ERROR_INVALID_PARAMETER;
202
203         *security = 0;
204
205         while (NULL != fgets(buf, sizeof(buf), fp)) {
206                 if (strncmp(buf, "interface", strlen("interface")) == 0) {
207                         *softap_interface = g_strdup(strrchr(buf, '=') + 1);
208                         WMESH_LOGD("Interface: %s", *softap_interface);
209                 } else if (strncmp(buf, "ssid", strlen("ssid")) == 0) {
210                         *ssid = g_strdup(strrchr(buf, '=') + 1);
211                         WMESH_LOGD("SSID: %s", *ssid);
212                 } else if (strncmp(buf, "hw_mode", strlen("hw_mode")) == 0) {
213                         *mode = g_strdup(strrchr(buf, '=') + 1);
214                         WMESH_LOGD("Mode: %s", *mode);
215                 } else if (strncmp(buf, "channel", strlen("channel")) == 0) {
216                         *channel = atoi(strrchr(buf, '=') + 1);
217                         WMESH_LOGD("Channel: %d", *channel);
218                 } else if (strncmp(buf, "ignore_broadcast_ssid",
219                                                    strlen("ignore_broadcast_ssid")) == 0) {
220                         *visibility = atoi(strrchr(buf, '=') + 1) == 0 ? 1 : 0;
221                         WMESH_LOGD("Visibility: %d", *visibility);
222                 } else if (strncmp(buf, "max_num_sta", strlen("max_num_sta")) == 0) {
223                         *max_sta = atoi(strrchr(buf, '=') + 1);
224                         WMESH_LOGD("Max Station: %d", *max_sta);
225                 } else if (strncmp(buf, "wpa=", strlen("wpa=")) == 0) {
226                         *security = 1;
227                         *passphrase = g_strdup(g_passphrase);
228                         WMESH_LOGD("Security: %d", *security);
229                         WMESH_LOGD("Passphrase: %s", *passphrase);
230                 }
231         }
232
233         return WMESHD_ERROR_NONE;
234 }
235
236 static int __open_hostapd_intf(const char* softap_interface, int *fd,
237                 const char *intf)
238 {
239         int retry = 0;
240         char ctrl_intf[255] = {0, };
241         struct sockaddr_un src;
242         struct sockaddr_un dest;
243         struct stat stat_buf;
244
245         if (fd == NULL || intf == NULL) {
246                 WMESH_LOGE("fd is NULL");
247                 return WMESHD_ERROR_INVALID_PARAMETER;
248         }
249
250         *fd = socket(PF_UNIX, SOCK_DGRAM, 0);
251         if (*fd < 0) {
252                 WMESH_LOGE("socket is failed");
253                 return WMESHD_ERROR_OPERATION_FAILED;
254         }
255
256         src.sun_family = AF_UNIX;
257         g_strlcpy(src.sun_path, intf, sizeof(src.sun_path));
258
259         if (stat(src.sun_path, &stat_buf) == 0)
260                 unlink(src.sun_path);
261
262         if (bind(*fd, (struct sockaddr *)&src, sizeof(src)) < 0) {
263                 WMESH_LOGE("bind is failed");
264                 close(*fd);
265                 *fd = -1;
266                 unlink(src.sun_path);
267                 return WMESHD_ERROR_OPERATION_FAILED;
268         }
269
270         snprintf(ctrl_intf, sizeof(ctrl_intf), "%s/%s",
271                         HOSTAPD_CTRL_INTF_DIR, softap_interface);
272         dest.sun_family = AF_UNIX;
273         g_strlcpy(dest.sun_path, ctrl_intf, sizeof(dest.sun_path));
274
275         while (connect(*fd, (struct sockaddr *)&dest, sizeof(dest)) < 0) {
276                 if (++retry >= HOSTAPD_RETRY_MAX)
277                         goto FAIL;
278                 usleep(HOSTAPD_RETRY_DELAY);
279         }
280         WMESH_LOGD("Successfully open interface[%s] to hostapd", intf);
281
282         return WMESHD_ERROR_NONE;
283
284 FAIL:
285         WMESH_LOGE("Cannot make connection to hostapd");
286         close(*fd);
287         *fd = -1;
288         unlink(src.sun_path);
289
290         return WMESHD_ERROR_OPERATION_FAILED;
291 }
292
293 static int __close_hostapd_intf(int *fd)
294 {
295         if (fd == NULL) {
296                 WMESH_LOGE("fd is NULL");
297                 return WMESHD_ERROR_INVALID_PARAMETER;
298         }
299
300         if (*fd > 0)
301                 close(*fd);
302         *fd = -1;
303
304         return WMESHD_ERROR_NONE;
305 }
306
307 static int __get_pid_of_hostapd(pid_t *pid)
308 {
309         FILE *fd = NULL;
310         int ret = -1;
311         int rv = 0;
312         char error_buf[256] = {0, };
313         char file_path[256] = {0, };
314
315         snprintf(file_path, 256, "%s", HOSTAPD_PID_FILE);
316
317         if (0 == access(file_path, F_OK)) {
318                 fd = fopen(file_path, "r");
319                 if (fd == NULL) {
320                         WMESH_LOGE("Error! Could not open pid file");
321                         return WMESHD_ERROR_IO_ERROR;
322                 }
323         } else {
324                 WMESH_LOGE("Error! Could not access pid file");
325                 return WMESHD_ERROR_IO_ERROR;
326         }
327
328         errno = 0;
329         rv = fscanf(fd, "%d", &ret);
330         if (rv < 0) {
331                 strerror_r(errno, error_buf, 256);
332                 WMESH_LOGE("Error! Failed to read from file, rv:[%d], error:[%s]",
333                                 rv, error_buf);
334                 fclose(fd);
335                 return WMESHD_ERROR_IO_ERROR;
336         }
337
338         *pid = (pid_t)ret;
339         WMESH_LOGD("  PID: [%d]", ret);
340
341         fclose(fd);
342         return WMESHD_ERROR_NONE;
343 }
344
345 static int __terminate_hostapd()
346 {
347         int ret;
348         pid_t hostapd_pid = 0;
349
350         /* Get pid */
351         ret = __get_pid_of_hostapd(&hostapd_pid);
352         if (WMESHD_ERROR_NONE != ret) {
353                 WMESH_LOGE("There is no hostapd");
354                 return WMESHD_ERROR_NONE;
355         }
356
357         if (hostapd_pid == 0) {
358                 WMESH_LOGE("There is no hostapd");
359                 return WMESHD_ERROR_NONE;
360         }
361
362         ret = __close_hostapd_intf(&hostapd_ctrl_fd);
363         if (ret != WMESHD_ERROR_NONE)
364                 WMESH_LOGE("__close_hostapd_intf is failed");
365
366         kill(hostapd_pid, SIGTERM);
367
368         return WMESHD_ERROR_NONE;
369 }
370
371 static int __execute_hostapd()
372 {
373         pid_t pid = 0;
374         int status;
375         int ret = WMESHD_ERROR_NONE;
376
377         ret = __get_pid_of_hostapd(&pid);
378         if (0 != pid) {
379                 WMESH_LOGE("hostapd is running already");
380                 return WMESHD_ERROR_NONE;
381         }
382
383         pid = fork();
384         if (pid < 0) {
385                 WMESH_LOGE("fork failed");
386                 return WMESHD_ERROR_IO_ERROR;
387         }
388
389         if (pid == 0) {
390                 if (execl(HOSTAPD_BIN, HOSTAPD_BIN, "-e", HOSTAPD_ENTROPY_FILE,
391                                         HOSTAPD_MESH_CONF_FILE,
392                                         "-f", HOSTAPD_DEBUG_FILE,
393                                         "-P", HOSTAPD_PID_FILE,
394                                         "-ddd", "-B",
395                                         (char *)NULL)) {
396                         WMESH_LOGE("execl failed");
397                 }
398
399                 WMESH_LOGE("Should not get here!");
400                 exit(1);
401         } else {
402                 /* Reap child */
403                 waitpid(pid, &status, 0);
404                 WMESH_LOGD("  child [%d] reaped with status(%d)", pid, status);
405         }
406
407         return WMESHD_ERROR_NONE;
408 }
409
410 int wmesh_softap_set_configuration(const char* softap_interface,
411                 const char *ssid, const char* mode, int channel, int visibility,
412                 int max_sta, int security, const char* passphrase)
413 {
414         int ret = WMESHD_ERROR_NONE;
415         const char *sec = (security == 0) ? NULL : "wpa2-psk";
416         int mac_filter = 0;
417
418         ret = __config_hostapd(softap_interface, ssid, sec, passphrase,
419                         mode, channel, visibility, mac_filter, max_sta);
420
421         if (ret == WMESHD_ERROR_NONE) {
422                 g_free(g_passphrase);
423                 g_passphrase = g_strdup(passphrase);
424         }
425
426         return ret;
427 }
428
429 int wmesh_softap_get_configuration(char **softap_interface, char **ssid,
430                         char **mode, int *channel, int *visibility,
431                 int *max_sta, int *security, char **passphrase)
432 {
433         int ret = WMESHD_ERROR_NONE;
434
435         ret = __read_hostapd_config(softap_interface, ssid, mode, channel,
436                                                         visibility, max_sta, security, passphrase);
437
438         return ret;
439 }
440
441 int wmesh_softap_enable_softap(const char* softap_interface)
442 {
443         int ret = WMESHD_ERROR_NONE;
444
445         if (NULL == softap_interface) {
446                 WMESH_LOGE("Invalid parameter");
447                 return WMESHD_ERROR_INVALID_PARAMETER;
448         }
449
450         ret = __execute_hostapd();
451         if (ret != WMESHD_ERROR_NONE) {
452                 WMESH_LOGE("__execute_hostapd is failed");
453                 return WMESHD_ERROR_OPERATION_FAILED;
454         }
455
456         ret = __open_hostapd_intf(softap_interface,
457                         &hostapd_ctrl_fd, MH_CTRL_INTF);
458         if (ret != WMESHD_ERROR_NONE) {
459                 WMESH_LOGE("__open_hostapd_intf is failed");
460                 __terminate_hostapd();
461         }
462
463         return ret;
464 }
465
466 int wmesh_softap_disable_softap()
467 {
468         int ret = WMESHD_ERROR_NONE;
469
470         ret = __terminate_hostapd();
471         if (ret != WMESHD_ERROR_NONE) {
472                 WMESH_LOGE("__execute_hostapd is failed");
473                 return WMESHD_ERROR_OPERATION_FAILED;
474         }
475
476         return ret;
477 }