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