1/*-
2 * Copyright (c) 2016 The FreeBSD Foundation
3 * All rights reserved.
4 *
5 * This software was developed by Konstantin Belousov <kib@FreeBSD.org>
6 * under sponsorship from the FreeBSD Foundation.
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 OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD$");
32
33#include <sys/procctl.h>
34#include <err.h>
35#include <stdbool.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
40
41enum {
42	MODE_ASLR,
43	MODE_INVALID,
44	MODE_TRACE,
45	MODE_TRAPCAP,
46	MODE_PROTMAX,
47	MODE_STACKGAP,
48#ifdef PROC_KPTI_CTL
49	MODE_KPTI,
50#endif
51#ifdef PROC_LA_CTL
52	MODE_LA57,
53	MODE_LA48,
54#endif
55};
56
57static pid_t
58str2pid(const char *str)
59{
60	pid_t res;
61	char *tail;
62
63	res = strtol(str, &tail, 0);
64	if (*tail != '\0') {
65		warnx("non-numeric pid");
66		return (-1);
67	}
68	return (res);
69}
70
71#ifdef PROC_KPTI_CTL
72#define	KPTI_USAGE "|kpti"
73#else
74#define	KPTI_USAGE
75#endif
76#ifdef PROC_LA_CTL
77#define	LA_USAGE "|la48|la57"
78#else
79#define	LA_USAGE
80#endif
81
82static void __dead2
83usage(void)
84{
85
86	fprintf(stderr, "Usage: proccontrol -m (aslr|protmax|trace|trapcap|"
87	    "stackgap"KPTI_USAGE LA_USAGE") [-q] "
88	    "[-s (enable|disable)] [-p pid | command]\n");
89	exit(1);
90}
91
92int
93main(int argc, char *argv[])
94{
95	int arg, ch, error, mode;
96	pid_t pid;
97	bool enable, do_command, query;
98
99	mode = MODE_INVALID;
100	enable = true;
101	pid = -1;
102	query = false;
103	while ((ch = getopt(argc, argv, "m:qs:p:")) != -1) {
104		switch (ch) {
105		case 'm':
106			if (strcmp(optarg, "aslr") == 0)
107				mode = MODE_ASLR;
108			else if (strcmp(optarg, "protmax") == 0)
109				mode = MODE_PROTMAX;
110			else if (strcmp(optarg, "trace") == 0)
111				mode = MODE_TRACE;
112			else if (strcmp(optarg, "trapcap") == 0)
113				mode = MODE_TRAPCAP;
114			else if (strcmp(optarg, "stackgap") == 0)
115				mode = MODE_STACKGAP;
116#ifdef PROC_KPTI_CTL
117			else if (strcmp(optarg, "kpti") == 0)
118				mode = MODE_KPTI;
119#endif
120#ifdef PROC_LA_CTL
121			else if (strcmp(optarg, "la57") == 0)
122				mode = MODE_LA57;
123			else if (strcmp(optarg, "la48") == 0)
124				mode = MODE_LA48;
125#endif
126			else
127				usage();
128			break;
129		case 's':
130			if (strcmp(optarg, "enable") == 0)
131				enable = true;
132			else if (strcmp(optarg, "disable") == 0)
133				enable = false;
134			else
135				usage();
136			break;
137		case 'p':
138			pid = str2pid(optarg);
139			break;
140		case 'q':
141			query = true;
142			break;
143		case '?':
144		default:
145			usage();
146			break;
147		}
148	}
149	argc -= optind;
150	argv += optind;
151	do_command = argc != 0;
152	if (do_command) {
153		if (pid != -1 || query)
154			usage();
155		pid = getpid();
156	} else if (pid == -1) {
157		pid = getpid();
158	}
159
160	if (query) {
161		switch (mode) {
162		case MODE_ASLR:
163			error = procctl(P_PID, pid, PROC_ASLR_STATUS, &arg);
164			break;
165		case MODE_TRACE:
166			error = procctl(P_PID, pid, PROC_TRACE_STATUS, &arg);
167			break;
168		case MODE_TRAPCAP:
169			error = procctl(P_PID, pid, PROC_TRAPCAP_STATUS, &arg);
170			break;
171		case MODE_PROTMAX:
172			error = procctl(P_PID, pid, PROC_PROTMAX_STATUS, &arg);
173			break;
174		case MODE_STACKGAP:
175			error = procctl(P_PID, pid, PROC_STACKGAP_STATUS, &arg);
176			break;
177#ifdef PROC_KPTI_CTL
178		case MODE_KPTI:
179			error = procctl(P_PID, pid, PROC_KPTI_STATUS, &arg);
180			break;
181#endif
182#ifdef PROC_LA_CTL
183		case MODE_LA57:
184		case MODE_LA48:
185			error = procctl(P_PID, pid, PROC_LA_STATUS, &arg);
186			break;
187#endif
188		default:
189			usage();
190			break;
191		}
192		if (error != 0)
193			err(1, "procctl status");
194		switch (mode) {
195		case MODE_ASLR:
196			switch (arg & ~PROC_ASLR_ACTIVE) {
197			case PROC_ASLR_FORCE_ENABLE:
198				printf("force enabled");
199				break;
200			case PROC_ASLR_FORCE_DISABLE:
201				printf("force disabled");
202				break;
203			case PROC_ASLR_NOFORCE:
204				printf("not forced");
205				break;
206			}
207			if ((arg & PROC_ASLR_ACTIVE) != 0)
208				printf(", active\n");
209			else
210				printf(", not active\n");
211			break;
212		case MODE_TRACE:
213			if (arg == -1)
214				printf("disabled\n");
215			else if (arg == 0)
216				printf("enabled, no debugger\n");
217			else
218				printf("enabled, traced by %d\n", arg);
219			break;
220		case MODE_TRAPCAP:
221			switch (arg) {
222			case PROC_TRAPCAP_CTL_ENABLE:
223				printf("enabled\n");
224				break;
225			case PROC_TRAPCAP_CTL_DISABLE:
226				printf("disabled\n");
227				break;
228			}
229			break;
230		case MODE_PROTMAX:
231			switch (arg & ~PROC_PROTMAX_ACTIVE) {
232			case PROC_PROTMAX_FORCE_ENABLE:
233				printf("force enabled");
234				break;
235			case PROC_PROTMAX_FORCE_DISABLE:
236				printf("force disabled");
237				break;
238			case PROC_PROTMAX_NOFORCE:
239				printf("not forced");
240				break;
241			}
242			if ((arg & PROC_PROTMAX_ACTIVE) != 0)
243				printf(", active\n");
244			else
245				printf(", not active\n");
246			break;
247		case MODE_STACKGAP:
248			switch (arg & (PROC_STACKGAP_ENABLE |
249			    PROC_STACKGAP_DISABLE)) {
250			case PROC_STACKGAP_ENABLE:
251				printf("enabled\n");
252				break;
253			case PROC_STACKGAP_DISABLE:
254				printf("disabled\n");
255				break;
256			}
257			switch (arg & (PROC_STACKGAP_ENABLE_EXEC |
258			    PROC_STACKGAP_DISABLE_EXEC)) {
259			case PROC_STACKGAP_ENABLE_EXEC:
260				printf("enabled after exec\n");
261				break;
262			case PROC_STACKGAP_DISABLE_EXEC:
263				printf("disabled after exec\n");
264				break;
265			}
266			break;
267#ifdef PROC_KPTI_CTL
268		case MODE_KPTI:
269			switch (arg & ~PROC_KPTI_STATUS_ACTIVE) {
270			case PROC_KPTI_CTL_ENABLE_ON_EXEC:
271				printf("enabled");
272				break;
273			case PROC_KPTI_CTL_DISABLE_ON_EXEC:
274				printf("disabled");
275				break;
276			}
277			if ((arg & PROC_KPTI_STATUS_ACTIVE) != 0)
278				printf(", active\n");
279			else
280				printf(", not active\n");
281			break;
282#endif
283#ifdef PROC_LA_CTL
284		case MODE_LA57:
285		case MODE_LA48:
286			switch (arg & ~(PROC_LA_STATUS_LA48 |
287			    PROC_LA_STATUS_LA57)) {
288			case PROC_LA_CTL_LA48_ON_EXEC:
289				printf("la48 on exec");
290				break;
291			case PROC_LA_CTL_LA57_ON_EXEC:
292				printf("la57 on exec");
293				break;
294			case PROC_LA_CTL_DEFAULT_ON_EXEC:
295				printf("default on exec");
296				break;
297			}
298			if ((arg & PROC_LA_STATUS_LA48) != 0)
299				printf(", la48 active\n");
300			else if ((arg & PROC_LA_STATUS_LA57) != 0)
301				printf(", la57 active\n");
302			break;
303#endif
304		}
305	} else {
306		switch (mode) {
307		case MODE_ASLR:
308			arg = enable ? PROC_ASLR_FORCE_ENABLE :
309			    PROC_ASLR_FORCE_DISABLE;
310			error = procctl(P_PID, pid, PROC_ASLR_CTL, &arg);
311			break;
312		case MODE_TRACE:
313			arg = enable ? PROC_TRACE_CTL_ENABLE :
314			    PROC_TRACE_CTL_DISABLE;
315			error = procctl(P_PID, pid, PROC_TRACE_CTL, &arg);
316			break;
317		case MODE_TRAPCAP:
318			arg = enable ? PROC_TRAPCAP_CTL_ENABLE :
319			    PROC_TRAPCAP_CTL_DISABLE;
320			error = procctl(P_PID, pid, PROC_TRAPCAP_CTL, &arg);
321			break;
322		case MODE_PROTMAX:
323			arg = enable ? PROC_PROTMAX_FORCE_ENABLE :
324			    PROC_PROTMAX_FORCE_DISABLE;
325			error = procctl(P_PID, pid, PROC_PROTMAX_CTL, &arg);
326			break;
327		case MODE_STACKGAP:
328			arg = enable ? PROC_STACKGAP_ENABLE_EXEC :
329			    (PROC_STACKGAP_DISABLE |
330			    PROC_STACKGAP_DISABLE_EXEC);
331			error = procctl(P_PID, pid, PROC_STACKGAP_CTL, &arg);
332			break;
333#ifdef PROC_KPTI_CTL
334		case MODE_KPTI:
335			arg = enable ? PROC_KPTI_CTL_ENABLE_ON_EXEC :
336			    PROC_KPTI_CTL_DISABLE_ON_EXEC;
337			error = procctl(P_PID, pid, PROC_KPTI_CTL, &arg);
338			break;
339#endif
340#ifdef PROC_LA_CTL
341		case MODE_LA57:
342			arg = enable ? PROC_LA_CTL_LA57_ON_EXEC :
343			    PROC_LA_CTL_DEFAULT_ON_EXEC;
344			error = procctl(P_PID, pid, PROC_LA_CTL, &arg);
345			break;
346		case MODE_LA48:
347			arg = enable ? PROC_LA_CTL_LA48_ON_EXEC :
348			    PROC_LA_CTL_DEFAULT_ON_EXEC;
349			error = procctl(P_PID, pid, PROC_LA_CTL, &arg);
350			break;
351#endif
352		default:
353			usage();
354			break;
355		}
356		if (error != 0)
357			err(1, "procctl ctl");
358		if (do_command) {
359			error = execvp(argv[0], argv);
360			err(1, "exec");
361		}
362	}
363	exit(0);
364}
365