xref: /freebsd/usr.bin/proccontrol/proccontrol.c (revision 3a56015a2f5d630910177fa79a522bb95511ccf7)
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 	MODE_LOGSIGEXIT,
55 };
56 
57 static const struct {
58 	enum mode mode;
59 	const char *name;
60 } modes[] = {
61 	{ MODE_ASLR,		"aslr" },
62 	{ MODE_TRACE,		"trace" },
63 	{ MODE_TRAPCAP,		"trapcap" },
64 	{ MODE_PROTMAX,		"protmax" },
65 	{ MODE_STACKGAP, 	"stackgap" },
66 	{ MODE_NO_NEW_PRIVS,	"nonewprivs" },
67 	{ MODE_WXMAP,		"wxmap" },
68 #ifdef PROC_KPTI_CTL
69 	{ MODE_KPTI, 		"kpti" },
70 #endif
71 #ifdef PROC_LA_CTL
72 	{ MODE_LA57,		"la57" },
73 	{ MODE_LA48,		"la48" },
74 #endif
75 	{ MODE_LOGSIGEXIT,	"logsigexit" },
76 };
77 
78 static pid_t
79 str2pid(const char *str)
80 {
81 	pid_t res;
82 	char *tail;
83 
84 	res = strtol(str, &tail, 0);
85 	if (*tail != '\0') {
86 		warnx("non-numeric pid");
87 		return (-1);
88 	}
89 	return (res);
90 }
91 
92 static void __dead2
93 usage(void)
94 {
95 	fprintf(stderr, "Usage:\n");
96 	fprintf(stderr, "    proccontrol -m mode -s (enable|disable) "
97 	    "(-p pid | command)\n");
98 	fprintf(stderr, "    proccontrol -m mode -q [-p pid]\n");
99 	fprintf(stderr, "Modes: ");
100 	for (size_t i = 0; i < nitems(modes); i++)
101 		fprintf(stderr, "%s%s", i == 0 ? "" : "|", modes[i].name);
102 	fprintf(stderr, "\n");
103 	exit(1);
104 }
105 
106 int
107 main(int argc, char *argv[])
108 {
109 	int arg, ch, error, mode;
110 	pid_t pid;
111 	bool enable, do_command, query;
112 
113 	mode = MODE_INVALID;
114 	enable = true;
115 	pid = -1;
116 	query = false;
117 	while ((ch = getopt(argc, argv, "m:qs:p:")) != -1) {
118 		switch (ch) {
119 		case 'm':
120 			if (mode != MODE_INVALID)
121 				usage();
122 			for (size_t i = 0; i < nitems(modes); i++) {
123 				if (strcmp(optarg, modes[i].name) == 0) {
124 					mode = modes[i].mode;
125 					break;
126 				}
127 			}
128 			if (mode == MODE_INVALID)
129 				usage();
130 			break;
131 		case 's':
132 			if (strcmp(optarg, "enable") == 0)
133 				enable = true;
134 			else if (strcmp(optarg, "disable") == 0)
135 				enable = false;
136 			else
137 				usage();
138 			break;
139 		case 'p':
140 			pid = str2pid(optarg);
141 			break;
142 		case 'q':
143 			query = true;
144 			break;
145 		case '?':
146 		default:
147 			usage();
148 			break;
149 		}
150 	}
151 	argc -= optind;
152 	argv += optind;
153 	do_command = argc != 0;
154 	if (do_command) {
155 		if (pid != -1 || query)
156 			usage();
157 		pid = getpid();
158 	} else if (pid == -1) {
159 		if (!query)
160 			usage();
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 		case MODE_LOGSIGEXIT:
200 			error = procctl(P_PID, pid, PROC_LOGSIGEXIT_STATUS,
201 			    &arg);
202 			break;
203 		default:
204 			usage();
205 			break;
206 		}
207 		if (error != 0)
208 			err(1, "procctl status");
209 		switch (mode) {
210 		case MODE_ASLR:
211 			switch (arg & ~PROC_ASLR_ACTIVE) {
212 			case PROC_ASLR_FORCE_ENABLE:
213 				printf("force enabled");
214 				break;
215 			case PROC_ASLR_FORCE_DISABLE:
216 				printf("force disabled");
217 				break;
218 			case PROC_ASLR_NOFORCE:
219 				printf("not forced");
220 				break;
221 			}
222 			if ((arg & PROC_ASLR_ACTIVE) != 0)
223 				printf(", active\n");
224 			else
225 				printf(", not active\n");
226 			break;
227 		case MODE_TRACE:
228 			if (arg == -1)
229 				printf("disabled\n");
230 			else if (arg == 0)
231 				printf("enabled, no debugger\n");
232 			else
233 				printf("enabled, traced by %d\n", arg);
234 			break;
235 		case MODE_TRAPCAP:
236 			switch (arg) {
237 			case PROC_TRAPCAP_CTL_ENABLE:
238 				printf("enabled\n");
239 				break;
240 			case PROC_TRAPCAP_CTL_DISABLE:
241 				printf("disabled\n");
242 				break;
243 			}
244 			break;
245 		case MODE_PROTMAX:
246 			switch (arg & ~PROC_PROTMAX_ACTIVE) {
247 			case PROC_PROTMAX_FORCE_ENABLE:
248 				printf("force enabled");
249 				break;
250 			case PROC_PROTMAX_FORCE_DISABLE:
251 				printf("force disabled");
252 				break;
253 			case PROC_PROTMAX_NOFORCE:
254 				printf("not forced");
255 				break;
256 			}
257 			if ((arg & PROC_PROTMAX_ACTIVE) != 0)
258 				printf(", active\n");
259 			else
260 				printf(", not active\n");
261 			break;
262 		case MODE_STACKGAP:
263 			switch (arg & (PROC_STACKGAP_ENABLE |
264 			    PROC_STACKGAP_DISABLE)) {
265 			case PROC_STACKGAP_ENABLE:
266 				printf("enabled\n");
267 				break;
268 			case PROC_STACKGAP_DISABLE:
269 				printf("disabled\n");
270 				break;
271 			}
272 			switch (arg & (PROC_STACKGAP_ENABLE_EXEC |
273 			    PROC_STACKGAP_DISABLE_EXEC)) {
274 			case PROC_STACKGAP_ENABLE_EXEC:
275 				printf("enabled after exec\n");
276 				break;
277 			case PROC_STACKGAP_DISABLE_EXEC:
278 				printf("disabled after exec\n");
279 				break;
280 			}
281 			break;
282 		case MODE_NO_NEW_PRIVS:
283 			switch (arg) {
284 			case PROC_NO_NEW_PRIVS_ENABLE:
285 				printf("enabled\n");
286 				break;
287 			case PROC_NO_NEW_PRIVS_DISABLE:
288 				printf("disabled\n");
289 				break;
290 			}
291 			break;
292 		case MODE_WXMAP:
293 			if ((arg & PROC_WX_MAPPINGS_PERMIT) != 0)
294 				printf("enabled");
295 			else
296 				printf("disabled");
297 			if ((arg & PROC_WX_MAPPINGS_DISALLOW_EXEC) != 0)
298 				printf(", disabled on exec");
299 			if ((arg & PROC_WXORX_ENFORCE) != 0)
300 				printf(", wxorx enforced");
301 			printf("\n");
302 			break;
303 #ifdef PROC_KPTI_CTL
304 		case MODE_KPTI:
305 			switch (arg & ~PROC_KPTI_STATUS_ACTIVE) {
306 			case PROC_KPTI_CTL_ENABLE_ON_EXEC:
307 				printf("enabled");
308 				break;
309 			case PROC_KPTI_CTL_DISABLE_ON_EXEC:
310 				printf("disabled");
311 				break;
312 			}
313 			if ((arg & PROC_KPTI_STATUS_ACTIVE) != 0)
314 				printf(", active\n");
315 			else
316 				printf(", not active\n");
317 			break;
318 #endif
319 #ifdef PROC_LA_CTL
320 		case MODE_LA57:
321 		case MODE_LA48:
322 			switch (arg & ~(PROC_LA_STATUS_LA48 |
323 			    PROC_LA_STATUS_LA57)) {
324 			case PROC_LA_CTL_LA48_ON_EXEC:
325 				printf("la48 on exec");
326 				break;
327 			case PROC_LA_CTL_LA57_ON_EXEC:
328 				printf("la57 on exec");
329 				break;
330 			case PROC_LA_CTL_DEFAULT_ON_EXEC:
331 				printf("default on exec");
332 				break;
333 			}
334 			if ((arg & PROC_LA_STATUS_LA48) != 0)
335 				printf(", la48 active\n");
336 			else if ((arg & PROC_LA_STATUS_LA57) != 0)
337 				printf(", la57 active\n");
338 			break;
339 #endif
340 		case MODE_LOGSIGEXIT:
341 			switch (arg) {
342 			case PROC_LOGSIGEXIT_CTL_NOFORCE:
343 				printf("not forced\n");
344 				break;
345 			case PROC_LOGSIGEXIT_CTL_FORCE_ENABLE:
346 				printf("force enabled\n");
347 				break;
348 			case PROC_LOGSIGEXIT_CTL_FORCE_DISABLE:
349 				printf("force disabled\n");
350 				break;
351 			}
352 			break;
353 		}
354 	} else {
355 		switch (mode) {
356 		case MODE_ASLR:
357 			arg = enable ? PROC_ASLR_FORCE_ENABLE :
358 			    PROC_ASLR_FORCE_DISABLE;
359 			error = procctl(P_PID, pid, PROC_ASLR_CTL, &arg);
360 			break;
361 		case MODE_TRACE:
362 			arg = enable ? PROC_TRACE_CTL_ENABLE :
363 			    PROC_TRACE_CTL_DISABLE;
364 			error = procctl(P_PID, pid, PROC_TRACE_CTL, &arg);
365 			break;
366 		case MODE_TRAPCAP:
367 			arg = enable ? PROC_TRAPCAP_CTL_ENABLE :
368 			    PROC_TRAPCAP_CTL_DISABLE;
369 			error = procctl(P_PID, pid, PROC_TRAPCAP_CTL, &arg);
370 			break;
371 		case MODE_PROTMAX:
372 			arg = enable ? PROC_PROTMAX_FORCE_ENABLE :
373 			    PROC_PROTMAX_FORCE_DISABLE;
374 			error = procctl(P_PID, pid, PROC_PROTMAX_CTL, &arg);
375 			break;
376 		case MODE_STACKGAP:
377 			arg = enable ? PROC_STACKGAP_ENABLE_EXEC :
378 			    (PROC_STACKGAP_DISABLE |
379 			    PROC_STACKGAP_DISABLE_EXEC);
380 			error = procctl(P_PID, pid, PROC_STACKGAP_CTL, &arg);
381 			break;
382 		case MODE_NO_NEW_PRIVS:
383 			arg = enable ? PROC_NO_NEW_PRIVS_ENABLE :
384 			    PROC_NO_NEW_PRIVS_DISABLE;
385 			error = procctl(P_PID, pid, PROC_NO_NEW_PRIVS_CTL,
386 			    &arg);
387 			break;
388 		case MODE_WXMAP:
389 			arg = enable ? PROC_WX_MAPPINGS_PERMIT :
390 			    PROC_WX_MAPPINGS_DISALLOW_EXEC;
391 			error = procctl(P_PID, pid, PROC_WXMAP_CTL, &arg);
392 			break;
393 #ifdef PROC_KPTI_CTL
394 		case MODE_KPTI:
395 			arg = enable ? PROC_KPTI_CTL_ENABLE_ON_EXEC :
396 			    PROC_KPTI_CTL_DISABLE_ON_EXEC;
397 			error = procctl(P_PID, pid, PROC_KPTI_CTL, &arg);
398 			break;
399 #endif
400 #ifdef PROC_LA_CTL
401 		case MODE_LA57:
402 			arg = enable ? PROC_LA_CTL_LA57_ON_EXEC :
403 			    PROC_LA_CTL_DEFAULT_ON_EXEC;
404 			error = procctl(P_PID, pid, PROC_LA_CTL, &arg);
405 			break;
406 		case MODE_LA48:
407 			arg = enable ? PROC_LA_CTL_LA48_ON_EXEC :
408 			    PROC_LA_CTL_DEFAULT_ON_EXEC;
409 			error = procctl(P_PID, pid, PROC_LA_CTL, &arg);
410 			break;
411 #endif
412 		case MODE_LOGSIGEXIT:
413 			arg = enable ? PROC_LOGSIGEXIT_CTL_FORCE_ENABLE :
414 			    PROC_LOGSIGEXIT_CTL_FORCE_DISABLE;
415 			error = procctl(P_PID, pid, PROC_LOGSIGEXIT_CTL, &arg);
416 			break;
417 		default:
418 			usage();
419 			break;
420 		}
421 		if (error != 0)
422 			err(1, "procctl ctl");
423 		if (do_command) {
424 			error = execvp(argv[0], argv);
425 			err(1, "exec");
426 		}
427 	}
428 	exit(0);
429 }
430