On 8/24/23 05:59, Peter J. Philipp wrote:
> Hi,
>
> I have modified ping(8) to grab a raw descriptor from a daemon over AF_UNIX
> sockets. This seems to work. While what I call the sun daemon needs to be
> tightened a lot more it should work to make people understand my concept.
>
> benefits:
> we lose inet pledge
> we lose the setuid to root bit
> root can bypass this entirely so it works in single user mode
> it can be broadened to other programs such as traceroute
>
> drawbacks:
> not fully tested
> sund needs more tightening or there is a security problem
> if sund dies ping doesn't work for regular users
I don't think having a daemon for ping (or other trivial network
operations) might be the best design. There's nothing about the service
that demands a continuously running process in the background.
Aisha
> Here is a demonstration:
> pjp@polarstern$ ls -l /tmp/ping
> -rwxr-xr-x 2 root wheel 1442864 Aug 24 11:38 /tmp/ping
> pjp@polarstern$ /tmp/ping -D -c 1 127.0.0.1
> PING 127.0.0.1 (127.0.0.1): 56 data bytes
> 64 bytes from 127.0.0.1: icmp_seq=0 ttl=255 time=0.073 ms
>
> --- 127.0.0.1 ping statistics ---
> 1 packets transmitted, 1 packets received, 0.0% packet loss
> round-trip min/avg/max/std-dev = 0.073/0.073/0.073/0.000 ms
> pjp@polarstern$ /tmp/ping6 -D -c 1 centroid.eu
> PING centroid.eu (2a03:6000:6f68:631::170): 56 data bytes
> 64 bytes from 2a03:6000:6f68:631::170: icmp_seq=0 hlim=54 time=31.059 ms
>
> --- centroid.eu ping statistics ---
> 1 packets transmitted, 1 packets received, 0.0% packet loss
> round-trip min/avg/max/std-dev = 31.059/31.059/31.059/0.000 ms
>
> Here is the sund.c code (needs improving):
>
>
> #include <sys/types.h>
> #include <sys/socket.h>
> #include <sys/select.h>
> #include <sys/stat.h>
> #include <sys/syslimits.h>
> #include <sys/uio.h>
> #include <sys/un.h>
>
> #include <netinet/in.h>
> #include <netdb.h>
>
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
> #include <unistd.h>
>
>
> #define EFFECTIVE_PINGUSER 1000
> #define EFFECTIVE_PINGGROUP 1000
>
> #define SUND_PATH "/var/run/sund.sock"
>
> void desc_write(int, int);
>
> int
> main(void)
> {
> int so, new, sel;
> int raw, error;
>
> struct addrinfo hints, *res;
> struct sockaddr_un sun;
> fd_set rset;
>
> uid_t euid;
> gid_t egid;
>
> char buf[INET6_ADDRSTRLEN + 1];
> socklen_t sunsz = sizeof(struct sockaddr_un);
> size_t l;
>
> unveil(SUND_PATH, "rwc");
> unveil(NULL, NULL);
>
> unlink(SUND_PATH);
>
>
> so = socket(AF_UNIX, SOCK_STREAM, 0);
> if (so < 0) {
> perror("socket");
> exit(1);
> }
>
> memset(&sun, 0, sizeof(sun));
> sun.sun_family = AF_UNIX;
> sun.sun_len = sizeof(struct sockaddr_un);
>
> strlcpy(sun.sun_path, "/var/run/sund.sock", sizeof(sun.sun_path));
>
> if (bind(so, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
> perror("bind");
> exit(1);
> }
> chmod(SUND_PATH, 0666);
>
> listen(so, 5);
> daemon(0, 0);
>
> if (pledge("stdio inet sendfd", NULL) == -1) {
> perror("pledge");
> exit(1);
> }
>
> for (;;) {
> FD_ZERO(&rset);
> FD_SET(so, &rset);
>
> sel = select(so + 1, &rset, NULL, NULL, NULL);
> switch (sel) {
> case -1:
> perror("select");
> continue;
> default:
> break;
> }
>
> if (FD_ISSET(so, &rset)) {
> new = accept(so, (struct sockaddr *)&sun, &sunsz);
> if (new == -1) {
> perror("accept");
> continue;
> }
>
> if (getpeereid(new, &euid, &egid) == -1) {
> perror("getpeereid");
> close(new);
> continue;
> }
>
>
> if ((euid != EFFECTIVE_PINGUSER) &&
> (egid != EFFECTIVE_PINGGROUP)) {
> close(new);
> continue;
> }
>
> if ((l = recv(new, buf, sizeof(buf), 0)) == -1) {
> close(new);
> continue;
> }
>
> buf[l] = '\0';
>
> memset(&hints, 0, sizeof(hints));
> if (strchr(buf, '.') != NULL) {
> hints.ai_family = AF_INET;
> } else {
> hints.ai_family = AF_INET6;
> }
>
> hints.ai_flags = AI_NUMERICHOST;
>
> if ((error = getaddrinfo(buf,"53",&hints,&res)) != 0) {
> fprintf(stderr, "getaddrinfo: %s\n",
> gai_strerror(error));
> close(new);
> continue;
> }
>
> if ((raw = socket(res->ai_family, SOCK_RAW,
> res->ai_family == AF_INET ? IPPROTO_ICMP :
> IPPROTO_ICMPV6)) == -1) {
> perror("socket");
> close(new);
> continue;
> }
>
> freeaddrinfo(res);
>
>
> /* send_descriptor */
> desc_write(new, raw);
> close(new);
> }
> }
> }
>
>
> /*
> * based on msgbuf_write() libutil/imsg
> */
>
> void
> desc_write(int new, int raw)
> {
> struct iovec iov[IOV_MAX];
> ssize_t n;
> struct msghdr msg;
> struct cmsghdr *cmsg;
> union {
> struct cmsghdr hdr;
> char buf[CMSG_SPACE(sizeof(int))];
> } cmsgbuf;
>
> memset(&iov, 0, sizeof(iov));
> memset(&msg, 0, sizeof(msg));
> memset(&cmsgbuf, 0, sizeof(cmsgbuf));
>
> msg.msg_iov = iov;
> msg.msg_iovlen = 0;
>
> msg.msg_control = (caddr_t)&cmsgbuf.buf;
> msg.msg_controllen = sizeof(cmsgbuf.buf);
>
> cmsg = CMSG_FIRSTHDR(&msg);
> cmsg->cmsg_len = CMSG_LEN(sizeof(int));
> cmsg->cmsg_level = SOL_SOCKET;
> cmsg->cmsg_type = SCM_RIGHTS;
>
> *(int *)CMSG_DATA(cmsg) = raw;
>
>
> if ((n = sendmsg(new, &msg, 0)) == -1) {
> perror("sending descriptor");
> close(raw);
> return;
> }
>
> close(raw); /* assumption */
> }
>
> And here is the ping(8) patch:
>
>
> Index: ping.c
> ===================================================================
> RCS file: /cvs/src/sbin/ping/ping.c,v
> retrieving revision 1.248
> diff -u -p -u -r1.248 ping.c
> --- ping.c 1 Dec 2022 07:34:06 -0000 1.248
> +++ ping.c 24 Aug 2023 09:39:37 -0000
> @@ -81,6 +81,7 @@
> #include <sys/socket.h>
> #include <sys/time.h>
> #include <sys/uio.h>
> +#include <sys/un.h>
>
> #include <netinet/in.h>
> #include <netinet/ip.h>
> @@ -213,6 +214,7 @@ void summary(void);
> void onsignal(int);
> void retransmit(int);
> int pinger(int);
> +int get_icmp_descriptor(char *);
> const char *pr_addr(struct sockaddr *, socklen_t);
> void pr_pack(u_char *, int, struct msghdr *);
> __dead void usage(void);
> @@ -251,7 +253,7 @@ main(int argc, char *argv[])
> struct passwd *pw;
> socklen_t maxsizelen;
> int64_t preload;
> - int ch, i, optval = 1, packlen, maxsize, error, s, flooddone = 0;
> + int ch, i, optval = 1, packlen, maxsize, error, s = -1, flooddone = 0;
> int df = 0, tos = 0, bufspace = IP_MAXPACKET, hoplimit = -1, mflag = 0;
> u_char *datap, *packet;
> u_char ttl = MAXTTL;
> @@ -267,21 +269,25 @@ main(int argc, char *argv[])
> /* Cannot pledge due to special setsockopt()s below */
> if (unveil("/", "r") == -1)
> err(1, "unveil /");
> + if (unveil("/var/run/sund.sock", "rw") == -1)
> + err(1, "unveil /var/run/sund.sock");
> if (unveil(NULL, NULL) == -1)
> err(1, "unveil");
>
> + ouid = getuid();
> if (strcmp("ping6", __progname) == 0) {
> v6flag = 1;
> maxpayload = MAXPAYLOAD6;
> - if ((s = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) == -1)
> + if (ouid == 0 &&
> + (s = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) == -1)
> err(1, "socket");
> } else {
> - if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1)
> + if (ouid == 0 &&
> + (s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1)
> err(1, "socket");
> }
>
> /* revoke privs */
> - ouid = getuid();
> if (ouid == 0 && (pw = getpwnam(PING_USER)) != NULL) {
> uid = pw->pw_uid;
> gid = pw->pw_gid;
> @@ -289,10 +295,12 @@ main(int argc, char *argv[])
> uid = getuid();
> gid = getgid();
> }
> - if (ouid && (setgroups(1, &gid) ||
> +#if NOTNEEDEDANYMORE
> + if (ouid == 0 && ((setgroups(1, &gid) ||
> setresgid(gid, gid, gid) ||
> - setresuid(uid, uid, uid)))
> + setresuid(uid, uid, uid))))
> err(1, "unable to revoke privs");
> +#endif
>
> preload = 0;
> datap = &outpack[ECHOLEN + ECHOTMLEN];
> @@ -419,9 +427,6 @@ main(int argc, char *argv[])
> if (errstr)
> errx(1, "rtable value is %s: %s", errstr,
> optarg);
> - if (setsockopt(s, SOL_SOCKET, SO_RTABLE, &rtableid,
> - sizeof(rtableid)) == -1)
> - err(1, "setsockopt SO_RTABLE");
> break;
> case 'v':
> options |= F_VERBOSE;
> @@ -437,9 +442,9 @@ main(int argc, char *argv[])
> }
> }
>
> - if (ouid == 0 && (setgroups(1, &gid) ||
> + if (ouid == 0 && ((setgroups(1, &gid) ||
> setresgid(gid, gid, gid) ||
> - setresuid(uid, uid, uid)))
> + setresuid(uid, uid, uid))))
> err(1, "unable to revoke privs");
>
> argc -= optind;
> @@ -484,6 +489,25 @@ main(int argc, char *argv[])
> err(1, "malloc");
> }
>
> + if (s == -1) {
> + char passbuf[INET6_ADDRSTRLEN];
> +
> + inet_ntop(v6flag ? AF_INET6 : AF_INET, dst,
> + passbuf, sizeof(passbuf));
> +
> + s = get_icmp_descriptor(passbuf);
> + if (s == -1) {
> + fprintf(stderr, "sun daemon (sund) is not running or
> defective, or you're not root\n");
> + exit(1);
> + }
> + }
> +
> + if (rtableid != 0) {
> + if (setsockopt(s, SOL_SOCKET, SO_RTABLE, &rtableid,
> + sizeof(rtableid)) == -1)
> + err(1, "setsockopt SO_RTABLE");
> + }
> +
> if (res->ai_next) {
> if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf,
> sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0)
> @@ -493,6 +517,7 @@ main(int argc, char *argv[])
> }
> freeaddrinfo(res);
>
> +
> if (source) {
> memset(&hints, 0, sizeof(hints));
> hints.ai_family = dst->sa_family;
> @@ -768,10 +793,10 @@ main(int argc, char *argv[])
> }
>
> if (options & F_HOSTNAME) {
> - if (pledge("stdio inet dns", NULL) == -1)
> + if (pledge("stdio unix dns recvfd", NULL) == -1)
> err(1, "pledge");
> } else {
> - if (pledge("stdio inet", NULL) == -1)
> + if (pledge("stdio unix recvfd", NULL) == -1)
> err(1, "pledge");
> }
>
> @@ -2273,4 +2298,98 @@ usage(void)
> "\n\t[-t ttl] [-V rtable] [-w maxwait] host\n");
> }
> exit(1);
> +}
> +
> +/*
> + * stolen from imsg.c mostly
> + */
> +
> +int
> +get_icmp_descriptor(char *ipname)
> +{
> + int so, raw, sel, j;
> + fd_set rset;
> +
> + struct sockaddr_un sun;
> + struct msghdr msg;
> + struct cmsghdr *cmsg;
> + struct iovec iov;
> +
> + union {
> + struct cmsghdr hdr;
> + char buf[CMSG_SPACE(sizeof(int) * 1)];
> + } cmsgbuf;
> +
> + ssize_t n = -1;
> +
> + memset(&msg, 0, sizeof(msg));
> + memset(&cmsgbuf, 0, sizeof(cmsgbuf));
> +
> + iov.iov_base = NULL;
> + iov.iov_len = 0;
> + msg.msg_iov = &iov;
> + msg.msg_iovlen = 1;
> + msg.msg_control = &cmsgbuf.buf;
> + msg.msg_controllen = sizeof(cmsgbuf.buf);
> +
> + so = socket(AF_UNIX, SOCK_STREAM, 0);
> + if (so < 0) {
> + return -1;
> + }
> +
> + memset(&sun, 0, sizeof(sun));
> + sun.sun_family = AF_UNIX;
> + sun.sun_len = sizeof(struct sockaddr_un);
> +
> + strlcpy(sun.sun_path, "/var/run/sund.sock", sizeof(sun.sun_path));
> +
> + if (connect(so, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
> + goto bad;
> + }
> +
> + if (send(so, ipname, strlen(ipname), 0) < 0) {
> + goto bad;
> + }
> +
> + for (;;) {
> + FD_ZERO(&rset);
> + FD_SET(so, &rset);
> +
> + sel = select(so + 1, &rset, NULL, NULL, NULL);
> + switch (sel) {
> + case -1:
> + continue;
> + default:
> + break;
> + }
> +
> + if (FD_ISSET(so, &rset)) {
> + /* get descriptor */
> + if ((n = recvmsg(so, &msg, 0)) == -1) {
> + goto bad;
> + }
> +
> + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
> + cmsg = CMSG_NXTHDR(&msg, cmsg)) {
> + if (cmsg->cmsg_level == SOL_SOCKET &&
> + cmsg->cmsg_type == SCM_RIGHTS) {
> + j = ((char *)cmsg + cmsg->cmsg_len -
> + (char *)CMSG_DATA(cmsg)) /
> + sizeof(int);
> +
> + if (j == 1) {
> + raw = ((int
> *)CMSG_DATA(cmsg))[0];
> + } else {
> + goto bad;
> + }
> + close(so);
> + return (raw);
> + }
> + }
> +bad:
> + close(so);
> + return (-1);
> + }
> + }
> + /* NOTREACHED */
> }
>
>