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 */ > } > >