1 /*
2  * Copyright (c) 2019 The Linux Foundation. All rights reserved.
3  *
4  * wpa_supplicant/hostapd control interface library
5  * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
6  *
7  * This software may be distributed under the terms of the BSD license.
8  * redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are
10  * met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in the
17  * documentation and/or other materials provided with the distribution.
18  *
19  * 3. Neither the name(s) of the above-listed copyright holder(s) nor the
20  * names of its contributors may be used to endorse or promote products
21  * derived from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 #include "wifi_hal_ctrl.h"
37 
wifihal_ctrl_open2(const char * ctrl_path,const char * cli_path)38 struct wifihal_ctrl * wifihal_ctrl_open2(const char *ctrl_path,
39                                          const char *cli_path)
40 {
41     struct wifihal_ctrl *ctrl;
42     static int counter = 0;
43     int retval;
44     size_t res;
45     int tries = 0;
46     int flags;
47 #ifdef ANDROID
48     struct group *grp_wifi;
49     gid_t gid_wifi;
50     struct passwd *pwd_system;
51     uid_t uid_system;
52 #endif
53 
54     if (ctrl_path == NULL)
55         return NULL;
56 
57     ctrl = malloc(sizeof(*ctrl));
58     if (ctrl == NULL) {
59         return NULL;
60     }
61 
62     memset(ctrl, 0, sizeof(*ctrl));
63     ctrl->s = socket(PF_UNIX, SOCK_DGRAM, 0);
64     if (ctrl->s < 0) {
65          free(ctrl);
66          return NULL;
67     }
68 
69     ctrl->local.sun_family = AF_UNIX;
70 
71 try_again:
72     if (cli_path && cli_path[0] == '/') {
73        res = strlcpy(ctrl->local.sun_path, cli_path,
74                      sizeof(ctrl->local.sun_path));
75 
76        if (res >= sizeof(ctrl->local.sun_path)) {
77              close(ctrl->s);
78              free(ctrl);
79              return NULL;
80          }
81 
82     } else {
83        counter++;
84        retval = snprintf(ctrl->local.sun_path,
85                       sizeof(ctrl->local.sun_path),
86                       CONFIG_CTRL_IFACE_CLIENT_DIR "/"
87                       CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d",
88                       (int) getpid(), counter);
89     }
90     tries++;
91     if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
92              sizeof(ctrl->local)) < 0) {
93        if (errno == EADDRINUSE && tries < 2) {
94            /*
95             * getpid() returns unique identifier for this instance
96             * of wifihal_ctrl, so the existing socket file must have
97             * been left by unclean termination of an earlier run.
98             * Remove the file and try again.
99             */
100            unlink(ctrl->local.sun_path);
101            goto try_again;
102         }
103        close(ctrl->s);
104        free(ctrl);
105        return NULL;
106     }
107 
108 #ifdef ANDROID
109     chmod(ctrl->local.sun_path, S_IRWXU | S_IRWXG );
110 
111     /* Set group even if we do not have privileges to change owner */
112     grp_wifi = getgrnam("wifi");
113     gid_wifi = grp_wifi ? grp_wifi->gr_gid : 0;
114     pwd_system = getpwnam("system");
115     uid_system = pwd_system ? pwd_system->pw_uid : 0;
116     if (!gid_wifi || !uid_system) {
117         close(ctrl->s);
118         unlink(ctrl->local.sun_path);
119         free(ctrl);
120         return NULL;
121     }
122     chown(ctrl->local.sun_path, -1, gid_wifi);
123     chown(ctrl->local.sun_path, uid_system, gid_wifi);
124 
125 
126     if (*ctrl_path != '/') {
127             free(ctrl);
128             return NULL;
129            }
130 #endif /* ANDROID */
131 
132        ctrl->dest.sun_family = AF_UNIX;
133        res = strlcpy(ctrl->dest.sun_path, ctrl_path,
134                      sizeof(ctrl->dest.sun_path));
135        if (res >= sizeof(ctrl->dest.sun_path)) {
136            close(ctrl->s);
137            free(ctrl);
138            return NULL;
139        }
140        if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest,
141             sizeof(ctrl->dest)) < 0) {
142         close(ctrl->s);
143         unlink(ctrl->local.sun_path);
144         free(ctrl);
145         return NULL;
146        }
147        /*
148         * Make socket non-blocking so that we don't hang forever if
149         * target dies unexpectedly.
150         */
151        flags = fcntl(ctrl->s, F_GETFL);
152        if (flags >= 0) {
153         flags |= O_NONBLOCK;
154         if (fcntl(ctrl->s, F_SETFL, flags) < 0) {
155              perror("fcntl(ctrl->s, O_NONBLOCK)");
156              /* Not fatal, continue on.*/
157         }
158        }
159        return ctrl;
160 }
161 
wifihal_ctrl_open(const char * ctrl_path)162 struct wifihal_ctrl * wifihal_ctrl_open(const char *ctrl_path)
163 {
164     return wifihal_ctrl_open2(ctrl_path, NULL);
165 }
166 
wifihal_ctrl_close(struct wifihal_ctrl * ctrl)167 void wifihal_ctrl_close(struct wifihal_ctrl *ctrl)
168 {
169     if (ctrl == NULL)
170         return;
171     unlink(ctrl->local.sun_path);
172     if (ctrl->s >= 0)
173         close(ctrl->s);
174     free(ctrl);
175 }
176 
wifihal_ctrl_request(struct wifihal_ctrl * ctrl,const char * cmd,size_t cmd_len,char * reply,size_t * reply_len)177 int wifihal_ctrl_request(struct wifihal_ctrl *ctrl, const char *cmd, size_t cmd_len,
178                          char *reply, size_t *reply_len)
179 {
180     struct timeval tv;
181     int counter = 0, res;
182     fd_set rfds;
183     const char *_cmd;
184     size_t _cmd_len;
185     char *cmd_buf = NULL;
186 
187     _cmd = cmd;
188     _cmd_len = cmd_len;
189 
190     errno = 0;
191 retry_send:
192     if (sendto(ctrl->s, _cmd, _cmd_len, 0, (struct sockaddr *)&ctrl->dest, sizeof(ctrl->dest)) < 0) {
193         if (errno == EAGAIN || errno == EBUSY || errno == EWOULDBLOCK)
194         {
195           /*
196            * Must be a non-blocking socket... Try for a bit
197            * longer before giving up.
198            */
199               if(counter == 5) {
200                  goto send_err;
201                 } else {
202                  counter++;
203                 }
204                 sleep(1);
205                 goto retry_send;
206          }
207          send_err:
208          free(cmd_buf);
209          return -1;
210      }
211      free(cmd_buf);
212 
213      for (;;) {
214         tv.tv_sec = 10;
215         tv.tv_usec = 0;
216         FD_ZERO(&rfds);
217         FD_SET(ctrl->s, &rfds);
218         res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv);
219         if (res < 0 && errno == EINTR)
220             continue;
221         if (res < 0)
222             return res;
223         if (FD_ISSET(ctrl->s, &rfds)) {
224                 res = recv(ctrl->s, reply, *reply_len, 0);
225                 if (res < 0)
226                     return res;
227                 *reply_len = res;
228                 break;
229         } else {
230            return -2;
231         }
232      }
233      return 0;
234 }
235