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 37863c43b2c6 ("KEYS: prevent KEYCTL_READ on
20  * negative key").  This is CVE-2017-12192.
21  */
22 
23 #include <errno.h>
24 #include <stdlib.h>
25 #include <sys/wait.h>
26 
27 #include "tst_test.h"
28 #include "lapi/keyctl.h"
29 
30 static void try_to_read_negative_key(void)
31 {
32 	key_serial_t key_id;
33 	char buffer[128];
34 
35 	/*
36 	 * Create a negatively instantiated key of the "user" key type.  This
37 	 * key type is chosen because it has a ->read() method (which makes the
38 	 * bug reachable) and is available whenever CONFIG_KEYS is enabled.
39 	 *
40 	 * request_key() will result in the creation of a negative key provided
41 	 * that /sbin/request-key isn't configured to positively instantiate the
42 	 * key, based on the provided type, description, and callout_info.  If
43 	 * /sbin/request-key doesn't exist, errno will be ENOENT; while if it
44 	 * does exist and we specify some random unprefixed description, errno
45 	 * should be ENOKEY (since /sbin/request-key should not be configured to
46 	 * instantiate random user keys).  In either case a negative key should
47 	 * be created and we can continue on with the test.  Negative keys last
48 	 * for 60 seconds so there should be plenty of time for the test.
49 	 */
50 	TEST(request_key("user", "description", "callout_info",
51 			 KEY_SPEC_PROCESS_KEYRING));
52 	if (TEST_RETURN != -1)
53 		tst_brk(TBROK, "request_key() unexpectedly succeeded");
54 
55 	if (TEST_ERRNO != ENOKEY && TEST_ERRNO != ENOENT) {
56 		tst_brk(TBROK | TTERRNO,
57 			"request_key() failed with unexpected error");
58 	}
59 
60 	/* Get the ID of the negative key by reading the keyring */
61 	TEST(keyctl(KEYCTL_READ, KEY_SPEC_PROCESS_KEYRING,
62 		    &key_id, sizeof(key_id)));
63 	if (TEST_RETURN < 0)
64 		tst_brk(TBROK | TTERRNO, "KEYCTL_READ unexpectedly failed");
65 	if (TEST_RETURN != sizeof(key_id)) {
66 		tst_brk(TBROK, "KEYCTL_READ returned %ld but expected %zu",
67 			TEST_RETURN, sizeof(key_id));
68 	}
69 
70 	/*
71 	 * Now try to read the negative key.  Unpatched kernels will oops trying
72 	 * to read from memory address 0x00000000ffffff92.
73 	 */
74 	tst_res(TINFO, "trying to read from the negative key...");
75 	TEST(keyctl(KEYCTL_READ, key_id, buffer, sizeof(buffer)));
76 	if (TEST_RETURN != -1) {
77 		tst_brk(TFAIL,
78 			"KEYCTL_READ on negative key unexpectedly succeeded");
79 	}
80 	if (TEST_ERRNO != ENOKEY) {
81 		tst_brk(TFAIL | TTERRNO,
82 			"KEYCTL_READ on negative key failed with unexpected error");
83 	}
84 	tst_res(TPASS,
85 		"KEYCTL_READ on negative key expectedly failed with ENOKEY");
86 }
87 
88 static void do_test(void)
89 {
90 	int status;
91 
92 	if (SAFE_FORK() == 0) {
93 		try_to_read_negative_key();
94 		return;
95 	}
96 
97 	SAFE_WAIT(&status);
98 
99 	if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
100 		tst_res(TPASS, "didn't crash while reading from negative key");
101 		return;
102 	}
103 
104 	if (WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) {
105 		tst_res(TFAIL, "reading from negative key caused kernel oops");
106 		return;
107 	}
108 
109 	if (WIFEXITED(status) && WEXITSTATUS(status) == TCONF)
110 		tst_brk(TCONF, "syscall not implemented");
111 
112 	tst_brk(TBROK, "Child %s", tst_strstatus(status));
113 }
114 
115 static struct tst_test test = {
116 	.test_all = do_test,
117 	.forks_child = 1,
118 };
119