Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F168844284
D23022.id66304.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
9 KB
Referenced Files
None
Subscribers
None
D23022.id66304.diff
View Options
Index: usr.sbin/inetd/builtins.c
===================================================================
--- usr.sbin/inetd/builtins.c
+++ usr.sbin/inetd/builtins.c
@@ -39,6 +39,7 @@
#include <sys/uio.h>
#include <sys/utsname.h>
+#include <capsicum_helpers.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
@@ -75,30 +76,37 @@
struct biltin biltins[] = {
/* Echo received data */
- { "echo", SOCK_STREAM, 1, -1, echo_stream },
- { "echo", SOCK_DGRAM, 0, 1, echo_dg },
+ { "echo", SOCK_STREAM, true, -1, echo_stream, true },
+ { "echo", SOCK_DGRAM, false, 1, echo_dg, false },
/* Internet /dev/null */
- { "discard", SOCK_STREAM, 1, -1, discard_stream },
- { "discard", SOCK_DGRAM, 0, 1, discard_dg },
+ { "discard", SOCK_STREAM, true, -1, discard_stream, true },
+ { "discard", SOCK_DGRAM, false, 1, discard_dg, false },
/* Return 32 bit time since 1900 */
- { "time", SOCK_STREAM, 0, -1, machtime_stream },
- { "time", SOCK_DGRAM, 0, 1, machtime_dg },
+ { "time", SOCK_STREAM, false, -1, machtime_stream,
+ false },
+ { "time", SOCK_DGRAM, false, 1, machtime_dg, false },
/* Return human-readable time */
- { "daytime", SOCK_STREAM, 0, -1, daytime_stream },
- { "daytime", SOCK_DGRAM, 0, 1, daytime_dg },
+ { "daytime", SOCK_STREAM, false, -1, daytime_stream, false },
+ { "daytime", SOCK_DGRAM, false, 1, daytime_dg, false },
/* Familiar character generator */
- { "chargen", SOCK_STREAM, 1, -1, chargen_stream },
- { "chargen", SOCK_DGRAM, 0, 1, chargen_dg },
+ { "chargen", SOCK_STREAM, true, -1, chargen_stream, true },
+ { "chargen", SOCK_DGRAM, false, 1, chargen_dg, false },
- { "tcpmux", SOCK_STREAM, 1, -1, (bi_fn_t *)tcpmux },
+ /*
+ * 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, true, -1, (bi_fn_t *)tcpmux,
+ false },
- { "auth", SOCK_STREAM, 1, -1, ident_stream },
+ { "auth", SOCK_STREAM, true, -1, ident_stream, false },
- { NULL, 0, 0, 0, NULL }
+ { NULL, 0, 0, 0, NULL, false }
};
/*
@@ -621,6 +629,8 @@
iderror(lport, fport, s, ID_UNKNOWN);
fakeid_fd = open(p, O_RDONLY | O_NONBLOCK);
free(p);
+ if (caph_limit_stream(fakeid_fd, CAPH_READ) == -1)
+ goto fakeid_fail;
if (fakeid_fd == -1 || fstat(fakeid_fd, &sb) == -1 ||
!S_ISREG(sb.st_mode))
goto fakeid_fail;
Index: usr.sbin/inetd/inetd.h
===================================================================
--- usr.sbin/inetd/inetd.h
+++ usr.sbin/inetd/inetd.h
@@ -38,6 +38,7 @@
#include <netinet/in.h>
+#include <stdbool.h>
#include <stdio.h>
#define BUFSIZE 8192
@@ -146,8 +147,9 @@
struct biltin {
const char *bi_service; /* internally provided service name */
int bi_socktype; /* type of socket supported */
- short bi_fork; /* 1 if should fork before call */
+ bool bi_fork; /* 1 if should fork before call */
int bi_maxchild; /* max number of children, -1=default */
bi_fn_t *bi_fn; /* function which performs it */
+ bool bi_capenter; /* enter capability mode upon fork */
};
extern struct biltin biltins[];
Index: usr.sbin/inetd/inetd.c
===================================================================
--- usr.sbin/inetd/inetd.c
+++ usr.sbin/inetd/inetd.c
@@ -111,6 +111,7 @@
* #endif
*/
#include <sys/param.h>
+#include <sys/capsicum.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/wait.h>
@@ -125,6 +126,7 @@
#include <rpc/rpc.h>
#include <rpc/pmap_clnt.h>
+#include <capsicum_helpers.h>
#include <ctype.h>
#include <errno.h>
#include <err.h>
@@ -295,6 +297,9 @@
static LIST_HEAD(, procinfo) proctable[PERIPSIZE];
+static cap_rights_t ctrl_rights, dgram_svc_rights, svc_rights;
+static unsigned long *ctrl_cmds, nctrl_cmds;
+
static int
getvalue(const char *arg, int *value, const char *whine)
{
@@ -328,6 +333,99 @@
}
#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_cmds[] = { 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_cmds = nitems(caph_stream_cmds) + nitems(std_ctrl_cmds);
+ ctrl_cmds = reallocarray(NULL, nctrl_cmds, sizeof(*ctrl_cmds));
+ if (ctrl_cmds == NULL) {
+ syslog(LOG_ERR, "reallocarray: %m");
+ exit(EX_OSERR);
+ }
+
+ for (i = 0, j = 0; i < nitems(caph_stream_cmds); ++i, ++j)
+ ctrl_cmds[j] = caph_stream_cmds[i];
+ for (i = 0; i < nitems(std_ctrl_cmds); ++i, ++j)
+ ctrl_cmds[j] = std_ctrl_cmds[i];
+}
+
+/*
+ * Both service and control capabilities are handled through here, to simplify
+ * paths needed to read to understand what can be done. 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.
+ */
+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) {
+ syslog(LOG_ERR, "failed to limit %s sock: %m",
+ sep != NULL ? "service" : "control");
+ exit(EX_OSERR);
+ }
+ if (sep == NULL && caph_ioctls_limit(fd, ctrl_cmds, nctrl_cmds) == -1) {
+ syslog(LOG_ERR, "failed to limit control ioctls: %m");
+ exit(EX_OSERR);
+ }
+}
+
int
main(int argc, char **argv)
{
@@ -335,7 +433,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
@@ -352,8 +450,16 @@
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();
+
+ if (caph_limit_stdio() == -1) {
+ syslog(LOG_ERR, "caph_limit_stdio: %m");
+ exit(1);
+ }
while ((ch = getopt(argc, argv, "dlwWR:a:c:C:p:s:")) != -1)
switch(ch) {
@@ -558,10 +664,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++;
@@ -644,6 +747,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");
@@ -661,6 +771,8 @@
}
} else
ctrl = sep->se_fd;
+ if (sep->se_socktype == SOCK_DGRAM)
+ setup_ctrl_caps(ctrl, sep);
if (dolog && !ISWRAP(sep)) {
char pname[NI_MAXHOST] = "unknown";
socklen_t sl;
@@ -756,6 +868,7 @@
_exit(0);
}
}
+
#ifdef LIBWRAP
if (ISWRAP(sep)) {
inetd_setproctitle("wrapping", ctrl);
@@ -786,6 +899,12 @@
}
}
#endif
+
+ if (dofork && sep->se_bi != NULL &&
+ sep->se_bi->bi_capenter && caph_enter() == -1) {
+ syslog(LOG_ERR, "cap_enter: %m");
+ _exit(0);
+ }
if (sep->se_bi) {
(*sep->se_bi->bi_fn)(ctrl, sep);
} else {
@@ -1268,6 +1387,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) &&
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Aug 31, 10:17 AM (4 h, 57 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
37676748
Default Alt Text
D23022.id66304.diff (9 KB)
Attached To
Mode
D23022: inetd: a light introduction to capsicum
Attached
Detach File
Event Timeline
Log In to Comment