xref: /aosp_15_r20/external/ltp/testcases/cve/icmp_rate_limit01.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 2020 SUSE LLC
4  * Author: Nicolai Stange <[email protected]>
5  * LTP port: Martin Doucha <[email protected]>
6  */
7 
8 /*\
9  * CVE-2020-25705
10  *
11  * Test of ICMP rate limiting behavior that may be abused for DNS cache
12  * poisoning attack. Send a few batches of 100 packets to a closed UDP port
13  * and count the ICMP errors. If the number of errors is always the same
14  * for each batch (not randomized), the system is vulnerable. Send packets
15  * from multiple IP addresses to bypass per-address ICMP throttling.
16  *
17  * Fixed in:
18  *
19  *  commit b38e7819cae946e2edf869e604af1e65a5d241c5
20  *  Author: Eric Dumazet <[email protected]>
21  *  Date:   Thu Oct 15 11:42:00 2020 -0700
22  *
23  *  icmp: randomize the global rate limiter
24  */
25 
26 #include <time.h>
27 #include <sys/socket.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
30 #include <linux/errqueue.h>
31 
32 #include <limits.h>
33 
34 #include "lapi/if_addr.h"
35 #include "tst_test.h"
36 #include "tst_netdevice.h"
37 #include "lapi/sched.h"
38 
39 #define DSTNET 0xfa444e00 /* 250.68.78.0 */
40 #define SRCNET 0xfa444e40 /* 250.68.78.64 */
41 #define DSTADDR 0xfa444e02 /* 250.68.78.2 */
42 #define SRCADDR_BASE 0xfa444e41 /* 250.68.78.65 */
43 #define SRCADDR_COUNT 50
44 #define NETMASK 26
45 #define BATCH_COUNT 8
46 #define BUFSIZE 1024
47 
48 static int parentns = -1, childns = -1;
49 static int fds[SRCADDR_COUNT];
50 
setup(void)51 static void setup(void)
52 {
53 	struct sockaddr_in ipaddr = { .sin_family = AF_INET };
54 	uint32_t addr;
55 	int i;
56 
57 	for (i = 0; i < SRCADDR_COUNT; i++)
58 		fds[i] = -1;
59 
60 	tst_setup_netns();
61 
62 	/*
63 	 * Create network namespace to hide the destination interface from
64 	 * the test process.
65 	 */
66 	parentns = SAFE_OPEN("/proc/self/ns/net", O_RDONLY);
67 	SAFE_UNSHARE(CLONE_NEWNET);
68 
69 	/* Do NOT close this FD, or both interfaces will be destroyed */
70 	childns = SAFE_OPEN("/proc/self/ns/net", O_RDONLY);
71 
72 	/* Configure child namespace */
73 	CREATE_VETH_PAIR("ltp_veth1", "ltp_veth2");
74 	NETDEV_ADD_ADDRESS_INET("ltp_veth2", htonl(DSTADDR), NETMASK,
75 		IFA_F_NOPREFIXROUTE);
76 	NETDEV_SET_STATE("ltp_veth2", 1);
77 	NETDEV_ADD_ROUTE_INET("ltp_veth2", 0, 0, htonl(SRCNET), NETMASK, 0);
78 
79 	/* Configure parent namespace */
80 	NETDEV_CHANGE_NS_FD("ltp_veth1", parentns);
81 	SAFE_SETNS(parentns, CLONE_NEWNET);
82 	addr = SRCADDR_BASE;
83 
84 	for (i = 0; i < SRCADDR_COUNT; i++, addr++) {
85 		NETDEV_ADD_ADDRESS_INET("ltp_veth1", htonl(addr), NETMASK,
86 			IFA_F_NOPREFIXROUTE);
87 	}
88 
89 	NETDEV_SET_STATE("ltp_veth1", 1);
90 	NETDEV_ADD_ROUTE_INET("ltp_veth1", 0, 0, htonl(DSTNET), NETMASK, 0);
91 	SAFE_FILE_PRINTF("/proc/sys/net/ipv4/conf/ltp_veth1/forwarding", "1");
92 
93 	/* Open test sockets */
94 	for (i = 0; i < SRCADDR_COUNT; i++) {
95 		ipaddr.sin_addr.s_addr = htonl(SRCADDR_BASE + i);
96 		fds[i] = SAFE_SOCKET(AF_INET, SOCK_DGRAM, 0);
97 		SAFE_SETSOCKOPT_INT(fds[i], IPPROTO_IP, IP_RECVERR, 1);
98 		SAFE_BIND(fds[i], (struct sockaddr *)&ipaddr, sizeof(ipaddr));
99 	}
100 }
101 
count_icmp_errors(int fd)102 static int count_icmp_errors(int fd)
103 {
104 	int error_count = 0;
105 	ssize_t len;
106 	char msgbuf[BUFSIZE], errbuf[BUFSIZE];
107 	struct sockaddr_in addr;
108 	struct cmsghdr *cmsg;
109 	struct sock_extended_err exterr;
110 	struct iovec iov = {
111 		.iov_base = msgbuf,
112 		.iov_len = BUFSIZE
113 	};
114 
115 	while (1) {
116 		struct msghdr msg = {
117 			.msg_name = (struct sockaddr *)&addr,
118 			.msg_namelen = sizeof(addr),
119 			.msg_iov = &iov,
120 			.msg_iovlen = 1,
121 			.msg_flags = 0,
122 			.msg_control = errbuf,
123 			.msg_controllen = BUFSIZE
124 		};
125 
126 		memset(errbuf, 0, BUFSIZE);
127 		errno = 0;
128 		len = recvmsg(fd, &msg, MSG_ERRQUEUE);
129 
130 		if (len == -1) {
131 			if (errno == EWOULDBLOCK || errno == EAGAIN)
132 				break;
133 
134 			tst_brk(TBROK | TERRNO, "recvmsg() failed");
135 		}
136 
137 		if (len < 0) {
138 			tst_brk(TBROK | TERRNO,
139 				"Invalid recvmsg() return value %zd", len);
140 		}
141 
142 		for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
143 			cmsg = CMSG_NXTHDR(&msg, cmsg)) {
144 			if (cmsg->cmsg_level != SOL_IP)
145 				continue;
146 
147 			if (cmsg->cmsg_type != IP_RECVERR)
148 				continue;
149 
150 			memcpy(&exterr, CMSG_DATA(cmsg), sizeof(exterr));
151 
152 			if (exterr.ee_origin != SO_EE_ORIGIN_ICMP)
153 				tst_brk(TBROK, "Unexpected non-ICMP error");
154 
155 			if (exterr.ee_errno != ECONNREFUSED) {
156 				TST_ERR = exterr.ee_errno;
157 				tst_brk(TBROK | TTERRNO,
158 					"Unexpected ICMP error");
159 			}
160 
161 			error_count++;
162 		}
163 	}
164 
165 	return error_count;
166 }
167 
packet_batch(const struct sockaddr * addr,socklen_t addrsize)168 static int packet_batch(const struct sockaddr *addr, socklen_t addrsize)
169 {
170 	int i, j, error_count = 0;
171 	char data = 0;
172 
173 	for (i = 0; i < SRCADDR_COUNT; i++) {
174 		for (j = 0; j < 2; j++) {
175 			error_count += count_icmp_errors(fds[i]);
176 			TEST(sendto(fds[i], &data, sizeof(data), 0, addr,
177 				addrsize));
178 
179 			if (TST_RET == -1) {
180 				if (TST_ERR == ECONNREFUSED) {
181 					j--; /* flush ICMP errors and retry */
182 					continue;
183 				}
184 
185 				tst_brk(TBROK | TTERRNO, "sento() failed");
186 			}
187 
188 			if (TST_RET < 0) {
189 				tst_brk(TBROK | TTERRNO,
190 					"Invalid sento() return value %ld",
191 					TST_RET);
192 			}
193 		}
194 	}
195 
196 	/*
197 	 * Wait and collect pending ICMP errors. Waiting less than 2 seconds
198 	 * will make the test unreliable. Looping over each socket multiple
199 	 * times (with or without poll()) will cause kernel to silently
200 	 * discard ICMP errors, allowing the test to pass on vulnerable
201 	 * systems.
202 	 */
203 	sleep(2);
204 
205 	for (i = 0; i < SRCADDR_COUNT; i++)
206 		error_count += count_icmp_errors(fds[i]);
207 
208 	return error_count;
209 }
210 
run(void)211 static void run(void)
212 {
213 	int i, errors_baseline, errors;
214 	struct sockaddr_in addr = {
215 		.sin_family = AF_INET,
216 		.sin_port = TST_GET_UNUSED_PORT(AF_INET, SOCK_DGRAM),
217 		.sin_addr = { htonl(DSTADDR) }
218 	};
219 
220 	errors_baseline = packet_batch((struct sockaddr *)&addr, sizeof(addr));
221 	errors = errors_baseline;
222 	tst_res(TINFO, "Batch 0: Got %d ICMP errors", errors);
223 
224 	for (i = 1; i < BATCH_COUNT && errors == errors_baseline; i++) {
225 		errors = packet_batch((struct sockaddr *)&addr, sizeof(addr));
226 		tst_res(TINFO, "Batch %d: Got %d ICMP errors", i, errors);
227 	}
228 
229 	if (errors == errors_baseline) {
230 		tst_res(TFAIL,
231 			"ICMP rate limit not randomized, system is vulnerable");
232 		return;
233 	}
234 
235 	tst_res(TPASS, "ICMP rate limit is randomized");
236 }
237 
cleanup(void)238 static void cleanup(void)
239 {
240 	int i;
241 
242 	for (i = 0; i < SRCADDR_COUNT; i++)
243 		if (fds[i] >= 0)
244 			SAFE_CLOSE(fds[i]);
245 
246 	if (childns >= 0)
247 		SAFE_CLOSE(childns);
248 
249 	if (parentns >= 0)
250 		SAFE_CLOSE(parentns);
251 }
252 
253 static struct tst_test test = {
254 	.test_all = run,
255 	.setup = setup,
256 	.cleanup = cleanup,
257 	.needs_kconfigs = (const char *[]) {
258 		"CONFIG_VETH",
259 		"CONFIG_USER_NS=y",
260 		"CONFIG_NET_NS=y",
261 		NULL
262 	},
263 	.save_restore = (const struct tst_path_val[]) {
264 		{"/proc/sys/user/max_user_namespaces", "1024", TST_SR_SKIP},
265 		{}
266 	},
267 	.tags = (const struct tst_tag[]) {
268 		{"linux-git", "b38e7819cae9"},
269 		{"CVE", "2020-25705"},
270 		{}
271 	}
272 };
273