1 /*
2 *
3 ***** BEGIN LICENSE BLOCK *****
4
5 Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
6
7 This file is part of CLIXON.
8
9 Licensed under the Apache License, Version 2.0 (the "License");
10 you may not use this file except in compliance with the License.
11 You may obtain a copy of the License at
12
13 http://www.apache.org/licenses/LICENSE-2.0
14
15 Unless required by applicable law or agreed to in writing, software
16 distributed under the License is distributed on an "AS IS" BASIS,
17 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 See the License for the specific language governing permissions and
19 limitations under the License.
20
21 Alternatively, the contents of this file may be used under the terms of
22 the GNU General Public License Version 3 or later (the "GPL"),
23 in which case the provisions of the GPL are applicable instead
24 of those above. If you wish to allow use of your version of this file only
25 under the terms of the GPL, and not to allow others to
26 use your version of this file under the terms of Apache License version 2,
27 indicate your decision by deleting the provisions above and replace them with
28 the notice and other provisions required by the GPL. If you do not delete
29 the provisions above, a recipient may use your version of this file under
30 the terms of any one of the Apache License version 2 or the GPL.
31
32 ***** END LICENSE BLOCK *****
33
34 */
35
36 #ifdef HAVE_CONFIG_H
37 #include "clixon_config.h" /* generated by config & autoconf */
38 #endif
39
40 #include <stdio.h>
41 #include <string.h>
42 #include <stdlib.h>
43 #include <unistd.h>
44 #include <stdarg.h>
45 #include <errno.h>
46 #include <signal.h>
47 #include <fcntl.h>
48 #include <time.h>
49 #include <syslog.h>
50 #include <grp.h>
51 #include <sys/time.h>
52 #include <sys/stat.h>
53 #include <sys/param.h>
54 #define __USE_GNU /* for ucred */
55 #define _GNU_SOURCE /* for ucred */
56 #include <sys/socket.h>
57 #ifdef HAVE_LOCAL_PEERCRED
58 #include <sys/ucred.h>
59 #endif
60 #include <sys/param.h>
61 #include <sys/types.h>
62
63 #include <sys/un.h>
64 #include <netinet/in.h>
65 #include <arpa/inet.h>
66
67 #include <cligen/cligen.h>
68
69 /* clicon */
70 #include <clixon/clixon.h>
71
72 #include "backend_socket.h"
73 #include "backend_client.h"
74 #include "backend_handle.h"
75
76 /*! Open an INET stream socket and bind it to a file descriptor
77 *
78 o * @param[in] h Clicon handle
79 * @param[in] dst IPv4 address (see inet_pton(3))
80 * @retval s Socket file descriptor (see socket(2))
81 * @retval -1 Error
82 */
83 static int
config_socket_init_ipv4(clicon_handle h,char * dst)84 config_socket_init_ipv4(clicon_handle h,
85 char *dst)
86 {
87 int s;
88 struct sockaddr_in addr;
89 uint16_t port;
90
91 port = clicon_sock_port(h);
92
93 /* create inet socket */
94 if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
95 clicon_err(OE_UNIX, errno, "socket");
96 return -1;
97 }
98 // setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&one, sizeof(one));
99 memset(&addr, 0, sizeof(addr));
100 addr.sin_family = AF_INET;
101 addr.sin_port = htons(port);
102 if (inet_pton(addr.sin_family, dst, &addr.sin_addr) != 1){
103 clicon_err(OE_UNIX, errno, "inet_pton: %s (Expected IPv4 address. Check settings of CLICON_SOCK_FAMILY and CLICON_SOCK)", dst);
104 goto err; /* Could check getaddrinfo */
105 }
106 if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0){
107 clicon_err(OE_UNIX, errno, "bind");
108 goto err;
109 }
110 clicon_debug(1, "Listen on server socket at %s:%hu", dst, port);
111 if (listen(s, 5) < 0){
112 clicon_err(OE_UNIX, errno, "listen");
113 goto err;
114 }
115 return s;
116 err:
117 close(s);
118 return -1;
119 }
120
121 /*! Open a UNIX domain socket and bind it to a file descriptor
122 *
123 * The socket is accessed via CLICON_SOCK option, has 770 permissions
124 * and group according to CLICON_SOCK_GROUP option.
125 * @param[in] h Clicon handle
126 * @param[in] sock Unix file-system path
127 * @retval s Socket file descriptor (see socket(2))
128 * @retval -1 Error
129 */
130 static int
config_socket_init_unix(clicon_handle h,char * sock)131 config_socket_init_unix(clicon_handle h,
132 char *sock)
133 {
134 int s;
135 struct sockaddr_un addr;
136 mode_t old_mask;
137 char *config_group;
138 gid_t gid;
139 struct stat st;
140
141 if (lstat(sock, &st) == 0 && unlink(sock) < 0){
142 clicon_err(OE_UNIX, errno, "unlink(%s)", sock);
143 return -1;
144 }
145 /* then find configuration group (for clients) and find its groupid */
146 if ((config_group = clicon_sock_group(h)) == NULL){
147 clicon_err(OE_FATAL, 0, "clicon_sock_group option not set");
148 return -1;
149 }
150 if (group_name2gid(config_group, &gid) < 0)
151 return -1;
152 #if 0
153 if (gid == 0)
154 clicon_log(LOG_WARNING, "%s: No such group: %s", __FUNCTION__, config_group);
155 #endif
156 /* create unix socket */
157 if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
158 clicon_err(OE_UNIX, errno, "socket");
159 return -1;
160 }
161 // setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&one, sizeof(one));
162 memset(&addr, 0, sizeof(addr));
163 addr.sun_family = AF_UNIX;
164 strncpy(addr.sun_path, sock, sizeof(addr.sun_path)-1);
165 old_mask = umask(S_IRWXO | S_IXGRP | S_IXUSR);
166 if (bind(s, (struct sockaddr *)&addr, SUN_LEN(&addr)) < 0){
167 clicon_err(OE_UNIX, errno, "bind");
168 umask(old_mask);
169 goto err;
170 }
171 umask(old_mask);
172 /* change socket path file group */
173 if (lchown(sock, -1, gid) < 0){
174 clicon_err(OE_UNIX, errno, "lchown(%s, %s)", sock, config_group);
175 goto err;
176 }
177 clicon_debug(1, "Listen on server socket at %s", addr.sun_path);
178 if (listen(s, 5) < 0){
179 clicon_err(OE_UNIX, errno, "listen");
180 goto err;
181 }
182 return s;
183 err:
184 close(s);
185 return -1;
186 }
187
188 /*! Open backend socket, the one clients send requests to, either ip or unix
189 *
190 * @param[in] h Clicon handle
191 * @retval s Socket file descriptor (see socket(2))
192 * @retval -1 Error
193 */
194 int
backend_socket_init(clicon_handle h)195 backend_socket_init(clicon_handle h)
196 {
197 char *sock; /* unix path or ip address string */
198
199 if ((sock = clicon_sock(h)) == NULL){
200 clicon_err(OE_FATAL, 0, "CLICON_SOCK option not set");
201 return -1;
202 }
203 switch (clicon_sock_family(h)){
204 case AF_UNIX:
205 return config_socket_init_unix(h, sock);
206 break;
207 case AF_INET:
208 return config_socket_init_ipv4(h, sock);
209 break;
210 default:
211 clicon_err(OE_UNIX, EINVAL, "No such address family: %d",
212 clicon_sock_family(h));
213 break;
214 }
215 return -1;
216 }
217
218 /*! Accept new socket client
219 * @param[in] fd Socket (unix or ip)
220 * @param[in] arg typecast clicon_handle
221 */
222 int
backend_accept_client(int fd,void * arg)223 backend_accept_client(int fd,
224 void *arg)
225 {
226 int retval = -1;
227 clicon_handle h = (clicon_handle)arg;
228 int s;
229 struct sockaddr from = {0,};
230 socklen_t len;
231 struct client_entry *ce;
232 char *name = NULL;
233 #ifdef HAVE_SO_PEERCRED /* Linux. */
234 socklen_t clen;
235 struct ucred cr = {0,};
236 #elif defined(HAVE_GETPEEREID) /* FreeBSD */
237 uid_t euid;
238 uid_t guid;
239 #endif
240
241 clicon_debug(2, "%s", __FUNCTION__);
242 len = sizeof(from);
243 if ((s = accept(fd, &from, &len)) < 0){
244 clicon_err(OE_UNIX, errno, "accept");
245 goto done;
246 }
247 if ((ce = backend_client_add(h, &from)) == NULL)
248 goto done;
249 ce->ce_handle = h;
250
251 /*
252 * Get credentials of connected peer - only for unix socket
253 */
254 switch (from.sa_family){
255 case AF_UNIX:
256 #if defined(HAVE_SO_PEERCRED)
257 clen = sizeof(cr);
258 if(getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &clen) < 0){
259 clicon_err(OE_UNIX, errno, "getsockopt");
260 goto done;
261 }
262 if (uid2name(cr.uid, &name) < 0)
263 goto done;
264 #elif defined(HAVE_GETPEEREID)
265 if (getpeereid(s, &euid, &guid) < 0)
266 goto done;
267 if (uid2name(euid, &name) < 0)
268 goto done;
269 #else
270 #error "Need getsockopt O_PEERCRED or getpeereid for unix socket peer cred"
271 #endif
272 if (name != NULL){
273 if ((ce->ce_username = name) == NULL){
274 clicon_err(OE_UNIX, errno, "strdup");
275 name = NULL;
276 goto done;
277 }
278 name = NULL;
279 }
280 break;
281 case AF_INET:
282 break;
283 case AF_INET6:
284 default:
285 break;
286 }
287 ce->ce_s = s;
288
289 /*
290 * Here we register callbacks for actual data socket
291 */
292 if (clixon_event_reg_fd(s, from_client, (void*)ce, "local netconf client socket") < 0)
293 goto done;
294 retval = 0;
295 done:
296 if (name)
297 free(name);
298 return retval;
299 }
300