Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F142943822
D23022.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
8 KB
Referenced Files
None
Subscribers
None
D23022.id.diff
View Options
diff --git a/usr.sbin/inetd/builtins.c b/usr.sbin/inetd/builtins.c
--- a/usr.sbin/inetd/builtins.c
+++ b/usr.sbin/inetd/builtins.c
@@ -37,6 +37,7 @@
#include <sys/uio.h>
#include <sys/utsname.h>
+#include <capsicum_helpers.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
@@ -73,12 +74,12 @@
struct biltin biltins[] = {
/* Echo received data */
- { "echo", SOCK_STREAM, BIF_FORK, -1, echo_stream },
+ { "echo", SOCK_STREAM, BIF_CAPENTER, -1, echo_stream },
{ "echo", SOCK_DGRAM, 0, 1, echo_dg },
/* Internet /dev/null */
- { "discard", SOCK_STREAM, BIF_FORK, -1, discard_stream },
- { "discard", SOCK_DGRAM, 0, 1, discard_dg },
+ { "discard", SOCK_STREAM, BIF_CAPENTER, -1, discard_stream },
+ { "discard", SOCK_DGRAM, BIF_CAPENTER, 1, discard_dg },
/* Return 32 bit time since 1900 */
{ "time", SOCK_STREAM, 0, -1, machtime_stream },
@@ -89,9 +90,14 @@
{ "daytime", SOCK_DGRAM, 0, 1, daytime_dg },
/* Familiar character generator */
- { "chargen", SOCK_STREAM, BIF_FORK, -1, chargen_stream },
+ { "chargen", SOCK_STREAM, BIF_CAPENTER, -1, chargen_stream },
{ "chargen", SOCK_DGRAM, 0, 1, chargen_dg },
+ /*
+ * tcpmux cap_enter is effectively ignored; we explicitly check if the
+ * service is tcpmux, then we invoke it manually and proceed as if the
+ * service it resolved to was selected.
+ */
{ "tcpmux", SOCK_STREAM, BIF_FORK, -1, (bi_fn_t *)tcpmux },
{ "auth", SOCK_STREAM, BIF_FORK, -1, ident_stream },
diff --git a/usr.sbin/inetd/inetd.h b/usr.sbin/inetd/inetd.h
--- a/usr.sbin/inetd/inetd.h
+++ b/usr.sbin/inetd/inetd.h
@@ -36,6 +36,7 @@
#include <netinet/in.h>
+#include <stdbool.h>
#include <stdio.h>
#define BUFSIZE 8192
@@ -151,6 +152,11 @@
extern struct biltin biltins[];
#define BIF_FORK 0x0001 /* should fork before call */
+#define BIF_CAPENTER 0x0002 /* enter capability mode upon fork */
#define SERVTAB_FORK(sep) \
- ((sep)->se_bi == NULL || ((sep)->se_bi->bi_flags & BIF_FORK) != 0)
+ ((sep)->se_bi == NULL || \
+ ((sep)->se_bi->bi_flags & (BIF_FORK | BIF_CAPENTER)) != 0)
+
+#define SERVTAB_CAPENTER(sep) \
+ ((sep)->se_bi != NULL && ((sep)->se_bi->bi_flags & BIF_CAPENTER) != 0)
diff --git a/usr.sbin/inetd/inetd.c b/usr.sbin/inetd/inetd.c
--- a/usr.sbin/inetd/inetd.c
+++ b/usr.sbin/inetd/inetd.c
@@ -96,6 +96,7 @@
* #endif
*/
#include <sys/param.h>
+#include <sys/capsicum.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/wait.h>
@@ -110,6 +111,7 @@
#include <rpc/rpc.h>
#include <rpc/pmap_clnt.h>
+#include <capsicum_helpers.h>
#include <ctype.h>
#include <errno.h>
#include <err.h>
@@ -280,6 +282,9 @@
static LIST_HEAD(, procinfo) proctable[PERIPSIZE];
+static cap_rights_t ctrl_rights, dgram_svc_rights, svc_rights;
+static unsigned long *ctrl_ioctls, nctrl_ioctls;
+
static int
getvalue(const char *arg, int *value, const char *whine)
{
@@ -313,6 +318,101 @@
}
#endif
+static void
+setup_sigpipe(void)
+{
+ const unsigned long sigpipe_cmds[] = { FIONREAD };
+ cap_rights_t sigrxpipe_rights, sigtxpipe_rights;
+
+ cap_rights_init(&sigtxpipe_rights, CAP_WRITE);
+ cap_rights_init(&sigrxpipe_rights, CAP_READ, CAP_IOCTL, CAP_EVENT);
+ if (pipe2(signalpipe, O_CLOEXEC) != 0) {
+ syslog(LOG_ERR, "pipe: %m");
+ exit(EX_OSERR);
+ }
+ if (caph_rights_limit(signalpipe[1], &sigtxpipe_rights) == -1) {
+ syslog(LOG_ERR, "failed to limit tx signalpipe: %m");
+ exit(EX_OSERR);
+ }
+ if (caph_rights_limit(signalpipe[0], &sigrxpipe_rights) == -1 ||
+ caph_ioctls_limit(signalpipe[0], sigpipe_cmds,
+ nitems(sigpipe_cmds)) == -1) {
+ syslog(LOG_ERR, "failed to limit rx signalpipe: %m");
+ exit(EX_OSERR);
+ }
+}
+
+static void
+prepare_ctrl_caps(void)
+{
+ const unsigned long std_ctrl_ioctls[] = { FIONBIO, FIONREAD };
+ size_t i, j;
+
+ /*
+ * The rights we're imposing on these sockets will be passed down to
+ * inetd services as stdio, so we need to both be somewhat respectful of
+ * what they may reasonably attempt to do and we need to make sure we
+ * apply a superset of the standard rights we grant to stdio.
+ */
+ caph_stream_rights(&ctrl_rights, CAPH_READ | CAPH_WRITE);
+ caph_stream_rights(&svc_rights, CAPH_READ | CAPH_WRITE);
+
+ /* Control rights need to be a superset of service rights. */
+ cap_rights_set(&ctrl_rights, CAP_ACCEPT, CAP_BIND, CAP_CONNECT,
+ CAP_LISTEN, CAP_SETSOCKOPT, CAP_GETSOCKNAME);
+ cap_rights_set(&svc_rights, CAP_SETSOCKOPT, CAP_GETSOCKNAME);
+#ifdef LIBWRAP
+ cap_rights_set(&ctrl_rights, CAP_GETPEERNAME);
+ cap_rights_set(&svc_rights, CAP_GETPEERNAME);
+#endif
+ /* Now build dgram_svc_rights as based on svc_rights + CAP_CONNECT. */
+ cap_rights_init(&dgram_svc_rights);
+ cap_rights_merge(&dgram_svc_rights, &svc_rights);
+ cap_rights_set(&dgram_svc_rights, CAP_CONNECT);
+
+ nctrl_ioctls = nitems(caph_stream_cmds) + nitems(std_ctrl_ioctls);
+ ctrl_ioctls = calloc(nctrl_ioctls, sizeof(*ctrl_ioctls));
+ if (ctrl_ioctls == NULL) {
+ syslog(LOG_ERR, "calloc: %m");
+ exit(EX_OSERR);
+ }
+
+ for (i = 0, j = 0; i < nitems(caph_stream_cmds); ++i, ++j)
+ ctrl_ioctls[j] = caph_stream_cmds[i];
+ for (i = 0; i < nitems(std_ctrl_ioctls); ++i, ++j)
+ ctrl_ioctls[j] = std_ctrl_ioctls[i];
+}
+
+/*
+ * Service sockets are created via accept(2), thus inheriting the rights of the
+ * control socket. Therefore, control caps must be a superset of those needed
+ * by the services. Apply rights here for both service sockets and control
+ * socket to make it easier to audit what is happening.
+ *
+ * rights(4) are applied based both on whether we're a control or service
+ * socket, and whether we're UDP or TCP for service sockets. ioctl limitations
+ * are applied purely for control sockets.
+ */
+static void
+setup_ctrl_caps(int fd, struct servtab *sep)
+{
+ cap_rights_t *rights;
+
+ if (sep != NULL && sep->se_socktype == SOCK_DGRAM)
+ rights = &dgram_svc_rights;
+ else if (sep != NULL)
+ rights = &svc_rights;
+ else
+ rights = &ctrl_rights;
+
+ if (caph_rights_limit(fd, rights) == -1 || (sep == NULL &&
+ caph_ioctls_limit(fd, ctrl_ioctls, nctrl_ioctls) == -1)) {
+ syslog(LOG_ERR, "failed to limit %s sock: %m",
+ sep != NULL ? "service" : "control");
+ exit(EX_OSERR);
+ }
+}
+
int
main(int argc, char **argv)
{
@@ -320,7 +420,7 @@
struct passwd *pwd;
struct group *grp;
struct sigaction sa, saalrm, sachld, sahup, sapipe;
- int ch, dofork;
+ int ch;
pid_t pid;
char buf[50];
#ifdef LOGIN_CAP
@@ -337,8 +437,17 @@
const char *servname;
int error;
struct conninfo *conn;
+ bool dofork;
openlog("inetd", LOG_PID | LOG_NOWAIT | LOG_PERROR, LOG_DAEMON);
+ /* Relies on syslog(3). */
+ prepare_ctrl_caps();
+
+ caph_cache_catpages();
+ if (caph_limit_stdio() == -1) {
+ syslog(LOG_ERR, "caph_limit_stdio: %m");
+ exit(EX_OSERR);
+ }
while ((ch = getopt(argc, argv, "dlwWR:a:c:C:p:s:")) != -1)
switch(ch) {
@@ -543,10 +652,7 @@
(void)setenv("inetd_dummy", dummy, 1);
}
- if (pipe2(signalpipe, O_CLOEXEC) != 0) {
- syslog(LOG_ERR, "pipe: %m");
- exit(EX_OSERR);
- }
+ setup_sigpipe();
FD_SET(signalpipe[0], &allsock);
#ifdef SANITY_CHECK
nsock++;
@@ -629,6 +735,13 @@
close(ctrl);
continue;
}
+
+ /*
+ * This will limit the service effectively to
+ * read/write/select for all spawned processes, both
+ * builtin and external.
+ */
+ setup_ctrl_caps(ctrl, sep);
i = 0;
if (ioctl(sep->se_fd, FIONBIO, &i) < 0)
syslog(LOG_ERR, "ioctl1(FIONBIO, 0): %m");
@@ -644,8 +757,18 @@
close(ctrl);
continue;
}
- } else
+ } else {
+ /*
+ * "wait" sockets and datagram sockets won't have
+ * any further limiting applied to them. For the
+ * former, the service needs to be able to accept(2)
+ * other sockets. The latter has already been
+ * limited as far as it can go, since we won't be
+ * needing any further bind(2).
+ */
ctrl = sep->se_fd;
+ }
+
if (dolog && !ISWRAP(sep)) {
char pname[NI_MAXHOST] = "unknown";
socklen_t sl;
@@ -741,6 +864,7 @@
_exit(0);
}
}
+
#ifdef LIBWRAP
if (ISWRAP(sep)) {
inetd_setproctitle("wrapping", ctrl);
@@ -771,7 +895,13 @@
}
}
#endif
+
if (sep->se_bi) {
+ if (SERVTAB_CAPENTER(sep) &&
+ caph_enter() == -1) {
+ syslog(LOG_ERR, "cap_enter: %m");
+ _exit(0);
+ }
(*sep->se_bi->bi_fn)(ctrl, sep);
} else {
if (debug)
@@ -1253,6 +1383,8 @@
sep->se_service, sep->se_proto);
return;
}
+
+ setup_ctrl_caps(sep->se_fd, NULL);
#define turnon(fd, opt) \
setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on))
if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
@@ -1368,6 +1500,8 @@
}
if (sep->se_socktype == SOCK_STREAM)
listen(sep->se_fd, -1);
+ else if (sep->se_socktype == SOCK_DGRAM)
+ setup_ctrl_caps(sep->se_fd, sep);
enable(sep);
if (debug) {
warnx("registered %s on %d",
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Jan 25, 8:59 PM (11 h, 43 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
27968563
Default Alt Text
D23022.id.diff (8 KB)
Attached To
Mode
D23022: inetd: a light introduction to capsicum
Attached
Detach File
Event Timeline
Log In to Comment