Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F162318412
D48222.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
14 KB
Referenced Files
None
Subscribers
None
D48222.diff
View Options
diff --git a/usr.sbin/syslogd/syslogd.h b/usr.sbin/syslogd/syslogd.h
--- a/usr.sbin/syslogd/syslogd.h
+++ b/usr.sbin/syslogd/syslogd.h
@@ -127,6 +127,11 @@
F_PIPE, /* pipe to program */
};
+struct forw_addr {
+ struct sockaddr_storage laddr;
+ struct sockaddr_storage raddr;
+};
+
/*
* This structure represents the files that will have log
* copies printed.
@@ -158,6 +163,7 @@
char f_hname[MAXHOSTNAMELEN];
int *f_addr_fds;
size_t f_num_addr_fds;
+ struct forw_addr *f_addrs;
}; /* F_FORW */
struct {
char f_pname[MAXPATHLEN];
diff --git a/usr.sbin/syslogd/syslogd.c b/usr.sbin/syslogd/syslogd.c
--- a/usr.sbin/syslogd/syslogd.c
+++ b/usr.sbin/syslogd/syslogd.c
@@ -373,6 +373,7 @@
switch (f->f_type) {
case F_FORW:
if (f->f_addr_fds != NULL) {
+ free(f->f_addrs);
for (size_t i = 0; i < f->f_num_addr_fds; ++i)
close(f->f_addr_fds[i]);
free(f->f_addr_fds);
@@ -2987,11 +2988,152 @@
return (q);
}
+static int
+maybe_dup_forw_socket(const nvlist_t *nvl, const struct sockaddr *rsa,
+ const struct sockaddr *lsa)
+{
+ const nvlist_t * const *line;
+ size_t linecount;
+
+ if (!nvlist_exists_nvlist_array(nvl, "filed_list"))
+ return (-1);
+ line = nvlist_get_nvlist_array(nvl, "filed_list", &linecount);
+ for (size_t i = 0; i < linecount; i++) {
+ const struct forw_addr *forw;
+ const int *fdp;
+ size_t fdc;
+
+ if (nvlist_get_number(line[i], "f_type") != F_FORW)
+ continue;
+ fdp = nvlist_get_descriptor_array(line[i], "f_addr_fds", &fdc);
+ forw = nvlist_get_binary(line[i], "f_addrs", NULL);
+ for (size_t j = 0; j < fdc; j++) {
+ int fd;
+
+ if (memcmp(&forw[j].raddr, rsa, rsa->sa_len) != 0 ||
+ memcmp(&forw[j].laddr, lsa, lsa->sa_len) != 0)
+ continue;
+
+ fd = dup(fdp[j]);
+ if (fd < 0)
+ err(1, "dup");
+ return (fd);
+ }
+ }
+
+ return (-1);
+}
+
+/*
+ * Create a UDP socket that will forward messages from "lai" to "ai".
+ * Capsicum doesn't permit connect() or sendto(), so we can't reuse the (bound)
+ * sockets used to listen for messages.
+ */
+static int
+make_forw_socket(const nvlist_t *nvl, struct addrinfo *ai, struct addrinfo *lai)
+{
+ int s;
+
+ s = socket(ai->ai_family, ai->ai_socktype, 0);
+ if (s < 0)
+ err(1, "socket");
+ if (lai != NULL) {
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &(int){1},
+ sizeof(int)) < 0)
+ err(1, "setsockopt");
+ if (bind(s, lai->ai_addr, lai->ai_addrlen) < 0)
+ err(1, "bind");
+ }
+ if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
+ if (errno == EADDRINUSE && lai != NULL) {
+ int s1;
+
+ s1 = maybe_dup_forw_socket(nvl, ai->ai_addr,
+ lai->ai_addr);
+ if (s1 < 0)
+ errc(1, EADDRINUSE, "connect");
+ (void)close(s);
+ s = s1;
+ } else {
+ err(1, "connect");
+ }
+ }
+ /* Make it a write-only socket. */
+ if (shutdown(s, SHUT_RD) < 0)
+ err(1, "shutdown");
+
+ return (s);
+}
+
+static void
+make_forw_socket_array(const nvlist_t *nvl, struct filed *f,
+ struct addrinfo *res)
+{
+ struct addrinfo *ai;
+ size_t i;
+
+ f->f_num_addr_fds = 0;
+
+ /* How many sockets do we need? */
+ for (ai = res; ai != NULL; ai = ai->ai_next) {
+ struct socklist *boundsock;
+ int count;
+
+ count = 0;
+ STAILQ_FOREACH(boundsock, &shead, next) {
+ if (boundsock->sl_ai.ai_family == ai->ai_family)
+ count++;
+ }
+ if (count == 0)
+ count = 1;
+ f->f_num_addr_fds += count;
+ }
+
+ f->f_addr_fds = calloc(f->f_num_addr_fds, sizeof(*f->f_addr_fds));
+ f->f_addrs = calloc(f->f_num_addr_fds, sizeof(*f->f_addrs));
+ if (f->f_addr_fds == NULL || f->f_addrs == NULL)
+ err(1, "malloc failed");
+
+ /*
+ * Create our forwarding sockets: for each bound socket
+ * belonging to the destination address, create one socket
+ * connected to the destination and bound to the address of the
+ * listening socket.
+ */
+ i = 0;
+ for (ai = res; ai != NULL; ai = ai->ai_next) {
+ struct socklist *boundsock;
+ int count;
+
+ count = 0;
+ STAILQ_FOREACH(boundsock, &shead, next) {
+ if (boundsock->sl_ai.ai_family ==
+ ai->ai_family) {
+ memcpy(&f->f_addrs[i].raddr, ai->ai_addr,
+ ai->ai_addrlen);
+ memcpy(&f->f_addrs[i].laddr,
+ boundsock->sl_ai.ai_addr,
+ boundsock->sl_ai.ai_addrlen);
+ f->f_addr_fds[i++] = make_forw_socket(nvl, ai,
+ &boundsock->sl_ai);
+ count++;
+ }
+ }
+ if (count == 0) {
+ memcpy(&f->f_addrs[i].raddr, ai->ai_addr,
+ ai->ai_addrlen);
+ f->f_addr_fds[i++] = make_forw_socket(nvl, ai, NULL);
+ }
+ }
+ assert(i == f->f_num_addr_fds);
+}
+
static void
-parse_action(const char *p, struct filed *f)
+parse_action(const nvlist_t *nvl, const char *p, struct filed *f)
{
- struct addrinfo *ai, hints, *res;
- int error, i;
+ struct addrinfo hints, *res;
+ size_t i;
+ int error;
const char *q;
bool syncfile;
@@ -3045,27 +3187,7 @@
dprintf("%s\n", gai_strerror(error));
break;
}
-
- for (ai = res; ai != NULL; ai = ai->ai_next)
- ++f->f_num_addr_fds;
-
- f->f_addr_fds = calloc(f->f_num_addr_fds,
- sizeof(*f->f_addr_fds));
- if (f->f_addr_fds == NULL)
- err(1, "malloc failed");
-
- for (ai = res, i = 0; ai != NULL; ai = ai->ai_next, ++i) {
- int *sockp = &f->f_addr_fds[i];
-
- *sockp = socket(ai->ai_family, ai->ai_socktype, 0);
- if (*sockp < 0)
- err(1, "socket");
- if (connect(*sockp, ai->ai_addr, ai->ai_addrlen) < 0)
- err(1, "connect");
- /* Make it a write-only socket. */
- if (shutdown(*sockp, SHUT_RD) < 0)
- err(1, "shutdown");
- }
+ make_forw_socket_array(nvl, f, res);
freeaddrinfo(res);
f->f_type = F_FORW;
break;
@@ -3161,7 +3283,7 @@
/* skip to action part */
while (*p == '\t' || *p == ' ')
p++;
- parse_action(p, &f);
+ parse_action(nvl, p, &f);
/* An nvlist is heap allocated heap here. */
nvl_filed = filed_to_nvlist(&f);
@@ -3772,6 +3894,13 @@
return (NULL);
}
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &(int){1},
+ sizeof(int)) < 0) {
+ logerror("setsockopt(SO_REUSEPORT)");
+ close(s);
+ return (NULL);
+ }
+
/*
* For AF_LOCAL sockets, the process umask is applied to the
* mode set above, so temporarily clear it to ensure that the
diff --git a/usr.sbin/syslogd/syslogd_cap_config.c b/usr.sbin/syslogd/syslogd_cap_config.c
--- a/usr.sbin/syslogd/syslogd_cap_config.c
+++ b/usr.sbin/syslogd/syslogd_cap_config.c
@@ -140,6 +140,8 @@
nvlist_add_string(nvl_filed, "f_hname", filed->f_hname);
nvlist_add_descriptor_array(nvl_filed, "f_addr_fds",
filed->f_addr_fds, filed->f_num_addr_fds);
+ nvlist_add_binary(nvl_filed, "f_addrs", filed->f_addrs,
+ filed->f_num_addr_fds * sizeof(*filed->f_addrs));
} else if (filed->f_type == F_PIPE) {
nvlist_add_string(nvl_filed, "f_pname", filed->f_pname);
if (filed->f_procdesc >= 0) {
diff --git a/usr.sbin/syslogd/tests/syslogd_test.sh b/usr.sbin/syslogd/tests/syslogd_test.sh
--- a/usr.sbin/syslogd/tests/syslogd_test.sh
+++ b/usr.sbin/syslogd/tests/syslogd_test.sh
@@ -2,6 +2,7 @@
# SPDX-License-Identifier: BSD-2-Clause
#
# Copyright (c) 2021, 2023 The FreeBSD Foundation
+# Copyright (c) 2024 Mark Johnston <markj@FreeBSD.org>
#
# This software was developed by Mark Johnston under sponsorship from
# the FreeBSD Foundation.
@@ -337,6 +338,217 @@
jail -r syslogd_noinet
}
+# Create a pair of jails, connected by an epair. The idea is to run syslogd in
+# one jail (syslogd_allowed_peer), listening on 169.254.0.1, and logger(1) can
+# send messages from the other jail (syslogd_client) using source addrs
+# 169.254.0.2 or 169.254.0.3.
+allowed_peer_test_setup()
+{
+ local epair
+
+ atf_check jail -c name=syslogd_allowed_peer vnet persist
+ atf_check jail -c name=syslogd_client vnet persist
+
+ atf_check -o save:epair ifconfig epair create
+ epair=$(cat epair)
+ epair=${epair%%a}
+
+ atf_check ifconfig ${epair}a vnet syslogd_allowed_peer
+ atf_check ifconfig ${epair}b vnet syslogd_client
+ atf_check jexec syslogd_allowed_peer ifconfig ${epair}a inet 169.254.0.1/16
+ atf_check jexec syslogd_allowed_peer ifconfig lo0 inet 127.0.0.1/8
+ atf_check jexec syslogd_client ifconfig ${epair}b inet 169.254.0.2/16
+ atf_check jexec syslogd_client ifconfig ${epair}b alias 169.254.0.3/16
+ atf_check jexec syslogd_client ifconfig lo0 inet 127.0.0.1/8
+}
+
+allowed_peer_test_cleanup()
+{
+ jail -r syslogd_allowed_peer
+ jail -r syslogd_client
+ ifconfig $(cat epair) destroy
+}
+
+atf_test_case allowed_peer "cleanup"
+allowed_peer_head()
+{
+ atf_set descr "syslogd -a works"
+ atf_set require.user root
+}
+allowed_peer_body()
+{
+ local logfile
+
+ allowed_peer_test_setup
+
+ logfile="${PWD}/jail.log"
+ printf "user.debug\t${logfile}\n" > "${SYSLOGD_CONFIG}"
+ syslogd_start -j syslogd_allowed_peer -b 169.254.0.1:514 -a '169.254.0.2/32'
+
+ # Make sure that a message from 169.254.0.2:514 is logged.
+ atf_check jexec syslogd_client \
+ logger -p user.debug -t test1 -h 169.254.0.1 -S 169.254.0.2:514 "hello, world"
+ atf_check -o match:"test1: hello, world" cat "${logfile}"
+ # ... but not a message from port 515.
+ atf_check -o ignore jexec syslogd_client \
+ logger -p user.debug -t test2 -h 169.254.0.1 -S 169.254.0.2:515 "hello, world"
+ atf_check -o not-match:"test2: hello, world" cat "${logfile}"
+ atf_check -o ignore jexec syslogd_client \
+ logger -p user.debug -t test2 -h 169.254.0.1 -S 169.254.0.3:515 "hello, world"
+ atf_check -o not-match:"test2: hello, world" cat "${logfile}"
+
+ syslogd_stop
+
+ # Now make sure that we can filter by port.
+ syslogd_start -j syslogd_allowed_peer -b 169.254.0.1:514 -a '169.254.0.2/32:515'
+
+ atf_check jexec syslogd_client \
+ logger -p user.debug -t test3 -h 169.254.0.1 -S 169.254.0.2:514 "hello, world"
+ atf_check -o not-match:"test3: hello, world" cat "${logfile}"
+ atf_check jexec syslogd_client \
+ logger -p user.debug -t test4 -h 169.254.0.1 -S 169.254.0.2:515 "hello, world"
+ atf_check -o match:"test4: hello, world" cat "${logfile}"
+
+ syslogd_stop
+}
+allowed_peer_cleanup()
+{
+ allowed_peer_test_cleanup
+}
+
+atf_test_case allowed_peer_forwarding "cleanup"
+allowed_peer_forwarding_head()
+{
+ atf_set descr "syslogd forwards messages from its listening port"
+ atf_set require.user root
+}
+allowed_peer_forwarding_body()
+{
+ local logfile
+
+ allowed_peer_test_setup
+
+ printf "user.debug\t@169.254.0.1\n" > client_config
+ printf "mark.debug\t@169.254.0.1:515\n" >> client_config
+ syslogd_start -j syslogd_client -b 169.254.0.2:514 -f ${PWD}/client_config
+
+ logfile="${PWD}/jail.log"
+ printf "+169.254.0.2\nuser.debug\t${logfile}\n" > "${SYSLOGD_CONFIG}"
+ syslogd_start -j syslogd_allowed_peer -P ${SYSLOGD_PIDFILE}.2 \
+ -b 169.254.0.1:514 -a 169.254.0.2/32
+
+ # A message forwarded to 169.254.0.1:514 should be logged, but one
+ # forwarded to 169.254.0.1:515 should not.
+ atf_check jexec syslogd_client \
+ logger -h 169.254.0.2 -p user.debug -t test1 "hello, world"
+ atf_check jexec syslogd_client \
+ logger -h 169.254.0.2 -p mark.debug -t test2 "hello, world"
+
+ atf_check -o match:"test1: hello, world" cat "${logfile}"
+ atf_check -o not-match:"test2: hello, world" cat "${logfile}"
+}
+allowed_peer_forwarding_cleanup()
+{
+ allowed_peer_test_cleanup
+}
+
+atf_test_case allowed_peer_wildcard "cleanup"
+allowed_peer_wildcard_head()
+{
+ atf_set descr "syslogd -a works with port wildcards"
+ atf_set require.user root
+}
+allowed_peer_wildcard_body()
+{
+ local logfile
+
+ allowed_peer_test_setup
+
+ logfile="${PWD}/jail.log"
+ printf "user.debug\t${logfile}\n" > "${SYSLOGD_CONFIG}"
+ syslogd_start -j syslogd_allowed_peer -b 169.254.0.1:514 -a '169.254.0.2/32:*'
+
+ # Make sure that a message from 169.254.0.2:514 is logged.
+ atf_check jexec syslogd_client \
+ logger -p user.debug -t test1 -h 169.254.0.1 -S 169.254.0.2:514 "hello, world"
+ atf_check -o match:"test1: hello, world" cat "${logfile}"
+ # ... as is a message from 169.254.0.2:515, allowed by the wildcard.
+ atf_check jexec syslogd_client \
+ logger -p user.debug -t test2 -h 169.254.0.1 -S 169.254.0.2:515 "hello, world"
+ atf_check -o match:"test2: hello, world" cat "${logfile}"
+ # ... but not a message from 169.254.0.3.
+ atf_check -o ignore jexec syslogd_client \
+ logger -p user.debug -t test3 -h 169.254.0.1 -S 169.254.0.3:514 "hello, world"
+ atf_check -o not-match:"test3: hello, world" cat "${logfile}"
+ atf_check -o ignore jexec syslogd_client \
+ logger -p user.debug -t test3 -h 169.254.0.1 -S 169.254.0.3:515 "hello, world"
+ atf_check -o not-match:"test3: hello, world" cat "${logfile}"
+
+ syslogd_stop
+}
+allowed_peer_wildcard_cleanup()
+{
+ allowed_peer_test_cleanup
+}
+
+atf_test_case "forward" "cleanup"
+forward_head()
+{
+ atf_set descr "syslogd forwards messages to a remote host"
+ atf_set require.user root
+}
+forward_body()
+{
+ local epair logfile
+
+ atf_check -o save:epair ifconfig epair create
+ epair=$(cat epair)
+ epair=${epair%%a}
+
+ atf_check jail -c name=syslogd_server vnet persist
+ atf_check ifconfig ${epair}a vnet syslogd_server
+ atf_check jexec syslogd_server ifconfig ${epair}a inet 169.254.0.1/16
+ atf_check jexec syslogd_server ifconfig ${epair}a alias 169.254.0.2/16
+ atf_check jexec syslogd_server ifconfig lo0 inet 127.0.0.1/8
+
+ atf_check jail -c name=syslogd_client vnet persist
+ atf_check ifconfig ${epair}b vnet syslogd_client
+ atf_check jexec syslogd_client ifconfig ${epair}b inet 169.254.0.3/16
+ atf_check jexec syslogd_client ifconfig lo0 inet 127.0.0.1/8
+
+ cat <<__EOF__ > ./client_config
+user.debug @169.254.0.1
+mail.debug @169.254.0.2
+ftp.debug @169.254.0.1
+__EOF__
+
+ logfile="${PWD}/jail.log"
+ cat <<__EOF__ > ./server_config
+user.debug ${logfile}
+mail.debug ${logfile}
+ftp.debug ${logfile}
+__EOF__
+
+ syslogd_start -j syslogd_server -f ${PWD}/server_config -b 169.254.0.1 -b 169.254.0.2
+ syslogd_start -j syslogd_client -f ${PWD}/client_config -P ${SYSLOGD_PIDFILE}.2
+
+ atf_check jexec syslogd_client \
+ logger -h 169.254.0.3 -P $SYSLOGD_UDP_PORT -p user.debug -t test1 "hello, world"
+ atf_check jexec syslogd_client \
+ logger -h 169.254.0.3 -P $SYSLOGD_UDP_PORT -p mail.debug -t test2 "you've got mail"
+ atf_check jexec syslogd_client \
+ logger -h 169.254.0.3 -P $SYSLOGD_UDP_PORT -p ftp.debug -t test3 "transfer complete"
+
+ atf_check -o match:"test1: hello, world" cat "${logfile}"
+ atf_check -o match:"test2: you've got mail" cat "${logfile}"
+ atf_check -o match:"test3: transfer complete" cat "${logfile}"
+}
+forward_cleanup()
+{
+ jail -r syslogd_server
+ jail -r syslogd_client
+}
+
atf_init_test_cases()
{
atf_add_test_case "unix"
@@ -349,4 +561,8 @@
atf_add_test_case "host_action"
atf_add_test_case "pipe_action"
atf_add_test_case "jail_noinet"
+ atf_add_test_case "allowed_peer"
+ atf_add_test_case "allowed_peer_forwarding"
+ atf_add_test_case "allowed_peer_wildcard"
+ atf_add_test_case "forward"
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Jul 13, 12:31 AM (3 h, 7 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
35018019
Default Alt Text
D48222.diff (14 KB)
Attached To
Mode
D48222: syslogd: Ensure that forwarded messages are sent from port 514
Attached
Detach File
Event Timeline
Log In to Comment