xref: /freebsd/sys/security/mac_veriexec/veriexec_fingerprint.c (revision 5ca8e32633c4ffbbcd6762e5888b6a4ba0708c6c)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2011, 2012, 2013, 2015, 2016, Juniper Networks, Inc.
5  * All rights reserved.
6  *
7  * Originally derived from:
8  *	$NetBSD: kern_verifiedexec.c,v 1.7 2003/11/18 13:13:03 martin Exp $
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 
34 #include "opt_mac.h"
35 
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/imgact.h>
39 #include <sys/jail.h>
40 #include <sys/kernel.h>
41 #include <sys/lock.h>
42 #include <sys/malloc.h>
43 #include <sys/mount.h>
44 #include <sys/mutex.h>
45 #include <sys/priv.h>
46 #include <sys/proc.h>
47 #include <sys/sbuf.h>
48 #include <sys/syslog.h>
49 #include <sys/vnode.h>
50 
51 #include <security/mac/mac_framework.h>
52 
53 #include "mac_veriexec.h"
54 #include "mac_veriexec_internal.h"
55 
56 /**
57  * @var fpops_list
58  * @internal
59  * @brief Fingerprint operations list
60  *
61  * This is essentially the list of fingerprint modules currently loaded
62  */
63 static LIST_HEAD(fpopshead, mac_veriexec_fpops) fpops_list;
64 
65 static int mac_veriexec_late;
66 
67 static int sysctl_mac_veriexec_algorithms(SYSCTL_HANDLER_ARGS);
68 
69 SYSCTL_PROC(_security_mac_veriexec, OID_AUTO, algorithms,
70     CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
71     0, 0, sysctl_mac_veriexec_algorithms, "A",
72     "Verified execution supported hashing algorithms");
73 
74 static int
75 sysctl_mac_veriexec_algorithms(SYSCTL_HANDLER_ARGS)
76 {
77 	struct sbuf sb;
78 	struct mac_veriexec_fpops *fpops;
79 	int algorithms, error;
80 
81 	algorithms = 0;
82 	sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND);
83 	LIST_FOREACH(fpops, &fpops_list, entries) {
84 		if (algorithms++)
85 			sbuf_printf(&sb, " ");
86 		sbuf_printf(&sb, "%s", fpops->type);
87 	}
88 	sbuf_finish(&sb);
89 	error = SYSCTL_OUT(req, sbuf_data(&sb), sbuf_len(&sb));
90 	sbuf_delete(&sb);
91 	return (error);
92 }
93 
94 /**
95  * @internal
96  * @brief Consistently identify file encountering errors
97  *
98  * @param imgp		image params to display
99  * @param td		calling thread
100  * @param msg		message to display
101  *
102  * @return String form of the information stored in @p imgp
103  */
104 static void
105 identify_error (struct image_params *imgp, struct thread *td, const char *msg)
106 {
107 	struct proc *parent;
108 	pid_t ppid, gppid;
109 
110 	parent = imgp->proc->p_pptr;
111 	ppid = (parent != NULL) ? parent->p_pid : 0;
112 	gppid = (parent != NULL && parent->p_pptr != NULL) ?
113 	    parent->p_pptr->p_pid : 0;
114 
115 	log(LOG_ERR, MAC_VERIEXEC_FULLNAME ": %s (file=%s fsid=%ju fileid=%ju "
116 	    "gen=%lu uid=%u pid=%u ppid=%u gppid=%u)", msg,
117 	    (imgp->args != NULL) ? imgp->args->fname : "",
118 	    (uintmax_t)imgp->attr->va_fsid, (uintmax_t)imgp->attr->va_fileid,
119 	    imgp->attr->va_gen, td->td_ucred->cr_ruid, imgp->proc->p_pid,
120 	    ppid, gppid);
121 }
122 
123 /**
124  * @internal
125  * @brief Check the fingerprint type for the given file and evaluate the
126  * fingerprint for that file.
127  *
128  * It is assumed that @p fingerprint has sufficient storage to hold the
129  * resulting fingerprint string.
130  *
131  * @param vp		vnode to check
132  * @param ip		file info from the meta-data store
133  * @param td		calling thread
134  * @param file_size	size of the file to read
135  * @param fingerprint	resulting fingerprint
136  *
137  * @return 0 on success, otherwise an error code.
138  */
139 static int
140 evaluate_fingerprint(struct vnode *vp, struct mac_veriexec_file_info *ip,
141     struct thread *td, off_t file_size, unsigned char *fingerprint)
142 {
143 	uint8_t *filebuf;
144 	void *ctx;
145 	off_t offset;
146 	size_t count, nread, resid;
147 	int error = EINVAL;
148 
149 	filebuf = malloc(PAGE_SIZE, M_VERIEXEC, M_WAITOK);
150 	ctx = malloc(ip->ops->context_size, M_VERIEXEC, M_WAITOK);
151 
152 	(ip->ops->init)(ctx);
153 	for (offset = 0; offset < file_size; offset += nread) {
154 		if ((offset + PAGE_SIZE) > file_size)
155 			count = file_size - offset;
156 		else
157 			count = PAGE_SIZE;
158 
159 		error = vn_rdwr_inchunks(UIO_READ, vp, filebuf, count, offset,
160 		    UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, &resid,
161 		    td);
162 		if (error)
163 			goto failed;
164 
165 		nread = count - resid;
166 		(ip->ops->update)(ctx, filebuf, nread);
167 	}
168 	(ip->ops->final)(fingerprint, ctx);
169 
170 #ifdef DEBUG_VERIEXEC_FINGERPRINT
171 	for (offset = 0; offset < ip->ops->digest_len; offset++)
172 		printf("%02x", fingerprint[offset]);
173 	printf("\n");
174 #endif
175 
176 failed:
177 	free(ctx, M_VERIEXEC);
178 	free(filebuf, M_VERIEXEC);
179 	return (error);
180 }
181 
182 /**
183  * @internal
184  * @brief Compare the two given fingerprints to see if they are the same.
185  *
186  * Differing fingerprint methods may have differing lengths which
187  * is handled by this routine.
188  *
189  * @param ip		file info from the meta-data store
190  * @param digest	digest to compare
191  *
192  * @return 0 if the fingerprints match and non-zero if they do not.
193  */
194 static int
195 fingerprintcmp(struct mac_veriexec_file_info *ip, unsigned char *digest)
196 {
197 
198 	return memcmp(ip->fingerprint, digest, ip->ops->digest_len);
199 }
200 
201 /**
202  * @brief Check if @p fingerprint matches the one associated with the vnode
203  *     @p vp
204  *
205  * @param vp		vnode to check
206  * @param ip		file info from the meta-data store
207  * @param td		calling thread
208  * @param file_size	size of the file to read
209  * @param fingerprint	fingerprint to compare
210  *
211  * @return 0 if they match, otherwise an error code.
212  */
213 int
214 mac_veriexec_fingerprint_check_vnode(struct vnode *vp,
215     struct mac_veriexec_file_info *ip, struct thread *td, off_t file_size,
216     unsigned char *fingerprint)
217 {
218 	int error;
219 
220 	/* reject fingerprint if writers are active */
221 	if (vp->v_writecount > 0)
222 		return (ETXTBSY);
223 
224 	if ((vp->v_mount->mnt_flag & MNT_VERIFIED) != 0) {
225 		VERIEXEC_DEBUG(2, ("file %ju.%lu on verified %s mount\n",
226 		    (uintmax_t)ip->fileid, ip->gen,
227 		    vp->v_mount->mnt_vfc->vfc_name));
228 
229 		/*
230 		 * The VFS is backed by a file which has been verified.
231 		 * No need to waste time here.
232 		 */
233 		return (0);
234 	}
235 
236 	error = evaluate_fingerprint(vp, ip, td, file_size, fingerprint);
237 	if (error)
238 		return (error);
239 
240 	if (fingerprintcmp(ip, fingerprint) != 0)
241 		return (EAUTH);
242 
243 	return (0);
244 }
245 
246 /**
247  * @brief Check a file signature and validate it.
248  *
249  * @param imgp		parameters for the image to check
250  * @param check_files	if 1, check the files list first, otherwise check the
251  * 			exectuables list first
252  * @param td		calling thread
253  *
254  * @note Called with imgp->vp locked.
255  *
256  * @return 0 if the signature is valid, otherwise an error code.
257  */
258 int
259 mac_veriexec_fingerprint_check_image(struct image_params *imgp,
260     int check_files, struct thread *td)
261 {
262 	struct vnode *vp = imgp->vp;
263 	int error;
264 	fingerprint_status_t status;
265 
266 	if (!mac_veriexec_in_state(VERIEXEC_STATE_ACTIVE))
267 		return 0;
268 
269 	error = mac_veriexec_metadata_fetch_fingerprint_status(vp, imgp->attr,
270 	    td, check_files);
271 	if (error && error != EAUTH)
272 		return (error);
273 
274 	/*
275 	 * By now status is set.
276 	 */
277 	status = mac_veriexec_get_fingerprint_status(vp);
278 	switch (status) {
279 	case FINGERPRINT_INVALID: /* should not happen */
280 		identify_error(imgp, td, "got unexpected FINGERPRINT_INVALID");
281 		error = EPERM;
282 		break;
283 
284 	case FINGERPRINT_FILE:
285 		if (!check_files) {
286 			if (prison0.pr_securelevel > 1 ||
287 			    mac_veriexec_in_state(VERIEXEC_STATE_ENFORCE))
288 				error = EPERM;
289 		}
290 		break;
291 
292 	case FINGERPRINT_VALID: /* is ok - report so if debug is on */
293 		VERIEXEC_DEBUG(4, ("Fingerprint matches\n"));
294 		break;
295 
296 	case FINGERPRINT_INDIRECT: /* fingerprint ok but need to check
297 				      for direct execution */
298 		if (!imgp->interpreted &&
299 		    mac_priv_grant(td->td_ucred, PRIV_VERIEXEC_DIRECT) != 0) {
300 			identify_error(imgp, td, "attempted direct execution");
301 			if (prison0.pr_securelevel > 1 ||
302 			    mac_veriexec_in_state(VERIEXEC_STATE_ENFORCE))
303 				error = EPERM;
304 		}
305 		break;
306 
307 	case FINGERPRINT_NOMATCH: /* does not match - whine about it */
308 		identify_error(imgp, td,
309 		    "fingerprint does not match loaded value");
310 		if (prison0.pr_securelevel > 1 ||
311 		    mac_veriexec_in_state(VERIEXEC_STATE_ENFORCE))
312 			error = EAUTH;
313 		break;
314 
315 	case FINGERPRINT_NOENTRY: /* no entry in the list, complain */
316 		identify_error(imgp, td, "no fingerprint");
317 		if (prison0.pr_securelevel > 1 ||
318 		    mac_veriexec_in_state(VERIEXEC_STATE_ENFORCE))
319 			error = EAUTH;
320 		break;
321 
322 	case FINGERPRINT_NODEV: /* no signatures for the device, complain */
323 		identify_error(imgp, td, "no signatures for device");
324 		if (prison0.pr_securelevel > 1 ||
325 		    mac_veriexec_in_state(VERIEXEC_STATE_ENFORCE))
326 			error = EAUTH;
327 		break;
328 
329 	default: /* this should never happen. */
330 		identify_error(imgp, td, "invalid status field for vnode");
331 		error = EPERM;
332 	}
333 	switch (status) {
334 	case FINGERPRINT_NODEV:
335 	case FINGERPRINT_NOENTRY:
336 		/*
337 		 * Check if this process has override allowed.
338 		 * This will only be true if PRIV_VERIEXEC_DIRECT
339 		 * already succeeded.
340 		 */
341 		if (error == EAUTH &&
342 		    mac_priv_grant(td->td_ucred, PRIV_VERIEXEC_NOVERIFY) == 0) {
343 			error = 0;
344 		}
345 		break;
346 	default:
347 		break;
348 	}
349 
350 	return error;
351 }
352 
353 /**
354  * @brief Look up the fingerprint operations for a specific digest type
355  *
356  * @return A pointer to fingerprint operations, if found, or else @c NULL.
357  */
358 struct mac_veriexec_fpops *
359 mac_veriexec_fingerprint_lookup_ops(const char *type)
360 {
361 	struct mac_veriexec_fpops *fpops;
362 
363 	if (type == NULL)
364 		return (NULL);
365 
366 	LIST_FOREACH(fpops, &fpops_list, entries) {
367 		if (!strcasecmp(type, fpops->type))
368 			break;
369 	}
370 	return (fpops);
371 }
372 
373 /**
374  * @brief Add fingerprint operations for a specific digest type
375  *
376  * Any attempts to add a duplicate digest type results in an error.
377  *
378  * @return 0 if the ops were added successfully, otherwise an error code.
379  */
380 int
381 mac_veriexec_fingerprint_add_ops(struct mac_veriexec_fpops *fpops)
382 {
383 
384 	/* Sanity check the ops */
385 	if (fpops->type == NULL || fpops->digest_len == 0 ||
386 	    fpops->context_size == 0 || fpops->init == NULL ||
387 	    fpops->update == NULL || fpops->final == NULL)
388 		return (EINVAL);
389 
390 	/* Make sure we do not already have ops for this digest type */
391 	if (mac_veriexec_fingerprint_lookup_ops(fpops->type))
392 		return (EEXIST);
393 
394 	/* Add the ops to the list */
395 	LIST_INSERT_HEAD(&fpops_list, fpops, entries);
396 
397 	printf("MAC/veriexec fingerprint module loaded: %s\n", fpops->type);
398 
399 	return (0);
400 }
401 
402 /**
403  * @brief Initialize the fingerprint operations list
404  */
405 void
406 mac_veriexec_fingerprint_init(void)
407 {
408 
409 	LIST_INIT(&fpops_list);
410 }
411 
412 /**
413  * @brief Handle fingerprint module events
414  *
415  * This function is called by the @c MAC_VERIEXEC_FPMOD macro.
416  *
417  * @param mod		module information
418  * @param type		event type
419  * @param data		event-specific data
420  *
421  * @return On @c MOD_LOAD, 0 if the fingerprint ops were added successfully,
422  *     otherwise an error code. All other event types result in an error code.
423  */
424 int
425 mac_veriexec_fingerprint_modevent(module_t mod, int type, void *data)
426 {
427 	struct mac_veriexec_fpops *fpops;
428 	int error;
429 
430 	error = 0;
431 	fpops = (struct mac_veriexec_fpops *) data;
432 
433 	switch (type) {
434 	case MOD_LOAD:
435 		/* We do not allow late loading of fingerprint modules */
436 		if (mac_veriexec_late) {
437 			printf("%s: can't load %s fingerprint module after "
438 			    "booting\n", __func__, fpops->type);
439 			error = EBUSY;
440 			break;
441 		}
442 		error = mac_veriexec_fingerprint_add_ops(fpops);
443 		break;
444 	case MOD_UNLOAD:
445 		error = EBUSY;
446 		break;
447 	default:
448 		error = EOPNOTSUPP;
449 		break;
450 	}
451 
452 	return (error);
453 }
454 
455 /**
456  * @internal
457  * @brief Mark veriexec late initialization flag
458  */
459 static void
460 mac_veriexec_late_init(void)
461 {
462 
463 	mac_veriexec_late = 1;
464 }
465 
466 SYSINIT(mac_veriexec_late, SI_SUB_MAC_LATE, SI_ORDER_ANY,
467     mac_veriexec_late_init, NULL);
468