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