xref: /freebsd/usr.bin/proccontrol/proccontrol.c (revision 87b759f0fa1f7554d50ce640c40138512bbded44)
1 /*-
2  * Copyright (c) 2016 The FreeBSD Foundation
3  *
4  * This software was developed by Konstantin Belousov <kib@FreeBSD.org>
5  * under sponsorship from the FreeBSD Foundation.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/param.h>
30 #include <sys/procctl.h>
31 #include <err.h>
32 #include <stdbool.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 
38 enum mode {
39 	MODE_INVALID,
40 	MODE_ASLR,
41 	MODE_TRACE,
42 	MODE_TRAPCAP,
43 	MODE_PROTMAX,
44 	MODE_STACKGAP,
45 	MODE_NO_NEW_PRIVS,
46 	MODE_WXMAP,
47 #ifdef PROC_KPTI_CTL
48 	MODE_KPTI,
49 #endif
50 #ifdef PROC_LA_CTL
51 	MODE_LA57,
52 	MODE_LA48,
53 #endif
54 };
55 
56 static const struct {
57 	enum mode mode;
58 	const char *name;
59 } modes[] = {
60 	{ MODE_ASLR,		"aslr" },
61 	{ MODE_TRACE,		"trace" },
62 	{ MODE_TRAPCAP,		"trapcap" },
63 	{ MODE_PROTMAX,		"protmax" },
64 	{ MODE_STACKGAP, 	"stackgap" },
65 	{ MODE_NO_NEW_PRIVS,	"nonewprivs" },
66 	{ MODE_WXMAP,		"wxmap" },
67 #ifdef PROC_KPTI_CTL
68 	{ MODE_KPTI, 		"kpti" },
69 #endif
70 #ifdef PROC_LA_CTL
71 	{ MODE_LA57,		"la57" },
72 	{ MODE_LA48,		"la48" },
73 #endif
74 };
75 
76 static pid_t
77 str2pid(const char *str)
78 {
79 	pid_t res;
80 	char *tail;
81 
82 	res = strtol(str, &tail, 0);
83 	if (*tail != '\0') {
84 		warnx("non-numeric pid");
85 		return (-1);
86 	}
87 	return (res);
88 }
89 
90 static void __dead2
91 usage(void)
92 {
93 	fprintf(stderr, "Usage:\n");
94 	fprintf(stderr, "    proccontrol -m mode -s (enable|disable) "
95 	    "(-p pid | command)\n");
96 	fprintf(stderr, "    proccontrol -m mode -q [-p pid]\n");
97 	fprintf(stderr, "Modes: ");
98 	for (size_t i = 0; i < nitems(modes); i++)
99 		fprintf(stderr, "%s%s", i == 0 ? "" : "|", modes[i].name);
100 	fprintf(stderr, "\n");
101 	exit(1);
102 }
103 
104 int
105 main(int argc, char *argv[])
106 {
107 	int arg, ch, error, mode;
108 	pid_t pid;
109 	bool enable, do_command, query;
110 
111 	mode = MODE_INVALID;
112 	enable = true;
113 	pid = -1;
114 	query = false;
115 	while ((ch = getopt(argc, argv, "m:qs:p:")) != -1) {
116 		switch (ch) {
117 		case 'm':
118 			if (mode != MODE_INVALID)
119 				usage();
120 			for (size_t i = 0; i < nitems(modes); i++) {
121 				if (strcmp(optarg, modes[i].name) == 0) {
122 					mode = modes[i].mode;
123 					break;
124 				}
125 			}
126 			if (mode == MODE_INVALID)
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 		if (!query)
158 			usage();
159 		pid = getpid();
160 	}
161 
162 	if (query) {
163 		switch (mode) {
164 		case MODE_ASLR:
165 			error = procctl(P_PID, pid, PROC_ASLR_STATUS, &arg);
166 			break;
167 		case MODE_TRACE:
168 			error = procctl(P_PID, pid, PROC_TRACE_STATUS, &arg);
169 			break;
170 		case MODE_TRAPCAP:
171 			error = procctl(P_PID, pid, PROC_TRAPCAP_STATUS, &arg);
172 			break;
173 		case MODE_PROTMAX:
174 			error = procctl(P_PID, pid, PROC_PROTMAX_STATUS, &arg);
175 			break;
176 		case MODE_STACKGAP:
177 			error = procctl(P_PID, pid, PROC_STACKGAP_STATUS, &arg);
178 			break;
179 		case MODE_NO_NEW_PRIVS:
180 			error = procctl(P_PID, pid, PROC_NO_NEW_PRIVS_STATUS,
181 			    &arg);
182 			break;
183 		case MODE_WXMAP:
184 			error = procctl(P_PID, pid, PROC_WXMAP_STATUS, &arg);
185 			break;
186 #ifdef PROC_KPTI_CTL
187 		case MODE_KPTI:
188 			error = procctl(P_PID, pid, PROC_KPTI_STATUS, &arg);
189 			break;
190 #endif
191 #ifdef PROC_LA_CTL
192 		case MODE_LA57:
193 		case MODE_LA48:
194 			error = procctl(P_PID, pid, PROC_LA_STATUS, &arg);
195 			break;
196 #endif
197 		default:
198 			usage();
199 			break;
200 		}
201 		if (error != 0)
202 			err(1, "procctl status");
203 		switch (mode) {
204 		case MODE_ASLR:
205 			switch (arg & ~PROC_ASLR_ACTIVE) {
206 			case PROC_ASLR_FORCE_ENABLE:
207 				printf("force enabled");
208 				break;
209 			case PROC_ASLR_FORCE_DISABLE:
210 				printf("force disabled");
211 				break;
212 			case PROC_ASLR_NOFORCE:
213 				printf("not forced");
214 				break;
215 			}
216 			if ((arg & PROC_ASLR_ACTIVE) != 0)
217 				printf(", active\n");
218 			else
219 				printf(", not active\n");
220 			break;
221 		case MODE_TRACE:
222 			if (arg == -1)
223 				printf("disabled\n");
224 			else if (arg == 0)
225 				printf("enabled, no debugger\n");
226 			else
227 				printf("enabled, traced by %d\n", arg);
228 			break;
229 		case MODE_TRAPCAP:
230 			switch (arg) {
231 			case PROC_TRAPCAP_CTL_ENABLE:
232 				printf("enabled\n");
233 				break;
234 			case PROC_TRAPCAP_CTL_DISABLE:
235 				printf("disabled\n");
236 				break;
237 			}
238 			break;
239 		case MODE_PROTMAX:
240 			switch (arg & ~PROC_PROTMAX_ACTIVE) {
241 			case PROC_PROTMAX_FORCE_ENABLE:
242 				printf("force enabled");
243 				break;
244 			case PROC_PROTMAX_FORCE_DISABLE:
245 				printf("force disabled");
246 				break;
247 			case PROC_PROTMAX_NOFORCE:
248 				printf("not forced");
249 				break;
250 			}
251 			if ((arg & PROC_PROTMAX_ACTIVE) != 0)
252 				printf(", active\n");
253 			else
254 				printf(", not active\n");
255 			break;
256 		case MODE_STACKGAP:
257 			switch (arg & (PROC_STACKGAP_ENABLE |
258 			    PROC_STACKGAP_DISABLE)) {
259 			case PROC_STACKGAP_ENABLE:
260 				printf("enabled\n");
261 				break;
262 			case PROC_STACKGAP_DISABLE:
263 				printf("disabled\n");
264 				break;
265 			}
266 			switch (arg & (PROC_STACKGAP_ENABLE_EXEC |
267 			    PROC_STACKGAP_DISABLE_EXEC)) {
268 			case PROC_STACKGAP_ENABLE_EXEC:
269 				printf("enabled after exec\n");
270 				break;
271 			case PROC_STACKGAP_DISABLE_EXEC:
272 				printf("disabled after exec\n");
273 				break;
274 			}
275 			break;
276 		case MODE_NO_NEW_PRIVS:
277 			switch (arg) {
278 			case PROC_NO_NEW_PRIVS_ENABLE:
279 				printf("enabled\n");
280 				break;
281 			case PROC_NO_NEW_PRIVS_DISABLE:
282 				printf("disabled\n");
283 				break;
284 			}
285 			break;
286 		case MODE_WXMAP:
287 			if ((arg & PROC_WX_MAPPINGS_PERMIT) != 0)
288 				printf("enabled");
289 			else
290 				printf("disabled");
291 			if ((arg & PROC_WX_MAPPINGS_DISALLOW_EXEC) != 0)
292 				printf(", disabled on exec");
293 			if ((arg & PROC_WXORX_ENFORCE) != 0)
294 				printf(", wxorx enforced");
295 			printf("\n");
296 			break;
297 #ifdef PROC_KPTI_CTL
298 		case MODE_KPTI:
299 			switch (arg & ~PROC_KPTI_STATUS_ACTIVE) {
300 			case PROC_KPTI_CTL_ENABLE_ON_EXEC:
301 				printf("enabled");
302 				break;
303 			case PROC_KPTI_CTL_DISABLE_ON_EXEC:
304 				printf("disabled");
305 				break;
306 			}
307 			if ((arg & PROC_KPTI_STATUS_ACTIVE) != 0)
308 				printf(", active\n");
309 			else
310 				printf(", not active\n");
311 			break;
312 #endif
313 #ifdef PROC_LA_CTL
314 		case MODE_LA57:
315 		case MODE_LA48:
316 			switch (arg & ~(PROC_LA_STATUS_LA48 |
317 			    PROC_LA_STATUS_LA57)) {
318 			case PROC_LA_CTL_LA48_ON_EXEC:
319 				printf("la48 on exec");
320 				break;
321 			case PROC_LA_CTL_LA57_ON_EXEC:
322 				printf("la57 on exec");
323 				break;
324 			case PROC_LA_CTL_DEFAULT_ON_EXEC:
325 				printf("default on exec");
326 				break;
327 			}
328 			if ((arg & PROC_LA_STATUS_LA48) != 0)
329 				printf(", la48 active\n");
330 			else if ((arg & PROC_LA_STATUS_LA57) != 0)
331 				printf(", la57 active\n");
332 			break;
333 #endif
334 		}
335 	} else {
336 		switch (mode) {
337 		case MODE_ASLR:
338 			arg = enable ? PROC_ASLR_FORCE_ENABLE :
339 			    PROC_ASLR_FORCE_DISABLE;
340 			error = procctl(P_PID, pid, PROC_ASLR_CTL, &arg);
341 			break;
342 		case MODE_TRACE:
343 			arg = enable ? PROC_TRACE_CTL_ENABLE :
344 			    PROC_TRACE_CTL_DISABLE;
345 			error = procctl(P_PID, pid, PROC_TRACE_CTL, &arg);
346 			break;
347 		case MODE_TRAPCAP:
348 			arg = enable ? PROC_TRAPCAP_CTL_ENABLE :
349 			    PROC_TRAPCAP_CTL_DISABLE;
350 			error = procctl(P_PID, pid, PROC_TRAPCAP_CTL, &arg);
351 			break;
352 		case MODE_PROTMAX:
353 			arg = enable ? PROC_PROTMAX_FORCE_ENABLE :
354 			    PROC_PROTMAX_FORCE_DISABLE;
355 			error = procctl(P_PID, pid, PROC_PROTMAX_CTL, &arg);
356 			break;
357 		case MODE_STACKGAP:
358 			arg = enable ? PROC_STACKGAP_ENABLE_EXEC :
359 			    (PROC_STACKGAP_DISABLE |
360 			    PROC_STACKGAP_DISABLE_EXEC);
361 			error = procctl(P_PID, pid, PROC_STACKGAP_CTL, &arg);
362 			break;
363 		case MODE_NO_NEW_PRIVS:
364 			arg = enable ? PROC_NO_NEW_PRIVS_ENABLE :
365 			    PROC_NO_NEW_PRIVS_DISABLE;
366 			error = procctl(P_PID, pid, PROC_NO_NEW_PRIVS_CTL,
367 			    &arg);
368 			break;
369 		case MODE_WXMAP:
370 			arg = enable ? PROC_WX_MAPPINGS_PERMIT :
371 			    PROC_WX_MAPPINGS_DISALLOW_EXEC;
372 			error = procctl(P_PID, pid, PROC_WXMAP_CTL, &arg);
373 			break;
374 #ifdef PROC_KPTI_CTL
375 		case MODE_KPTI:
376 			arg = enable ? PROC_KPTI_CTL_ENABLE_ON_EXEC :
377 			    PROC_KPTI_CTL_DISABLE_ON_EXEC;
378 			error = procctl(P_PID, pid, PROC_KPTI_CTL, &arg);
379 			break;
380 #endif
381 #ifdef PROC_LA_CTL
382 		case MODE_LA57:
383 			arg = enable ? PROC_LA_CTL_LA57_ON_EXEC :
384 			    PROC_LA_CTL_DEFAULT_ON_EXEC;
385 			error = procctl(P_PID, pid, PROC_LA_CTL, &arg);
386 			break;
387 		case MODE_LA48:
388 			arg = enable ? PROC_LA_CTL_LA48_ON_EXEC :
389 			    PROC_LA_CTL_DEFAULT_ON_EXEC;
390 			error = procctl(P_PID, pid, PROC_LA_CTL, &arg);
391 			break;
392 #endif
393 		default:
394 			usage();
395 			break;
396 		}
397 		if (error != 0)
398 			err(1, "procctl ctl");
399 		if (do_command) {
400 			error = execvp(argv[0], argv);
401 			err(1, "exec");
402 		}
403 	}
404 	exit(0);
405 }
406