2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2011, 2012, 2013, 2015, 2016, Juniper Networks, Inc.
7 * Originally derived from:
8 * $NetBSD: kern_verifiedexec.c,v 1.7 2003/11/18 13:13:03 martin Exp $
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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
32 #include <sys/cdefs.h>
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/imgact.h>
40 #include <sys/kernel.h>
42 #include <sys/malloc.h>
43 #include <sys/mount.h>
44 #include <sys/mutex.h>
48 #include <sys/syslog.h>
49 #include <sys/vnode.h>
51 #include <security/mac/mac_framework.h>
53 #include "mac_veriexec.h"
54 #include "mac_veriexec_internal.h"
59 * @brief Fingerprint operations list
61 * This is essentially the list of fingerprint modules currently loaded
63 static LIST_HEAD(fpopshead, mac_veriexec_fpops) fpops_list;
65 static int mac_veriexec_late;
67 static int sysctl_mac_veriexec_algorithms(SYSCTL_HANDLER_ARGS);
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");
75 sysctl_mac_veriexec_algorithms(SYSCTL_HANDLER_ARGS)
78 struct mac_veriexec_fpops *fpops;
79 int algorithms, error;
82 sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND);
83 LIST_FOREACH(fpops, &fpops_list, entries) {
85 sbuf_printf(&sb, " ");
86 sbuf_printf(&sb, "%s", fpops->type);
89 error = SYSCTL_OUT(req, sbuf_data(&sb), sbuf_len(&sb));
96 * @brief Consistently identify file encountering errors
98 * @param imgp image params to display
99 * @param td calling thread
100 * @param msg message to display
102 * @return String form of the information stored in @p imgp
105 identify_error (struct image_params *imgp, struct thread *td, const char *msg)
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;
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,
125 * @brief Check the fingerprint type for the given file and evaluate the
126 * fingerprint for that file.
128 * It is assumed that @p fingerprint has sufficient storage to hold the
129 * resulting fingerprint string.
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
137 * @return 0 on success, otherwise an error code.
140 evaluate_fingerprint(struct vnode *vp, struct mac_veriexec_file_info *ip,
141 struct thread *td, off_t file_size, unsigned char *fingerprint)
146 size_t count, nread, resid;
149 filebuf = malloc(PAGE_SIZE, M_VERIEXEC, M_WAITOK);
150 ctx = malloc(ip->ops->context_size, M_VERIEXEC, M_WAITOK);
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;
159 error = vn_rdwr_inchunks(UIO_READ, vp, filebuf, count, offset,
160 UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, &resid,
165 nread = count - resid;
166 (ip->ops->update)(ctx, filebuf, nread);
168 (ip->ops->final)(fingerprint, ctx);
170 #ifdef DEBUG_VERIEXEC_FINGERPRINT
171 for (offset = 0; offset < ip->ops->digest_len; offset++)
172 printf("%02x", fingerprint[offset]);
177 free(ctx, M_VERIEXEC);
178 free(filebuf, M_VERIEXEC);
184 * @brief Compare the two given fingerprints to see if they are the same.
186 * Differing fingerprint methods may have differing lengths which
187 * is handled by this routine.
189 * @param ip file info from the meta-data store
190 * @param digest digest to compare
192 * @return 0 if the fingerprints match and non-zero if they do not.
195 fingerprintcmp(struct mac_veriexec_file_info *ip, unsigned char *digest)
198 return memcmp(ip->fingerprint, digest, ip->ops->digest_len);
202 * @brief Check if @p fingerprint matches the one associated with the vnode
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
211 * @return 0 if they match, otherwise an error code.
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)
220 /* reject fingerprint if writers are active */
221 if (vp->v_writecount > 0)
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));
230 * The VFS is backed by a file which has been verified.
231 * No need to waste time here.
236 error = evaluate_fingerprint(vp, ip, td, file_size, fingerprint);
240 if (fingerprintcmp(ip, fingerprint) != 0)
247 * @brief Check a file signature and validate it.
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
254 * @note Called with imgp->vp locked.
256 * @return 0 if the signature is valid, otherwise an error code.
259 mac_veriexec_fingerprint_check_image(struct image_params *imgp,
260 int check_files, struct thread *td)
262 struct vnode *vp = imgp->vp;
264 fingerprint_status_t status;
266 if (!mac_veriexec_in_state(VERIEXEC_STATE_ACTIVE))
269 error = mac_veriexec_metadata_fetch_fingerprint_status(vp, imgp->attr,
271 if (error && error != EAUTH)
275 * By now status is set.
277 status = mac_veriexec_get_fingerprint_status(vp);
279 case FINGERPRINT_INVALID: /* should not happen */
280 identify_error(imgp, td, "got unexpected FINGERPRINT_INVALID");
284 case FINGERPRINT_FILE:
286 if (prison0.pr_securelevel > 1 ||
287 mac_veriexec_in_state(VERIEXEC_STATE_ENFORCE))
292 case FINGERPRINT_VALID: /* is ok - report so if debug is on */
293 VERIEXEC_DEBUG(4, ("Fingerprint matches\n"));
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))
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))
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))
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))
329 default: /* this should never happen. */
330 identify_error(imgp, td, "invalid status field for vnode");
334 case FINGERPRINT_NODEV:
335 case FINGERPRINT_NOENTRY:
337 * Check if this process has override allowed.
338 * This will only be true if PRIV_VERIEXEC_DIRECT
341 if (error == EAUTH &&
342 mac_priv_grant(td->td_ucred, PRIV_VERIEXEC_NOVERIFY) == 0) {
354 * @brief Look up the fingerprint operations for a specific digest type
356 * @return A pointer to fingerprint operations, if found, or else @c NULL.
358 struct mac_veriexec_fpops *
359 mac_veriexec_fingerprint_lookup_ops(const char *type)
361 struct mac_veriexec_fpops *fpops;
366 LIST_FOREACH(fpops, &fpops_list, entries) {
367 if (!strcasecmp(type, fpops->type))
374 * @brief Add fingerprint operations for a specific digest type
376 * Any attempts to add a duplicate digest type results in an error.
378 * @return 0 if the ops were added successfully, otherwise an error code.
381 mac_veriexec_fingerprint_add_ops(struct mac_veriexec_fpops *fpops)
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)
390 /* Make sure we do not already have ops for this digest type */
391 if (mac_veriexec_fingerprint_lookup_ops(fpops->type))
394 /* Add the ops to the list */
395 LIST_INSERT_HEAD(&fpops_list, fpops, entries);
397 printf("MAC/veriexec fingerprint module loaded: %s\n", fpops->type);
403 * @brief Initialize the fingerprint operations list
406 mac_veriexec_fingerprint_init(void)
409 LIST_INIT(&fpops_list);
413 * @brief Handle fingerprint module events
415 * This function is called by the @c MAC_VERIEXEC_FPMOD macro.
417 * @param mod module information
418 * @param type event type
419 * @param data event-specific data
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.
425 mac_veriexec_fingerprint_modevent(module_t mod, int type, void *data)
427 struct mac_veriexec_fpops *fpops;
431 fpops = (struct mac_veriexec_fpops *) data;
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);
442 error = mac_veriexec_fingerprint_add_ops(fpops);
457 * @brief Mark veriexec late initialization flag
460 mac_veriexec_late_init(void)
463 mac_veriexec_late = 1;
466 SYSINIT(mac_veriexec_late, SI_SUB_MAC_LATE, SI_ORDER_ANY,
467 mac_veriexec_late_init, NULL);