1/*
2 * Copyright (c) 2017 Richard Palethorpe <rpalethorpe@suse.com>
3 * Based on repro-compatReleaseEntry.c by NCC group
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18/*
19 * Test for CVE-2016-4997
20 *
21 * For a full explanation of how the vulnerability works see:
22 * https://github.com/nccgroup/TriforceLinuxSyscallFuzzer/tree/master/crash_reports/report_compatIpt
23 *
24 * The original vulnerability was present in the 32-bit compatibility system
25 * call, so the test should be compiled with -m32 and run on a 64-bit kernel.
26 * For simplicities sake the test requests root privliges instead of creating
27 * a user namespace.
28 */
29
30#include <stdint.h>
31#include <sys/types.h>
32#include <sys/socket.h>
33#include <netinet/in.h>
34#include <net/if.h>
35#include <limits.h>
36#include <linux/netfilter_ipv4/ip_tables.h>
37
38#include "tst_test.h"
39#include "tst_safe_net.h"
40#include "tst_kernel.h"
41
42#define TOO_SMALL_OFFSET 74
43#define OFFSET_OVERWRITE 0xFFFF
44#define NEXT_OFFSET (sizeof(struct ipt_entry)		\
45		     + sizeof(struct xt_entry_match)	\
46		     + sizeof(struct xt_entry_target))
47#define PADDING (OFFSET_OVERWRITE - NEXT_OFFSET)
48
49#ifndef HAVE_STRUCT_XT_ENTRY_MATCH
50struct xt_entry_match {
51	union {
52		struct {
53			uint16_t match_size;
54			char name[29];
55			uint8_t revision;
56		} user;
57		struct {
58			uint16_t match_size;
59			void *match;
60		} kernel;
61		uint16_t match_size;
62	} u;
63	unsigned char data[0];
64};
65#endif
66
67#ifndef HAVE_STRUCT_XT_ENTRY_TARGET
68struct xt_entry_target {
69	union {
70		struct {
71			uint16_t target_size;
72			char name[29];
73			uint8_t revision;
74		} user;
75		struct {
76			uint16_t target_size;
77			void *target;
78		} kernel;
79		uint16_t target_size;
80	} u;
81	unsigned char data[0];
82};
83#endif
84
85struct payload {
86	struct ipt_replace repl;
87	struct ipt_entry ent;
88	struct xt_entry_match match;
89	struct xt_entry_target targ;
90	char padding[PADDING];
91	struct xt_entry_target targ2;
92};
93
94static void setup(void)
95{
96	if (tst_kernel_bits() == 32 || sizeof(long) > 4)
97		tst_res(TCONF,
98			"The vulnerability was only present in 32-bit compat mode");
99}
100
101static void run(void)
102{
103	int ret, sock_fd;
104	struct payload p = { 0 };
105
106	sock_fd = SAFE_SOCKET(AF_INET, SOCK_DGRAM, 0);
107
108	strncpy(p.match.u.user.name, "icmp", sizeof(p.match.u.user.name));
109	p.match.u.match_size = OFFSET_OVERWRITE;
110
111	p.ent.next_offset = NEXT_OFFSET;
112	p.ent.target_offset = TOO_SMALL_OFFSET;
113
114	p.repl.num_entries = 2;
115	p.repl.num_counters = 1;
116	p.repl.size = sizeof(struct payload);
117	p.repl.valid_hooks = 0;
118
119	ret = setsockopt(sock_fd, SOL_IP, IPT_SO_SET_REPLACE,
120			 &p, sizeof(struct payload));
121	tst_res(TPASS | TERRNO, "We didn't cause a crash, setsockopt returned %d", ret);
122}
123
124static struct tst_test test = {
125	.min_kver = "2.6.32",
126	.setup = setup,
127	.test_all = run,
128	.needs_root = 1,
129};
130