1// SPDX-License-Identifier: GPL-2.0
2/*
3 *   Copyright (c) International Business Machines  Corp., 2001
4 *
5 *   Ported by John George
6 */
7
8/*
9 * Test that setregid() fails and sets the proper errno values when a
10 * non-root user attemps to change the real or effective group id to a
11 * value other than the current gid or the current effective gid.
12 */
13
14#include <errno.h>
15#include <pwd.h>
16#include <grp.h>
17#include <stdlib.h>
18
19#include "tst_test.h"
20#include "tst_uid.h"
21#include "compat_tst_16.h"
22
23static gid_t root_gid, nobody_gid, other_gid, neg_one = -1;
24
25static struct passwd *ltpuser;
26
27/*
28 * The following structure contains all test data.  Each structure in the array
29 * is used for a separate test.  The tests are executed in the for loop below.
30 */
31
32static struct tcase {
33	gid_t *real_gid;
34	gid_t *eff_gid;
35	int exp_errno;
36	gid_t *exp_real_usr;
37	gid_t *exp_eff_usr;
38	char *test_msg;
39} tcases[] = {
40	{
41	&neg_one, &root_gid, EPERM, &nobody_gid, &nobody_gid,
42		    "After setregid(-1, root),"}, {
43	&neg_one, &other_gid, EPERM, &nobody_gid, &nobody_gid,
44		    "After setregid(-1, other)"}, {
45	&root_gid, &neg_one, EPERM, &nobody_gid, &nobody_gid,
46		    "After setregid(root,-1),"}, {
47	&other_gid, &neg_one, EPERM, &nobody_gid, &nobody_gid,
48		    "After setregid(other, -1),"}, {
49	&root_gid, &other_gid, EPERM, &nobody_gid, &nobody_gid,
50		    "After setregid(root, other)"}, {
51	&other_gid, &root_gid, EPERM, &nobody_gid, &nobody_gid,
52		    "After setregid(other, root),"}
53};
54
55void gid_verify(gid_t rg, gid_t eg, char *when)
56{
57	if ((getgid() != rg) || (getegid() != eg)) {
58		tst_res(TFAIL, "ERROR: %s real gid = %d; effective gid = %d",
59			 when, getgid(), getegid());
60		tst_res(TINFO, "Expected: real gid = %d; effective gid = %d",
61			 rg, eg);
62		return;
63	}
64
65	tst_res(TPASS, "real or effective gid wasn't modified as expected");
66}
67
68static void run(unsigned int n)
69{
70	struct tcase *tc = &tcases[n];
71
72	/* Set the real or effective group id */
73	TEST(SETREGID(*tc->real_gid, *tc->eff_gid));
74
75	if (TST_RET == -1) {
76		if (tc->exp_errno == TST_ERR) {
77			tst_res(TPASS | TTERRNO,
78				"setregid(%d, %d) failed as expected",
79				*tc->real_gid, *tc->eff_gid);
80		} else {
81			tst_res(TFAIL | TTERRNO,
82				"setregid(%d, %d) failed unexpectedly, expected %s",
83				*tc->real_gid, *tc->eff_gid,
84				tst_strerrno(tc->exp_errno));
85		}
86	} else {
87		tst_res(TFAIL,
88			"setregid(%d, %d) did not fail (ret: %ld) as expected (ret: -1).",
89			*tc->real_gid, *tc->eff_gid, TST_RET);
90	}
91	gid_verify(*tc->exp_real_usr, *tc->exp_eff_usr, tc->test_msg);
92}
93
94static void setup(void)
95{
96	gid_t test_groups[3];
97
98	ltpuser = SAFE_GETPWNAM("nobody");
99	nobody_gid = test_groups[0] = ltpuser->pw_gid;
100	root_gid = test_groups[1] = getgid();
101	tst_get_gids(test_groups, 2, 3);
102	other_gid = test_groups[2];
103
104	GID16_CHECK(root_gid, setregid);
105	GID16_CHECK(nobody_gid, setregid);
106	GID16_CHECK(other_gid, setregid);
107
108	SAFE_SETGID(ltpuser->pw_gid);
109	SAFE_SETUID(ltpuser->pw_uid);
110}
111
112static struct tst_test test = {
113	.tcnt = ARRAY_SIZE(tcases),
114	.needs_root = 1,
115	.test = run,
116	.setup = setup,
117};
118