diff --git a/usr.sbin/syslogd/Makefile b/usr.sbin/syslogd/Makefile --- a/usr.sbin/syslogd/Makefile +++ b/usr.sbin/syslogd/Makefile @@ -17,7 +17,8 @@ .if ${MK_CASPER} != "no" SRCS+= syslogd_cap.c \ - syslogd_cap_config.c + syslogd_cap_config.c \ + syslogd_cap_log.c CFLAGS+= -DWITH_CASPER LIBADD+= cap_net casper nv .endif 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 @@ -66,6 +66,7 @@ #include #include #include +#include #define SYSLOG_NAMES #include @@ -74,6 +75,8 @@ #include #include +#include "ttymsg.h" + #define MAXLINE 8192 /* maximum line length */ #define MAXSVLINE MAXLINE /* maximum saved line length */ #define MAXUNAMES 20 /* maximum number of user names */ @@ -179,11 +182,23 @@ }; extern STAILQ_HEAD(filed_list, filed) fhead; +/* + * List of iovecs to which entries can be appended. + * Used for constructing the message to be logged. + */ +struct iovlist { + struct iovec iov[TTYMSG_IOV_MAX]; + size_t iovcnt; + size_t totalsize; +}; + extern char LocalHostName[MAXHOSTNAMELEN]; void closelogfiles(void); void logerror(const char *); +int p_open(struct filed *, int *); void parseconfigfile(FILE *, bool); void readconfigfile(const char *); +void wallmsg(const struct filed *, struct iovec *, const int); #endif /* !_SYSLOGD_H_ */ 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 @@ -142,7 +142,6 @@ #include "pathnames.h" #include "syslogd.h" #include "syslogd_cap.h" -#include "ttymsg.h" static const char *ConfFile = _PATH_LOGCONF; static const char *PidFile = _PATH_LOGPID; @@ -352,12 +351,10 @@ static struct prop_filter *prop_filter_compile(const char *); static void parsemsg(const char *, char *); static void printsys(char *); -static int p_open(const char *, pid_t *); static const char *ttymsg_check(struct iovec *, int, char *, int); static void usage(void); static bool validate(struct sockaddr *, const char *); static void unmapped(struct sockaddr *); -static void wallmsg(struct filed *, struct iovec *, const int iovlen); static int waitdaemon(int); static void increase_rcvbuf(int); @@ -1672,16 +1669,6 @@ needdofsync = false; } -/* - * List of iovecs to which entries can be appended. - * Used for constructing the message to be logged. - */ -struct iovlist { - struct iovec iov[TTYMSG_IOV_MAX]; - size_t iovcnt; - size_t totalsize; -}; - static void iovlist_init(struct iovlist *il) { @@ -1838,8 +1825,8 @@ dprintf(" %s\n", f->fu_pipe_pname); iovlist_append(il, "\n"); if (f->fu_pipe_pd == -1) { - if ((f->f_file = p_open(f->fu_pipe_pname, - &f->fu_pipe_pd)) < 0) { + f->f_file = p_open(f, &f->fu_pipe_pd); + if (f->f_file < 0) { logerror(f->fu_pipe_pname); break; } @@ -2081,8 +2068,8 @@ * Write the specified message to either the entire * world, or a list of approved users. */ -static void -wallmsg(struct filed *f, struct iovec *iov, const int iovlen) +void +wallmsg(const struct filed *f, struct iovec *iov, const int iovlen) { static int reenter; /* avoid calling ourselves */ struct utmpx *ut; @@ -3470,8 +3457,8 @@ * Fairly similar to popen(3), but returns an open descriptor, as * opposed to a FILE *. */ -static int -p_open(const char *prog, int *rpd) +int +p_open(struct filed *f, int *rpd) { struct sigaction act = { }; int pfd[2], pd; @@ -3490,7 +3477,7 @@ (void)setsid(); /* Avoid catching SIGHUPs. */ argv[0] = strdup("sh"); argv[1] = strdup("-c"); - argv[2] = strdup(prog); + argv[2] = strdup(f->fu_pipe_pname); argv[3] = NULL; if (argv[0] == NULL || argv[1] == NULL || argv[2] == NULL) { logerror("strdup"); diff --git a/usr.sbin/syslogd/syslogd_cap.h b/usr.sbin/syslogd/syslogd_cap.h --- a/usr.sbin/syslogd/syslogd_cap.h +++ b/usr.sbin/syslogd/syslogd_cap.h @@ -43,16 +43,30 @@ #include "syslogd.h" +int cap_p_open(cap_channel_t *, struct filed *, int *); void cap_readconfigfile(cap_channel_t *, const char *); +const char *cap_ttymsg(cap_channel_t *, struct iovec *, int, const char *, int); +void cap_wallmsg(cap_channel_t *, const struct filed *, struct iovec *, + const int); + +int casper_p_open(nvlist_t *, nvlist_t *); int casper_readconfigfile(nvlist_t *, nvlist_t *); +int casper_ttymsg(nvlist_t *, nvlist_t *); +int casper_wallmsg(nvlist_t *); nvlist_t *filed_to_nvlist(const struct filed *); struct filed *nvlist_to_filed(const nvlist_t *); #else /* !WITH_CASPER */ +#define cap_p_open(chan, f, rpd) \ + p_open(f, rpd) #define cap_readconfigfile(chan, cf) \ readconfigfile(cf) +#define cap_ttymsg(chan, iov, iovcnt, line, tmout) \ + ttymsg(iov, iovcnt, line, tmout) +#define cap_wallmsg(chan, f, iov, iovcnt) \ + wallmsg(f, iov, iovcnt) #endif /* WITH_CASPER */ diff --git a/usr.sbin/syslogd/syslogd_cap.c b/usr.sbin/syslogd/syslogd_cap.c --- a/usr.sbin/syslogd/syslogd_cap.c +++ b/usr.sbin/syslogd/syslogd_cap.c @@ -209,8 +209,14 @@ { int error = EINVAL; - if (strcmp(cmd, "readconfigfile") == 0) + if (strcmp(cmd, "p_open") == 0) + error = casper_p_open(nvlin, nvlout); + else if (strcmp(cmd, "readconfigfile") == 0) error = casper_readconfigfile(nvlin, nvlout); + else if (strcmp(cmd, "ttymsg") == 0) + error = casper_ttymsg(nvlin, nvlout); + else if (strcmp(cmd, "wallmsg") == 0) + error = casper_wallmsg(nvlin); return (error); } diff --git a/usr.sbin/syslogd/syslogd_cap_log.c b/usr.sbin/syslogd/syslogd_cap_log.c new file mode 100644 --- /dev/null +++ b/usr.sbin/syslogd/syslogd_cap_log.c @@ -0,0 +1,223 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 The FreeBSD Foundation + * + * This software was developed by Jake Freeland + * under sponsorship from the FreeBSD Foundation. + * + * 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 AUTHOR 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 AUTHOR 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. + */ + +#include +#include + +#include "syslogd_cap.h" + +static void +iovec_to_nvlist(const struct iovec *iov, nvlist_t *nvl) +{ + nvlist_add_string(nvl, "iov_base", iov->iov_base); + nvlist_add_number(nvl, "iov_len", iov->iov_len); +} + +static void +nvlist_to_iovec(const nvlist_t *nvl, struct iovec *iov) +{ + iov->iov_base = strdup(nvlist_get_string(nvl, "iov_base")); + iov->iov_len = nvlist_get_number(nvl, "iov_len"); +} + +int +cap_p_open(cap_channel_t *chan, struct filed *f_to_find, int *procdesc) +{ + nvlist_t *nvl = nvlist_create(0); + struct filed *f_in_list; + int error, i = 0, pipedesc_w; + + nvlist_add_string(nvl, "cmd", "p_open"); + STAILQ_FOREACH(f_in_list, &fhead, next) { + if (f_in_list == f_to_find) + break; + ++i; + } + nvlist_add_number(nvl, "filed_idx", i); + nvl = cap_xfer_nvlist(chan, nvl); + if (nvl == NULL) { + logerror("Failed to xfer p_open nvlist"); + exit(1); + } + error = nvlist_get_number(nvl, "error"); + if (error != 0) { + errno = error; + logerror("Failed to open piped command"); + } + pipedesc_w = dnvlist_take_descriptor(nvl, "pipedesc_w", -1); + *procdesc = dnvlist_take_descriptor(nvl, "procdesc", -1); + + nvlist_destroy(nvl); + return (pipedesc_w); +} + +int +casper_p_open(nvlist_t *nvlin, nvlist_t *nvlout) +{ + struct filed *f; + int filed_idx, i, pipedesc_w, procdesc = -1; + + filed_idx = nvlist_get_number(nvlin, "filed_idx"); + i = 0; + STAILQ_FOREACH(f, &fhead, next) { + if (i == filed_idx) + break; + ++i; + } + + pipedesc_w = p_open(f, &procdesc); + if (pipedesc_w != -1) + nvlist_add_descriptor(nvlout, "pipedesc_w", pipedesc_w); + if (procdesc != -1) + nvlist_add_descriptor(nvlout, "procdesc", procdesc); + return (0); +} + +const char * +cap_ttymsg(cap_channel_t *chan, struct iovec *iov, int iovcnt, + const char *line, int tmout) +{ + nvlist_t *nvl = nvlist_create(0); + int error; + static char errbuf[1024]; + char *ret = NULL; + + nvlist_add_string(nvl, "cmd", "ttymsg"); + for (int i = 0; i < iovcnt; ++i) { + nvlist_t *nvl_iov = nvlist_create(0); + iovec_to_nvlist(iov + i, nvl_iov); + nvlist_append_nvlist_array(nvl, "iovec_list", nvl_iov); + nvlist_destroy(nvl_iov); + } + nvlist_add_number(nvl, "iovec_count", iovcnt); + nvlist_add_string(nvl, "line", line); + nvlist_add_number(nvl, "tmout", tmout); + + nvl = cap_xfer_nvlist(chan, nvl); + if (nvl == NULL) { + logerror("Failed to xfer ttymsg nvlist"); + exit(1); + } + error = nvlist_get_number(nvl, "error"); + if (error != 0) { + errno = error; + logerror("Failed to ttymsg"); + } + if (nvlist_exists_string(nvl, "errstr")) { + const char *errstr = nvlist_get_string(nvl, "errstr"); + (void)strlcpy(errbuf, errstr, sizeof(errbuf)); + ret = errbuf; + } + + nvlist_destroy(nvl); + return (ret); +} + +int +casper_ttymsg(nvlist_t *nvlin, nvlist_t *nvlout) +{ + const nvlist_t * const *nvl_iov; + struct iovec *iov; + size_t iovcnt; + int tmout; + const char *line; + + iovcnt = nvlist_get_number(nvlin, "iovec_count"); + nvl_iov = nvlist_get_nvlist_array(nvlin, "iovec_list", &iovcnt); + iov = calloc(iovcnt, sizeof(*iov)); + for (size_t i = 0; i < iovcnt; ++i) + nvlist_to_iovec(nvl_iov[i], iov + i); + line = nvlist_get_string(nvlin, "line"); + tmout = nvlist_get_number(nvlin, "tmout"); + line = ttymsg(iov, iovcnt, line, tmout); + if (line != NULL) + nvlist_add_string(nvlout, "errstr", line); + + free(iov); + return (0); +} + +void +cap_wallmsg(cap_channel_t *chan, const struct filed *f, struct iovec *iov, + int iovcnt) +{ + nvlist_t *nvl = nvlist_create(0); + int error; + + nvlist_add_string(nvl, "cmd", "wallmsg"); + /* + * The filed_to_nvlist() function is not needed + * here because wallmsg() only uses f_type and + * fu_uname members, which are both inline. + */ + nvlist_add_binary(nvl, "filed", f, sizeof(*f)); + for (int i = 0; i < iovcnt; ++i) { + nvlist_t *nvl_iov = nvlist_create(0); + iovec_to_nvlist(iov + i, nvl_iov); + nvlist_append_nvlist_array(nvl, "iovec_list", nvl_iov); + nvlist_destroy(nvl_iov); + } + nvlist_add_number(nvl, "iovec_count", iovcnt); + + nvl = cap_xfer_nvlist(chan, nvl); + if (nvl == NULL) { + logerror("Failed to xfer wallmsg nvlist"); + exit(1); + } + error = nvlist_get_number(nvl, "error"); + if (error != 0) { + errno = error; + logerror("Failed to wallmsg"); + } + nvlist_destroy(nvl); +} + +int +casper_wallmsg(nvlist_t *nvlin) +{ + const struct filed *f; + const nvlist_t * const *nvl_iov; + struct iovec *iov; + size_t iovcnt, sz; + + sz = sizeof(*f); + f = nvlist_get_binary(nvlin, "filed", &sz); + iovcnt = nvlist_get_number(nvlin, "iovec_count"); + nvl_iov = nvlist_get_nvlist_array(nvlin, "iovec_list", &iovcnt); + iov = calloc(iovcnt, sizeof(*iov)); + for (size_t i = 0; i < iovcnt; ++i) + nvlist_to_iovec(nvl_iov[i], iov + i); + wallmsg(f, iov, iovcnt); + + for (size_t i = 0; i < iovcnt; ++i) + free(iov[i].iov_base); + free(iov); + return (0); +}