xref: /freebsd/usr.bin/proccontrol/proccontrol.c (revision 2e3507c25e42292b45a5482e116d278f5515d04d)
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 
39 enum {
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 
57 static pid_t
58 str2pid(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 
82 static void __dead2
83 usage(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 
92 int
93 main(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