etc/services - sync with NetBSD-8
[minix.git] / external / bsd / blacklist / lib / bl.c
blobb762460703c3dd8f2d1096c922beb45dc2b30d1a
1 /* $NetBSD: bl.c,v 1.26 2015/05/28 01:01:37 christos Exp $ */
3 /*-
4 * Copyright (c) 2014 The NetBSD Foundation, Inc.
5 * All rights reserved.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Christos Zoulas.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
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.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
35 #include <sys/cdefs.h>
36 __RCSID("$NetBSD: bl.c,v 1.26 2015/05/28 01:01:37 christos Exp $");
38 #include <sys/param.h>
39 #include <sys/types.h>
40 #include <sys/socket.h>
41 #include <sys/stat.h>
42 #include <sys/un.h>
44 #include <stdio.h>
45 #include <string.h>
46 #include <syslog.h>
47 #include <signal.h>
48 #include <fcntl.h>
49 #include <stdlib.h>
50 #include <unistd.h>
51 #include <stdint.h>
52 #include <stdbool.h>
53 #include <errno.h>
54 #include <stdarg.h>
55 #include <netinet/in.h>
57 #include "bl.h"
59 typedef struct {
60 uint32_t bl_len;
61 uint32_t bl_version;
62 uint32_t bl_type;
63 uint32_t bl_salen;
64 struct sockaddr_storage bl_ss;
65 char bl_data[];
66 } bl_message_t;
68 struct blacklist {
69 int b_fd;
70 int b_connected;
71 struct sockaddr_un b_sun;
72 void (*b_fun)(int, const char *, va_list);
73 bl_info_t b_info;
76 #define BL_VERSION 1
78 bool
79 bl_isconnected(bl_t b)
81 return b->b_connected == 0;
84 int
85 bl_getfd(bl_t b)
87 return b->b_fd;
90 static void
91 bl_reset(bl_t b)
93 int serrno = errno;
94 close(b->b_fd);
95 errno = serrno;
96 b->b_fd = -1;
97 b->b_connected = -1;
100 static void
101 bl_log(void (*fun)(int, const char *, va_list), int level,
102 const char *fmt, ...)
104 va_list ap;
105 int serrno = errno;
107 va_start(ap, fmt);
108 (*fun)(level, fmt, ap);
109 va_end(ap);
110 errno = serrno;
113 static int
114 bl_init(bl_t b, bool srv)
116 static int one = 1;
117 /* AF_UNIX address of local logger */
118 mode_t om;
119 int rv, serrno;
120 struct sockaddr_un *sun = &b->b_sun;
122 #ifndef SOCK_NONBLOCK
123 #define SOCK_NONBLOCK 0
124 #endif
125 #ifndef SOCK_CLOEXEC
126 #define SOCK_CLOEXEC 0
127 #endif
128 #ifndef SOCK_NOSIGPIPE
129 #define SOCK_NOSIGPIPE 0
130 #endif
132 if (b->b_fd == -1) {
133 b->b_fd = socket(PF_LOCAL,
134 SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK|SOCK_NOSIGPIPE, 0);
135 if (b->b_fd == -1) {
136 bl_log(b->b_fun, LOG_ERR, "%s: socket failed (%m)",
137 __func__);
138 return -1;
140 #if SOCK_CLOEXEC == 0
141 fcntl(b->b_fd, F_SETFD, FD_CLOEXEC);
142 #endif
143 #if SOCK_NONBLOCK == 0
144 fcntl(b->b_fd, F_SETFL, fcntl(b->b_fd, F_GETFL) | O_NONBLOCK);
145 #endif
146 #if SOCK_NOSIGPIPE == 0
147 #ifdef SO_NOSIGPIPE
148 int o = 1;
149 setsockopt(b->b_fd, SOL_SOCKET, SO_NOSIGPIPE, &o, sizeof(o));
150 #else
151 signal(SIGPIPE, SIG_IGN);
152 #endif
153 #endif
156 if (bl_isconnected(b))
157 return 0;
159 rv = connect(b->b_fd, (const void *)sun, (socklen_t)sizeof(*sun));
160 if (rv == 0) {
161 if (srv) {
162 bl_log(b->b_fun, LOG_ERR,
163 "%s: another daemon is handling `%s'",
164 __func__, sun->sun_path);
165 goto out;
167 } else {
168 if (!srv) {
170 * If the daemon is not running, we just try a
171 * connect, so leave the socket alone until it does
172 * and only log once.
174 if (b->b_connected != 1) {
175 bl_log(b->b_fun, LOG_DEBUG,
176 "%s: connect failed for `%s' (%m)",
177 __func__, sun->sun_path);
178 b->b_connected = 1;
180 return -1;
182 bl_log(b->b_fun, LOG_DEBUG, "Connected to blacklist server",
183 __func__);
186 if (srv) {
187 (void)unlink(sun->sun_path);
188 om = umask(0);
189 rv = bind(b->b_fd, (const void *)sun, (socklen_t)sizeof(*sun));
190 serrno = errno;
191 (void)umask(om);
192 errno = serrno;
193 if (rv == -1) {
194 bl_log(b->b_fun, LOG_ERR,
195 "%s: bind failed for `%s' (%m)",
196 __func__, sun->sun_path);
197 goto out;
201 b->b_connected = 0;
202 #define GOT_FD 1
203 #if defined(LOCAL_CREDS)
204 #define CRED_LEVEL 0
205 #define CRED_NAME LOCAL_CREDS
206 #define CRED_SC_UID sc_euid
207 #define CRED_SC_GID sc_egid
208 #define CRED_MESSAGE SCM_CREDS
209 #define CRED_SIZE SOCKCREDSIZE(NGROUPS_MAX)
210 #define CRED_TYPE struct sockcred
211 #define GOT_CRED 2
212 #elif defined(SO_PASSCRED)
213 #define CRED_LEVEL SOL_SOCKET
214 #define CRED_NAME SO_PASSCRED
215 #define CRED_SC_UID uid
216 #define CRED_SC_GID gid
217 #define CRED_MESSAGE SCM_CREDENTIALS
218 #define CRED_SIZE sizeof(struct ucred)
219 #define CRED_TYPE struct ucred
220 #define GOT_CRED 2
221 #else
222 #define GOT_CRED 0
224 * getpeereid() and LOCAL_PEERCRED don't help here
225 * because we are not a stream socket!
227 #define CRED_SIZE 0
228 #define CRED_TYPE void * __unused
229 #endif
231 #ifdef CRED_LEVEL
232 if (setsockopt(b->b_fd, CRED_LEVEL, CRED_NAME,
233 &one, (socklen_t)sizeof(one)) == -1) {
234 bl_log(b->b_fun, LOG_ERR, "%s: setsockopt %s "
235 "failed (%m)", __func__, __STRING(CRED_NAME));
236 goto out;
238 #endif
240 return 0;
241 out:
242 bl_reset(b);
243 return -1;
246 bl_t
247 bl_create(bool srv, const char *path, void (*fun)(int, const char *, va_list))
249 bl_t b = calloc(1, sizeof(*b));
250 if (b == NULL)
251 goto out;
252 b->b_fun = fun == NULL ? vsyslog : fun;
253 b->b_fd = -1;
254 b->b_connected = -1;
256 memset(&b->b_sun, 0, sizeof(b->b_sun));
257 b->b_sun.sun_family = AF_LOCAL;
258 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
259 b->b_sun.sun_len = sizeof(b->b_sun);
260 #endif
261 strlcpy(b->b_sun.sun_path,
262 path ? path : _PATH_BLSOCK, sizeof(b->b_sun.sun_path));
264 bl_init(b, srv);
265 return b;
266 out:
267 free(b);
268 bl_log(fun, LOG_ERR, "%s: malloc failed (%m)", __func__);
269 return NULL;
272 void
273 bl_destroy(bl_t b)
275 bl_reset(b);
276 free(b);
279 static int
280 bl_getsock(bl_t b, struct sockaddr_storage *ss, const struct sockaddr *sa,
281 socklen_t slen, const char *ctx)
283 uint8_t family;
285 memset(ss, 0, sizeof(*ss));
287 switch (slen) {
288 case 0:
289 return 0;
290 case sizeof(struct sockaddr_in):
291 family = AF_INET;
292 break;
293 case sizeof(struct sockaddr_in6):
294 family = AF_INET6;
295 break;
296 default:
297 bl_log(b->b_fun, LOG_ERR, "%s: invalid socket len %u (%s)",
298 __func__, (unsigned)slen, ctx);
299 errno = EINVAL;
300 return -1;
303 memcpy(ss, sa, slen);
305 if (ss->ss_family != family) {
306 bl_log(b->b_fun, LOG_INFO,
307 "%s: correcting socket family %d to %d (%s)",
308 __func__, ss->ss_family, family, ctx);
309 ss->ss_family = family;
312 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
313 if (ss->ss_len != slen) {
314 bl_log(b->b_fun, LOG_INFO,
315 "%s: correcting socket len %u to %u (%s)",
316 __func__, ss->ss_len, (unsigned)slen, ctx);
317 ss->ss_len = (uint8_t)slen;
319 #endif
320 return 0;
324 bl_send(bl_t b, bl_type_t e, int pfd, const struct sockaddr *sa,
325 socklen_t slen, const char *ctx)
327 struct msghdr msg;
328 struct iovec iov;
329 union {
330 char ctrl[CMSG_SPACE(sizeof(int))];
331 uint32_t fd;
332 } ua;
333 struct cmsghdr *cmsg;
334 union {
335 bl_message_t bl;
336 char buf[512];
337 } ub;
338 size_t ctxlen, tried;
339 #define NTRIES 5
341 ctxlen = strlen(ctx);
342 if (ctxlen > 128)
343 ctxlen = 128;
345 iov.iov_base = ub.buf;
346 iov.iov_len = sizeof(bl_message_t) + ctxlen;
347 ub.bl.bl_len = (uint32_t)iov.iov_len;
348 ub.bl.bl_version = BL_VERSION;
349 ub.bl.bl_type = (uint32_t)e;
351 if (bl_getsock(b, &ub.bl.bl_ss, sa, slen, ctx) == -1)
352 return -1;
355 ub.bl.bl_salen = slen;
356 memcpy(ub.bl.bl_data, ctx, ctxlen);
358 msg.msg_name = NULL;
359 msg.msg_namelen = 0;
360 msg.msg_iov = &iov;
361 msg.msg_iovlen = 1;
362 msg.msg_flags = 0;
364 msg.msg_control = ua.ctrl;
365 msg.msg_controllen = sizeof(ua.ctrl);
367 cmsg = CMSG_FIRSTHDR(&msg);
368 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
369 cmsg->cmsg_level = SOL_SOCKET;
370 cmsg->cmsg_type = SCM_RIGHTS;
372 memcpy(CMSG_DATA(cmsg), &pfd, sizeof(pfd));
374 tried = 0;
375 again:
376 if (bl_init(b, false) == -1)
377 return -1;
379 if ((sendmsg(b->b_fd, &msg, 0) == -1) && tried++ < NTRIES) {
380 bl_reset(b);
381 goto again;
383 return tried >= NTRIES ? -1 : 0;
386 bl_info_t *
387 bl_recv(bl_t b)
389 struct msghdr msg;
390 struct iovec iov;
391 union {
392 char ctrl[CMSG_SPACE(sizeof(int)) + CMSG_SPACE(CRED_SIZE)];
393 uint32_t fd;
394 CRED_TYPE sc;
395 } ua;
396 struct cmsghdr *cmsg;
397 CRED_TYPE *sc;
398 union {
399 bl_message_t bl;
400 char buf[512];
401 } ub;
402 int got;
403 ssize_t rlen;
404 bl_info_t *bi = &b->b_info;
406 got = 0;
407 memset(bi, 0, sizeof(*bi));
409 iov.iov_base = ub.buf;
410 iov.iov_len = sizeof(ub);
412 msg.msg_name = NULL;
413 msg.msg_namelen = 0;
414 msg.msg_iov = &iov;
415 msg.msg_iovlen = 1;
416 msg.msg_flags = 0;
418 msg.msg_control = ua.ctrl;
419 msg.msg_controllen = sizeof(ua.ctrl) + 100;
421 rlen = recvmsg(b->b_fd, &msg, 0);
422 if (rlen == -1) {
423 bl_log(b->b_fun, LOG_ERR, "%s: recvmsg failed (%m)", __func__);
424 return NULL;
427 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
428 if (cmsg->cmsg_level != SOL_SOCKET) {
429 bl_log(b->b_fun, LOG_ERR,
430 "%s: unexpected cmsg_level %d",
431 __func__, cmsg->cmsg_level);
432 continue;
434 switch (cmsg->cmsg_type) {
435 case SCM_RIGHTS:
436 if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
437 bl_log(b->b_fun, LOG_ERR,
438 "%s: unexpected cmsg_len %d != %zu",
439 __func__, cmsg->cmsg_len,
440 CMSG_LEN(2 * sizeof(int)));
441 continue;
443 memcpy(&bi->bi_fd, CMSG_DATA(cmsg), sizeof(bi->bi_fd));
444 got |= GOT_FD;
445 break;
446 #ifdef CRED_MESSAGE
447 case CRED_MESSAGE:
448 sc = (void *)CMSG_DATA(cmsg);
449 bi->bi_uid = sc->CRED_SC_UID;
450 bi->bi_gid = sc->CRED_SC_GID;
451 got |= GOT_CRED;
452 break;
453 #endif
454 default:
455 bl_log(b->b_fun, LOG_ERR,
456 "%s: unexpected cmsg_type %d",
457 __func__, cmsg->cmsg_type);
458 continue;
463 if (got != (GOT_CRED|GOT_FD)) {
464 bl_log(b->b_fun, LOG_ERR, "message missing %s %s",
465 #if GOT_CRED != 0
466 (got & GOT_CRED) == 0 ? "cred" :
467 #endif
468 "", (got & GOT_FD) == 0 ? "fd" : "");
470 return NULL;
473 if ((size_t)rlen <= sizeof(ub.bl)) {
474 bl_log(b->b_fun, LOG_ERR, "message too short %zd", rlen);
475 return NULL;
478 if (ub.bl.bl_version != BL_VERSION) {
479 bl_log(b->b_fun, LOG_ERR, "bad version %d", ub.bl.bl_version);
480 return NULL;
483 bi->bi_type = ub.bl.bl_type;
484 bi->bi_slen = ub.bl.bl_salen;
485 bi->bi_ss = ub.bl.bl_ss;
486 #ifndef CRED_MESSAGE
487 bi->bi_uid = -1;
488 bi->bi_gid = -1;
489 #endif
490 strlcpy(bi->bi_msg, ub.bl.bl_data, MIN(sizeof(bi->bi_msg),
491 ((size_t)rlen - sizeof(ub.bl) + 1)));
492 return bi;