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