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#include <sys/procctl.h>
32#include <err.h>
33#include <stdbool.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <unistd.h>
38
39enum {
40	MODE_ASLR,
41	MODE_INVALID,
42	MODE_TRACE,
43	MODE_TRAPCAP,
44	MODE_PROTMAX,
45	MODE_STACKGAP,
46	MODE_NO_NEW_PRIVS,
47	MODE_WXMAP,
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|nonewprivs|wxmap"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			else if (strcmp(optarg, "nonewprivs") == 0)
117				mode = MODE_NO_NEW_PRIVS;
118			else if (strcmp(optarg, "wxmap") == 0)
119				mode = MODE_WXMAP;
120#ifdef PROC_KPTI_CTL
121			else if (strcmp(optarg, "kpti") == 0)
122				mode = MODE_KPTI;
123#endif
124#ifdef PROC_LA_CTL
125			else if (strcmp(optarg, "la57") == 0)
126				mode = MODE_LA57;
127			else if (strcmp(optarg, "la48") == 0)
128				mode = MODE_LA48;
129#endif
130			else
131				usage();
132			break;
133		case 's':
134			if (strcmp(optarg, "enable") == 0)
135				enable = true;
136			else if (strcmp(optarg, "disable") == 0)
137				enable = false;
138			else
139				usage();
140			break;
141		case 'p':
142			pid = str2pid(optarg);
143			break;
144		case 'q':
145			query = true;
146			break;
147		case '?':
148		default:
149			usage();
150			break;
151		}
152	}
153	argc -= optind;
154	argv += optind;
155	do_command = argc != 0;
156	if (do_command) {
157		if (pid != -1 || query)
158			usage();
159		pid = getpid();
160	} else if (pid == -1) {
161		pid = getpid();
162	}
163
164	if (query) {
165		switch (mode) {
166		case MODE_ASLR:
167			error = procctl(P_PID, pid, PROC_ASLR_STATUS, &arg);
168			break;
169		case MODE_TRACE:
170			error = procctl(P_PID, pid, PROC_TRACE_STATUS, &arg);
171			break;
172		case MODE_TRAPCAP:
173			error = procctl(P_PID, pid, PROC_TRAPCAP_STATUS, &arg);
174			break;
175		case MODE_PROTMAX:
176			error = procctl(P_PID, pid, PROC_PROTMAX_STATUS, &arg);
177			break;
178		case MODE_STACKGAP:
179			error = procctl(P_PID, pid, PROC_STACKGAP_STATUS, &arg);
180			break;
181		case MODE_NO_NEW_PRIVS:
182			error = procctl(P_PID, pid, PROC_NO_NEW_PRIVS_STATUS,
183			    &arg);
184			break;
185		case MODE_WXMAP:
186			error = procctl(P_PID, pid, PROC_WXMAP_STATUS, &arg);
187			break;
188#ifdef PROC_KPTI_CTL
189		case MODE_KPTI:
190			error = procctl(P_PID, pid, PROC_KPTI_STATUS, &arg);
191			break;
192#endif
193#ifdef PROC_LA_CTL
194		case MODE_LA57:
195		case MODE_LA48:
196			error = procctl(P_PID, pid, PROC_LA_STATUS, &arg);
197			break;
198#endif
199		default:
200			usage();
201			break;
202		}
203		if (error != 0)
204			err(1, "procctl status");
205		switch (mode) {
206		case MODE_ASLR:
207			switch (arg & ~PROC_ASLR_ACTIVE) {
208			case PROC_ASLR_FORCE_ENABLE:
209				printf("force enabled");
210				break;
211			case PROC_ASLR_FORCE_DISABLE:
212				printf("force disabled");
213				break;
214			case PROC_ASLR_NOFORCE:
215				printf("not forced");
216				break;
217			}
218			if ((arg & PROC_ASLR_ACTIVE) != 0)
219				printf(", active\n");
220			else
221				printf(", not active\n");
222			break;
223		case MODE_TRACE:
224			if (arg == -1)
225				printf("disabled\n");
226			else if (arg == 0)
227				printf("enabled, no debugger\n");
228			else
229				printf("enabled, traced by %d\n", arg);
230			break;
231		case MODE_TRAPCAP:
232			switch (arg) {
233			case PROC_TRAPCAP_CTL_ENABLE:
234				printf("enabled\n");
235				break;
236			case PROC_TRAPCAP_CTL_DISABLE:
237				printf("disabled\n");
238				break;
239			}
240			break;
241		case MODE_PROTMAX:
242			switch (arg & ~PROC_PROTMAX_ACTIVE) {
243			case PROC_PROTMAX_FORCE_ENABLE:
244				printf("force enabled");
245				break;
246			case PROC_PROTMAX_FORCE_DISABLE:
247				printf("force disabled");
248				break;
249			case PROC_PROTMAX_NOFORCE:
250				printf("not forced");
251				break;
252			}
253			if ((arg & PROC_PROTMAX_ACTIVE) != 0)
254				printf(", active\n");
255			else
256				printf(", not active\n");
257			break;
258		case MODE_STACKGAP:
259			switch (arg & (PROC_STACKGAP_ENABLE |
260			    PROC_STACKGAP_DISABLE)) {
261			case PROC_STACKGAP_ENABLE:
262				printf("enabled\n");
263				break;
264			case PROC_STACKGAP_DISABLE:
265				printf("disabled\n");
266				break;
267			}
268			switch (arg & (PROC_STACKGAP_ENABLE_EXEC |
269			    PROC_STACKGAP_DISABLE_EXEC)) {
270			case PROC_STACKGAP_ENABLE_EXEC:
271				printf("enabled after exec\n");
272				break;
273			case PROC_STACKGAP_DISABLE_EXEC:
274				printf("disabled after exec\n");
275				break;
276			}
277			break;
278		case MODE_NO_NEW_PRIVS:
279			switch (arg) {
280			case PROC_NO_NEW_PRIVS_ENABLE:
281				printf("enabled\n");
282				break;
283			case PROC_NO_NEW_PRIVS_DISABLE:
284				printf("disabled\n");
285				break;
286			}
287			break;
288		case MODE_WXMAP:
289			if ((arg & PROC_WX_MAPPINGS_PERMIT) != 0)
290				printf("enabled");
291			else
292				printf("disabled");
293			if ((arg & PROC_WX_MAPPINGS_DISALLOW_EXEC) != 0)
294				printf(", disabled on exec");
295			if ((arg & PROC_WXORX_ENFORCE) != 0)
296				printf(", wxorx enforced");
297			printf("\n");
298			break;
299#ifdef PROC_KPTI_CTL
300		case MODE_KPTI:
301			switch (arg & ~PROC_KPTI_STATUS_ACTIVE) {
302			case PROC_KPTI_CTL_ENABLE_ON_EXEC:
303				printf("enabled");
304				break;
305			case PROC_KPTI_CTL_DISABLE_ON_EXEC:
306				printf("disabled");
307				break;
308			}
309			if ((arg & PROC_KPTI_STATUS_ACTIVE) != 0)
310				printf(", active\n");
311			else
312				printf(", not active\n");
313			break;
314#endif
315#ifdef PROC_LA_CTL
316		case MODE_LA57:
317		case MODE_LA48:
318			switch (arg & ~(PROC_LA_STATUS_LA48 |
319			    PROC_LA_STATUS_LA57)) {
320			case PROC_LA_CTL_LA48_ON_EXEC:
321				printf("la48 on exec");
322				break;
323			case PROC_LA_CTL_LA57_ON_EXEC:
324				printf("la57 on exec");
325				break;
326			case PROC_LA_CTL_DEFAULT_ON_EXEC:
327				printf("default on exec");
328				break;
329			}
330			if ((arg & PROC_LA_STATUS_LA48) != 0)
331				printf(", la48 active\n");
332			else if ((arg & PROC_LA_STATUS_LA57) != 0)
333				printf(", la57 active\n");
334			break;
335#endif
336		}
337	} else {
338		switch (mode) {
339		case MODE_ASLR:
340			arg = enable ? PROC_ASLR_FORCE_ENABLE :
341			    PROC_ASLR_FORCE_DISABLE;
342			error = procctl(P_PID, pid, PROC_ASLR_CTL, &arg);
343			break;
344		case MODE_TRACE:
345			arg = enable ? PROC_TRACE_CTL_ENABLE :
346			    PROC_TRACE_CTL_DISABLE;
347			error = procctl(P_PID, pid, PROC_TRACE_CTL, &arg);
348			break;
349		case MODE_TRAPCAP:
350			arg = enable ? PROC_TRAPCAP_CTL_ENABLE :
351			    PROC_TRAPCAP_CTL_DISABLE;
352			error = procctl(P_PID, pid, PROC_TRAPCAP_CTL, &arg);
353			break;
354		case MODE_PROTMAX:
355			arg = enable ? PROC_PROTMAX_FORCE_ENABLE :
356			    PROC_PROTMAX_FORCE_DISABLE;
357			error = procctl(P_PID, pid, PROC_PROTMAX_CTL, &arg);
358			break;
359		case MODE_STACKGAP:
360			arg = enable ? PROC_STACKGAP_ENABLE_EXEC :
361			    (PROC_STACKGAP_DISABLE |
362			    PROC_STACKGAP_DISABLE_EXEC);
363			error = procctl(P_PID, pid, PROC_STACKGAP_CTL, &arg);
364			break;
365		case MODE_NO_NEW_PRIVS:
366			arg = enable ? PROC_NO_NEW_PRIVS_ENABLE :
367			    PROC_NO_NEW_PRIVS_DISABLE;
368			error = procctl(P_PID, pid, PROC_NO_NEW_PRIVS_CTL,
369			    &arg);
370			break;
371		case MODE_WXMAP:
372			arg = enable ? PROC_WX_MAPPINGS_PERMIT :
373			    PROC_WX_MAPPINGS_DISALLOW_EXEC;
374			error = procctl(P_PID, pid, PROC_WXMAP_CTL, &arg);
375			break;
376#ifdef PROC_KPTI_CTL
377		case MODE_KPTI:
378			arg = enable ? PROC_KPTI_CTL_ENABLE_ON_EXEC :
379			    PROC_KPTI_CTL_DISABLE_ON_EXEC;
380			error = procctl(P_PID, pid, PROC_KPTI_CTL, &arg);
381			break;
382#endif
383#ifdef PROC_LA_CTL
384		case MODE_LA57:
385			arg = enable ? PROC_LA_CTL_LA57_ON_EXEC :
386			    PROC_LA_CTL_DEFAULT_ON_EXEC;
387			error = procctl(P_PID, pid, PROC_LA_CTL, &arg);
388			break;
389		case MODE_LA48:
390			arg = enable ? PROC_LA_CTL_LA48_ON_EXEC :
391			    PROC_LA_CTL_DEFAULT_ON_EXEC;
392			error = procctl(P_PID, pid, PROC_LA_CTL, &arg);
393			break;
394#endif
395		default:
396			usage();
397			break;
398		}
399		if (error != 0)
400			err(1, "procctl ctl");
401		if (do_command) {
402			error = execvp(argv[0], argv);
403			err(1, "exec");
404		}
405	}
406	exit(0);
407}
408