xref: /freebsd/lib/libveriexec/veriexec_get.c (revision edf8578117e8844e02c0121147f45e4609b30680)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2021-2023, Juniper Networks, Inc.
5  * All rights reserved.
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 ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24  * 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 
30 #include <sys/cdefs.h>
31 #include <sys/types.h>
32 #include <sys/errno.h>
33 #include <sys/mac.h>
34 
35 #include <unistd.h>
36 #include <string.h>
37 
38 #include <security/mac_veriexec/mac_veriexec.h>
39 
40 /**
41  * @brief get veriexec params for a process
42  *
43  * @return
44  * @li 0 if successful
45  */
46 int
47 veriexec_get_pid_params(pid_t pid,
48     struct mac_veriexec_syscall_params *params)
49 {
50 	struct mac_veriexec_syscall_params_args args;
51 
52 	if (params == NULL)
53 		return EINVAL;
54 
55 	args.u.pid = pid;
56 	args.params = params;
57 	return mac_syscall(MAC_VERIEXEC_NAME,
58 	    MAC_VERIEXEC_GET_PARAMS_PID_SYSCALL, &args);
59 }
60 
61 /**
62  * @brief get veriexec params for a path
63  *
64  * @return
65  * @li 0 if successful
66  */
67 int
68 veriexec_get_path_params(const char *file,
69     struct mac_veriexec_syscall_params *params)
70 {
71 	struct mac_veriexec_syscall_params_args args;
72 
73 	if (file == NULL || params == NULL)
74 		return EINVAL;
75 
76 	args.u.filename = file;
77 	args.params = params;
78 	return mac_syscall(MAC_VERIEXEC_NAME,
79 	    MAC_VERIEXEC_GET_PARAMS_PATH_SYSCALL, &args);
80 }
81 
82 /**
83  * @brief return label associated with a path
84  *
85  * @param[in] file
86  *	pathname of file to lookup.
87  *
88  * @prarm[in] buf
89  *	if not NULL and big enough copy label to buf.
90  *	otherwise return a copy of label.
91  *
92  * @param[in] bufsz
93  *	size of buf, must be greater than found label length.
94  *
95  * @return
96  * @li NULL if no label
97  * @li pointer to label
98  */
99 char *
100 veriexec_get_path_label(const char *file, char *buf, size_t bufsz)
101 {
102 	struct mac_veriexec_syscall_params params;
103 	char *cp;
104 
105 	cp = NULL;
106 	if (veriexec_get_path_params(file, &params) == 0) {
107 		/* Does label contain a label */
108 		if (params.labellen > 0) {
109 			if (buf != NULL && bufsz > params.labellen) {
110 				strlcpy(buf, params.label, bufsz);
111 				cp = buf;
112 			} else
113 				cp = strdup(params.label);
114 		}
115 	}
116 	return cp;
117 }
118 
119 /**
120  * @brief return label of a process
121  *
122  *
123  * @param[in] pid
124  *	process id of interest.
125  *
126  * @prarm[in] buf
127  *	if not NULL and big enough copy label to buf.
128  *	otherwise return a copy of label.
129  *
130  * @param[in] bufsz
131  *	size of buf, must be greater than found label length.
132  *
133  * @return
134  * @li NULL if no label
135  * @li pointer to label
136  */
137 char *
138 veriexec_get_pid_label(pid_t pid, char *buf, size_t bufsz)
139 {
140 	struct mac_veriexec_syscall_params params;
141 	char *cp;
142 
143 	cp = NULL;
144 	if (veriexec_get_pid_params(pid, &params) == 0) {
145 		/* Does label contain a label */
146 		if (params.labellen > 0) {
147 			if (buf != NULL && bufsz > params.labellen) {
148 				strlcpy(buf, params.label, bufsz);
149 				cp = buf;
150 			} else
151 				cp = strdup(params.label);
152 		}
153 	}
154 	return cp;
155 }
156 
157 /*
158  * we match
159  * ^want$
160  * ^want,
161  * ,want,
162  * ,want$
163  *
164  * and if want ends with / then we match that prefix too.
165  */
166 static int
167 check_label_want(const char *label, size_t labellen,
168     const char *want, size_t wantlen)
169 {
170 	char *cp;
171 
172 	/* Does label contain [,]<want>[,] ? */
173 	if (labellen > 0 && wantlen > 0 &&
174 	    (cp = strstr(label, want)) != NULL) {
175 		if (cp == label || cp[-1] == ',') {
176 			if (cp[wantlen] == '\0' || cp[wantlen] == ',' ||
177 			    (cp[wantlen-1] == '/' && want[wantlen-1] == '/'))
178 				return 1; /* yes */
179 		}
180 	}
181 	return 0;			/* no */
182 }
183 
184 /**
185  * @brief check if a process has label that contains what we want
186  *
187  * @param[in] pid
188  *	process id of interest.
189  *
190  * @param[in] want
191  *	the label we are looking for
192  *	if want ends with ``/`` it is assumed a prefix
193  *	otherwise we expect it to be followed by ``,`` or end of string.
194  *
195  * @return
196  * @li 0 if no
197  * @li 1 if yes
198  */
199 int
200 veriexec_check_pid_label(pid_t pid, const char *want)
201 {
202 	struct mac_veriexec_syscall_params params;
203 	size_t n;
204 
205 	if (want != NULL &&
206 	    (n = strlen(want)) > 0 &&
207 	    veriexec_get_pid_params(pid, &params) == 0) {
208 		return check_label_want(params.label, params.labellen,
209 		    want, n);
210 	}
211 	return 0;			/* no */
212 }
213 
214 /**
215  * @brief check if a path has label that contains what we want
216  *
217  * @param[in] path
218  *	pathname of interest.
219  *
220  * @param[in] want
221  *	the label we are looking for
222  *	if want ends with ``/`` it is assumed a prefix
223  *	otherwise we expect it to be followed by ``,`` or end of string.
224  *
225  * @return
226  * @li 0 if no
227  * @li 1 if yes
228  */
229 int
230 veriexec_check_path_label(const char *file, const char *want)
231 {
232 	struct mac_veriexec_syscall_params params;
233 	size_t n;
234 
235 	if (want != NULL && file != NULL &&
236 	    (n = strlen(want)) > 0 &&
237 	    veriexec_get_path_params(file, &params) == 0) {
238 		return check_label_want(params.label, params.labellen,
239 		    want, n);
240 	}
241 	return 0;			/* no */
242 }
243 
244 #ifdef UNIT_TEST
245 #include <stdlib.h>
246 #include <stdio.h>
247 #include <err.h>
248 
249 static char *
250 hash2hex(char *type, unsigned char *digest)
251 {
252 	static char buf[2*MAXFINGERPRINTLEN+1];
253 	size_t n;
254 	int i;
255 
256 	if (strcmp(type, "SHA1") == 0) {
257 		n = 20;
258 	} else if (strcmp(type, "SHA256") == 0) {
259 		n = 32;
260 	} else if (strcmp(type, "SHA384") == 0) {
261 		n = 48;
262 	}
263 	for (i = 0; i < n; i++) {
264 		sprintf(&buf[2*i], "%02x", (unsigned)digest[i]);
265 	}
266 	return buf;
267 }
268 
269 int
270 main(int argc, char *argv[])
271 {
272 	struct mac_veriexec_syscall_params params;
273 	pid_t pid;
274 	char buf[BUFSIZ];
275 	const char *cp;
276 	char *want = NULL;
277 	int lflag = 0;
278 	int pflag = 0;
279 	int error;
280 	int c;
281 
282 	while ((c = getopt(argc, argv, "lpw:")) != -1) {
283 		switch (c) {
284 		case 'l':
285 			lflag = 1;
286 			break;
287 		case 'p':
288 			pflag = 1;
289 			break;
290 		case 'w':
291 			want = optarg;
292 			break;
293 		default:
294 			break;
295 		}
296 	}
297 	for (; optind < argc; optind++) {
298 
299 		if (pflag) {
300 			pid = atoi(argv[optind]);
301 			if (lflag) {
302 				cp = veriexec_get_pid_label(pid, buf, sizeof(buf));
303 				if (cp)
304 					printf("pid=%d label='%s'\n", pid, cp);
305 				continue;
306 			}
307 			if (want) {
308 				error = veriexec_check_pid_label(pid, want);
309 				printf("pid=%d want='%s': %d\n",
310 				    pid, want, error);
311 				continue;
312 			}
313 			error = veriexec_get_pid_params(pid, &params);
314 		} else {
315 			if (lflag) {
316 				cp = veriexec_get_path_label(argv[optind],
317 				    buf, sizeof(buf));
318 				if (cp)
319 					printf("path='%s' label='%s'\n",
320 					    argv[optind], cp);
321 				continue;
322 			}
323 			if (want) {
324 				error = veriexec_check_path_label(argv[optind], want);
325 				printf("path='%s' want='%s': %d\n",
326 				    argv[optind], want, error);
327 				continue;
328 			}
329 			error = veriexec_get_path_params(argv[optind], &params);
330 		}
331 		if (error) {
332 			err(2, "%s, error=%d", argv[optind], error);
333 		}
334 
335 		printf("arg=%s, type=%s, flags=%u, label='%s', fingerprint='%s'\n",
336 		    argv[optind], params.fp_type, (unsigned)params.flags,
337 		    params.label,
338 		    hash2hex(params.fp_type, params.fingerprint));
339 	}
340 	return 0;
341 }
342 #endif
343