priv_sched_rtprio.c revision 162271
1/*-
2 * Copyright (c) 2006 nCircle Network Security, Inc.
3 * All rights reserved.
4 *
5 * This software was developed by Robert N. M. Watson for the TrustedBSD
6 * Project under contract to nCircle Network Security, Inc.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR, NCIRCLE NETWORK SECURITY,
21 * INC., OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
23 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 * $FreeBSD: head/tools/regression/priv/priv_sched_rtprio.c 162271 2006-09-13 09:05:39Z rwatson $
30 */
31
32/*
33 * Test privilege associated with real time process settings.  There are
34 * three relevant notions of privilege:
35 *
36 * - Privilege to set the real-time priority of the current process.
37 * - Privilege to set the real-time priority of another process.
38 * - Privilege to set the idle priority of another process.
39 * - No privilege to set the idle priority of the current process.
40 *
41 * This requires a test process and a target (dummy) process running with
42 * various uids.  This test is based on the code in the setpriority() test.
43 */
44
45#include <sys/types.h>
46#include <sys/rtprio.h>
47#include <sys/wait.h>
48
49#include <err.h>
50#include <errno.h>
51#include <signal.h>
52#include <stdlib.h>
53#include <unistd.h>
54
55#include "main.h"
56
57static void
58dummy(void)
59{
60
61	while (1)
62		sleep(1);
63}
64
65static void
66collect(pid_t test_pid, pid_t dummy_pid)
67{
68	pid_t pid;
69
70	/*
71	 * First, collect the main test process.  When it has exited, then
72	 * kill off the dummy process.
73	 */
74	if (test_pid > 0) {
75		while (1) {
76			pid = waitpid(test_pid, NULL, 0);
77			if (pid == -1)
78				warn("waitpid(%d (test), NULL, 0)", test_pid);
79			if (pid == test_pid)
80				break;
81		}
82	}
83
84	if (kill(dummy_pid, SIGKILL) < 0)
85		err(-1, "kill(%d, SIGKILL)", dummy_pid);
86
87	while (1) {
88		pid = waitpid(dummy_pid, NULL, 0);
89		if (pid == -1)
90			warn("waitpid(%d, NULL, 0)", dummy_pid);
91		if (pid == dummy_pid)
92			return;
93	}
94}
95
96static void
97test(pid_t dummy_pid)
98{
99	struct rtprio rtp;
100	int error;
101
102	/*
103	 * Tests first as root.  Test that we can set normal, realtime, and
104	 * idle priorities on the current thread and on the dummy thread.
105	 */
106	rtp.type = RTP_PRIO_REALTIME;
107	rtp.prio = 0;
108	if (rtprio(RTP_SET, 0, &rtp) < 0)
109		err(-1, "rtprio(RTP_SET, 0, {REALTIME, 0}) as root");
110
111	rtp.type = RTP_PRIO_IDLE;
112	rtp.prio = 0;
113	if (rtprio(RTP_SET, 0, &rtp) < 0)
114		err(-1, "rtprio(RTP_SET, 0, {IDLE, 0}) as root");
115
116	rtp.type = RTP_PRIO_NORMAL;
117	rtp.prio = 0;
118	if (rtprio(RTP_SET, 0, &rtp) < 0)
119		err(-1, "rtprio(RTP_SET, 0, {NORMAL, 0) as root");
120
121	rtp.type = RTP_PRIO_REALTIME;
122	rtp.prio = 0;
123	if (rtprio(RTP_SET, dummy_pid, &rtp) < 0)
124		err(-1, "rtprio(RTP_SET, %d, {REALTIME, 0}) as root",
125		    dummy_pid);
126
127	rtp.type = RTP_PRIO_IDLE;
128	rtp.prio = 0;
129	if (rtprio(RTP_SET, dummy_pid, &rtp) < 0)
130		err(-1, "rtprio(RTP_SET, %d, {IDLE, 0}) as root", dummy_pid);
131
132	rtp.type = RTP_PRIO_NORMAL;
133	rtp.prio = 0;
134	if (rtprio(RTP_SET, dummy_pid, &rtp) < 0)
135		err(-1, "rtprio(RTP_SET, %d, {NORMAL, 0) as root",
136		    dummy_pid);
137
138	/*
139	 * Then test again as a different credential.
140	 */
141	if (setresuid(UID_OTHER, UID_OTHER, UID_OTHER) < 0)
142		err(-1, "setresuid(%d)", UID_OTHER);
143
144	rtp.type = RTP_PRIO_REALTIME;
145	rtp.prio = 0;
146	error = rtprio(RTP_SET, 0, &rtp);
147	if (error == 0)
148		errx(-1,
149		    "rtprio(RTP_SET, 0, {REALTIME, 0}) succeeded as !root");
150	if (errno != EPERM)
151		err(-1, "rtprio(RTP_SET, 0, {REALTIME, 0}) wrong errno %d as"
152		    " !root", errno);
153
154	rtp.type = RTP_PRIO_IDLE;
155	rtp.prio = 0;
156	error = rtprio(RTP_SET, 0, &rtp);
157	if (error == 0)
158		errx(-1, "rtprio(RTP_SET, 0, {IDLE, 0}) succeeded as !root");
159	if (errno != EPERM)
160		err(-1, "rtprio(RTP_SET, 0, {IDLE, 0}) wrong errno %d as "
161		    "!root", errno);
162
163	rtp.type = RTP_PRIO_NORMAL;
164	rtp.prio = 0;
165	if (rtprio(RTP_SET, 0, &rtp) < 0)
166		err(-1, "rtprio(RTP_SET, 0, {NORMAL, 0}) as !root");
167
168	rtp.type = RTP_PRIO_REALTIME;
169	rtp.prio = 0;
170	error = rtprio(RTP_SET, dummy_pid, &rtp);
171	if (error == 0)
172		errx(-1,
173		    "rtprio(RTP_SET, %d, {REALTIME, 0}) succeeded as !root",
174		    dummy_pid);
175	if (errno != EPERM)
176		err(-1, "rtprio(RTP_SET, %d, {REALTIME, 0}) wrong errno %d as"
177		    " !root", dummy_pid, errno);
178
179	rtp.type = RTP_PRIO_IDLE;
180	rtp.prio = 0;
181	error = rtprio(RTP_SET, dummy_pid, &rtp);
182	if (error == 0)
183		errx(-1, "rtprio(RTP_SET, %d, {IDLE, 0}) succeeded as !root",
184		    dummy_pid);
185	if (errno != EPERM)
186		err(-1,
187		    "rtprio(RTP_SET, %d, {IDLE, 0}) wrong errno %d as !root",
188		    dummy_pid, errno);
189
190	rtp.type = RTP_PRIO_NORMAL;
191	rtp.prio = 0;
192	error = rtprio(RTP_SET, dummy_pid, &rtp);
193	if (error == 0)
194		errx(-1,
195		    "rtprio(RTP_SET, %d, {NORMAL, 0) succeeded as !root",
196		    dummy_pid);
197	if (errno != EPERM)
198		err(-1, "rtprio(RTP_SET, %d, {NORMAL, 0}) wrong errno %d as "
199		    "!root", dummy_pid, errno);
200
201	exit(0);
202}
203
204void
205priv_sched_rtprio(void)
206{
207	pid_t dummy_pid, test_pid;
208
209	assert_root();
210
211	/*
212	 * Set up dummy process, which we will kill before exiting.
213	 */
214	dummy_pid = fork();
215	if (dummy_pid < 0)
216		err(-1, "fork - dummy");
217	if (dummy_pid == 0) {
218		if (setresuid(UID_THIRD, UID_THIRD, UID_THIRD) < 0)
219			err(-1, "setresuid(%d)", UID_THIRD);
220		dummy();
221	}
222	sleep(1);	/* Allow dummy thread to change uids. */
223
224	test_pid = fork();
225	if (test_pid < 0) {
226		warn("fork - test");
227		collect(-1, dummy_pid);
228		return;
229	}
230	if (test_pid == 0)
231		test(dummy_pid);
232
233	collect(test_pid, dummy_pid);
234}
235