From 49058d70a5a1ace807fa73f01d1744bb00adbe3c Mon Sep 17 00:00:00 2001 From: oshogbo Date: Sun, 6 Sep 2020 14:04:02 +0000 Subject: [PATCH] traceroute6: capsicumize it Submitted by: Shubh Gupta Sponsored by: Google (GSOC 2020) Differential Revision: https://reviews.freebsd.org/D25604 --- usr.sbin/traceroute6/Makefile | 12 ++- usr.sbin/traceroute6/traceroute6.c | 134 ++++++++++++++++++++++++----- 2 files changed, 122 insertions(+), 24 deletions(-) diff --git a/usr.sbin/traceroute6/Makefile b/usr.sbin/traceroute6/Makefile index 2b79744bf4b5..91b003c9dee7 100644 --- a/usr.sbin/traceroute6/Makefile +++ b/usr.sbin/traceroute6/Makefile @@ -13,6 +13,10 @@ # A PARTICULAR PURPOSE. # $FreeBSD$ +.include + +.include + TRACEROUTE_DISTDIR?= ${SRCTOP}/contrib/traceroute .PATH: ${TRACEROUTE_DISTDIR} @@ -26,7 +30,13 @@ BINMODE= 4555 CFLAGS+= -DIPSEC -DHAVE_POLL CFLAGS+= -I${.CURDIR} -I${TRACEROUTE_DISTDIR} -I. -LIBADD= ipsec +.if ${MK_CASPER} != "no" +LIBADD+= casper +LIBADD+= cap_dns +CFLAGS+= -DWITH_CASPER +.endif + +LIBADD+= ipsec .include diff --git a/usr.sbin/traceroute6/traceroute6.c b/usr.sbin/traceroute6/traceroute6.c index d377384fac36..182ab44221f5 100644 --- a/usr.sbin/traceroute6/traceroute6.c +++ b/usr.sbin/traceroute6/traceroute6.c @@ -249,6 +249,7 @@ static const char rcsid[] = */ #include +#include #include #include #include @@ -260,6 +261,10 @@ static const char rcsid[] = #include +#include +#include +#include + #include #include #include @@ -289,11 +294,6 @@ static const char rcsid[] = #define MAXPACKET 65535 /* max ip packet size */ -#ifndef HAVE_GETIPNODEBYNAME -#define getipnodebyname(x, y, z, u) gethostbyname2((x), (y)) -#define freehostent(x) -#endif - static u_char packet[512]; /* last inbound (icmp) packet */ static char *outpacket; /* last output packet */ @@ -304,6 +304,7 @@ int setpolicy(int so, char *policy); #endif void send_probe(int, u_long); void *get_uphdr(struct ip6_hdr *, u_char *); +void capdns_open(void); int get_hoplim(struct msghdr *); double deltaT(struct timeval *, struct timeval *); const char *pr_type(int); @@ -312,6 +313,8 @@ void print(struct msghdr *, int); const char *inetname(struct sockaddr *); u_int32_t sctp_crc32c(void *, u_int32_t); u_int16_t in_cksum(u_int16_t *addr, int); +u_int16_t udp_cksum(struct sockaddr_in6 *, struct sockaddr_in6 *, + void *, u_int32_t); u_int16_t tcp_chksum(struct sockaddr_in6 *, struct sockaddr_in6 *, void *, u_int32_t); void usage(void); @@ -335,6 +338,8 @@ static struct cmsghdr *cmsg; static char *source = NULL; static char *hostname; +static cap_channel_t *capdns; + static u_long nprobes = 3; static u_long first_hop = 1; static u_long max_hops = 30; @@ -368,6 +373,9 @@ main(int argc, char *argv[]) char ipsec_inpolicy[] = "in bypass"; char ipsec_outpolicy[] = "out bypass"; #endif + cap_rights_t rights; + + capdns_open(); /* * Receive ICMP @@ -429,6 +437,7 @@ main(int argc, char *argv[]) } break; case 'g': + /* XXX use after capability mode is entered */ hp = getipnodebyname(optarg, AF_INET6, 0, &h_errno); if (hp == NULL) { fprintf(stderr, @@ -560,8 +569,8 @@ main(int argc, char *argv[]) sndsock = rcvsock; break; case IPPROTO_UDP: - if ((sndsock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { - perror("socket(SOCK_DGRAM)"); + if ((sndsock = socket(AF_INET6, SOCK_RAW, IPPROTO_UDP)) < 0) { + perror("socket(SOCK_RAW)"); exit(5); } break; @@ -606,7 +615,9 @@ main(int argc, char *argv[]) hints.ai_socktype = SOCK_RAW; hints.ai_protocol = IPPROTO_ICMPV6; hints.ai_flags = AI_CANONNAME; - error = getaddrinfo(*argv, NULL, &hints, &res); + + error = cap_getaddrinfo(capdns, *argv, NULL, &hints, &res); + if (error) { fprintf(stderr, "traceroute6: %s\n", gai_strerror(error)); @@ -624,7 +635,7 @@ main(int argc, char *argv[]) exit(1); } if (res->ai_next) { - if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, + if (cap_getnameinfo(capdns, res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) strlcpy(hbuf, "?", sizeof(hbuf)); fprintf(stderr, "traceroute6: Warning: %s has multiple " @@ -803,7 +814,7 @@ main(int argc, char *argv[]) hints.ai_family = AF_INET6; hints.ai_socktype = SOCK_DGRAM; /*dummy*/ hints.ai_flags = AI_NUMERICHOST; - error = getaddrinfo(source, "0", &hints, &res); + error = cap_getaddrinfo(capdns, source, "0", &hints, &res); if (error) { printf("traceroute6: %s: %s\n", source, gai_strerror(error)); @@ -839,7 +850,7 @@ main(int argc, char *argv[]) perror("getsockname"); exit(1); } - if (getnameinfo((struct sockaddr *)&Src, Src.sin6_len, + if (cap_getnameinfo(capdns, (struct sockaddr *)&Src, Src.sin6_len, src0, sizeof(src0), NULL, 0, NI_NUMERICHOST)) { fprintf(stderr, "getnameinfo failed for source\n"); exit(1); @@ -879,7 +890,7 @@ main(int argc, char *argv[]) /* * Message to users */ - if (getnameinfo((struct sockaddr *)&Dst, Dst.sin6_len, hbuf, + if (cap_getnameinfo(capdns, (struct sockaddr *)&Dst, Dst.sin6_len, hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST)) strlcpy(hbuf, "(invalid)", sizeof(hbuf)); fprintf(stderr, "traceroute6"); @@ -894,6 +905,30 @@ main(int argc, char *argv[]) if (first_hop > 1) printf("Skipping %lu intermediate hops\n", first_hop - 1); + if (connect(sndsock, (struct sockaddr *)&Dst, + sizeof(Dst)) != 0) { + fprintf(stderr, "connect: %s\n", strerror(errno)); + exit(1); + } + + /* + * Here we enter capability mode. Further down access to global + * namespaces (e.g filesystem) is restricted (see capsicum(4)). + * We must connect(2) our socket before this point. + */ + + if (caph_enter_casper() < 0) { + fprintf(stderr, "caph_enter_casper: %s\n", strerror(errno)); + exit(1); + } + + cap_rights_init(&rights, CAP_SEND, CAP_SETSOCKOPT); + if (caph_rights_limit(sndsock, &rights) < 0) { + fprintf(stderr, "caph_rights_limit sndsock: %s\n", + strerror(errno)); + exit(1); + } + /* * Main loop */ @@ -1038,6 +1073,7 @@ send_probe(int seq, u_long hops) { struct icmp6_hdr *icp; struct sctphdr *sctp; + struct udphdr *outudp; struct sctp_chunkhdr *chk; struct sctp_init_chunk *init; struct sctp_paramhdr *param; @@ -1063,6 +1099,11 @@ send_probe(int seq, u_long hops) icp->icmp6_seq = htons(seq); break; case IPPROTO_UDP: + outudp = (struct udphdr *) outpacket; + outudp->uh_sport = htons(ident); + outudp->uh_dport = htons(port+seq); + outudp->uh_ulen = htons(datalen); + outudp->uh_sum = udp_cksum(&Src, &Dst, outpacket, datalen); break; case IPPROTO_NONE: /* No space for anything. No harm as seq/tv32 are decorative. */ @@ -1149,12 +1190,11 @@ send_probe(int seq, u_long hops) fprintf(stderr, "Unknown probe protocol %d.\n", useproto); exit(1); } - - i = sendto(sndsock, (char *)outpacket, datalen, 0, - (struct sockaddr *)&Dst, Dst.sin6_len); + + i = send(sndsock, (char *)outpacket, datalen, 0); if (i < 0 || (u_long)i != datalen) { if (i < 0) - perror("sendto"); + perror("send"); printf("traceroute6: wrote %s %lu chars, ret=%d\n", hostname, datalen, i); (void) fflush(stdout); @@ -1266,7 +1306,7 @@ packet_ok(struct msghdr *mhdr, int cc, int seq, u_char *type, u_char *code) hlen = sizeof(struct ip6_hdr); if (cc < hlen + sizeof(struct icmp6_hdr)) { if (verbose) { - if (getnameinfo((struct sockaddr *)from, from->sin6_len, + if (cap_getnameinfo(capdns, (struct sockaddr *)from, from->sin6_len, hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) strlcpy(hbuf, "invalid", sizeof(hbuf)); printf("packet too short (%d bytes) from %s\n", cc, @@ -1279,7 +1319,7 @@ packet_ok(struct msghdr *mhdr, int cc, int seq, u_char *type, u_char *code) #else if (cc < (int)sizeof(struct icmp6_hdr)) { if (verbose) { - if (getnameinfo((struct sockaddr *)from, from->sin6_len, + if (cap_getnameinfo(capdns, (struct sockaddr *)from, from->sin6_len, hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) strlcpy(hbuf, "invalid", sizeof(hbuf)); printf("data too short (%d bytes) from %s\n", cc, hbuf); @@ -1345,7 +1385,7 @@ packet_ok(struct msghdr *mhdr, int cc, int seq, u_char *type, u_char *code) break; case IPPROTO_UDP: udp = (struct udphdr *)up; - if (udp->uh_sport == htons(srcport) && + if (udp->uh_sport == htons(ident) && udp->uh_dport == htons(port + seq)) return (1); break; @@ -1401,7 +1441,7 @@ packet_ok(struct msghdr *mhdr, int cc, int seq, u_char *type, u_char *code) u_int8_t *p; int i; - if (getnameinfo((struct sockaddr *)from, from->sin6_len, + if (cap_getnameinfo(capdns, (struct sockaddr *)from, from->sin6_len, sbuf, sizeof(sbuf), NULL, 0, NI_NUMERICHOST) != 0) strlcpy(sbuf, "invalid", sizeof(sbuf)); printf("\n%d bytes from %s to %s", cc, sbuf, @@ -1474,13 +1514,34 @@ get_uphdr(struct ip6_hdr *ip6, u_char *lim) return (NULL); } +void +capdns_open() +{ + const char *types[] = { "NAME", "ADDR" }; + int families[1]; + cap_channel_t *casper; + + casper = cap_init(); + if (casper == NULL) + errx(1, "unable to create casper process"); + capdns = cap_service_open(casper, "system.dns"); + if (capdns == NULL) + errx(1, "unable to open system.dns service"); + if (cap_dns_type_limit(capdns, types, nitems(types)) < 0) + errx(1, "unable to limit access to system.dns service"); + families[0] = AF_INET6; + if (cap_dns_family_limit(capdns, families, nitems(families)) < 0) + errx(1, "unable to limit access to system.dns service"); + cap_close(casper); +} + void print(struct msghdr *mhdr, int cc) { struct sockaddr_in6 *from = (struct sockaddr_in6 *)mhdr->msg_name; char hbuf[NI_MAXHOST]; - if (getnameinfo((struct sockaddr *)from, from->sin6_len, + if (cap_getnameinfo(capdns, (struct sockaddr *)from, from->sin6_len, hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) strlcpy(hbuf, "invalid", sizeof(hbuf)); if (as_path) @@ -1527,7 +1588,7 @@ inetname(struct sockaddr *sa) } cp = NULL; if (!nflag) { - if (getnameinfo(sa, sa->sa_len, line, sizeof(line), NULL, 0, + if (cap_getnameinfo(capdns, sa, sa->sa_len, line, sizeof(line), NULL, 0, NI_NAMEREQD) == 0) { if ((cp = strchr(line, '.')) && !strcmp(cp + 1, domain)) @@ -1538,7 +1599,7 @@ inetname(struct sockaddr *sa) if (cp) return cp; - if (getnameinfo(sa, sa->sa_len, line, sizeof(line), NULL, 0, + if (cap_getnameinfo(capdns, sa, sa->sa_len, line, sizeof(line), NULL, 0, NI_NUMERICHOST) != 0) strlcpy(line, "invalid", sizeof(line)); return line; @@ -1668,6 +1729,33 @@ in_cksum(u_int16_t *addr, int len) return (answer); } +u_int16_t +udp_cksum(struct sockaddr_in6 *src, struct sockaddr_in6 *dst, + void *payload, u_int32_t len) +{ + struct { + struct in6_addr src; + struct in6_addr dst; + u_int32_t len; + u_int8_t zero[3]; + u_int8_t next; + } pseudo_hdr; + u_int16_t sum[2]; + + pseudo_hdr.src = src->sin6_addr; + pseudo_hdr.dst = dst->sin6_addr; + pseudo_hdr.len = htonl(len); + pseudo_hdr.zero[0] = 0; + pseudo_hdr.zero[1] = 0; + pseudo_hdr.zero[2] = 0; + pseudo_hdr.next = IPPROTO_UDP; + + sum[1] = in_cksum((u_int16_t *)&pseudo_hdr, sizeof(pseudo_hdr)); + sum[0] = in_cksum(payload, len); + + return (~in_cksum(sum, sizeof(sum))); +} + u_int16_t tcp_chksum(struct sockaddr_in6 *src, struct sockaddr_in6 *dst, void *payload, u_int32_t len)