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 */
63static LIST_HEAD(fpopshead, mac_veriexec_fpops) fpops_list;
64
65static int mac_veriexec_late;
66
67static int sysctl_mac_veriexec_algorithms(SYSCTL_HANDLER_ARGS);
68
69SYSCTL_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
74static int
75sysctl_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 */
104static void
105identify_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 */
139static int
140evaluate_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
176failed:
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 */
194static int
195fingerprintcmp(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 */
213int
214mac_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 */
258int
259mac_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 */
358struct mac_veriexec_fpops *
359mac_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 */
380int
381mac_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 */
405void
406mac_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 */
424int
425mac_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 */
459static void
460mac_veriexec_late_init(void)
461{
462
463	mac_veriexec_late = 1;
464}
465
466SYSINIT(mac_veriexec_late, SI_SUB_MAC_LATE, SI_ORDER_ANY,
467    mac_veriexec_late_init, NULL);
468