mirror of
https://github.com/rsyslog/rsyslog.git
synced 2026-06-15 23:22:50 +02:00
Merge pull request #7114 from rsyslog/codex/fix-imudp-listenportfilename-vulnerability
imudp: harden listenPortFileName writes
This commit is contained in:
commit
d05585d712
@ -38,7 +38,10 @@ with ``listenPortFileName`` because one file would be ambiguous. Set
|
||||
with separate ``listenPortFileName`` values.
|
||||
|
||||
When used with a nonzero single port, rsyslog writes the actual bound port
|
||||
number to the file after the bind succeeds.
|
||||
number to the file after the bind succeeds. The file is created with owner-only
|
||||
permissions when it does not already exist, and rsyslog refuses symlinks, FIFOs,
|
||||
and other special files. Configure this path in a trusted directory such as
|
||||
``/run/rsyslog`` rather than a directory writable by unprivileged users.
|
||||
|
||||
Input usage
|
||||
-----------
|
||||
@ -47,7 +50,7 @@ Input usage
|
||||
|
||||
.. code-block:: rsyslog
|
||||
|
||||
input(type="imudp" port="0" listenPortFileName="/tmp/imudp.port")
|
||||
input(type="imudp" port="0" listenPortFileName="/run/rsyslog/imudp.port")
|
||||
|
||||
See also
|
||||
--------
|
||||
|
||||
@ -30,8 +30,10 @@
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <poll.h>
|
||||
@ -262,34 +264,82 @@ finalize_it:
|
||||
}
|
||||
|
||||
static rsRetVal writeListenPortFile(const uchar *const pszLstnPortFileName, const unsigned listenPort) {
|
||||
FILE *fp = NULL;
|
||||
char portBuf[32];
|
||||
const char *const path = (const char *)pszLstnPortFileName;
|
||||
struct stat st;
|
||||
ssize_t len;
|
||||
ssize_t done = 0;
|
||||
int fd = -1;
|
||||
DEFiRet;
|
||||
|
||||
if ((fp = fopen((const char *)pszLstnPortFileName, "w")) == NULL) {
|
||||
const int lstatRet = lstat(path, &st);
|
||||
if (lstatRet == 0 && !S_ISREG(st.st_mode)) {
|
||||
LogError(0, RS_RET_IO_ERROR,
|
||||
"imudp: listenPortFileName: "
|
||||
"refusing to write to non-regular file");
|
||||
ABORT_FINALIZE(RS_RET_IO_ERROR);
|
||||
} else if (lstatRet != 0 && errno != ENOENT) {
|
||||
LogError(errno, RS_RET_IO_ERROR,
|
||||
"imudp: listenPortFileName: "
|
||||
"error while trying to inspect file");
|
||||
ABORT_FINALIZE(RS_RET_IO_ERROR);
|
||||
}
|
||||
|
||||
fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW | O_CLOEXEC | O_NONBLOCK, S_IRUSR | S_IWUSR);
|
||||
if (fd == -1) {
|
||||
LogError(errno, RS_RET_IO_ERROR,
|
||||
"imudp: listenPortFileName: "
|
||||
"error while trying to open file");
|
||||
ABORT_FINALIZE(RS_RET_IO_ERROR);
|
||||
}
|
||||
if (fprintf(fp, "%u", listenPort) < 0) {
|
||||
if (fstat(fd, &st) != 0) {
|
||||
LogError(errno, RS_RET_IO_ERROR,
|
||||
"imudp: listenPortFileName: "
|
||||
"error while trying to write file");
|
||||
fclose(fp);
|
||||
fp = NULL;
|
||||
"error while trying to inspect open file");
|
||||
ABORT_FINALIZE(RS_RET_IO_ERROR);
|
||||
}
|
||||
if (fclose(fp) != 0) {
|
||||
fp = NULL;
|
||||
if (!S_ISREG(st.st_mode)) {
|
||||
LogError(0, RS_RET_IO_ERROR,
|
||||
"imudp: listenPortFileName: "
|
||||
"refusing to write to non-regular file");
|
||||
ABORT_FINALIZE(RS_RET_IO_ERROR);
|
||||
}
|
||||
|
||||
len = snprintf(portBuf, sizeof(portBuf), "%u", listenPort);
|
||||
portBuf[sizeof(portBuf) - 1] = '\0';
|
||||
if (len < 0 || len >= (ssize_t)sizeof(portBuf)) {
|
||||
LogError(0, RS_RET_IO_ERROR,
|
||||
"imudp: listenPortFileName: "
|
||||
"port string truncated or encoding error");
|
||||
ABORT_FINALIZE(RS_RET_IO_ERROR);
|
||||
}
|
||||
while (done < len) {
|
||||
const ssize_t written = write(fd, portBuf + done, (size_t)(len - done));
|
||||
if (written < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
LogError(errno, RS_RET_IO_ERROR,
|
||||
"imudp: listenPortFileName: "
|
||||
"error while trying to write file");
|
||||
ABORT_FINALIZE(RS_RET_IO_ERROR);
|
||||
} else if (written == 0) {
|
||||
LogError(0, RS_RET_IO_ERROR,
|
||||
"imudp: listenPortFileName: "
|
||||
"error while trying to write file");
|
||||
ABORT_FINALIZE(RS_RET_IO_ERROR);
|
||||
}
|
||||
done += written;
|
||||
}
|
||||
if (close(fd) != 0) {
|
||||
fd = -1;
|
||||
LogError(errno, RS_RET_IO_ERROR,
|
||||
"imudp: listenPortFileName: "
|
||||
"error while trying to close file");
|
||||
ABORT_FINALIZE(RS_RET_IO_ERROR);
|
||||
}
|
||||
fp = NULL;
|
||||
fd = -1;
|
||||
|
||||
finalize_it:
|
||||
if (fp != NULL) fclose(fp);
|
||||
if (fd != -1) close(fd);
|
||||
RETiRet;
|
||||
}
|
||||
|
||||
|
||||
@ -265,6 +265,7 @@ TESTS_DEFAULT = \
|
||||
sndrcv_udp_nonstdpt.sh \
|
||||
sndrcv_udp_nonstdpt_v6.sh \
|
||||
imudp_thread_hang.sh \
|
||||
imudp-listenportfilename-secure.sh \
|
||||
imudp_ratelimit_name.sh \
|
||||
omfwd_ratelimit_name.sh \
|
||||
omelasticsearch_ratelimit_name.sh \
|
||||
|
||||
34
tests/imudp-listenportfilename-secure.sh
Executable file
34
tests/imudp-listenportfilename-secure.sh
Executable file
@ -0,0 +1,34 @@
|
||||
#!/bin/bash
|
||||
# Regression test for imudp listenPortFileName path handling. The listener
|
||||
# writes its bound port before privilege drop, so the oracle is an internal
|
||||
# diagnostic that rejects a symlink handoff path plus an unchanged symlink
|
||||
# target. The timeout only bounds the foreground daemon after config activation.
|
||||
. ${srcdir:=.}/diag.sh init
|
||||
|
||||
TARGET_FILE="${RSYSLOG_DYNNAME}.target"
|
||||
PORT_FILE="${RSYSLOG_DYNNAME}.imudp_port"
|
||||
printf 'must-stay-intact\n' > "$TARGET_FILE"
|
||||
ln -s "$TARGET_FILE" "$PORT_FILE"
|
||||
|
||||
generate_conf
|
||||
add_conf '
|
||||
module(load="../plugins/imudp/.libs/imudp")
|
||||
input(type="imudp" address="127.0.0.1" port="0" listenPortFileName="'$PORT_FILE'")
|
||||
'
|
||||
|
||||
startup_common
|
||||
set +e
|
||||
timeout 3 ../tools/rsyslogd -C -n -i"${RSYSLOG_PIDBASE}.pid" -M"$RSYSLOG_MODDIR" -f"$CONF_FILE" \
|
||||
>"${RSYSLOG_DYNNAME}.startup.log" 2>&1
|
||||
rc=$?
|
||||
set -e
|
||||
if [ "$rc" -eq 0 ]; then
|
||||
echo "FAIL: expected foreground rsyslogd to keep running or report an error after activation, rc=$rc"
|
||||
cat "${RSYSLOG_DYNNAME}.startup.log"
|
||||
error_exit 1
|
||||
fi
|
||||
|
||||
printf 'must-stay-intact\n' > "${RSYSLOG_DYNNAME}.expected"
|
||||
cmp_exact_file "${RSYSLOG_DYNNAME}.expected" "$TARGET_FILE"
|
||||
content_check "listenPortFileName: refusing to write to non-regular file" "${RSYSLOG_DYNNAME}.startup.log"
|
||||
exit_test
|
||||
Loading…
x
Reference in New Issue
Block a user