aboutsummaryrefslogtreecommitdiff
path: root/contrib/blocklist/lib/old_bl.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/blocklist/lib/old_bl.c')
-rw-r--r--contrib/blocklist/lib/old_bl.c554
1 files changed, 554 insertions, 0 deletions
diff --git a/contrib/blocklist/lib/old_bl.c b/contrib/blocklist/lib/old_bl.c
new file mode 100644
index 000000000000..ffbbd3f620ac
--- /dev/null
+++ b/contrib/blocklist/lib/old_bl.c
@@ -0,0 +1,554 @@
+/* $NetBSD: bl.c,v 1.9 2025/03/30 01:53:59 christos Exp $ */
+
+/*-
+ * Copyright (c) 2014 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+__RCSID("$NetBSD: bl.c,v 1.9 2025/03/30 01:53:59 christos Exp $");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <netinet/in.h>
+#ifdef _REENTRANT
+#include <pthread.h>
+#endif
+
+#if defined(SO_RECVUCRED)
+#include <ucred.h>
+#endif
+
+#include "old_bl.h"
+
+typedef struct {
+ uint32_t bl_len;
+ uint32_t bl_version;
+ uint32_t bl_type;
+ uint32_t bl_salen;
+ struct sockaddr_storage bl_ss;
+ char bl_data[];
+} bl_message_t;
+
+struct blacklist {
+#ifdef _REENTRANT
+ pthread_mutex_t b_mutex;
+# define BL_INIT(b) pthread_mutex_init(&b->b_mutex, NULL)
+# define BL_LOCK(b) pthread_mutex_lock(&b->b_mutex)
+# define BL_UNLOCK(b) pthread_mutex_unlock(&b->b_mutex)
+#else
+# define BL_INIT(b) do {} while(/*CONSTCOND*/0)
+# define BL_LOCK(b) BL_INIT(b)
+# define BL_UNLOCK(b) BL_INIT(b)
+#endif
+ int b_fd;
+ int b_connected;
+ struct sockaddr_un b_sun;
+ struct syslog_data b_syslog_data;
+ void (*b_fun)(int, struct syslog_data *, const char *, va_list);
+ bl_info_t b_info;
+};
+
+#define BL_VERSION 1
+
+bool
+bl_isconnected(bl_t b)
+{
+ return b->b_connected == 0;
+}
+
+int
+bl_getfd(bl_t b)
+{
+ return b->b_fd;
+}
+
+static void
+bl_reset(bl_t b, bool locked)
+{
+ int serrno = errno;
+ if (!locked)
+ BL_LOCK(b);
+ close(b->b_fd);
+ errno = serrno;
+ b->b_fd = -1;
+ b->b_connected = -1;
+ if (!locked)
+ BL_UNLOCK(b);
+}
+
+static void
+bl_log(bl_t b, int level, const char *fmt, ...)
+{
+ va_list ap;
+ int serrno = errno;
+
+ if (b->b_fun == NULL)
+ return;
+
+ va_start(ap, fmt);
+ (*b->b_fun)(level, &b->b_syslog_data, fmt, ap);
+ va_end(ap);
+ errno = serrno;
+}
+
+static int
+bl_init(bl_t b, bool srv)
+{
+ static int one = 1;
+ /* AF_UNIX address of local logger */
+ mode_t om;
+ int rv, serrno;
+ struct sockaddr_un *sun = &b->b_sun;
+
+#ifndef SOCK_NONBLOCK
+#define SOCK_NONBLOCK 0
+#endif
+#ifndef SOCK_CLOEXEC
+#define SOCK_CLOEXEC 0
+#endif
+#ifndef SOCK_NOSIGPIPE
+#define SOCK_NOSIGPIPE 0
+#endif
+
+ BL_LOCK(b);
+
+ if (b->b_fd == -1) {
+ b->b_fd = socket(PF_LOCAL,
+ SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK|SOCK_NOSIGPIPE, 0);
+ if (b->b_fd == -1) {
+ bl_log(b, LOG_ERR, "%s: socket failed (%s)",
+ __func__, strerror(errno));
+ BL_UNLOCK(b);
+ return -1;
+ }
+#if SOCK_CLOEXEC == 0
+ fcntl(b->b_fd, F_SETFD, FD_CLOEXEC);
+#endif
+#if SOCK_NONBLOCK == 0
+ fcntl(b->b_fd, F_SETFL, fcntl(b->b_fd, F_GETFL) | O_NONBLOCK);
+#endif
+#if SOCK_NOSIGPIPE == 0
+#ifdef SO_NOSIGPIPE
+ int o = 1;
+ setsockopt(b->b_fd, SOL_SOCKET, SO_NOSIGPIPE, &o, sizeof(o));
+#else
+ signal(SIGPIPE, SIG_IGN);
+#endif
+#endif
+ }
+
+ if (bl_isconnected(b)) {
+ BL_UNLOCK(b);
+ return 0;
+ }
+
+ /*
+ * We try to connect anyway even when we are a server to verify
+ * that no other server is listening to the socket. If we succeed
+ * to connect and we are a server, someone else owns it.
+ */
+ rv = connect(b->b_fd, (const void *)sun, (socklen_t)sizeof(*sun));
+ if (rv == 0) {
+ if (srv) {
+ bl_log(b, LOG_ERR,
+ "%s: another daemon is handling `%s'",
+ __func__, sun->sun_path);
+ goto out;
+ }
+ } else {
+ if (!srv) {
+ /*
+ * If the daemon is not running, we just try a
+ * connect, so leave the socket alone until it does
+ * and only log once.
+ */
+ if (b->b_connected != 1) {
+ bl_log(b, LOG_DEBUG,
+ "%s: connect failed for `%s' (%s)",
+ __func__, sun->sun_path, strerror(errno));
+ b->b_connected = 1;
+ }
+ BL_UNLOCK(b);
+ return -1;
+ }
+ bl_log(b, LOG_DEBUG, "Connected to blacklist server", __func__);
+ }
+
+ if (srv) {
+ (void)unlink(sun->sun_path);
+ om = umask(0);
+ rv = bind(b->b_fd, (const void *)sun, (socklen_t)sizeof(*sun));
+ serrno = errno;
+ (void)umask(om);
+ errno = serrno;
+ if (rv == -1) {
+ bl_log(b, LOG_ERR, "%s: bind failed for `%s' (%s)",
+ __func__, sun->sun_path, strerror(errno));
+ goto out;
+ }
+ }
+
+ b->b_connected = 0;
+#define GOT_FD 1
+#if defined(LOCAL_CREDS)
+#define CRED_LEVEL 0
+#define CRED_NAME LOCAL_CREDS
+#define CRED_SC_UID(x) (x)->sc_euid
+#define CRED_SC_GID(x) (x)->sc_egid
+#define CRED_MESSAGE SCM_CREDS
+#define CRED_SIZE SOCKCREDSIZE(NGROUPS_MAX)
+#define CRED_TYPE struct sockcred
+#define GOT_CRED 2
+#elif defined(SO_PASSCRED)
+#define CRED_LEVEL SOL_SOCKET
+#define CRED_NAME SO_PASSCRED
+#define CRED_SC_UID(x) (x)->uid
+#define CRED_SC_GID(x) (x)->gid
+#define CRED_MESSAGE SCM_CREDENTIALS
+#define CRED_SIZE sizeof(struct ucred)
+#define CRED_TYPE struct ucred
+#define GOT_CRED 2
+#elif defined(SO_RECVUCRED)
+#define CRED_LEVEL SOL_SOCKET
+#define CRED_NAME SO_RECVUCRED
+#define CRED_SC_UID(x) ucred_geteuid(x)
+#define CRED_SC_GID(x) ucred_getegid(x)
+#define CRED_MESSAGE SCM_UCRED
+#define CRED_SIZE ucred_size()
+#define CRED_TYPE ucred_t
+#define GOT_CRED 2
+#else
+#define GOT_CRED 0
+/*
+ * getpeereid() and LOCAL_PEERCRED don't help here
+ * because we are not a stream socket!
+ */
+#define CRED_SIZE 0
+#define CRED_TYPE void * __unused
+#endif
+
+#ifdef CRED_LEVEL
+ if (setsockopt(b->b_fd, CRED_LEVEL, CRED_NAME,
+ &one, (socklen_t)sizeof(one)) == -1) {
+ bl_log(b, LOG_ERR, "%s: setsockopt %s "
+ "failed (%s)", __func__, __STRING(CRED_NAME),
+ strerror(errno));
+ goto out;
+ }
+#endif
+
+ BL_UNLOCK(b);
+ return 0;
+out:
+ bl_reset(b, true);
+ BL_UNLOCK(b);
+ return -1;
+}
+
+bl_t
+bl_create(bool srv, const char *path,
+ void (*fun)(int, struct syslog_data *, const char *, va_list))
+{
+ static struct syslog_data sd = SYSLOG_DATA_INIT;
+ bl_t b = calloc(1, sizeof(*b));
+ if (b == NULL)
+ return NULL;
+ b->b_fun = fun;
+ b->b_syslog_data = sd;
+ b->b_fd = -1;
+ b->b_connected = -1;
+ BL_INIT(b);
+
+ memset(&b->b_sun, 0, sizeof(b->b_sun));
+ b->b_sun.sun_family = AF_LOCAL;
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ b->b_sun.sun_len = sizeof(b->b_sun);
+#endif
+ strlcpy(b->b_sun.sun_path,
+ path ? path : _PATH_BLSOCK, sizeof(b->b_sun.sun_path));
+
+ bl_init(b, srv);
+ return b;
+}
+
+void
+bl_destroy(bl_t b)
+{
+ bl_reset(b, false);
+ free(b);
+}
+
+static int
+bl_getsock(bl_t b, struct sockaddr_storage *ss, const struct sockaddr *sa,
+ socklen_t slen, const char *ctx)
+{
+ uint8_t family;
+
+ memset(ss, 0, sizeof(*ss));
+
+ switch (slen) {
+ case 0:
+ return 0;
+ case sizeof(struct sockaddr_in):
+ family = AF_INET;
+ break;
+ case sizeof(struct sockaddr_in6):
+ family = AF_INET6;
+ break;
+ default:
+ bl_log(b, LOG_ERR, "%s: invalid socket len %u (%s)",
+ __func__, (unsigned)slen, ctx);
+ errno = EINVAL;
+ return -1;
+ }
+
+ memcpy(ss, sa, slen);
+
+ if (ss->ss_family != family) {
+ bl_log(b, LOG_INFO,
+ "%s: correcting socket family %d to %d (%s)",
+ __func__, ss->ss_family, family, ctx);
+ ss->ss_family = family;
+ }
+
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ if (ss->ss_len != slen) {
+ bl_log(b, LOG_INFO,
+ "%s: correcting socket len %u to %u (%s)",
+ __func__, ss->ss_len, (unsigned)slen, ctx);
+ ss->ss_len = (uint8_t)slen;
+ }
+#endif
+ return 0;
+}
+
+int
+bl_send(bl_t b, bl_type_t e, int pfd, const struct sockaddr *sa,
+ socklen_t slen, const char *ctx)
+{
+ struct msghdr msg;
+ struct iovec iov;
+ union {
+ char ctrl[CMSG_SPACE(sizeof(int))];
+ uint32_t fd;
+ } ua;
+ struct cmsghdr *cmsg;
+ union {
+ bl_message_t bl;
+ char buf[512];
+ } ub;
+ size_t ctxlen, tried;
+#define NTRIES 5
+
+ ctxlen = strlen(ctx);
+ if (ctxlen > 128)
+ ctxlen = 128;
+
+ iov.iov_base = ub.buf;
+ iov.iov_len = sizeof(bl_message_t) + ctxlen;
+ ub.bl.bl_len = (uint32_t)iov.iov_len;
+ ub.bl.bl_version = BL_VERSION;
+ ub.bl.bl_type = (uint32_t)e;
+
+ if (bl_getsock(b, &ub.bl.bl_ss, sa, slen, ctx) == -1)
+ return -1;
+
+
+ ub.bl.bl_salen = slen;
+ memcpy(ub.bl.bl_data, ctx, ctxlen);
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_flags = 0;
+
+ msg.msg_control = ua.ctrl;
+ msg.msg_controllen = sizeof(ua.ctrl);
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+
+ memcpy(CMSG_DATA(cmsg), &pfd, sizeof(pfd));
+
+ tried = 0;
+again:
+ if (bl_init(b, false) == -1)
+ return -1;
+
+ if ((sendmsg(b->b_fd, &msg, 0) == -1) && tried++ < NTRIES) {
+ bl_reset(b, false);
+ goto again;
+ }
+ return tried >= NTRIES ? -1 : 0;
+}
+
+bl_info_t *
+bl_recv(bl_t b)
+{
+ struct msghdr msg;
+ struct iovec iov;
+ union {
+ char ctrl[CMSG_SPACE(sizeof(int)) + CMSG_SPACE(CRED_SIZE)];
+ uint32_t fd;
+ } ua;
+ struct cmsghdr *cmsg;
+#if GOT_CRED != 0
+ CRED_TYPE *sc;
+#endif
+ union {
+ bl_message_t bl;
+ char buf[512];
+ } ub;
+ int got;
+ ssize_t rlen;
+ size_t rem;
+ bl_info_t *bi = &b->b_info;
+
+ got = 0;
+ memset(bi, 0, sizeof(*bi));
+
+ iov.iov_base = ub.buf;
+ iov.iov_len = sizeof(ub);
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_flags = 0;
+
+ msg.msg_control = ua.ctrl;
+ msg.msg_controllen = sizeof(ua.ctrl);
+
+ rlen = recvmsg(b->b_fd, &msg, 0);
+ if (rlen == -1) {
+ bl_log(b, LOG_ERR, "%s: recvmsg failed (%s)", __func__,
+ strerror(errno));
+ return NULL;
+ }
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level != SOL_SOCKET) {
+ bl_log(b, LOG_ERR,
+ "%s: unexpected cmsg_level %d",
+ __func__, cmsg->cmsg_level);
+ continue;
+ }
+ switch (cmsg->cmsg_type) {
+ case SCM_RIGHTS:
+ if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
+ int *fd = (void *)CMSG_DATA(cmsg);
+ size_t len = cmsg->cmsg_len / sizeof(int);
+ bl_log(b, LOG_ERR,
+ "%s: unexpected cmsg_len %d != %zu",
+ __func__, cmsg->cmsg_len,
+ CMSG_LEN(sizeof(int)));
+
+ for (size_t i = 0; i < len; i++)
+ (void)close(fd[i]);
+ continue;
+ }
+ memcpy(&bi->bi_fd, CMSG_DATA(cmsg), sizeof(bi->bi_fd));
+ got |= GOT_FD;
+ break;
+#ifdef CRED_MESSAGE
+ case CRED_MESSAGE:
+ sc = (void *)CMSG_DATA(cmsg);
+ bi->bi_uid = CRED_SC_UID(sc);
+ bi->bi_gid = CRED_SC_GID(sc);
+ got |= GOT_CRED;
+ break;
+#endif
+ default:
+ bl_log(b, LOG_ERR,
+ "%s: unexpected cmsg_type %d",
+ __func__, cmsg->cmsg_type);
+ continue;
+ }
+
+ }
+
+ if (got != (GOT_CRED|GOT_FD)) {
+ bl_log(b, LOG_ERR, "message missing %s %s",
+#if GOT_CRED != 0
+ (got & GOT_CRED) == 0 ? "cred" :
+#endif
+ "", (got & GOT_FD) == 0 ? "fd" : "");
+ return NULL;
+ }
+
+ rem = (size_t)rlen;
+ if (rem < sizeof(ub.bl)) {
+ bl_log(b, LOG_ERR, "message too short %zd", rlen);
+ return NULL;
+ }
+ rem -= sizeof(ub.bl);
+
+ if (ub.bl.bl_version != BL_VERSION) {
+ bl_log(b, LOG_ERR, "bad version %d", ub.bl.bl_version);
+ return NULL;
+ }
+
+ bi->bi_type = ub.bl.bl_type;
+ bi->bi_slen = ub.bl.bl_salen;
+ bi->bi_ss = ub.bl.bl_ss;
+#ifndef CRED_MESSAGE
+ bi->bi_uid = -1;
+ bi->bi_gid = -1;
+#endif
+ if (rem == 0)
+ bi->bi_msg[0] = '\0';
+ else {
+ rem = MIN(sizeof(bi->bi_msg) - 1, rem);
+ memcpy(bi->bi_msg, ub.bl.bl_data, rem);
+ bi->bi_msg[rem] = '\0';
+ }
+ return bi;
+}