xref: /freebsd/contrib/blocklist/lib/bl.c (revision 5f4c09dd85bff675e0ca63c55ea3c517e0fddfcc)
1*5f4c09ddSEd Maste /*	$NetBSD: bl.c,v 1.28 2016/07/29 17:13:09 christos Exp $	*/
2*5f4c09ddSEd Maste 
3*5f4c09ddSEd Maste /*-
4*5f4c09ddSEd Maste  * Copyright (c) 2014 The NetBSD Foundation, Inc.
5*5f4c09ddSEd Maste  * All rights reserved.
6*5f4c09ddSEd Maste  *
7*5f4c09ddSEd Maste  * This code is derived from software contributed to The NetBSD Foundation
8*5f4c09ddSEd Maste  * by Christos Zoulas.
9*5f4c09ddSEd Maste  *
10*5f4c09ddSEd Maste  * Redistribution and use in source and binary forms, with or without
11*5f4c09ddSEd Maste  * modification, are permitted provided that the following conditions
12*5f4c09ddSEd Maste  * are met:
13*5f4c09ddSEd Maste  * 1. Redistributions of source code must retain the above copyright
14*5f4c09ddSEd Maste  *    notice, this list of conditions and the following disclaimer.
15*5f4c09ddSEd Maste  * 2. Redistributions in binary form must reproduce the above copyright
16*5f4c09ddSEd Maste  *    notice, this list of conditions and the following disclaimer in the
17*5f4c09ddSEd Maste  *    documentation and/or other materials provided with the distribution.
18*5f4c09ddSEd Maste  *
19*5f4c09ddSEd Maste  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20*5f4c09ddSEd Maste  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21*5f4c09ddSEd Maste  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22*5f4c09ddSEd Maste  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23*5f4c09ddSEd Maste  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24*5f4c09ddSEd Maste  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25*5f4c09ddSEd Maste  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26*5f4c09ddSEd Maste  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27*5f4c09ddSEd Maste  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28*5f4c09ddSEd Maste  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29*5f4c09ddSEd Maste  * POSSIBILITY OF SUCH DAMAGE.
30*5f4c09ddSEd Maste  */
31*5f4c09ddSEd Maste #ifdef HAVE_CONFIG_H
32*5f4c09ddSEd Maste #include "config.h"
33*5f4c09ddSEd Maste #endif
34*5f4c09ddSEd Maste 
35*5f4c09ddSEd Maste #include <sys/cdefs.h>
36*5f4c09ddSEd Maste __RCSID("$NetBSD: bl.c,v 1.28 2016/07/29 17:13:09 christos Exp $");
37*5f4c09ddSEd Maste 
38*5f4c09ddSEd Maste #include <sys/param.h>
39*5f4c09ddSEd Maste #include <sys/types.h>
40*5f4c09ddSEd Maste #include <sys/socket.h>
41*5f4c09ddSEd Maste #include <sys/stat.h>
42*5f4c09ddSEd Maste #include <sys/un.h>
43*5f4c09ddSEd Maste 
44*5f4c09ddSEd Maste #include <stdio.h>
45*5f4c09ddSEd Maste #include <string.h>
46*5f4c09ddSEd Maste #include <syslog.h>
47*5f4c09ddSEd Maste #include <signal.h>
48*5f4c09ddSEd Maste #include <fcntl.h>
49*5f4c09ddSEd Maste #include <stdlib.h>
50*5f4c09ddSEd Maste #include <unistd.h>
51*5f4c09ddSEd Maste #include <stdint.h>
52*5f4c09ddSEd Maste #include <stdbool.h>
53*5f4c09ddSEd Maste #include <errno.h>
54*5f4c09ddSEd Maste #include <stdarg.h>
55*5f4c09ddSEd Maste #include <netinet/in.h>
56*5f4c09ddSEd Maste #ifdef _REENTRANT
57*5f4c09ddSEd Maste #include <pthread.h>
58*5f4c09ddSEd Maste #endif
59*5f4c09ddSEd Maste 
60*5f4c09ddSEd Maste #include "bl.h"
61*5f4c09ddSEd Maste 
62*5f4c09ddSEd Maste typedef struct {
63*5f4c09ddSEd Maste 	uint32_t bl_len;
64*5f4c09ddSEd Maste 	uint32_t bl_version;
65*5f4c09ddSEd Maste 	uint32_t bl_type;
66*5f4c09ddSEd Maste 	uint32_t bl_salen;
67*5f4c09ddSEd Maste 	struct sockaddr_storage bl_ss;
68*5f4c09ddSEd Maste 	char bl_data[];
69*5f4c09ddSEd Maste } bl_message_t;
70*5f4c09ddSEd Maste 
71*5f4c09ddSEd Maste struct blacklist {
72*5f4c09ddSEd Maste #ifdef _REENTRANT
73*5f4c09ddSEd Maste 	pthread_mutex_t b_mutex;
74*5f4c09ddSEd Maste # define BL_INIT(b)	pthread_mutex_init(&b->b_mutex, NULL)
75*5f4c09ddSEd Maste # define BL_LOCK(b)	pthread_mutex_lock(&b->b_mutex)
76*5f4c09ddSEd Maste # define BL_UNLOCK(b)	pthread_mutex_unlock(&b->b_mutex)
77*5f4c09ddSEd Maste #else
78*5f4c09ddSEd Maste # define BL_INIT(b)	do {} while(/*CONSTCOND*/0)
79*5f4c09ddSEd Maste # define BL_LOCK(b)	BL_INIT(b)
80*5f4c09ddSEd Maste # define BL_UNLOCK(b)	BL_INIT(b)
81*5f4c09ddSEd Maste #endif
82*5f4c09ddSEd Maste 	int b_fd;
83*5f4c09ddSEd Maste 	int b_connected;
84*5f4c09ddSEd Maste 	struct sockaddr_un b_sun;
85*5f4c09ddSEd Maste 	void (*b_fun)(int, const char *, va_list);
86*5f4c09ddSEd Maste 	bl_info_t b_info;
87*5f4c09ddSEd Maste };
88*5f4c09ddSEd Maste 
89*5f4c09ddSEd Maste #define BL_VERSION	1
90*5f4c09ddSEd Maste 
91*5f4c09ddSEd Maste bool
bl_isconnected(bl_t b)92*5f4c09ddSEd Maste bl_isconnected(bl_t b)
93*5f4c09ddSEd Maste {
94*5f4c09ddSEd Maste 	return b->b_connected == 0;
95*5f4c09ddSEd Maste }
96*5f4c09ddSEd Maste 
97*5f4c09ddSEd Maste int
bl_getfd(bl_t b)98*5f4c09ddSEd Maste bl_getfd(bl_t b)
99*5f4c09ddSEd Maste {
100*5f4c09ddSEd Maste 	return b->b_fd;
101*5f4c09ddSEd Maste }
102*5f4c09ddSEd Maste 
103*5f4c09ddSEd Maste static void
bl_reset(bl_t b,bool locked)104*5f4c09ddSEd Maste bl_reset(bl_t b, bool locked)
105*5f4c09ddSEd Maste {
106*5f4c09ddSEd Maste 	int serrno = errno;
107*5f4c09ddSEd Maste 	if (!locked)
108*5f4c09ddSEd Maste 		BL_LOCK(b);
109*5f4c09ddSEd Maste 	close(b->b_fd);
110*5f4c09ddSEd Maste 	errno = serrno;
111*5f4c09ddSEd Maste 	b->b_fd = -1;
112*5f4c09ddSEd Maste 	b->b_connected = -1;
113*5f4c09ddSEd Maste 	if (!locked)
114*5f4c09ddSEd Maste 		BL_UNLOCK(b);
115*5f4c09ddSEd Maste }
116*5f4c09ddSEd Maste 
117*5f4c09ddSEd Maste static void
bl_log(void (* fun)(int,const char *,va_list),int level,const char * fmt,...)118*5f4c09ddSEd Maste bl_log(void (*fun)(int, const char *, va_list), int level,
119*5f4c09ddSEd Maste     const char *fmt, ...)
120*5f4c09ddSEd Maste {
121*5f4c09ddSEd Maste 	va_list ap;
122*5f4c09ddSEd Maste 	int serrno = errno;
123*5f4c09ddSEd Maste 
124*5f4c09ddSEd Maste 	va_start(ap, fmt);
125*5f4c09ddSEd Maste 	(*fun)(level, fmt, ap);
126*5f4c09ddSEd Maste 	va_end(ap);
127*5f4c09ddSEd Maste 	errno = serrno;
128*5f4c09ddSEd Maste }
129*5f4c09ddSEd Maste 
130*5f4c09ddSEd Maste static int
bl_init(bl_t b,bool srv)131*5f4c09ddSEd Maste bl_init(bl_t b, bool srv)
132*5f4c09ddSEd Maste {
133*5f4c09ddSEd Maste 	static int one = 1;
134*5f4c09ddSEd Maste 	/* AF_UNIX address of local logger */
135*5f4c09ddSEd Maste 	mode_t om;
136*5f4c09ddSEd Maste 	int rv, serrno;
137*5f4c09ddSEd Maste 	struct sockaddr_un *sun = &b->b_sun;
138*5f4c09ddSEd Maste 
139*5f4c09ddSEd Maste #ifndef SOCK_NONBLOCK
140*5f4c09ddSEd Maste #define SOCK_NONBLOCK 0
141*5f4c09ddSEd Maste #endif
142*5f4c09ddSEd Maste #ifndef SOCK_CLOEXEC
143*5f4c09ddSEd Maste #define SOCK_CLOEXEC 0
144*5f4c09ddSEd Maste #endif
145*5f4c09ddSEd Maste #ifndef SOCK_NOSIGPIPE
146*5f4c09ddSEd Maste #define SOCK_NOSIGPIPE 0
147*5f4c09ddSEd Maste #endif
148*5f4c09ddSEd Maste 
149*5f4c09ddSEd Maste 	BL_LOCK(b);
150*5f4c09ddSEd Maste 
151*5f4c09ddSEd Maste 	if (b->b_fd == -1) {
152*5f4c09ddSEd Maste 		b->b_fd = socket(PF_LOCAL,
153*5f4c09ddSEd Maste 		    SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK|SOCK_NOSIGPIPE, 0);
154*5f4c09ddSEd Maste 		if (b->b_fd == -1) {
155*5f4c09ddSEd Maste 			bl_log(b->b_fun, LOG_ERR, "%s: socket failed (%s)",
156*5f4c09ddSEd Maste 			    __func__, strerror(errno));
157*5f4c09ddSEd Maste 			BL_UNLOCK(b);
158*5f4c09ddSEd Maste 			return -1;
159*5f4c09ddSEd Maste 		}
160*5f4c09ddSEd Maste #if SOCK_CLOEXEC == 0
161*5f4c09ddSEd Maste 		fcntl(b->b_fd, F_SETFD, FD_CLOEXEC);
162*5f4c09ddSEd Maste #endif
163*5f4c09ddSEd Maste #if SOCK_NONBLOCK == 0
164*5f4c09ddSEd Maste 		fcntl(b->b_fd, F_SETFL, fcntl(b->b_fd, F_GETFL) | O_NONBLOCK);
165*5f4c09ddSEd Maste #endif
166*5f4c09ddSEd Maste #if SOCK_NOSIGPIPE == 0
167*5f4c09ddSEd Maste #ifdef SO_NOSIGPIPE
168*5f4c09ddSEd Maste 		int o = 1;
169*5f4c09ddSEd Maste 		setsockopt(b->b_fd, SOL_SOCKET, SO_NOSIGPIPE, &o, sizeof(o));
170*5f4c09ddSEd Maste #else
171*5f4c09ddSEd Maste 		signal(SIGPIPE, SIG_IGN);
172*5f4c09ddSEd Maste #endif
173*5f4c09ddSEd Maste #endif
174*5f4c09ddSEd Maste 	}
175*5f4c09ddSEd Maste 
176*5f4c09ddSEd Maste 	if (bl_isconnected(b)) {
177*5f4c09ddSEd Maste 		BL_UNLOCK(b);
178*5f4c09ddSEd Maste 		return 0;
179*5f4c09ddSEd Maste 	}
180*5f4c09ddSEd Maste 
181*5f4c09ddSEd Maste 	/*
182*5f4c09ddSEd Maste 	 * We try to connect anyway even when we are a server to verify
183*5f4c09ddSEd Maste 	 * that no other server is listening to the socket. If we succeed
184*5f4c09ddSEd Maste 	 * to connect and we are a server, someone else owns it.
185*5f4c09ddSEd Maste 	 */
186*5f4c09ddSEd Maste 	rv = connect(b->b_fd, (const void *)sun, (socklen_t)sizeof(*sun));
187*5f4c09ddSEd Maste 	if (rv == 0) {
188*5f4c09ddSEd Maste 		if (srv) {
189*5f4c09ddSEd Maste 			bl_log(b->b_fun, LOG_ERR,
190*5f4c09ddSEd Maste 			    "%s: another daemon is handling `%s'",
191*5f4c09ddSEd Maste 			    __func__, sun->sun_path);
192*5f4c09ddSEd Maste 			goto out;
193*5f4c09ddSEd Maste 		}
194*5f4c09ddSEd Maste 	} else {
195*5f4c09ddSEd Maste 		if (!srv) {
196*5f4c09ddSEd Maste 			/*
197*5f4c09ddSEd Maste 			 * If the daemon is not running, we just try a
198*5f4c09ddSEd Maste 			 * connect, so leave the socket alone until it does
199*5f4c09ddSEd Maste 			 * and only log once.
200*5f4c09ddSEd Maste 			 */
201*5f4c09ddSEd Maste 			if (b->b_connected != 1) {
202*5f4c09ddSEd Maste 				bl_log(b->b_fun, LOG_DEBUG,
203*5f4c09ddSEd Maste 				    "%s: connect failed for `%s' (%s)",
204*5f4c09ddSEd Maste 				    __func__, sun->sun_path, strerror(errno));
205*5f4c09ddSEd Maste 				b->b_connected = 1;
206*5f4c09ddSEd Maste 			}
207*5f4c09ddSEd Maste 			BL_UNLOCK(b);
208*5f4c09ddSEd Maste 			return -1;
209*5f4c09ddSEd Maste 		}
210*5f4c09ddSEd Maste 		bl_log(b->b_fun, LOG_DEBUG, "Connected to blacklist server",
211*5f4c09ddSEd Maste 		    __func__);
212*5f4c09ddSEd Maste 	}
213*5f4c09ddSEd Maste 
214*5f4c09ddSEd Maste 	if (srv) {
215*5f4c09ddSEd Maste 		(void)unlink(sun->sun_path);
216*5f4c09ddSEd Maste 		om = umask(0);
217*5f4c09ddSEd Maste 		rv = bind(b->b_fd, (const void *)sun, (socklen_t)sizeof(*sun));
218*5f4c09ddSEd Maste 		serrno = errno;
219*5f4c09ddSEd Maste 		(void)umask(om);
220*5f4c09ddSEd Maste 		errno = serrno;
221*5f4c09ddSEd Maste 		if (rv == -1) {
222*5f4c09ddSEd Maste 			bl_log(b->b_fun, LOG_ERR,
223*5f4c09ddSEd Maste 			    "%s: bind failed for `%s' (%s)",
224*5f4c09ddSEd Maste 			    __func__, sun->sun_path, strerror(errno));
225*5f4c09ddSEd Maste 			goto out;
226*5f4c09ddSEd Maste 		}
227*5f4c09ddSEd Maste 	}
228*5f4c09ddSEd Maste 
229*5f4c09ddSEd Maste 	b->b_connected = 0;
230*5f4c09ddSEd Maste #define GOT_FD		1
231*5f4c09ddSEd Maste #if defined(LOCAL_CREDS)
232*5f4c09ddSEd Maste #define CRED_LEVEL	0
233*5f4c09ddSEd Maste #define	CRED_NAME	LOCAL_CREDS
234*5f4c09ddSEd Maste #define CRED_SC_UID	sc_euid
235*5f4c09ddSEd Maste #define CRED_SC_GID	sc_egid
236*5f4c09ddSEd Maste #define CRED_MESSAGE	SCM_CREDS
237*5f4c09ddSEd Maste #define CRED_SIZE	SOCKCREDSIZE(NGROUPS_MAX)
238*5f4c09ddSEd Maste #define CRED_TYPE	struct sockcred
239*5f4c09ddSEd Maste #define GOT_CRED	2
240*5f4c09ddSEd Maste #elif defined(SO_PASSCRED)
241*5f4c09ddSEd Maste #define CRED_LEVEL	SOL_SOCKET
242*5f4c09ddSEd Maste #define	CRED_NAME	SO_PASSCRED
243*5f4c09ddSEd Maste #define CRED_SC_UID	uid
244*5f4c09ddSEd Maste #define CRED_SC_GID	gid
245*5f4c09ddSEd Maste #define CRED_MESSAGE	SCM_CREDENTIALS
246*5f4c09ddSEd Maste #define CRED_SIZE	sizeof(struct ucred)
247*5f4c09ddSEd Maste #define CRED_TYPE	struct ucred
248*5f4c09ddSEd Maste #define GOT_CRED	2
249*5f4c09ddSEd Maste #else
250*5f4c09ddSEd Maste #define GOT_CRED	0
251*5f4c09ddSEd Maste /*
252*5f4c09ddSEd Maste  * getpeereid() and LOCAL_PEERCRED don't help here
253*5f4c09ddSEd Maste  * because we are not a stream socket!
254*5f4c09ddSEd Maste  */
255*5f4c09ddSEd Maste #define	CRED_SIZE	0
256*5f4c09ddSEd Maste #define CRED_TYPE	void * __unused
257*5f4c09ddSEd Maste #endif
258*5f4c09ddSEd Maste 
259*5f4c09ddSEd Maste #ifdef CRED_LEVEL
260*5f4c09ddSEd Maste 	if (setsockopt(b->b_fd, CRED_LEVEL, CRED_NAME,
261*5f4c09ddSEd Maste 	    &one, (socklen_t)sizeof(one)) == -1) {
262*5f4c09ddSEd Maste 		bl_log(b->b_fun, LOG_ERR, "%s: setsockopt %s "
263*5f4c09ddSEd Maste 		    "failed (%s)", __func__, __STRING(CRED_NAME),
264*5f4c09ddSEd Maste 		    strerror(errno));
265*5f4c09ddSEd Maste 		goto out;
266*5f4c09ddSEd Maste 	}
267*5f4c09ddSEd Maste #endif
268*5f4c09ddSEd Maste 
269*5f4c09ddSEd Maste 	BL_UNLOCK(b);
270*5f4c09ddSEd Maste 	return 0;
271*5f4c09ddSEd Maste out:
272*5f4c09ddSEd Maste 	bl_reset(b, true);
273*5f4c09ddSEd Maste 	BL_UNLOCK(b);
274*5f4c09ddSEd Maste 	return -1;
275*5f4c09ddSEd Maste }
276*5f4c09ddSEd Maste 
277*5f4c09ddSEd Maste bl_t
bl_create(bool srv,const char * path,void (* fun)(int,const char *,va_list))278*5f4c09ddSEd Maste bl_create(bool srv, const char *path, void (*fun)(int, const char *, va_list))
279*5f4c09ddSEd Maste {
280*5f4c09ddSEd Maste 	bl_t b = calloc(1, sizeof(*b));
281*5f4c09ddSEd Maste 	if (b == NULL)
282*5f4c09ddSEd Maste 		goto out;
283*5f4c09ddSEd Maste 	b->b_fun = fun == NULL ? vsyslog : fun;
284*5f4c09ddSEd Maste 	b->b_fd = -1;
285*5f4c09ddSEd Maste 	b->b_connected = -1;
286*5f4c09ddSEd Maste 	BL_INIT(b);
287*5f4c09ddSEd Maste 
288*5f4c09ddSEd Maste 	memset(&b->b_sun, 0, sizeof(b->b_sun));
289*5f4c09ddSEd Maste 	b->b_sun.sun_family = AF_LOCAL;
290*5f4c09ddSEd Maste #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
291*5f4c09ddSEd Maste 	b->b_sun.sun_len = sizeof(b->b_sun);
292*5f4c09ddSEd Maste #endif
293*5f4c09ddSEd Maste 	strlcpy(b->b_sun.sun_path,
294*5f4c09ddSEd Maste 	    path ? path : _PATH_BLSOCK, sizeof(b->b_sun.sun_path));
295*5f4c09ddSEd Maste 
296*5f4c09ddSEd Maste 	bl_init(b, srv);
297*5f4c09ddSEd Maste 	return b;
298*5f4c09ddSEd Maste out:
299*5f4c09ddSEd Maste 	free(b);
300*5f4c09ddSEd Maste 	bl_log(fun, LOG_ERR, "%s: malloc failed (%s)", __func__,
301*5f4c09ddSEd Maste 	    strerror(errno));
302*5f4c09ddSEd Maste 	return NULL;
303*5f4c09ddSEd Maste }
304*5f4c09ddSEd Maste 
305*5f4c09ddSEd Maste void
bl_destroy(bl_t b)306*5f4c09ddSEd Maste bl_destroy(bl_t b)
307*5f4c09ddSEd Maste {
308*5f4c09ddSEd Maste 	bl_reset(b, false);
309*5f4c09ddSEd Maste 	free(b);
310*5f4c09ddSEd Maste }
311*5f4c09ddSEd Maste 
312*5f4c09ddSEd Maste static int
bl_getsock(bl_t b,struct sockaddr_storage * ss,const struct sockaddr * sa,socklen_t slen,const char * ctx)313*5f4c09ddSEd Maste bl_getsock(bl_t b, struct sockaddr_storage *ss, const struct sockaddr *sa,
314*5f4c09ddSEd Maste     socklen_t slen, const char *ctx)
315*5f4c09ddSEd Maste {
316*5f4c09ddSEd Maste 	uint8_t family;
317*5f4c09ddSEd Maste 
318*5f4c09ddSEd Maste 	memset(ss, 0, sizeof(*ss));
319*5f4c09ddSEd Maste 
320*5f4c09ddSEd Maste 	switch (slen) {
321*5f4c09ddSEd Maste 	case 0:
322*5f4c09ddSEd Maste 		return 0;
323*5f4c09ddSEd Maste 	case sizeof(struct sockaddr_in):
324*5f4c09ddSEd Maste 		family = AF_INET;
325*5f4c09ddSEd Maste 		break;
326*5f4c09ddSEd Maste 	case sizeof(struct sockaddr_in6):
327*5f4c09ddSEd Maste 		family = AF_INET6;
328*5f4c09ddSEd Maste 		break;
329*5f4c09ddSEd Maste 	default:
330*5f4c09ddSEd Maste 		bl_log(b->b_fun, LOG_ERR, "%s: invalid socket len %u (%s)",
331*5f4c09ddSEd Maste 		    __func__, (unsigned)slen, ctx);
332*5f4c09ddSEd Maste 		errno = EINVAL;
333*5f4c09ddSEd Maste 		return -1;
334*5f4c09ddSEd Maste 	}
335*5f4c09ddSEd Maste 
336*5f4c09ddSEd Maste 	memcpy(ss, sa, slen);
337*5f4c09ddSEd Maste 
338*5f4c09ddSEd Maste 	if (ss->ss_family != family) {
339*5f4c09ddSEd Maste 		bl_log(b->b_fun, LOG_INFO,
340*5f4c09ddSEd Maste 		    "%s: correcting socket family %d to %d (%s)",
341*5f4c09ddSEd Maste 		    __func__, ss->ss_family, family, ctx);
342*5f4c09ddSEd Maste 		ss->ss_family = family;
343*5f4c09ddSEd Maste 	}
344*5f4c09ddSEd Maste 
345*5f4c09ddSEd Maste #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
346*5f4c09ddSEd Maste 	if (ss->ss_len != slen) {
347*5f4c09ddSEd Maste 		bl_log(b->b_fun, LOG_INFO,
348*5f4c09ddSEd Maste 		    "%s: correcting socket len %u to %u (%s)",
349*5f4c09ddSEd Maste 		    __func__, ss->ss_len, (unsigned)slen, ctx);
350*5f4c09ddSEd Maste 		ss->ss_len = (uint8_t)slen;
351*5f4c09ddSEd Maste 	}
352*5f4c09ddSEd Maste #endif
353*5f4c09ddSEd Maste 	return 0;
354*5f4c09ddSEd Maste }
355*5f4c09ddSEd Maste 
356*5f4c09ddSEd Maste int
bl_send(bl_t b,bl_type_t e,int pfd,const struct sockaddr * sa,socklen_t slen,const char * ctx)357*5f4c09ddSEd Maste bl_send(bl_t b, bl_type_t e, int pfd, const struct sockaddr *sa,
358*5f4c09ddSEd Maste     socklen_t slen, const char *ctx)
359*5f4c09ddSEd Maste {
360*5f4c09ddSEd Maste 	struct msghdr   msg;
361*5f4c09ddSEd Maste 	struct iovec    iov;
362*5f4c09ddSEd Maste 	union {
363*5f4c09ddSEd Maste 		char ctrl[CMSG_SPACE(sizeof(int))];
364*5f4c09ddSEd Maste 		uint32_t fd;
365*5f4c09ddSEd Maste 	} ua;
366*5f4c09ddSEd Maste 	struct cmsghdr *cmsg;
367*5f4c09ddSEd Maste 	union {
368*5f4c09ddSEd Maste 		bl_message_t bl;
369*5f4c09ddSEd Maste 		char buf[512];
370*5f4c09ddSEd Maste 	} ub;
371*5f4c09ddSEd Maste 	size_t ctxlen, tried;
372*5f4c09ddSEd Maste #define NTRIES	5
373*5f4c09ddSEd Maste 
374*5f4c09ddSEd Maste 	ctxlen = strlen(ctx);
375*5f4c09ddSEd Maste 	if (ctxlen > 128)
376*5f4c09ddSEd Maste 		ctxlen = 128;
377*5f4c09ddSEd Maste 
378*5f4c09ddSEd Maste 	iov.iov_base = ub.buf;
379*5f4c09ddSEd Maste 	iov.iov_len = sizeof(bl_message_t) + ctxlen;
380*5f4c09ddSEd Maste 	ub.bl.bl_len = (uint32_t)iov.iov_len;
381*5f4c09ddSEd Maste 	ub.bl.bl_version = BL_VERSION;
382*5f4c09ddSEd Maste 	ub.bl.bl_type = (uint32_t)e;
383*5f4c09ddSEd Maste 
384*5f4c09ddSEd Maste 	if (bl_getsock(b, &ub.bl.bl_ss, sa, slen, ctx) == -1)
385*5f4c09ddSEd Maste 		return -1;
386*5f4c09ddSEd Maste 
387*5f4c09ddSEd Maste 
388*5f4c09ddSEd Maste 	ub.bl.bl_salen = slen;
389*5f4c09ddSEd Maste 	memcpy(ub.bl.bl_data, ctx, ctxlen);
390*5f4c09ddSEd Maste 
391*5f4c09ddSEd Maste 	msg.msg_name = NULL;
392*5f4c09ddSEd Maste 	msg.msg_namelen = 0;
393*5f4c09ddSEd Maste 	msg.msg_iov = &iov;
394*5f4c09ddSEd Maste 	msg.msg_iovlen = 1;
395*5f4c09ddSEd Maste 	msg.msg_flags = 0;
396*5f4c09ddSEd Maste 
397*5f4c09ddSEd Maste 	msg.msg_control = ua.ctrl;
398*5f4c09ddSEd Maste 	msg.msg_controllen = sizeof(ua.ctrl);
399*5f4c09ddSEd Maste 
400*5f4c09ddSEd Maste 	cmsg = CMSG_FIRSTHDR(&msg);
401*5f4c09ddSEd Maste 	cmsg->cmsg_len = CMSG_LEN(sizeof(int));
402*5f4c09ddSEd Maste 	cmsg->cmsg_level = SOL_SOCKET;
403*5f4c09ddSEd Maste 	cmsg->cmsg_type = SCM_RIGHTS;
404*5f4c09ddSEd Maste 
405*5f4c09ddSEd Maste 	memcpy(CMSG_DATA(cmsg), &pfd, sizeof(pfd));
406*5f4c09ddSEd Maste 
407*5f4c09ddSEd Maste 	tried = 0;
408*5f4c09ddSEd Maste again:
409*5f4c09ddSEd Maste 	if (bl_init(b, false) == -1)
410*5f4c09ddSEd Maste 		return -1;
411*5f4c09ddSEd Maste 
412*5f4c09ddSEd Maste 	if ((sendmsg(b->b_fd, &msg, 0) == -1) && tried++ < NTRIES) {
413*5f4c09ddSEd Maste 		bl_reset(b, false);
414*5f4c09ddSEd Maste 		goto again;
415*5f4c09ddSEd Maste 	}
416*5f4c09ddSEd Maste 	return tried >= NTRIES ? -1 : 0;
417*5f4c09ddSEd Maste }
418*5f4c09ddSEd Maste 
419*5f4c09ddSEd Maste bl_info_t *
bl_recv(bl_t b)420*5f4c09ddSEd Maste bl_recv(bl_t b)
421*5f4c09ddSEd Maste {
422*5f4c09ddSEd Maste         struct msghdr   msg;
423*5f4c09ddSEd Maste         struct iovec    iov;
424*5f4c09ddSEd Maste 	union {
425*5f4c09ddSEd Maste 		char ctrl[CMSG_SPACE(sizeof(int)) + CMSG_SPACE(CRED_SIZE)];
426*5f4c09ddSEd Maste 		uint32_t fd;
427*5f4c09ddSEd Maste 		CRED_TYPE sc;
428*5f4c09ddSEd Maste 	} ua;
429*5f4c09ddSEd Maste 	struct cmsghdr *cmsg;
430*5f4c09ddSEd Maste 	CRED_TYPE *sc;
431*5f4c09ddSEd Maste 	union {
432*5f4c09ddSEd Maste 		bl_message_t bl;
433*5f4c09ddSEd Maste 		char buf[512];
434*5f4c09ddSEd Maste 	} ub;
435*5f4c09ddSEd Maste 	int got;
436*5f4c09ddSEd Maste 	ssize_t rlen;
437*5f4c09ddSEd Maste 	size_t rem;
438*5f4c09ddSEd Maste 	bl_info_t *bi = &b->b_info;
439*5f4c09ddSEd Maste 
440*5f4c09ddSEd Maste 	got = 0;
441*5f4c09ddSEd Maste 	memset(bi, 0, sizeof(*bi));
442*5f4c09ddSEd Maste 
443*5f4c09ddSEd Maste 	iov.iov_base = ub.buf;
444*5f4c09ddSEd Maste 	iov.iov_len = sizeof(ub);
445*5f4c09ddSEd Maste 
446*5f4c09ddSEd Maste 	msg.msg_name = NULL;
447*5f4c09ddSEd Maste 	msg.msg_namelen = 0;
448*5f4c09ddSEd Maste 	msg.msg_iov = &iov;
449*5f4c09ddSEd Maste 	msg.msg_iovlen = 1;
450*5f4c09ddSEd Maste 	msg.msg_flags = 0;
451*5f4c09ddSEd Maste 
452*5f4c09ddSEd Maste 	msg.msg_control = ua.ctrl;
453*5f4c09ddSEd Maste 	msg.msg_controllen = sizeof(ua.ctrl) + 100;
454*5f4c09ddSEd Maste 
455*5f4c09ddSEd Maste         rlen = recvmsg(b->b_fd, &msg, 0);
456*5f4c09ddSEd Maste         if (rlen == -1) {
457*5f4c09ddSEd Maste 		bl_log(b->b_fun, LOG_ERR, "%s: recvmsg failed (%s)", __func__,
458*5f4c09ddSEd Maste 		    strerror(errno));
459*5f4c09ddSEd Maste 		return NULL;
460*5f4c09ddSEd Maste         }
461*5f4c09ddSEd Maste 
462*5f4c09ddSEd Maste 	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
463*5f4c09ddSEd Maste 		if (cmsg->cmsg_level != SOL_SOCKET) {
464*5f4c09ddSEd Maste 			bl_log(b->b_fun, LOG_ERR,
465*5f4c09ddSEd Maste 			    "%s: unexpected cmsg_level %d",
466*5f4c09ddSEd Maste 			    __func__, cmsg->cmsg_level);
467*5f4c09ddSEd Maste 			continue;
468*5f4c09ddSEd Maste 		}
469*5f4c09ddSEd Maste 		switch (cmsg->cmsg_type) {
470*5f4c09ddSEd Maste 		case SCM_RIGHTS:
471*5f4c09ddSEd Maste 			if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
472*5f4c09ddSEd Maste 				bl_log(b->b_fun, LOG_ERR,
473*5f4c09ddSEd Maste 				    "%s: unexpected cmsg_len %d != %zu",
474*5f4c09ddSEd Maste 				    __func__, cmsg->cmsg_len,
475*5f4c09ddSEd Maste 				    CMSG_LEN(2 * sizeof(int)));
476*5f4c09ddSEd Maste 				continue;
477*5f4c09ddSEd Maste 			}
478*5f4c09ddSEd Maste 			memcpy(&bi->bi_fd, CMSG_DATA(cmsg), sizeof(bi->bi_fd));
479*5f4c09ddSEd Maste 			got |= GOT_FD;
480*5f4c09ddSEd Maste 			break;
481*5f4c09ddSEd Maste #ifdef CRED_MESSAGE
482*5f4c09ddSEd Maste 		case CRED_MESSAGE:
483*5f4c09ddSEd Maste 			sc = (void *)CMSG_DATA(cmsg);
484*5f4c09ddSEd Maste 			bi->bi_uid = sc->CRED_SC_UID;
485*5f4c09ddSEd Maste 			bi->bi_gid = sc->CRED_SC_GID;
486*5f4c09ddSEd Maste 			got |= GOT_CRED;
487*5f4c09ddSEd Maste 			break;
488*5f4c09ddSEd Maste #endif
489*5f4c09ddSEd Maste 		default:
490*5f4c09ddSEd Maste 			bl_log(b->b_fun, LOG_ERR,
491*5f4c09ddSEd Maste 			    "%s: unexpected cmsg_type %d",
492*5f4c09ddSEd Maste 			    __func__, cmsg->cmsg_type);
493*5f4c09ddSEd Maste 			continue;
494*5f4c09ddSEd Maste 		}
495*5f4c09ddSEd Maste 
496*5f4c09ddSEd Maste 	}
497*5f4c09ddSEd Maste 
498*5f4c09ddSEd Maste 	if (got != (GOT_CRED|GOT_FD)) {
499*5f4c09ddSEd Maste 		bl_log(b->b_fun, LOG_ERR, "message missing %s %s",
500*5f4c09ddSEd Maste #if GOT_CRED != 0
501*5f4c09ddSEd Maste 		    (got & GOT_CRED) == 0 ? "cred" :
502*5f4c09ddSEd Maste #endif
503*5f4c09ddSEd Maste 		    "", (got & GOT_FD) == 0 ? "fd" : "");
504*5f4c09ddSEd Maste 		return NULL;
505*5f4c09ddSEd Maste 	}
506*5f4c09ddSEd Maste 
507*5f4c09ddSEd Maste 	rem = (size_t)rlen;
508*5f4c09ddSEd Maste 	if (rem < sizeof(ub.bl)) {
509*5f4c09ddSEd Maste 		bl_log(b->b_fun, LOG_ERR, "message too short %zd", rlen);
510*5f4c09ddSEd Maste 		return NULL;
511*5f4c09ddSEd Maste 	}
512*5f4c09ddSEd Maste 	rem -= sizeof(ub.bl);
513*5f4c09ddSEd Maste 
514*5f4c09ddSEd Maste 	if (ub.bl.bl_version != BL_VERSION) {
515*5f4c09ddSEd Maste 		bl_log(b->b_fun, LOG_ERR, "bad version %d", ub.bl.bl_version);
516*5f4c09ddSEd Maste 		return NULL;
517*5f4c09ddSEd Maste 	}
518*5f4c09ddSEd Maste 
519*5f4c09ddSEd Maste 	bi->bi_type = ub.bl.bl_type;
520*5f4c09ddSEd Maste 	bi->bi_slen = ub.bl.bl_salen;
521*5f4c09ddSEd Maste 	bi->bi_ss = ub.bl.bl_ss;
522*5f4c09ddSEd Maste #ifndef CRED_MESSAGE
523*5f4c09ddSEd Maste 	bi->bi_uid = -1;
524*5f4c09ddSEd Maste 	bi->bi_gid = -1;
525*5f4c09ddSEd Maste #endif
526*5f4c09ddSEd Maste 	rem = MIN(sizeof(bi->bi_msg), rem);
527*5f4c09ddSEd Maste 	if (rem == 0)
528*5f4c09ddSEd Maste 		bi->bi_msg[0] = '\0';
529*5f4c09ddSEd Maste 	else
530*5f4c09ddSEd Maste 		strlcpy(bi->bi_msg, ub.bl.bl_data, rem);
531*5f4c09ddSEd Maste 	return bi;
532*5f4c09ddSEd Maste }
533