1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2017 Google, Inc.
4 */
5
6/*
7 * Regression test for commit 63a0b0509e70 ("KEYS: fix freeing uninitialized
8 * memory in key_update()").  Try to reproduce the crash in two different ways:
9 *
10 * 1. Try to update a key of a type that has a ->preparse() method but not an
11 *    ->update() method.  Examples are the "asymmetric" and "dns_resolver" key
12 *    types.  It crashes reliably for the "asymmetric" key type, since the
13 *    "asymmetric" key type's ->free_preparse() method will dereference a
14 *    pointer in the uninitialized memory, whereas other key types often just
15 *    free a pointer which tends be NULL in practice, depending on how the stack
16 *    is laid out.  However, to actually be able to add an "asymmetric" key, we
17 *    need a specially-formatted payload and several kernel config options.  We
18 *    do try it, but for completeness we also try the "dns_resolver" key type
19 *    (though that's not guaranteed to be available either).
20 *
21 * 2. Race keyctl_update() with another task removing write permission from the
22 *    key using keyctl_setperm().  This can cause a crash with almost any key
23 *    type.  "user" is a good one to try, since it's always available if
24 *    keyrings are supported at all.  However, depending on how the stack is
25 *    laid out the crash may not actually occur.
26 */
27
28#include <errno.h>
29#include <stdlib.h>
30
31#include "tst_test.h"
32#include "lapi/keyctl.h"
33
34/*
35 * A valid payload for the "asymmetric" key type.  This is an x509 certificate
36 * in DER format, generated using:
37 *
38 *	openssl req -x509 -newkey rsa:512 -batch -nodes -outform der \
39 *		| ~/linux/scripts/bin2c
40 */
41static const char x509_cert[] =
42	"\x30\x82\x01\xd3\x30\x82\x01\x7d\xa0\x03\x02\x01\x02\x02\x09\x00"
43	"\x92\x2a\x76\xff\x0c\x00\xfb\x9a\x30\x0d\x06\x09\x2a\x86\x48\x86"
44	"\xf7\x0d\x01\x01\x0b\x05\x00\x30\x45\x31\x0b\x30\x09\x06\x03\x55"
45	"\x04\x06\x13\x02\x41\x55\x31\x13\x30\x11\x06\x03\x55\x04\x08\x0c"
46	"\x0a\x53\x6f\x6d\x65\x2d\x53\x74\x61\x74\x65\x31\x21\x30\x1f\x06"
47	"\x03\x55\x04\x0a\x0c\x18\x49\x6e\x74\x65\x72\x6e\x65\x74\x20\x57"
48	"\x69\x64\x67\x69\x74\x73\x20\x50\x74\x79\x20\x4c\x74\x64\x30\x1e"
49	"\x17\x0d\x31\x37\x30\x37\x32\x38\x32\x31\x34\x31\x33\x34\x5a\x17"
50	"\x0d\x31\x37\x30\x38\x32\x37\x32\x31\x34\x31\x33\x34\x5a\x30\x45"
51	"\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x41\x55\x31\x13\x30"
52	"\x11\x06\x03\x55\x04\x08\x0c\x0a\x53\x6f\x6d\x65\x2d\x53\x74\x61"
53	"\x74\x65\x31\x21\x30\x1f\x06\x03\x55\x04\x0a\x0c\x18\x49\x6e\x74"
54	"\x65\x72\x6e\x65\x74\x20\x57\x69\x64\x67\x69\x74\x73\x20\x50\x74"
55	"\x79\x20\x4c\x74\x64\x30\x5c\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7"
56	"\x0d\x01\x01\x01\x05\x00\x03\x4b\x00\x30\x48\x02\x41\x00\xde\x0b"
57	"\x1c\x24\xe2\x0d\xf8\x17\xf2\xc3\x6f\xc9\x72\x3e\x9d\xb0\x2d\x47"
58	"\xe4\xc4\x85\x87\xed\xde\x06\xe3\xf3\xe9\x4c\x35\x6c\xe4\xcb\x0e"
59	"\x44\x28\x23\x66\x76\xec\x4e\xdf\x10\x93\x92\x1e\x52\xfb\xdf\x5c"
60	"\x08\xe7\x24\x04\x66\xe3\x06\x05\x27\x56\xfb\x3e\x91\x31\x02\x03"
61	"\x01\x00\x01\xa3\x50\x30\x4e\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16"
62	"\x04\x14\x6f\x39\x3a\x46\xdf\x29\x63\xde\x54\x7b\x6c\x31\x06\xd0"
63	"\x9f\x36\x16\xfb\x9c\xbf\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30"
64	"\x16\x80\x14\x6f\x39\x3a\x46\xdf\x29\x63\xde\x54\x7b\x6c\x31\x06"
65	"\xd0\x9f\x36\x16\xfb\x9c\xbf\x30\x0c\x06\x03\x55\x1d\x13\x04\x05"
66	"\x30\x03\x01\x01\xff\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01"
67	"\x01\x0b\x05\x00\x03\x41\x00\x73\xf0\x4b\x62\x56\xed\xf0\x8b\x7e"
68	"\xc4\x75\x78\x98\xa2\x7a\x6e\x75\x1f\xde\x9b\xa0\xbe\x1a\x1f\x86"
69	"\x44\x13\xcd\x45\x06\x7f\x86\xde\xf6\x36\x4e\xb6\x15\xfa\xf5\xb0"
70	"\x34\xd2\x5e\x0b\xb3\x2c\x03\x5a\x5a\x28\x97\x5e\x7b\xdf\x63\x75"
71	"\x83\x8d\x69\xda\xd6\x59\xbd"
72	;
73
74	static int fips_enabled;
75
76static void new_session_keyring(void)
77{
78	TEST(keyctl(KEYCTL_JOIN_SESSION_KEYRING, NULL));
79	if (TST_RET < 0)
80		tst_brk(TBROK | TTERRNO, "failed to join new session keyring");
81}
82
83static void test_update_nonupdatable(const char *type,
84				     const void *payload, size_t plen)
85{
86	key_serial_t keyid;
87
88	new_session_keyring();
89
90	int is_asymmetric = !strcmp(type, "asymmetric");
91
92	TEST(add_key(type, "desc", payload, plen, KEY_SPEC_SESSION_KEYRING));
93	if (TST_RET < 0) {
94		if (TST_ERR == EINVAL && is_asymmetric && fips_enabled) {
95			tst_res(TCONF, "key size not allowed in FIPS mode");
96			return;
97		}
98		if (TST_ERR == ENODEV) {
99			tst_res(TCONF, "kernel doesn't support key type '%s'",
100				type);
101			return;
102		}
103		if (TST_ERR == EBADMSG && is_asymmetric) {
104			tst_res(TCONF, "kernel is missing x509 cert parser "
105				"(CONFIG_X509_CERTIFICATE_PARSER)");
106			return;
107		}
108		if (TST_ERR == ENOENT && is_asymmetric) {
109			tst_res(TCONF, "kernel is missing crypto algorithms "
110				"needed to parse x509 cert (CONFIG_CRYPTO_RSA "
111				"and/or CONFIG_CRYPTO_SHA256)");
112			return;
113		}
114		tst_res(TFAIL | TTERRNO, "unexpected error adding '%s' key",
115			type);
116		return;
117	}
118	keyid = TST_RET;
119
120	/*
121	 * Non-updatable keys don't start with write permission, so we must
122	 * explicitly grant it.
123	 */
124	TEST(keyctl(KEYCTL_SETPERM, keyid, KEY_POS_ALL));
125	if (TST_RET != 0) {
126		tst_res(TFAIL | TTERRNO,
127			"failed to grant write permission to '%s' key", type);
128		return;
129	}
130
131	tst_res(TINFO, "Try to update the '%s' key...", type);
132	TEST(keyctl(KEYCTL_UPDATE, keyid, payload, plen));
133	if (TST_RET == 0) {
134		tst_res(TFAIL,
135			"updating '%s' key unexpectedly succeeded", type);
136		return;
137	}
138	if (TST_ERR != EOPNOTSUPP) {
139		tst_res(TFAIL | TTERRNO,
140			"updating '%s' key unexpectedly failed", type);
141		return;
142	}
143	tst_res(TPASS, "updating '%s' key expectedly failed with EOPNOTSUPP",
144		type);
145}
146
147/*
148 * Try to update a key, racing with removing write permission.
149 * This may crash buggy kernels.
150 */
151static void test_update_setperm_race(void)
152{
153	static const char payload[] = "payload";
154	key_serial_t keyid;
155	int i;
156
157	new_session_keyring();
158
159	TEST(add_key("user", "desc", payload, sizeof(payload),
160		KEY_SPEC_SESSION_KEYRING));
161	if (TST_RET < 0) {
162		tst_res(TFAIL | TTERRNO, "failed to add 'user' key");
163		return;
164	}
165	keyid = TST_RET;
166
167	if (SAFE_FORK() == 0) {
168		uint32_t perm = KEY_POS_ALL;
169
170		for (i = 0; i < 10000; i++) {
171			perm ^= KEY_POS_WRITE;
172			TEST(keyctl(KEYCTL_SETPERM, keyid, perm));
173			if (TST_RET != 0)
174				tst_brk(TBROK | TTERRNO, "setperm failed");
175		}
176		exit(0);
177	}
178
179	tst_res(TINFO, "Try to update the 'user' key...");
180	for (i = 0; i < 10000; i++) {
181		TEST(keyctl(KEYCTL_UPDATE, keyid, payload, sizeof(payload)));
182		if (TST_RET != 0 && TST_ERR != EACCES) {
183			tst_res(TFAIL | TTERRNO, "failed to update 'user' key");
184			return;
185		}
186	}
187	tst_reap_children();
188	tst_res(TPASS, "didn't crash while racing to update 'user' key");
189}
190
191static void setup(void)
192{
193	fips_enabled = tst_fips_enabled();
194}
195
196static void do_test(unsigned int i)
197{
198	/*
199	 * We need to pass check in dns_resolver_preparse(),
200	 * give it dummy server list request.
201	 */
202	static char dns_res_payload[] = { 0x00, 0x00, 0x01, 0xff, 0x00 };
203
204	switch (i) {
205	case 0:
206		test_update_nonupdatable("asymmetric",
207					 x509_cert, sizeof(x509_cert));
208		break;
209	case 1:
210		test_update_nonupdatable("dns_resolver", dns_res_payload,
211			sizeof(dns_res_payload));
212		break;
213	case 2:
214		test_update_setperm_race();
215		break;
216	}
217}
218
219static struct tst_test test = {
220	.tcnt = 3,
221	.setup = setup,
222	.test = do_test,
223	.forks_child = 1,
224	.tags = (const struct tst_tag[]) {
225		{"linux-git", "63a0b0509e70"},
226		{}
227	}
228};
229