1 /*
2 * Copyright (c) 2021 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #define _IP_VHL 1
30
31 #include <sys/fcntl.h>
32 #include <sys/socket.h>
33 #include <net/if.h>
34 #include <netinet/in.h>
35 #include <netinet/tcp.h>
36 #include <netinet/ip_icmp.h>
37 #include <arpa/inet.h>
38
39 #include <darwintest.h>
40 #include <string.h>
41 #include <unistd.h>
42
43 /*
44 * This test helps to reproduce a buffer overflow in the control plane:
45 * rdar://84355745
46 *
47 * The test allows to create a custom ICMP reply, and to send only a portion of it.
48 *
49 * To reproduce rdar://84355745, the test creates an ICMP "host unreachable" packet
50 * that contains a TCP header, and sends the first 28 bytes of the ICMP payload
51 * (48 including the outer IP header).
52 *
53 * +-----+-----+-----+-----+
54 * | IP | ICMP| IP | TCP |
55 * +-----+-----+-----+-----+
56 *
57 * <---------------->
58 * sent payload
59 *
60 * This allows us to ensure that the parsing of the (potentially truncated) TCP
61 * headers is done in a secure way.
62 */
63 static void
init_sin_address(struct sockaddr_in * sin)64 init_sin_address(struct sockaddr_in *sin)
65 {
66 memset(sin, 0, sizeof(struct sockaddr_in));
67 sin->sin_len = sizeof(struct sockaddr_in);
68 sin->sin_family = AF_INET;
69 }
70
71 static uint16_t
checksum_buffer(uint16_t * buf,size_t len)72 checksum_buffer(uint16_t *buf, size_t len)
73 {
74 unsigned long sum = 0;
75 while (len > 1) {
76 sum += *buf++;
77 len -= 2;
78 if (sum & 0x80000000) {
79 sum = (sum >> 16) + (sum & 0xFFF);
80 }
81 }
82 if (len == 1) {
83 sum += ((unsigned long)(*(uint8_t*)buf) << 8);
84 }
85 while (sum >> 16) {
86 sum = (sum >> 16) + (sum & 0xFFFF);
87 }
88
89 return (uint16_t)~sum;
90 }
91
92 #define MAXICMPBUFLEN 128
93 typedef struct icmp4_pcb {
94 int fd;
95 int id;
96 int seq;
97 int err;
98 int syserr;
99 size_t txlen;
100 uint16_t icmp_hdr_len;
101 struct icmp *icmp_hdr;
102 uint16_t inner_ip_hdr_len;
103 struct ip *inner_ip_hdr;
104 uint16_t inner_tcp_hdr_len;
105 struct tcphdr *inner_tcp_hdr;
106 struct in_addr in4addr_local;
107 struct in_addr in4addr_remote;
108 uint64_t buf[MAXICMPBUFLEN / 8];
109 } icmp4_pcb, *icmp4_pcb_t;
110
111 static void
icmp4_pcb_print(icmp4_pcb_t pcb)112 icmp4_pcb_print(icmp4_pcb_t pcb)
113 {
114 if (pcb == NULL) {
115 fprintf(stdout, "icmp pcb: null");
116 return;
117 }
118
119 fprintf(stdout, "icmp pcb: \n"
120 " fd=%d\n"
121 " id=%d\n"
122 " seq=%d\n"
123 " err=%d\n"
124 " syserr=%d\n"
125 " txlen=%lu\n"
126 " ICMP:\n"
127 " len=%hu\n"
128 " type=%d\n"
129 " code=%d\n"
130 " cksum=%hu\n"
131 " icmp_id=%hu\n"
132 " icmp_seq=%hu\n"
133 " IP:\n"
134 " len=%hu\n"
135 " hl=%hu\n"
136 " cksum=%hu\n"
137 " TCP:\n"
138 " len=%hu\n"
139 " sport=%hu [%hu]\n"
140 " dport=%hu [%hu]\n"
141 " cksum=%hu\n",
142 pcb->id,
143 pcb->id,
144 pcb->seq,
145 pcb->err,
146 pcb->syserr,
147 pcb->txlen,
148 pcb->icmp_hdr_len,
149 (uint16_t)(pcb->icmp_hdr == NULL ? -1 : pcb->icmp_hdr->icmp_type),
150 (uint16_t)(pcb->icmp_hdr == NULL ? -1 : pcb->icmp_hdr->icmp_code),
151 (uint16_t)(pcb->icmp_hdr == NULL ? -1 : pcb->icmp_hdr->icmp_cksum),
152 (uint16_t)(pcb->icmp_hdr == NULL ? -1 : pcb->icmp_hdr->icmp_id),
153 (uint16_t)(pcb->icmp_hdr == NULL ? -1 : pcb->icmp_hdr->icmp_seq),
154 pcb->inner_ip_hdr_len,
155 (uint16_t)(pcb->inner_ip_hdr == NULL ? -1 : IP_VHL_HL(pcb->inner_ip_hdr->ip_vhl) << 2),
156 (uint16_t)(pcb->inner_ip_hdr == NULL ? -1 : pcb->inner_ip_hdr->ip_sum),
157 pcb->inner_tcp_hdr_len,
158 (uint16_t)(pcb->inner_tcp_hdr == NULL ? -1 : pcb->inner_tcp_hdr->th_sport),
159 (uint16_t)(pcb->inner_tcp_hdr == NULL ? -1 : ntohs(pcb->inner_tcp_hdr->th_sport)),
160 (uint16_t)(pcb->inner_tcp_hdr == NULL ? -1 : pcb->inner_tcp_hdr->th_dport),
161 (uint16_t)(pcb->inner_tcp_hdr == NULL ? -1 : ntohs(pcb->inner_tcp_hdr->th_dport)),
162 (uint16_t)(pcb->inner_tcp_hdr == NULL ? -1 : pcb->inner_tcp_hdr->th_sum));
163 }
164
165 static void
icmp4_pcb_init(icmp4_pcb_t pcb)166 icmp4_pcb_init(icmp4_pcb_t pcb)
167 {
168 memset(pcb, 0, sizeof(struct icmp4_pcb));
169 }
170
171 static void
icmp4_pcb_close(icmp4_pcb_t pcb)172 icmp4_pcb_close(icmp4_pcb_t pcb)
173 {
174 if (pcb->fd != -1) {
175 close(pcb->fd);
176 pcb->fd = -1;
177 }
178 }
179
180 static int
icmp4_pcb_open(icmp4_pcb_t pcb,struct in_addr * local,struct in_addr * remote)181 icmp4_pcb_open(icmp4_pcb_t pcb, struct in_addr *local, struct in_addr *remote)
182 {
183 pcb->fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
184 if (pcb->fd == -1) {
185 pcb->syserr = errno;
186 pcb->err = -1;
187 goto out;
188 }
189 int on = 1;
190 if (setsockopt(pcb->fd, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on)) == -1) {
191 pcb->syserr = errno;
192 close(pcb->fd);
193 pcb->err = -2;
194 goto out;
195 }
196
197 struct sockaddr_in sin;
198 memset(&sin, 0, sizeof(struct sockaddr_in));
199 sin.sin_len = sizeof(struct sockaddr_in);
200 sin.sin_family = AF_INET;
201 memcpy(&sin.sin_addr, local, sizeof(struct in_addr));
202
203 if (bind(pcb->fd, (struct sockaddr*)&sin, sin.sin_len) == -1) {
204 pcb->syserr = errno;
205 pcb->err = -3;
206 goto out;
207 }
208 memcpy(&(pcb->in4addr_local), local, sizeof(struct in_addr));
209
210 memcpy(&sin.sin_addr, remote, sizeof(struct in_addr));
211 if (connect(pcb->fd, (struct sockaddr*)&sin, sin.sin_len) == -1) {
212 pcb->syserr = errno;
213 pcb->err = -4;
214 goto out;
215 }
216 memcpy(&(pcb->in4addr_remote), remote, sizeof(struct in_addr));
217
218 out:
219 if (pcb->err != 0) {
220 icmp4_pcb_close(pcb);
221 }
222 return pcb->err;
223 }
224
225 static size_t
icmp4_pcb_get_payload_len(icmp4_pcb_t pcb)226 icmp4_pcb_get_payload_len(icmp4_pcb_t pcb)
227 {
228 return pcb->icmp_hdr_len + pcb->inner_ip_hdr_len + pcb->inner_ip_hdr_len;
229 }
230
231 static size_t
icmp4_pcb_set_payload(icmp4_pcb_t pcb,struct icmp * icmp_in,struct ip * ip_in,struct tcphdr * tcp_in)232 icmp4_pcb_set_payload(icmp4_pcb_t pcb, struct icmp *icmp_in, struct ip *ip_in, struct tcphdr *tcp_in)
233 {
234 uint8_t *ptr = (uint8_t*)pcb->buf;
235 pcb->icmp_hdr_len = ICMP_MINLEN;
236 pcb->inner_ip_hdr_len = (uint16_t)(IP_VHL_HL(ip_in->ip_vhl) << 2);
237 pcb->inner_tcp_hdr_len = sizeof(struct tcphdr);
238
239 pcb->inner_tcp_hdr = (struct tcphdr*)(ptr + pcb->icmp_hdr_len + pcb->inner_ip_hdr_len);
240 pcb->inner_tcp_hdr->th_sport = htons(tcp_in->th_sport);
241 pcb->inner_tcp_hdr->th_dport = htons(tcp_in->th_dport);
242 pcb->inner_tcp_hdr->th_seq = htonl(tcp_in->th_seq);
243 pcb->inner_tcp_hdr->th_ack = htonl(tcp_in->th_ack);
244 pcb->inner_tcp_hdr->th_flags = tcp_in->th_flags;
245 pcb->inner_tcp_hdr->th_sum = 0;
246 pcb->inner_tcp_hdr->th_sum = checksum_buffer((uint16_t*)pcb->inner_tcp_hdr, pcb->inner_tcp_hdr_len);
247
248 pcb->inner_ip_hdr = (struct ip*)(ptr + pcb->icmp_hdr_len);
249 pcb->inner_ip_hdr->ip_vhl = ip_in->ip_vhl;
250 pcb->inner_ip_hdr->ip_tos = ip_in->ip_tos;
251 pcb->inner_ip_hdr->ip_len = pcb->inner_tcp_hdr_len + pcb->inner_ip_hdr_len;
252 pcb->inner_ip_hdr->ip_id = 1;
253 pcb->inner_ip_hdr->ip_off = 0;
254 pcb->inner_ip_hdr->ip_ttl = 64;
255 pcb->inner_ip_hdr->ip_p = IPPROTO_TCP;
256 pcb->inner_ip_hdr->ip_sum = 0;
257 memcpy(&(pcb->inner_ip_hdr->ip_src), &(pcb->in4addr_local), sizeof(struct in_addr));
258 memcpy(&(pcb->inner_ip_hdr->ip_dst), &(pcb->in4addr_remote), sizeof(struct in_addr));
259 pcb->inner_ip_hdr->ip_sum = checksum_buffer((uint16_t*)pcb->inner_ip_hdr, pcb->inner_ip_hdr_len);
260
261 pcb->icmp_hdr = (struct icmp*)pcb->buf;
262
263 pcb->icmp_hdr->icmp_type = icmp_in->icmp_type;
264 pcb->icmp_hdr->icmp_code = icmp_in->icmp_code;
265 pcb->icmp_hdr->icmp_cksum = 0;
266 pcb->icmp_hdr->icmp_id = htons(pcb->id++);
267 pcb->icmp_hdr->icmp_seq = htons(pcb->seq++);
268 pcb->icmp_hdr->icmp_cksum = checksum_buffer((uint16_t*)pcb->icmp_hdr, sizeof(struct icmp));
269
270 return icmp4_pcb_get_payload_len(pcb);
271 }
272
273 static int
icmp4_pcb_send_unreach(icmp4_pcb_t pcb,size_t maxlen)274 icmp4_pcb_send_unreach(icmp4_pcb_t pcb, size_t maxlen)
275 {
276 size_t out_len = icmp4_pcb_get_payload_len(pcb);
277 if (maxlen < out_len) {
278 out_len = maxlen;
279 }
280
281 fprintf(stderr, "Going to send %lu bytes of ICMP packet\n", out_len);
282 ssize_t len = send(pcb->fd, pcb->buf, out_len, 0);
283
284 if (len < 0 || (size_t)len != out_len) {
285 pcb->err = -6;
286 pcb->syserr = errno;
287 } else {
288 pcb->err = 0;
289 }
290 return pcb->err;
291 }
292
293 static void
icmp4_pcb_assert_payload_correct(icmp4_pcb_t pcb,size_t maxlen)294 icmp4_pcb_assert_payload_correct(icmp4_pcb_t pcb, size_t maxlen)
295 {
296 if (pcb == NULL) {
297 return;
298 }
299
300 T_ASSERT_NE(pcb->inner_ip_hdr, NULL, "IP hdr not set");
301
302 int icmplen = icmp4_pcb_get_payload_len(pcb);
303 T_ASSERT_LE(ICMP_MINLEN, icmplen, "ICMP payload smaller than minimal ICMP len");
304
305 T_ASSERT_GE(icmplen, ICMP_ADVLENMIN, "ICMP payload smaller than minimal advertised ICMP len");
306
307 // validate icmplen < ICMP_ADVLEN(icp) (ip_icmp.c:567)
308 int inner_ip_hdr_len = (IP_VHL_HL(pcb->inner_ip_hdr->ip_vhl) << 2);
309 int icmp_advlen = 8 + inner_ip_hdr_len + 8;
310 T_ASSERT_GE(icmplen, icmp_advlen, "ICMP payload smaller than advertised ICMP len");
311
312 // validate inner IP header length (ip_icmp.c:568)
313 T_ASSERT_GE(inner_ip_hdr_len, sizeof(struct ip), "IP payload smaller than IP header length");
314
315 // validate that the TCP header is outside of maxlen
316 size_t tcp_hdr_offset = (size_t)((uint8_t*)(pcb->inner_tcp_hdr) - (uint8_t*)(pcb->icmp_hdr));
317 fprintf(stdout, "tcp_hdr_offset: %lu, maxlen: %lu\n", tcp_hdr_offset, maxlen);
318 T_ASSERT_LE(maxlen, tcp_hdr_offset, "TCP header within maxlen");
319 }
320
321 T_DECL(icmp_send_malformed_packet_1, "ICMP packet with malformed TCP header")
322 {
323 struct sockaddr_in sin = {};
324
325 init_sin_address(&sin);
326 T_ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sin.sin_addr), 1, NULL);
327
328 icmp4_pcb pcb;
329 icmp4_pcb_init(&pcb);
330
331 T_ASSERT_EQ(icmp4_pcb_open(&pcb, &sin.sin_addr, &sin.sin_addr), 0, NULL);
332
333 struct icmp icmp_payload = {
334 .icmp_type = ICMP_UNREACH,
335 .icmp_code = ICMP_UNREACH_HOST,
336 };
337 struct ip ip_payload = {
338 .ip_vhl = 0x45,
339 .ip_tos = 0,
340 .ip_len = sizeof(struct ip) + sizeof(struct tcphdr),
341 .ip_id = 1,
342 .ip_off = 0,
343 .ip_ttl = 64,
344 };
345 struct tcphdr tcp_payload = {
346 .th_sport = 1234,
347 .th_dport = 80,
348 .th_seq = 1024,
349 .th_ack = 4096,
350 .th_flags = TH_FLAGS,
351 };
352
353 T_ASSERT_GT(icmp4_pcb_set_payload(&pcb, &icmp_payload, &ip_payload, &tcp_payload), 0L, NULL);
354
355 icmp4_pcb_print(&pcb);
356
357 size_t sendlen = 28;
358 icmp4_pcb_assert_payload_correct(&pcb, sendlen);
359
360 T_ASSERT_EQ(icmp4_pcb_send_unreach(&pcb, sendlen), 0, NULL);
361
362 icmp4_pcb_close(&pcb);
363 }
364