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