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

Reply via email to