Changeset View
Standalone View
sbin/ping/ping.c
Show First 20 Lines • Show All 957 Lines • ▼ Show 20 Lines | ||||||||||
#endif | #endif | |||||||||
msg.msg_namelen = sizeof(from); | msg.msg_namelen = sizeof(from); | |||||||||
if ((cc = recvmsg(srecv, &msg, 0)) < 0) { | if ((cc = recvmsg(srecv, &msg, 0)) < 0) { | |||||||||
if (errno == EINTR) | if (errno == EINTR) | |||||||||
continue; | continue; | |||||||||
warn("recvmsg"); | warn("recvmsg"); | |||||||||
continue; | continue; | |||||||||
} | } | |||||||||
/* If we have a 0 byte read from recvfrom continue */ | ||||||||||
if (cc == 0) | ||||||||||
continue; | ||||||||||
#ifdef SO_TIMESTAMP | #ifdef SO_TIMESTAMP | |||||||||
if (cmsg != NULL && | if (cmsg != NULL && | |||||||||
cmsg->cmsg_level == SOL_SOCKET && | cmsg->cmsg_level == SOL_SOCKET && | |||||||||
cmsg->cmsg_type == SCM_TIMESTAMP && | cmsg->cmsg_type == SCM_TIMESTAMP && | |||||||||
cmsg->cmsg_len == CMSG_LEN(sizeof *tv)) { | cmsg->cmsg_len == CMSG_LEN(sizeof *tv)) { | |||||||||
/* Copy to avoid alignment problems: */ | /* Copy to avoid alignment problems: */ | |||||||||
memcpy(&now, CMSG_DATA(cmsg), sizeof(now)); | memcpy(&now, CMSG_DATA(cmsg), sizeof(now)); | |||||||||
tv = &now; | tv = &now; | |||||||||
▲ Show 20 Lines • Show All 165 Lines • ▼ Show 20 Lines | ||||||||||
static void | static void | |||||||||
pr_pack(char *buf, ssize_t cc, struct sockaddr_in *from, struct timespec *tv) | pr_pack(char *buf, ssize_t cc, struct sockaddr_in *from, struct timespec *tv) | |||||||||
{ | { | |||||||||
struct in_addr ina; | struct in_addr ina; | |||||||||
u_char *cp, *dp, l; | u_char *cp, *dp, l; | |||||||||
struct icmp icp; | struct icmp icp; | |||||||||
struct ip ip; | struct ip ip; | |||||||||
const u_char *icmp_data_raw; | const u_char *icmp_data_raw; | |||||||||
ssize_t icmp_data_raw_len; | ||||||||||
double triptime; | double triptime; | |||||||||
int dupflag, hlen, i, j, recv_len; | int dupflag, i, j, recv_len; | |||||||||
uint8_t hlen; | ||||||||||
uint16_t seq; | uint16_t seq; | |||||||||
static int old_rrlen; | static int old_rrlen; | |||||||||
static char old_rr[MAX_IPOPTLEN]; | static char old_rr[MAX_IPOPTLEN]; | |||||||||
struct ip oip; | struct ip oip; | |||||||||
u_char oip_header_len; | u_char oip_header_len; | |||||||||
struct icmp oicmp; | struct icmp oicmp; | |||||||||
const u_char *oicmp_raw; | const u_char *oicmp_raw; | |||||||||
/* | /* | |||||||||
* Get size of IP header of the received packet. The | * Get size of IP header of the received packet. | |||||||||
* information is contained in the lower four bits of the | * The header length is contained in the lower four bits of the first | |||||||||
* first byte. | * byte and represents the number of 4 byte octets the header takes up. | |||||||||
markjUnsubmitted Done Inline Actions
markj: | ||||||||||
* | ||||||||||
* The IHL minimum value is 5 (20 bytes) and its maximum value is 15 | ||||||||||
* (60 bytes). | ||||||||||
*/ | */ | |||||||||
memcpy(&l, buf, sizeof(l)); | memcpy(&l, buf, sizeof(l)); | |||||||||
Done Inline ActionsWe're assuming here that cc > 0 but from reading the ping6 code (which handles this scenario), this might not be the case if we received only a control message. I'm not sure if that can arise in ping4, but I'd suggest adjusting the caller to check for this case. markj: We're assuming here that `cc > 0` but from reading the ping6 code (which handles this scenario)… | ||||||||||
hlen = (l & 0x0f) << 2; | hlen = (l & 0x0f) << 2; | |||||||||
memcpy(&ip, buf, hlen); | ||||||||||
/* Check the IP header */ | /* Reject IP packets with a short header */ | |||||||||
if (hlen < sizeof(struct ip)) { | ||||||||||
if (options & F_VERBOSE) | ||||||||||
warn("IHL too short (%d bytes) from %s", hlen, | ||||||||||
Not Done Inline Actions
markj: | ||||||||||
inet_ntoa(from->sin_addr)); | ||||||||||
return; | ||||||||||
} | ||||||||||
memcpy(&ip, buf, sizeof(struct ip)); | ||||||||||
/* Check packet has enough data to carry a valid ICMP header */ | ||||||||||
recv_len = cc; | recv_len = cc; | |||||||||
if (cc < hlen + ICMP_MINLEN) { | if (cc < hlen + ICMP_MINLEN) { | |||||||||
if (options & F_VERBOSE) | if (options & F_VERBOSE) | |||||||||
warn("packet too short (%zd bytes) from %s", cc, | warn("packet too short (%zd bytes) from %s", cc, | |||||||||
inet_ntoa(from->sin_addr)); | inet_ntoa(from->sin_addr)); | |||||||||
return; | return; | |||||||||
} | } | |||||||||
#ifndef icmp_data | #ifndef icmp_data | |||||||||
icmp_data_raw = buf + hlen + offsetof(struct icmp, icmp_ip); | icmp_data_raw = buf + hlen + offsetof(struct icmp, icmp_ip); | |||||||||
#else | #else | |||||||||
icmp_data_raw_len = cc - (hlen + offsetof(struct icmp, icmp_data)); | ||||||||||
Not Done Inline ActionsSuppose cc == hlen + ICMP_MINLEN. ICMP_MINLEN is 8, which is equal to offsetof(struct icmp, icmp_ip). Further down, we peek into the inner IP header to get its length with memcpy(&oip_header_len, icmp_data_raw, sizeof(oip_header_len));. Isn't this an out-of-bounds access if the packet length is exactly hlen + 8? markj: Suppose `cc == hlen + ICMP_MINLEN`. ICMP_MINLEN is 8, which is equal to `offsetof(struct icmp… | ||||||||||
Not Done Inline Actions@markj could you please re-review? We're getting very close to 12.4-RELEASE. asomers: @markj could you please re-review? We're getting very close to 12.4-RELEASE. | ||||||||||
icmp_data_raw = buf + hlen + offsetof(struct icmp, icmp_data); | icmp_data_raw = buf + hlen + offsetof(struct icmp, icmp_data); | |||||||||
#endif | #endif | |||||||||
Not Done Inline ActionsI think we should drop this ifdef, but that can happen as a follow-up commit. markj: I think we should drop this ifdef, but that can happen as a follow-up commit. | ||||||||||
/* Now the ICMP part */ | /* Now the ICMP part */ | |||||||||
cc -= hlen; | cc -= hlen; | |||||||||
memcpy(&icp, buf + hlen, MIN((ssize_t)sizeof(icp), cc)); | memcpy(&icp, buf + hlen, MIN((ssize_t)sizeof(icp), cc)); | |||||||||
if (icp.icmp_type == icmp_type_rsp) { | if (icp.icmp_type == icmp_type_rsp) { | |||||||||
if (icp.icmp_id != ident) | if (icp.icmp_id != ident) | |||||||||
return; /* 'Twas not our ECHO */ | return; /* 'Twas not our ECHO */ | |||||||||
++nreceived; | ++nreceived; | |||||||||
▲ Show 20 Lines • Show All 111 Lines • ▼ Show 20 Lines | (void)printf("\nwrong data byte #%d should be 0x%x but was 0x%x", | |||||||||
* See if it's a reply to something that we sent. | * See if it's a reply to something that we sent. | |||||||||
* We can compare IP destination, protocol, | * We can compare IP destination, protocol, | |||||||||
* and ICMP type and ID. | * and ICMP type and ID. | |||||||||
* | * | |||||||||
* Only print all the error messages if we are running | * Only print all the error messages if we are running | |||||||||
* as root to avoid leaking information not normally | * as root to avoid leaking information not normally | |||||||||
* available to those not running as root. | * available to those not running as root. | |||||||||
*/ | */ | |||||||||
/* | ||||||||||
* If we don't have enough bytes for a quoted IP header and an | ||||||||||
* ICMP header then stop. | ||||||||||
*/ | ||||||||||
if (icmp_data_raw_len < | ||||||||||
Done Inline ActionsI think you need to cast the RHS to have type ssize_t. Right now it's size_t, which means that the LHS will be converted to an unsigned type. Oh, but that's ok since ICMP_MINLEN == offsetof(struct icmp, icmp_data) in practice, so icmp_data_raw_len can't be negative due to the check above. Hmm, actually this doesn't compile due to the signed/unsigned comparison. So we should do the cast to ssize_t anyway. markj: I think you need to cast the RHS to have type `ssize_t`. Right now it's `size_t`, which means… | ||||||||||
(ssize_t)(sizeof(struct ip) + sizeof(struct icmp))) { | ||||||||||
if (options & F_VERBOSE) | ||||||||||
warnx("quoted data too short (%zd bytes) from %s", | ||||||||||
Done Inline ActionsThe compiler notes that oip_header_len can be uninitialized here. markj: The compiler notes that `oip_header_len` can be uninitialized here. | ||||||||||
icmp_data_raw_len, inet_ntoa(from->sin_addr)); | ||||||||||
return; | ||||||||||
} | ||||||||||
memcpy(&oip_header_len, icmp_data_raw, sizeof(oip_header_len)); | memcpy(&oip_header_len, icmp_data_raw, sizeof(oip_header_len)); | |||||||||
oip_header_len = (oip_header_len & 0x0f) << 2; | oip_header_len = (oip_header_len & 0x0f) << 2; | |||||||||
memcpy(&oip, icmp_data_raw, oip_header_len); | ||||||||||
/* Reject IP packets with a short header */ | ||||||||||
if (oip_header_len < sizeof(struct ip)) { | ||||||||||
if (options & F_VERBOSE) | ||||||||||
warnx("inner IHL too short (%d bytes) from %s", | ||||||||||
Not Done Inline Actions
markj: | ||||||||||
oip_header_len, inet_ntoa(from->sin_addr)); | ||||||||||
return; | ||||||||||
} | ||||||||||
/* | ||||||||||
* Check against the actual IHL length, to protect against | ||||||||||
* quoated packets carrying IP options. | ||||||||||
*/ | ||||||||||
Not Done Inline Actions
markj: | ||||||||||
if (icmp_data_raw_len < | ||||||||||
(ssize_t)(oip_header_len + sizeof(struct icmp))) { | ||||||||||
if (options & F_VERBOSE) | ||||||||||
warnx("inner packet too short (%zd bytes) from %s", | ||||||||||
icmp_data_raw_len, inet_ntoa(from->sin_addr)); | ||||||||||
return; | ||||||||||
} | ||||||||||
memcpy(&oip, icmp_data_raw, sizeof(struct ip)); | ||||||||||
oicmp_raw = icmp_data_raw + oip_header_len; | oicmp_raw = icmp_data_raw + oip_header_len; | |||||||||
memcpy(&oicmp, oicmp_raw, offsetof(struct icmp, icmp_id) + | memcpy(&oicmp, oicmp_raw, sizeof(struct icmp)); | |||||||||
sizeof(oicmp.icmp_id)); | ||||||||||
if (((options & F_VERBOSE) && uid == 0) || | if (((options & F_VERBOSE) && uid == 0) || | |||||||||
(!(options & F_QUIET2) && | (!(options & F_QUIET2) && | |||||||||
(oip.ip_dst.s_addr == whereto.sin_addr.s_addr) && | (oip.ip_dst.s_addr == whereto.sin_addr.s_addr) && | |||||||||
(oip.ip_p == IPPROTO_ICMP) && | (oip.ip_p == IPPROTO_ICMP) && | |||||||||
(oicmp.icmp_type == ICMP_ECHO) && | (oicmp.icmp_type == ICMP_ECHO) && | |||||||||
(oicmp.icmp_id == ident))) { | (oicmp.icmp_id == ident))) { | |||||||||
(void)printf("%zd bytes from %s: ", cc, | (void)printf("%zd bytes from %s: ", cc, | |||||||||
▲ Show 20 Lines • Show All 442 Lines • Show Last 20 Lines |