Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F146689884
D37210.id112417.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
7 KB
Referenced Files
None
Subscribers
None
D37210.id112417.diff
View Options
diff --git a/sbin/ping/ping.c b/sbin/ping/ping.c
--- a/sbin/ping/ping.c
+++ b/sbin/ping/ping.c
@@ -1144,8 +1144,10 @@
struct icmp icp;
struct ip ip;
const u_char *icmp_data_raw;
+ ssize_t icmp_data_raw_len;
double triptime;
- int dupflag, hlen, i, j, recv_len;
+ int dupflag, i, j, recv_len;
+ uint8_t hlen;
uint16_t seq;
static int old_rrlen;
static char old_rr[MAX_IPOPTLEN];
@@ -1155,15 +1157,27 @@
const u_char *oicmp_raw;
/*
- * Get size of IP header of the received packet. The
- * information is contained in the lower four bits of the
- * first byte.
+ * Get size of IP header of the received packet.
+ * The header length is contained in the lower four bits of the first
+ * byte and represents the number of 4 byte ocets the header takes up.
+ *
+ * The IHL minimum value is 5 (20 bytes) and its maximum value is 15
+ * (60 bytes).
*/
memcpy(&l, buf, sizeof(l));
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,
+ 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;
if (cc < hlen + ICMP_MINLEN) {
if (options & F_VERBOSE)
@@ -1175,6 +1189,7 @@
#ifndef icmp_data
icmp_data_raw = buf + hlen + offsetof(struct icmp, icmp_ip);
#else
+ icmp_data_raw_len = cc - (hlen + offsetof(struct icmp, icmp_ip));
icmp_data_raw = buf + hlen + offsetof(struct icmp, icmp_data);
#endif
@@ -1306,10 +1321,27 @@
*/
memcpy(&oip_header_len, icmp_data_raw, sizeof(oip_header_len));
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)
+ warn("inner IHL too short (%d bytes) from %s",
+ oip_header_len, inet_ntoa(from->sin_addr));
+ return;
+ }
+
+ if (icmp_data_raw_len <
+ (ssize_t)(oip_header_len + sizeof(struct icmp))) {
+ if (options & F_VERBOSE)
+ warn("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;
- memcpy(&oicmp, oicmp_raw, offsetof(struct icmp, icmp_id) +
- sizeof(oicmp.icmp_id));
+ memcpy(&oicmp, oicmp_raw, sizeof(struct icmp));
if (((options & F_VERBOSE) && uid == 0) ||
(!(options & F_QUIET2) &&
diff --git a/sbin/ping/tests/Makefile b/sbin/ping/tests/Makefile
--- a/sbin/ping/tests/Makefile
+++ b/sbin/ping/tests/Makefile
@@ -6,9 +6,13 @@
PACKAGE= tests
ATF_TESTS_SH+= ping_test
+# Exclusive because each injection test case uses the same IP addresses
+TEST_METADATA.ping_test+= is_exclusive="true"
+
${PACKAGE}FILES+= ping_c1_s56_t1.out
${PACKAGE}FILES+= ping_6_c1_s8_t1.out
${PACKAGE}FILES+= ping_c1_s56_t1_S127.out
${PACKAGE}FILES+= ping_c1_s8_t1_S1.out
+${PACKAGE}FILES+= injection.py
.include <bsd.test.mk>
diff --git a/sbin/ping/tests/injection.py b/sbin/ping/tests/injection.py
new file mode 100644
--- /dev/null
+++ b/sbin/ping/tests/injection.py
@@ -0,0 +1,80 @@
+#! /usr/bin/env python3
+# Used to inject various malformed packets
+
+import errno
+import subprocess
+import sys
+
+from scapy.all import Ether, IP, ICMP, IPOption
+import scapy.layers.all
+from scapy.layers.inet import ICMPEcho_am
+from scapy.layers.tuntap import TunTapInterface
+
+SRC_ADDR = "192.0.2.14"
+DST_ADDR = "192.0.2.15"
+
+mode = sys.argv[1]
+ip = None
+
+# fill opts with nop (0x01)
+opts = b''
+for x in range(40):
+ opts += b'\x01'
+
+
+# Create and configure a tun interface with an RFC5737 nonrouteable address
+create_proc = subprocess.run(
+ args=["ifconfig", "tun", "create"],
+ capture_output=True,
+ check=True,
+ text=True)
+iface = create_proc.stdout.strip()
+tun = TunTapInterface(iface)
+with open("tun.txt", "w") as f:
+ f.write(iface)
+subprocess.run(["ifconfig", tun.iface, "up"])
+subprocess.run(["ifconfig", tun.iface, SRC_ADDR, DST_ADDR])
+
+ping = subprocess.Popen(
+ args=["/sbin/ping", "-v", "-c1", "-t1", DST_ADDR],
+ text=True
+)
+# Wait for /sbin/ping to ping us
+echo_req = tun.recv()
+
+# Construct the response packet
+if mode == "opts":
+ # Sending reply with IP options
+ echo_reply = IP(
+ dst=SRC_ADDR,
+ src=DST_ADDR,
+ options=IPOption(opts)
+ )/ICMP(type=0, code=0, id=echo_req.payload.id)/echo_req.payload.payload
+elif mode == "pip":
+ # packet in packet (inner has options)
+
+ inner = IP(
+ dst=SRC_ADDR,
+ src=DST_ADDR,
+ options=IPOption(opts)
+ )/ICMP(type=0, code=0, id=echo_req.payload.id)/echo_req.payload.payload
+ outer = IP(
+ dst=SRC_ADDR,
+ src=DST_ADDR
+ )/ICMP(type=3, code=1) # host unreach
+
+ echo_reply = outer/inner
+elif mode == "reply":
+ # Sending normal echo reply
+ echo_reply = IP(
+ dst=SRC_ADDR,
+ src=DST_ADDR,
+ )/ICMP(type=0, code=0, id=echo_req.payload.id)/echo_req.payload.payload
+else:
+ print("unknown mode {}".format(mode))
+ exit(1)
+
+tun.send(echo_reply)
+outs, errs = ping.communicate()
+
+sys.exit(ping.returncode)
diff --git a/sbin/ping/tests/ping_test.sh b/sbin/ping/tests/ping_test.sh
--- a/sbin/ping/tests/ping_test.sh
+++ b/sbin/ping/tests/ping_test.sh
@@ -125,6 +125,47 @@
atf_check -s exit:1 -e ignore ping6 -4 -6
}
+atf_test_case "inject_opts" "cleanup"
+inject_opts_head() {
+ atf_set "descr" "Inejct an ECHO REPLY with IP options"
+ atf_set "require.user" "root"
+ atf_set "require.progs" "scapy"
+}
+inject_opts_body() {
+ atf_check -s exit:0 -o match:"wrong total length" -o match:"NOP" python3 $(atf_get_srcdir)/injection.py opts
+}
+inject_opts_cleanup() {
+ ifconfig `cat tun.txt` destroy
+}
+
+atf_test_case "inject_pip" "cleanup"
+inject_pip_head() {
+ atf_set "descr" "Inject an ICMP error with a quoted packet with IP options"
+ atf_set "require.user" "root"
+ atf_set "require.progs" "scapy"
+}
+inject_pip_body() {
+ atf_check -s exit:2 -o match:"Destination Host Unreachable" python3 $(atf_get_srcdir)/injection.py pip
+}
+inject_pip_cleanup() {
+ ifconfig `cat tun.txt` destroy
+}
+
+# This is redundant with the ping_ tests, but it serves to ensure that scapy.py
+# is working correctly.
+atf_test_case "inject_reply" "cleanup"
+inject_reply_head() {
+ atf_set "descr" "Basic ping test with packet injection"
+ atf_set "require.user" "root"
+ atf_set "require.progs" "scapy"
+}
+inject_reply_body() {
+ atf_check -s exit:0 -o match:"1 packets transmitted, 1 packets received" python3 $(atf_get_srcdir)/injection.py reply
+}
+inject_reply_cleanup() {
+ ifconfig `cat tun.txt` destroy
+}
+
atf_init_test_cases() {
atf_add_test_case ping_c1_s56_t1
@@ -136,6 +177,9 @@
atf_add_test_case ping6_c1t4
atf_add_test_case ping_46
atf_add_test_case ping6_46
+ atf_add_test_case inject_opts
+ atf_add_test_case inject_pip
+ atf_add_test_case inject_reply
}
check_ping_statistics() {
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Mar 5, 6:11 PM (18 h, 11 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
29269508
Default Alt Text
D37210.id112417.diff (7 KB)
Attached To
Mode
D37210: Add test cases for ping with IP options in the response
Attached
Detach File
Event Timeline
Log In to Comment