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 * 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 <edumazet@google.com>
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 <sched.h>
33 #include <limits.h>
34
35 #include "lapi/if_addr.h"
36 #include "tst_test.h"
37 #include "tst_netdevice.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 int real_uid = getuid();
57 int real_gid = getgid();
58
59 for (i = 0; i < SRCADDR_COUNT; i++)
60 fds[i] = -1;
61
62 SAFE_TRY_FILE_PRINTF("/proc/sys/user/max_user_namespaces", "%d", 10);
63
64 SAFE_UNSHARE(CLONE_NEWUSER);
65 SAFE_UNSHARE(CLONE_NEWNET);
66 SAFE_FILE_PRINTF("/proc/self/setgroups", "deny");
67 SAFE_FILE_PRINTF("/proc/self/uid_map", "0 %d 1\n", real_uid);
68 SAFE_FILE_PRINTF("/proc/self/gid_map", "0 %d 1\n", real_gid);
69
70 /*
71 * Create network namespace to hide the destination interface from
72 * the test process.
73 */
74 parentns = SAFE_OPEN("/proc/self/ns/net", O_RDONLY);
75 SAFE_UNSHARE(CLONE_NEWNET);
76
77 /* Do NOT close this FD, or both interfaces will be destroyed */
78 childns = SAFE_OPEN("/proc/self/ns/net", O_RDONLY);
79
80 /* Configure child namespace */
81 CREATE_VETH_PAIR("ltp_veth1", "ltp_veth2");
82 NETDEV_ADD_ADDRESS_INET("ltp_veth2", htonl(DSTADDR), NETMASK,
83 IFA_F_NOPREFIXROUTE);
84 NETDEV_SET_STATE("ltp_veth2", 1);
85 NETDEV_ADD_ROUTE_INET("ltp_veth2", 0, 0, htonl(SRCNET), NETMASK, 0);
86
87 /* Configure parent namespace */
88 NETDEV_CHANGE_NS_FD("ltp_veth1", parentns);
89 SAFE_SETNS(parentns, CLONE_NEWNET);
90 addr = SRCADDR_BASE;
91
92 for (i = 0; i < SRCADDR_COUNT; i++, addr++) {
93 NETDEV_ADD_ADDRESS_INET("ltp_veth1", htonl(addr), NETMASK,
94 IFA_F_NOPREFIXROUTE);
95 }
96
97 NETDEV_SET_STATE("ltp_veth1", 1);
98 NETDEV_ADD_ROUTE_INET("ltp_veth1", 0, 0, htonl(DSTNET), NETMASK, 0);
99 SAFE_FILE_PRINTF("/proc/sys/net/ipv4/conf/ltp_veth1/forwarding", "1");
100
101 /* Open test sockets */
102 for (i = 0; i < SRCADDR_COUNT; i++) {
103 ipaddr.sin_addr.s_addr = htonl(SRCADDR_BASE + i);
104 fds[i] = SAFE_SOCKET(AF_INET, SOCK_DGRAM, 0);
105 SAFE_SETSOCKOPT_INT(fds[i], IPPROTO_IP, IP_RECVERR, 1);
106 SAFE_BIND(fds[i], (struct sockaddr *)&ipaddr, sizeof(ipaddr));
107 }
108 }
109
count_icmp_errors(int fd)110 static int count_icmp_errors(int fd)
111 {
112 int error_count = 0;
113 ssize_t len;
114 char msgbuf[BUFSIZE], errbuf[BUFSIZE];
115 struct sockaddr_in addr;
116 struct cmsghdr *cmsg;
117 struct sock_extended_err exterr;
118 struct iovec iov = {
119 .iov_base = msgbuf,
120 .iov_len = BUFSIZE
121 };
122
123 while (1) {
124 struct msghdr msg = {
125 .msg_name = (struct sockaddr *)&addr,
126 .msg_namelen = sizeof(addr),
127 .msg_iov = &iov,
128 .msg_iovlen = 1,
129 .msg_flags = 0,
130 .msg_control = errbuf,
131 .msg_controllen = BUFSIZE
132 };
133
134 memset(errbuf, 0, BUFSIZE);
135 errno = 0;
136 len = recvmsg(fd, &msg, MSG_ERRQUEUE);
137
138 if (len == -1) {
139 if (errno == EWOULDBLOCK || errno == EAGAIN)
140 break;
141
142 tst_brk(TBROK | TERRNO, "recvmsg() failed");
143 }
144
145 if (len < 0) {
146 tst_brk(TBROK | TERRNO,
147 "Invalid recvmsg() return value %zd", len);
148 }
149
150 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
151 cmsg = CMSG_NXTHDR(&msg, cmsg)) {
152 if (cmsg->cmsg_level != SOL_IP)
153 continue;
154
155 if (cmsg->cmsg_type != IP_RECVERR)
156 continue;
157
158 memcpy(&exterr, CMSG_DATA(cmsg), sizeof(exterr));
159
160 if (exterr.ee_origin != SO_EE_ORIGIN_ICMP)
161 tst_brk(TBROK, "Unexpected non-ICMP error");
162
163 if (exterr.ee_errno != ECONNREFUSED) {
164 TST_ERR = exterr.ee_errno;
165 tst_brk(TBROK | TTERRNO,
166 "Unexpected ICMP error");
167 }
168
169 error_count++;
170 }
171 }
172
173 return error_count;
174 }
175
packet_batch(const struct sockaddr * addr,socklen_t addrsize)176 static int packet_batch(const struct sockaddr *addr, socklen_t addrsize)
177 {
178 int i, j, error_count = 0;
179 char data = 0;
180
181 for (i = 0; i < SRCADDR_COUNT; i++) {
182 for (j = 0; j < 2; j++) {
183 error_count += count_icmp_errors(fds[i]);
184 TEST(sendto(fds[i], &data, sizeof(data), 0, addr,
185 addrsize));
186
187 if (TST_RET == -1) {
188 if (TST_ERR == ECONNREFUSED) {
189 j--; /* flush ICMP errors and retry */
190 continue;
191 }
192
193 tst_brk(TBROK | TTERRNO, "sento() failed");
194 }
195
196 if (TST_RET < 0) {
197 tst_brk(TBROK | TTERRNO,
198 "Invalid sento() return value %ld",
199 TST_RET);
200 }
201 }
202 }
203
204 /*
205 * Wait and collect pending ICMP errors. Waiting less than 2 seconds
206 * will make the test unreliable. Looping over each socket multiple
207 * times (with or without poll()) will cause kernel to silently
208 * discard ICMP errors, allowing the test to pass on vulnerable
209 * systems.
210 */
211 sleep(2);
212
213 for (i = 0; i < SRCADDR_COUNT; i++)
214 error_count += count_icmp_errors(fds[i]);
215
216 return error_count;
217 }
218
run(void)219 static void run(void)
220 {
221 int i, errors_baseline, errors;
222 struct sockaddr_in addr = {
223 .sin_family = AF_INET,
224 .sin_port = TST_GET_UNUSED_PORT(AF_INET, SOCK_DGRAM),
225 .sin_addr = { htonl(DSTADDR) }
226 };
227
228 errors_baseline = packet_batch((struct sockaddr *)&addr, sizeof(addr));
229 errors = errors_baseline;
230 tst_res(TINFO, "Batch 0: Got %d ICMP errors", errors);
231
232 for (i = 1; i < BATCH_COUNT && errors == errors_baseline; i++) {
233 errors = packet_batch((struct sockaddr *)&addr, sizeof(addr));
234 tst_res(TINFO, "Batch %d: Got %d ICMP errors", i, errors);
235 }
236
237 if (errors == errors_baseline) {
238 tst_res(TFAIL,
239 "ICMP rate limit not randomized, system is vulnerable");
240 return;
241 }
242
243 tst_res(TPASS, "ICMP rate limit is randomized");
244 }
245
cleanup(void)246 static void cleanup(void)
247 {
248 int i;
249
250 for (i = 0; i < SRCADDR_COUNT; i++)
251 if (fds[i] >= 0)
252 SAFE_CLOSE(fds[i]);
253
254 if (childns >= 0)
255 SAFE_CLOSE(childns);
256
257 if (parentns >= 0)
258 SAFE_CLOSE(parentns);
259 }
260
261 static struct tst_test test = {
262 .test_all = run,
263 .setup = setup,
264 .cleanup = cleanup,
265 .needs_kconfigs = (const char *[]) {
266 "CONFIG_VETH",
267 "CONFIG_USER_NS=y",
268 "CONFIG_NET_NS=y",
269 NULL
270 },
271 .save_restore = (const char * const[]) {
272 "?/proc/sys/user/max_user_namespaces",
273 NULL,
274 },
275 .tags = (const struct tst_tag[]) {
276 {"linux-git", "b38e7819cae9"},
277 {"CVE", "2020-25705"},
278 {}
279 }
280 };
281