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