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