From nobody Wed Nov 27 22:27:12 2024 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4XzDZm48Zzz5fsTr; Wed, 27 Nov 2024 22:27:12 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R10" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4XzDZm36szz4V0N; Wed, 27 Nov 2024 22:27:12 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1732746432; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=GEuOM/EWwVNF2dmpUqOHS6cRudJjcVUaYPPGIkj4G/U=; b=Gn+OyGnBL/4g/uj1bPO4L95AAhmsj0oZTCsn0qjL3b9hCge+3eEH68MqOy9TcK0TGRV3AA XtsfJkwk4eNg+JYe0hNVOOQN7GaarCCACbMZyRK1d3R/Dv0xqFGABIXVV3B2TdcghQpZr9 sF68+Fy0+GHOhuMbSVgV/lEfNyPROSyy+PVJYoGa4fFOu45zioS+JkccZgYi+QwK5uhCtK 5DFL0Iw9A5Ms5efJ7pAGaCpiqaNdfKpsU7EGymp23KyuBNSAHFuTYvQz72chUy20z/LSTd UHE8iI0cq96R4xrpXpkGErV/wsOtMazSzv+e/Iz3CSPgQoCSSDf0W7gLtRpbQA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1732746432; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=GEuOM/EWwVNF2dmpUqOHS6cRudJjcVUaYPPGIkj4G/U=; b=u6G1D+FMZ9fuN/xt3wZxfUspA04iQnVJDlRLHYBHrc1FGwTLQ2cLqW8jAVVsRhSustEV6I h8jK7UV6J0zTTLauHzae5mrY6qJzeNvrck6qyo8aFJjFWIq2KziwpUmTlYt5wD5Gz6xrHW C23PDC3lVe2KCW9cc7C1YYKlzJvgkwi/xy/m95xGH6N5wZEDkhlW0b3qUiwkAovY6xA9QO yeei/9vkwnt9Wmnb7uzxvebVzCKAgicOybmextHbs/JMaXYryCG4p0zhG9CyOyZEEDAdW9 hXm0mAbfxv0A7rPxEpFX9JWQqJ06C5ZTizdnX0cQBLM+LMdPP/SKxwvPy+cpmA== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1732746432; a=rsa-sha256; cv=none; b=do2Q0GgUVyIKXG8q9xYnUC6MOrS1nGn3R/Zu7wbdDfB3SAEEXUTWWQOivABH41oFu4AhgY qXkElt2sziMYRDN2UhcZ+XLc7CcxYhBIFbmydBw2jtKWRKzMlup0G8ZCdQPcd6dQzYpcN4 L19mvIKh9dfr+do30eHtdzlPWI+Qyp6NaI/21PMiXmmB9OBMYmk4mFBzQkwR2GJzurzdSL qY35IG3moRTRWuG49PA5Ky0u/Q++hIncAsRYsOxLtdJLnNloL0wT1Tyv6NPd9ULy9IKlQo X0VBQrZOZ1mult7wrGFvDwQaBr1H4qx/4+IyqSw7SCFmAiolU5MnhUlR6O8IPg== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4XzDZm2kpBz1RYF; Wed, 27 Nov 2024 22:27:12 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.18.1/8.18.1) with ESMTP id 4ARMRCon026628; Wed, 27 Nov 2024 22:27:12 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 4ARMRC7T026625; Wed, 27 Nov 2024 22:27:12 GMT (envelope-from git) Date: Wed, 27 Nov 2024 22:27:12 GMT Message-Id: <202411272227.4ARMRC7T026625@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Jake Freeland Subject: git: 61a29eca550b - main - syslogd: Log messages using libcasper List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: jfree X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 61a29eca550b80d179934a7198c41ad4d255f81c Auto-Submitted: auto-generated The branch main has been updated by jfree: URL: https://cgit.FreeBSD.org/src/commit/?id=61a29eca550b80d179934a7198c41ad4d255f81c commit 61a29eca550b80d179934a7198c41ad4d255f81c Author: Jake Freeland AuthorDate: 2024-11-27 22:25:17 +0000 Commit: Jake Freeland CommitDate: 2024-11-27 22:25:17 +0000 syslogd: Log messages using libcasper Some logging operations require access to external resources to complete. Logging to F_WALL requires on-demand access to the user accounting database. Logging to F_CONSOLE requires access to the console. Logging to F_PIPE prompts execution of a command outside of capability mode. These operations cannot be performed in capability mode, so the "p_open", "ttymsg", and "wallmsg" commands may be sent to libcasper to circumvent these limitations. Reviewed by: markj Differential Revision: https://reviews.freebsd.org/D41465 --- usr.sbin/syslogd/Makefile | 3 +- usr.sbin/syslogd/syslogd.c | 77 ++++++------- usr.sbin/syslogd/syslogd.h | 15 +++ usr.sbin/syslogd/syslogd_cap.c | 8 +- usr.sbin/syslogd/syslogd_cap.h | 25 ++++ usr.sbin/syslogd/syslogd_cap_config.c | 34 +++++- usr.sbin/syslogd/syslogd_cap_log.c | 211 ++++++++++++++++++++++++++++++++++ 7 files changed, 330 insertions(+), 43 deletions(-) diff --git a/usr.sbin/syslogd/Makefile b/usr.sbin/syslogd/Makefile index 4088b2e03651..7b202c69f7f9 100644 --- a/usr.sbin/syslogd/Makefile +++ b/usr.sbin/syslogd/Makefile @@ -15,7 +15,8 @@ LIBADD= util .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.c b/usr.sbin/syslogd/syslogd.c index 0c05884cc17d..fcf7d4747706 100644 --- a/usr.sbin/syslogd/syslogd.c +++ b/usr.sbin/syslogd/syslogd.c @@ -129,7 +129,6 @@ #include "pathnames.h" #include "syslogd.h" #include "syslogd_cap.h" -#include "ttymsg.h" const char *ConfFile = _PATH_LOGCONF; static const char *PidFile = _PATH_LOGPID; @@ -337,12 +336,10 @@ static int evaluate_prop_filter(const struct prop_filter *filter, static nvlist_t *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); @@ -1665,16 +1662,6 @@ dofsync(void) 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) { @@ -1831,8 +1818,16 @@ fprintlog_write(struct filed *f, struct iovlist *il, int flags) dprintf(" %s\n", f->f_pname); iovlist_append(il, "\n"); if (f->f_procdesc == -1) { - if ((f->f_file = p_open(f->f_pname, - &f->f_procdesc)) < 0) { + struct filed *f_in_list; + size_t i = 0; + + STAILQ_FOREACH(f_in_list, &fhead, next) { + if (f_in_list == f) + break; + ++i; + } + f->f_file = p_open(i, f->f_pname, &f->f_procdesc); + if (f->f_file < 0) { logerror(f->f_pname); break; } @@ -2073,9 +2068,12 @@ fprintlog_successive(struct filed *f, int flags) * * Write the specified message to either the entire * world, or a list of approved users. + * + * Note: This function is wrapped by cap_wallmsg() when Capsicum support is + * enabled so ttymsg() can be called. */ -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; @@ -2091,10 +2089,8 @@ wallmsg(struct filed *f, struct iovec *iov, const int iovlen) continue; if (f->f_type == F_WALL) { if ((p = ttymsg(iov, iovlen, ut->ut_line, - TTYMSGTIME)) != NULL) { - errno = 0; /* already in msg */ - logerror(p); - } + TTYMSGTIME)) != NULL) + dprintf("%s\n", p); continue; } /* should we send the message to this user? */ @@ -2103,10 +2099,8 @@ wallmsg(struct filed *f, struct iovec *iov, const int iovlen) break; if (!strcmp(f->f_uname[i], ut->ut_user)) { if ((p = ttymsg_check(iov, iovlen, ut->ut_line, - TTYMSGTIME)) != NULL) { - errno = 0; /* already in msg */ - logerror(p); - } + TTYMSGTIME)) != NULL) + dprintf("%s\n", p); break; } } @@ -2392,6 +2386,13 @@ parseconfigfile(FILE *cf, bool allow_includes, nvlist_t *nvl_conf) return (nvl_conf); } +/* + * Read configuration file and create filed entries for each line. + * + * Note: This function is wrapped by cap_readconfigfile() when Capsicum + * support is enabled so resources can be acquired outside of the security + * sandbox. + */ nvlist_t * readconfigfile(const char *path) { @@ -3433,15 +3434,18 @@ validate(struct sockaddr *sa, const char *hname) /* * Fairly similar to popen(3), but returns an open descriptor, as * opposed to a FILE *. + * + * Note: This function is wrapped by cap_p_open() when Capsicum support is + * enabled, which allows piped processes to run outside of the capability + * sandbox. */ -static int +int p_open(const char *prog, int *rpd) { struct sigaction act = { }; int pfd[2], pd; pid_t pid; char *argv[4]; /* sh -c cmd NULL */ - char errmsg[200]; if (pipe(pfd) == -1) return (-1); @@ -3456,18 +3460,14 @@ p_open(const char *prog, int *rpd) argv[1] = strdup("-c"); argv[2] = strdup(prog); argv[3] = NULL; - if (argv[0] == NULL || argv[1] == NULL || argv[2] == NULL) { - logerror("strdup"); - exit(1); - } + if (argv[0] == NULL || argv[1] == NULL || argv[2] == NULL) + err(1, "strdup"); alarm(0); act.sa_handler = SIG_DFL; for (size_t i = 0; i < nitems(sigcatch); ++i) { - if (sigaction(sigcatch[i], &act, NULL) == -1) { - logerror("sigaction"); - exit(1); - } + if (sigaction(sigcatch[i], &act, NULL) == -1) + err(1, "sigaction"); } dup2(pfd[0], STDIN_FILENO); @@ -3490,11 +3490,8 @@ p_open(const char *prog, int *rpd) */ if (fcntl(pfd[1], F_SETFL, O_NONBLOCK) == -1) { /* This is bad. */ - (void)snprintf(errmsg, sizeof(errmsg), - "Warning: cannot change pipe to PID %d to " - "non-blocking behaviour.", - (int)pid); - logerror(errmsg); + dprintf("Warning: cannot change pipe to PID %d to non-blocking" + "behaviour.", pid); } *rpd = pd; return (pfd[1]); diff --git a/usr.sbin/syslogd/syslogd.h b/usr.sbin/syslogd/syslogd.h index 16de03590ada..2d74c4d0ebbb 100644 --- a/usr.sbin/syslogd/syslogd.h +++ b/usr.sbin/syslogd/syslogd.h @@ -67,6 +67,7 @@ #include #include #include +#include #define SYSLOG_NAMES #include @@ -75,6 +76,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 */ @@ -172,11 +175,23 @@ struct filed { STAILQ_ENTRY(filed) next; /* next in linked list */ }; +/* + * 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 const char *ConfFile; extern char LocalHostName[MAXHOSTNAMELEN]; void closelogfiles(void); void logerror(const char *); +int p_open(const char *, pid_t *); nvlist_t *readconfigfile(const char *); +void wallmsg(const struct filed *, struct iovec *, const int); #endif /* !_SYSLOGD_H_ */ diff --git a/usr.sbin/syslogd/syslogd_cap.c b/usr.sbin/syslogd/syslogd_cap.c index 6f64ee8878bf..7539e6b8661b 100644 --- a/usr.sbin/syslogd/syslogd_cap.c +++ b/usr.sbin/syslogd/syslogd_cap.c @@ -45,8 +45,14 @@ casper_command(const char *cmd, const nvlist_t *limits __unused, { 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.h b/usr.sbin/syslogd/syslogd_cap.h index 24b448ff2352..420676aa72f2 100644 --- a/usr.sbin/syslogd/syslogd_cap.h +++ b/usr.sbin/syslogd/syslogd_cap.h @@ -47,8 +47,27 @@ #include "syslogd.h" +/* + * Information used to verify filed integrity when executing outside of the + * security sandbox. + */ +struct cap_filed { + size_t idx; + char pipe_cmd[MAXPATHLEN]; + SLIST_ENTRY(cap_filed) next; +}; +extern SLIST_HEAD(cfiled_list, cap_filed) cfiled_head; + +int cap_p_open(cap_channel_t *, size_t, const char *, int *); nvlist_t *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 *); nvlist_t *prop_filter_to_nvlist(const struct prop_filter *pfilter); @@ -58,8 +77,14 @@ struct prop_filter *nvlist_to_prop_filter(const nvlist_t *nvl_prop_filter); #else /* !WITH_CASPER */ +#define cap_p_open(chan, f_idx, prog, rpd) \ + p_open(prog, 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_config.c b/usr.sbin/syslogd/syslogd_cap_config.c index 366c035925d6..09d49b0a41b2 100644 --- a/usr.sbin/syslogd/syslogd_cap_config.c +++ b/usr.sbin/syslogd/syslogd_cap_config.c @@ -277,6 +277,9 @@ cap_readconfigfile(cap_channel_t *chan, const char *path) int casper_readconfigfile(nvlist_t *nvlin, nvlist_t *nvlout) { + const nvlist_t * const *filed_list; + nvlist_t *nvl_conf; + size_t n_fileds; const char *path; /* @@ -291,6 +294,35 @@ casper_readconfigfile(nvlist_t *nvlin, nvlist_t *nvlout) strlcpy(LocalHostName, nvlist_get_string(nvlin, "LocalHostName"), sizeof(LocalHostName)); - nvlist_move_nvlist(nvlout, "nvl_conf", readconfigfile(path)); + nvl_conf = readconfigfile(path); + + /* Remove old filed data in case we are reloading. */ + while (!SLIST_EMPTY(&cfiled_head)) { + struct cap_filed *cfiled; + + cfiled = SLIST_FIRST(&cfiled_head); + SLIST_REMOVE_HEAD(&cfiled_head, next); + free(cfiled); + } + /* Record F_PIPE filed data for use in p_open(). */ + if (!nvlist_exists_nvlist_array(nvl_conf, "filed_list")) + return (0); + filed_list = nvlist_get_nvlist_array(nvl_conf, "filed_list", &n_fileds); + for (size_t i = 0; i < n_fileds; ++i) { + if (nvlist_get_number(filed_list[i], "f_type") == F_PIPE) { + struct cap_filed *cfiled; + const char *pipe_cmd; + + cfiled = malloc(sizeof(*cfiled)); + if (cfiled == NULL) + err(1, "malloc"); + cfiled->idx = i; + pipe_cmd = nvlist_get_string(filed_list[i], "f_pname"); + strlcpy(cfiled->pipe_cmd, pipe_cmd, sizeof(cfiled->pipe_cmd)); + SLIST_INSERT_HEAD(&cfiled_head, cfiled, next); + } + } + + nvlist_move_nvlist(nvlout, "nvl_conf", nvl_conf); return (0); } diff --git a/usr.sbin/syslogd/syslogd_cap_log.c b/usr.sbin/syslogd/syslogd_cap_log.c new file mode 100644 index 000000000000..0156cc6f6b6c --- /dev/null +++ b/usr.sbin/syslogd/syslogd_cap_log.c @@ -0,0 +1,211 @@ +/*- + * 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 + +#include "syslogd_cap.h" + +struct cfiled_list cfiled_head; + +int +cap_p_open(cap_channel_t *chan, size_t filed_idx, const char *prog, + int *procdesc) +{ + nvlist_t *nvl = nvlist_create(0); + int error, pipedesc_w; + + nvlist_add_string(nvl, "cmd", "p_open"); + nvlist_add_number(nvl, "filed_idx", filed_idx); + nvlist_add_string(nvl, "prog", prog); + 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 cap_filed *cfiled; + size_t filed_idx; + int pipedesc_w, procdesc = -1; + const char *prog; + + filed_idx = nvlist_get_number(nvlin, "filed_idx"); + prog = nvlist_get_string(nvlin, "prog"); + SLIST_FOREACH(cfiled, &cfiled_head, next) { + if (cfiled->idx != filed_idx) + continue; + if (strcmp(cfiled->pipe_cmd, prog) != 0) + return (-1); + + pipedesc_w = p_open(prog, &procdesc); + if (pipedesc_w == -1) + return (-1); + nvlist_move_descriptor(nvlout, "pipedesc_w", pipedesc_w); + nvlist_move_descriptor(nvlout, "procdesc", procdesc); + return (0); + } + + return (-1); +} + +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_append_string_array(nvl, "iov_strs", iov[i].iov_base); + 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) +{ + char **nvlstrs; + struct iovec *iov; + size_t iovcnt; + int tmout; + const char *line; + + nvlstrs = nvlist_take_string_array(nvlin, "iov_strs", &iovcnt); + assert(iovcnt <= TTYMSG_IOV_MAX); + iov = calloc(iovcnt, sizeof(*iov)); + if (iov == NULL) + err(EXIT_FAILURE, "calloc"); + for (size_t i = 0; i < iovcnt; ++i) { + iov[i].iov_base = nvlstrs[i]; + iov[i].iov_len = strlen(nvlstrs[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_append_string_array(nvl, "iov_strs", iov[i].iov_base); + + 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; + char **nvlstrs; + struct iovec *iov; + size_t sz; + + f = nvlist_get_binary(nvlin, "filed", &sz); + assert(sz == sizeof(*f)); + nvlstrs = nvlist_take_string_array(nvlin, "iov_strs", &sz); + assert(sz <= TTYMSG_IOV_MAX); + iov = calloc(sz, sizeof(*iov)); + if (iov == NULL) + err(EXIT_FAILURE, "calloc"); + for (size_t i = 0; i < sz; ++i) { + iov[i].iov_base = nvlstrs[i]; + iov[i].iov_len = strlen(nvlstrs[i]); + } + wallmsg(f, iov, sz); + + for (size_t i = 0; i < sz; ++i) + free(iov[i].iov_base); + free(iov); + return (0); +}