traceroute6: capsicumize it

Submitted by:	Shubh Gupta <shubh@freebsd.org>
Sponsored by: Google (GSOC 2020)
Differential Revision:	https://reviews.freebsd.org/D25604
This commit is contained in:
oshogbo 2020-09-06 14:04:02 +00:00
parent 24f28126a9
commit 49058d70a5
2 changed files with 122 additions and 24 deletions

View File

@ -13,6 +13,10 @@
# A PARTICULAR PURPOSE.
# $FreeBSD$
.include <src.opts.mk>
.include <src.opts.mk>
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 <bsd.prog.mk>

View File

@ -249,6 +249,7 @@ static const char rcsid[] =
*/
#include <sys/param.h>
#include <sys/capsicum.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/uio.h>
@ -260,6 +261,10 @@ static const char rcsid[] =
#include <arpa/inet.h>
#include <libcasper.h>
#include <casper/cap_dns.h>
#include <capsicum_helpers.h>
#include <netdb.h>
#include <stdio.h>
#include <err.h>
@ -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)