1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
2f08c3bdfSopenharmony_ci/*
3f08c3bdfSopenharmony_ci * Copyright (c) 2020 FUJITSU LIMITED. All rights reserved.
4f08c3bdfSopenharmony_ci * Author: Yang Xu <xuyang2018.jy@cn.fujitsu.com>
5f08c3bdfSopenharmony_ci *
6f08c3bdfSopenharmony_ci * Test unprivileged user can support the number of keys and the
7f08c3bdfSopenharmony_ci * number of bytes consumed in payloads of the keys. The default
8f08c3bdfSopenharmony_ci * value is 200 and 20000.
9f08c3bdfSopenharmony_ci *
10f08c3bdfSopenharmony_ci * This is also a regression test for
11f08c3bdfSopenharmony_ci * commit a08bf91ce28e ("KEYS: allow reaching the keys quotas exactly")
12f08c3bdfSopenharmony_ci * commit 2e356101e72a ("KEYS: reaching the keys quotas correctly")
13f08c3bdfSopenharmony_ci *
14f08c3bdfSopenharmony_ci * If you run this test with -i > 5 then expect to see some sporadic failures
15f08c3bdfSopenharmony_ci * where add_key fails with EDQUOT. Keys are freed asynchronously and we only
16f08c3bdfSopenharmony_ci * create up to 10 users to avoid race conditions.
17f08c3bdfSopenharmony_ci */
18f08c3bdfSopenharmony_ci
19f08c3bdfSopenharmony_ci#include <stdio.h>
20f08c3bdfSopenharmony_ci#include <stdlib.h>
21f08c3bdfSopenharmony_ci#include <pwd.h>
22f08c3bdfSopenharmony_ci#include "tst_test.h"
23f08c3bdfSopenharmony_ci#include "lapi/keyctl.h"
24f08c3bdfSopenharmony_ci
25f08c3bdfSopenharmony_ci#define MAX_USERS 10
26f08c3bdfSopenharmony_ci
27f08c3bdfSopenharmony_cistatic char *user_buf;
28f08c3bdfSopenharmony_cistatic uid_t ltpuser[MAX_USERS];
29f08c3bdfSopenharmony_ci
30f08c3bdfSopenharmony_cistatic unsigned int usern;
31f08c3bdfSopenharmony_cistatic unsigned int useri;
32f08c3bdfSopenharmony_ci
33f08c3bdfSopenharmony_cistatic void add_user(char n)
34f08c3bdfSopenharmony_ci{
35f08c3bdfSopenharmony_ci	char username[] = "ltp_add_key05_n";
36f08c3bdfSopenharmony_ci	const char *const cmd_useradd[] = {"useradd", username, NULL};
37f08c3bdfSopenharmony_ci	const char *const cmd_userdel[] = {"userdel", "-r", username, NULL};
38f08c3bdfSopenharmony_ci	const char *const cmd_groupdel[] = {"groupdel", username, NULL};
39f08c3bdfSopenharmony_ci	struct passwd *pw;
40f08c3bdfSopenharmony_ci
41f08c3bdfSopenharmony_ci	username[sizeof(username) - 2] = '0' + n;
42f08c3bdfSopenharmony_ci
43f08c3bdfSopenharmony_ci	tst_cmd(cmd_userdel, NULL, "/dev/null", TST_CMD_PASS_RETVAL);
44f08c3bdfSopenharmony_ci	tst_cmd(cmd_groupdel, NULL, "/dev/null", TST_CMD_PASS_RETVAL);
45f08c3bdfSopenharmony_ci
46f08c3bdfSopenharmony_ci	SAFE_CMD(cmd_useradd, NULL, NULL);
47f08c3bdfSopenharmony_ci	pw = SAFE_GETPWNAM(username);
48f08c3bdfSopenharmony_ci	ltpuser[(unsigned int)n] = pw->pw_uid;
49f08c3bdfSopenharmony_ci
50f08c3bdfSopenharmony_ci	tst_res(TINFO, "Created user %s", pw->pw_name);
51f08c3bdfSopenharmony_ci}
52f08c3bdfSopenharmony_ci
53f08c3bdfSopenharmony_cistatic void clean_user(char n)
54f08c3bdfSopenharmony_ci{
55f08c3bdfSopenharmony_ci	char username[] = "ltp_add_key05_n";
56f08c3bdfSopenharmony_ci	const char *const cmd_userdel[] = {"userdel", "-r", username, NULL};
57f08c3bdfSopenharmony_ci
58f08c3bdfSopenharmony_ci	username[sizeof(username) - 2] = '0' + n;
59f08c3bdfSopenharmony_ci
60f08c3bdfSopenharmony_ci	if (tst_cmd(cmd_userdel, NULL, NULL, TST_CMD_PASS_RETVAL))
61f08c3bdfSopenharmony_ci		tst_res(TWARN | TERRNO, "'userdel -r %s' failed", username);
62f08c3bdfSopenharmony_ci}
63f08c3bdfSopenharmony_ci
64f08c3bdfSopenharmony_cistatic inline void parse_proc_key_users(int *used_key, int *max_key, int *used_bytes, int *max_bytes)
65f08c3bdfSopenharmony_ci{
66f08c3bdfSopenharmony_ci	unsigned int val[4];
67f08c3bdfSopenharmony_ci	char fmt[1024];
68f08c3bdfSopenharmony_ci
69f08c3bdfSopenharmony_ci	sprintf(fmt, "%5u: %%*5d %%*d/%%*d %%d/%%d %%d/%%d", ltpuser[useri]);
70f08c3bdfSopenharmony_ci	SAFE_FILE_LINES_SCANF("/proc/key-users", fmt, &val[0], &val[1], &val[2], &val[3]);
71f08c3bdfSopenharmony_ci
72f08c3bdfSopenharmony_ci	if (used_key)
73f08c3bdfSopenharmony_ci		*used_key = val[0];
74f08c3bdfSopenharmony_ci	if (max_key)
75f08c3bdfSopenharmony_ci		*max_key = val[1];
76f08c3bdfSopenharmony_ci	if (used_bytes)
77f08c3bdfSopenharmony_ci		*used_bytes = val[2];
78f08c3bdfSopenharmony_ci	if (max_bytes)
79f08c3bdfSopenharmony_ci		*max_bytes = val[3];
80f08c3bdfSopenharmony_ci}
81f08c3bdfSopenharmony_ci
82f08c3bdfSopenharmony_cistatic void verify_max_bytes(void)
83f08c3bdfSopenharmony_ci{
84f08c3bdfSopenharmony_ci	char *buf;
85f08c3bdfSopenharmony_ci	int plen, invalid_plen, delta;
86f08c3bdfSopenharmony_ci	int used_bytes, max_bytes, tmp_used_bytes;
87f08c3bdfSopenharmony_ci
88f08c3bdfSopenharmony_ci	tst_res(TINFO, "test max bytes under unprivileged user");
89f08c3bdfSopenharmony_ci
90f08c3bdfSopenharmony_ci	parse_proc_key_users(NULL, NULL, &tmp_used_bytes, NULL);
91f08c3bdfSopenharmony_ci	TEST(add_key("user", "test2", user_buf, 64, KEY_SPEC_THREAD_KEYRING));
92f08c3bdfSopenharmony_ci	if (TST_RET == -1) {
93f08c3bdfSopenharmony_ci		tst_res(TFAIL | TTERRNO, "add key test2 failed");
94f08c3bdfSopenharmony_ci		return;
95f08c3bdfSopenharmony_ci	}
96f08c3bdfSopenharmony_ci	parse_proc_key_users(NULL, NULL, &used_bytes, &max_bytes);
97f08c3bdfSopenharmony_ci
98f08c3bdfSopenharmony_ci	/*
99f08c3bdfSopenharmony_ci	 * Compute delta between default datalen(in key_alloc) and actual
100f08c3bdfSopenharmony_ci	 * datlen(in key_payload_reserve).
101f08c3bdfSopenharmony_ci	 * more info see kernel code: security/keys/key.c
102f08c3bdfSopenharmony_ci	 */
103f08c3bdfSopenharmony_ci	delta = used_bytes - tmp_used_bytes - strlen("test2") - 1 - 64;
104f08c3bdfSopenharmony_ci	invalid_plen = max_bytes - used_bytes - delta - strlen("test_xxx");
105f08c3bdfSopenharmony_ci	buf = tst_alloc(invalid_plen);
106f08c3bdfSopenharmony_ci
107f08c3bdfSopenharmony_ci	TEST(add_key("user", "test_inv", buf, invalid_plen, KEY_SPEC_THREAD_KEYRING));
108f08c3bdfSopenharmony_ci	if (TST_RET != -1) {
109f08c3bdfSopenharmony_ci		tst_res(TFAIL, "add_key(test_inv) succeeded unexpectedltly");
110f08c3bdfSopenharmony_ci		return;
111f08c3bdfSopenharmony_ci	}
112f08c3bdfSopenharmony_ci	if (TST_ERR == EDQUOT)
113f08c3bdfSopenharmony_ci		tst_res(TPASS | TTERRNO, "add_key(test_inv) failed as expected");
114f08c3bdfSopenharmony_ci	else
115f08c3bdfSopenharmony_ci		tst_res(TFAIL | TTERRNO, "add_key(test_inv) failed expected EDQUOT got");
116f08c3bdfSopenharmony_ci
117f08c3bdfSopenharmony_ci	/*Reset delta*/
118f08c3bdfSopenharmony_ci	TEST(add_key("user", "test3", user_buf, 64, KEY_SPEC_THREAD_KEYRING));
119f08c3bdfSopenharmony_ci	if (TST_RET == -1) {
120f08c3bdfSopenharmony_ci		tst_res(TFAIL | TTERRNO, "add key test3 failed");
121f08c3bdfSopenharmony_ci		return;
122f08c3bdfSopenharmony_ci	}
123f08c3bdfSopenharmony_ci	TEST(add_key("user", "test4", user_buf, 64, KEY_SPEC_THREAD_KEYRING));
124f08c3bdfSopenharmony_ci	if (TST_RET == -1) {
125f08c3bdfSopenharmony_ci		tst_res(TFAIL | TTERRNO, "add key test4 failed");
126f08c3bdfSopenharmony_ci		return;
127f08c3bdfSopenharmony_ci	}
128f08c3bdfSopenharmony_ci	parse_proc_key_users(NULL, NULL, &used_bytes, &max_bytes);
129f08c3bdfSopenharmony_ci	plen = max_bytes - used_bytes - delta - strlen("test_xxx") - 1;
130f08c3bdfSopenharmony_ci	TEST(add_key("user", "test_max", buf, plen, KEY_SPEC_THREAD_KEYRING));
131f08c3bdfSopenharmony_ci	if (TST_RET == -1) {
132f08c3bdfSopenharmony_ci		tst_res(TFAIL | TTERRNO, "add_key(test_max) failed unexpectedly");
133f08c3bdfSopenharmony_ci		return;
134f08c3bdfSopenharmony_ci	}
135f08c3bdfSopenharmony_ci
136f08c3bdfSopenharmony_ci	tst_res(TPASS, "add_key(test_max) succeeded as expected");
137f08c3bdfSopenharmony_ci	parse_proc_key_users(NULL, NULL, &tmp_used_bytes, &max_bytes);
138f08c3bdfSopenharmony_ci	if (tmp_used_bytes == max_bytes)
139f08c3bdfSopenharmony_ci		tst_res(TPASS, "allow reaching the max bytes exactly");
140f08c3bdfSopenharmony_ci	else
141f08c3bdfSopenharmony_ci		tst_res(TFAIL, "max used bytes %u, key allow max bytes %u", tmp_used_bytes, max_bytes);
142f08c3bdfSopenharmony_ci}
143f08c3bdfSopenharmony_ci
144f08c3bdfSopenharmony_cistatic void verify_max_keys(void)
145f08c3bdfSopenharmony_ci{
146f08c3bdfSopenharmony_ci	int i, used_key, max_key;
147f08c3bdfSopenharmony_ci	char desc[10];
148f08c3bdfSopenharmony_ci
149f08c3bdfSopenharmony_ci	tst_res(TINFO, "test max keys under unprivileged user");
150f08c3bdfSopenharmony_ci	parse_proc_key_users(&used_key, &max_key, NULL, NULL);
151f08c3bdfSopenharmony_ci
152f08c3bdfSopenharmony_ci	for (i = used_key + 1; i <= max_key; i++) {
153f08c3bdfSopenharmony_ci		sprintf(desc, "abc%d", i);
154f08c3bdfSopenharmony_ci		TEST(add_key("user", desc, user_buf, 64, KEY_SPEC_THREAD_KEYRING));
155f08c3bdfSopenharmony_ci		if (TST_RET == -1) {
156f08c3bdfSopenharmony_ci			tst_res(TFAIL | TTERRNO, "add keyring key(%s) failed", desc);
157f08c3bdfSopenharmony_ci			goto count;
158f08c3bdfSopenharmony_ci		}
159f08c3bdfSopenharmony_ci	}
160f08c3bdfSopenharmony_ci
161f08c3bdfSopenharmony_ci	TEST(add_key("user", "test_invalid_key", user_buf, 64, KEY_SPEC_THREAD_KEYRING));
162f08c3bdfSopenharmony_ci	if (TST_RET != -1) {
163f08c3bdfSopenharmony_ci		tst_res(TFAIL, "add keyring key(test_invalid_key) succeeded unexpectedly");
164f08c3bdfSopenharmony_ci		goto count;
165f08c3bdfSopenharmony_ci	}
166f08c3bdfSopenharmony_ci	if (TST_ERR == EDQUOT)
167f08c3bdfSopenharmony_ci		tst_res(TPASS | TTERRNO, "add_key(test_invalid_key) failed as expected");
168f08c3bdfSopenharmony_ci	else
169f08c3bdfSopenharmony_ci		tst_res(TFAIL | TTERRNO, "add_key(test_invalid_key) failed expected EDQUOT got");
170f08c3bdfSopenharmony_ci
171f08c3bdfSopenharmony_cicount:
172f08c3bdfSopenharmony_ci	parse_proc_key_users(&used_key, &max_key, NULL, NULL);
173f08c3bdfSopenharmony_ci	if (used_key == max_key)
174f08c3bdfSopenharmony_ci		tst_res(TPASS, "allow reaching the max key(%u) exactly", max_key);
175f08c3bdfSopenharmony_ci	else
176f08c3bdfSopenharmony_ci		tst_res(TFAIL, "max used key %u, key allow max key %u", used_key, max_key);
177f08c3bdfSopenharmony_ci}
178f08c3bdfSopenharmony_ci
179f08c3bdfSopenharmony_cistatic void do_test(unsigned int n)
180f08c3bdfSopenharmony_ci{
181f08c3bdfSopenharmony_ci	if (usern < MAX_USERS)
182f08c3bdfSopenharmony_ci		add_user(usern++);
183f08c3bdfSopenharmony_ci
184f08c3bdfSopenharmony_ci	if (useri >= MAX_USERS) {
185f08c3bdfSopenharmony_ci		sleep(1);
186f08c3bdfSopenharmony_ci		useri = 0;
187f08c3bdfSopenharmony_ci	}
188f08c3bdfSopenharmony_ci
189f08c3bdfSopenharmony_ci	if (!SAFE_FORK()) {
190f08c3bdfSopenharmony_ci		SAFE_SETUID(ltpuser[useri]);
191f08c3bdfSopenharmony_ci		tst_res(TINFO, "User: %d, UID: %d", useri, ltpuser[useri]);
192f08c3bdfSopenharmony_ci		TEST(add_key("user", "test1", user_buf, 64, KEY_SPEC_THREAD_KEYRING));
193f08c3bdfSopenharmony_ci		if (TST_RET == -1) {
194f08c3bdfSopenharmony_ci			tst_res(TFAIL | TTERRNO, "add key test1 failed");
195f08c3bdfSopenharmony_ci			return;
196f08c3bdfSopenharmony_ci		}
197f08c3bdfSopenharmony_ci
198f08c3bdfSopenharmony_ci		if (n)
199f08c3bdfSopenharmony_ci			verify_max_keys();
200f08c3bdfSopenharmony_ci		else
201f08c3bdfSopenharmony_ci			verify_max_bytes();
202f08c3bdfSopenharmony_ci	}
203f08c3bdfSopenharmony_ci
204f08c3bdfSopenharmony_ci	tst_reap_children();
205f08c3bdfSopenharmony_ci	useri++;
206f08c3bdfSopenharmony_ci
207f08c3bdfSopenharmony_ci	return;
208f08c3bdfSopenharmony_ci}
209f08c3bdfSopenharmony_ci
210f08c3bdfSopenharmony_cistatic void cleanup(void)
211f08c3bdfSopenharmony_ci{
212f08c3bdfSopenharmony_ci	while (usern--)
213f08c3bdfSopenharmony_ci		clean_user(usern);
214f08c3bdfSopenharmony_ci}
215f08c3bdfSopenharmony_ci
216f08c3bdfSopenharmony_cistatic struct tst_test test = {
217f08c3bdfSopenharmony_ci	.test = do_test,
218f08c3bdfSopenharmony_ci	.tcnt = 2,
219f08c3bdfSopenharmony_ci	.needs_root = 1,
220f08c3bdfSopenharmony_ci	.forks_child = 1,
221f08c3bdfSopenharmony_ci	.cleanup = cleanup,
222f08c3bdfSopenharmony_ci	.save_restore = (const struct tst_path_val[]) {
223f08c3bdfSopenharmony_ci		{"/proc/sys/kernel/keys/gc_delay", "1",
224f08c3bdfSopenharmony_ci			TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
225f08c3bdfSopenharmony_ci		{"/proc/sys/kernel/keys/maxkeys", "200",
226f08c3bdfSopenharmony_ci			TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
227f08c3bdfSopenharmony_ci		{"/proc/sys/kernel/keys/maxbytes", "20000",
228f08c3bdfSopenharmony_ci			TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
229f08c3bdfSopenharmony_ci		{}
230f08c3bdfSopenharmony_ci	},
231f08c3bdfSopenharmony_ci	.bufs = (struct tst_buffers []) {
232f08c3bdfSopenharmony_ci		{&user_buf, .size = 64},
233f08c3bdfSopenharmony_ci		{}
234f08c3bdfSopenharmony_ci	},
235f08c3bdfSopenharmony_ci	.needs_cmds = (const char *const []) {
236f08c3bdfSopenharmony_ci		"useradd",
237f08c3bdfSopenharmony_ci		"userdel",
238f08c3bdfSopenharmony_ci		"groupdel",
239f08c3bdfSopenharmony_ci		NULL
240f08c3bdfSopenharmony_ci	},
241f08c3bdfSopenharmony_ci	.tags = (const struct tst_tag[]) {
242f08c3bdfSopenharmony_ci		{"linux-git", "a08bf91ce28"},
243f08c3bdfSopenharmony_ci		{"linux-git", "2e356101e72"},
244f08c3bdfSopenharmony_ci		{}
245f08c3bdfSopenharmony_ci	}
246f08c3bdfSopenharmony_ci};
247