1/*
2 * $FreeBSD$
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/proc.h>
46#include <sys/sbuf.h>
47#include <sys/syslog.h>
48#include <sys/vnode.h>
49
50#include "mac_veriexec.h"
51#include "mac_veriexec_internal.h"
52
53/**
54 * @var fpops_list
55 * @internal
56 * @brief Fingerprint operations list
57 *
58 * This is essentially the list of fingerprint modules currently loaded
59 */
60static LIST_HEAD(fpopshead, mac_veriexec_fpops) fpops_list;
61
62static int mac_veriexec_late;
63
64static int sysctl_mac_veriexec_algorithms(SYSCTL_HANDLER_ARGS);
65
66SYSCTL_PROC(_security_mac_veriexec, OID_AUTO, algorithms,
67    CTLTYPE_STRING | CTLFLAG_RD, 0, 0, sysctl_mac_veriexec_algorithms, "A",
68    "Verified execution supported hashing algorithms");
69
70static int
71sysctl_mac_veriexec_algorithms(SYSCTL_HANDLER_ARGS)
72{
73	struct sbuf sb;
74	struct mac_veriexec_fpops *fpops;
75	int algorithms, error;
76
77	algorithms = 0;
78	sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND);
79	LIST_FOREACH(fpops, &fpops_list, entries) {
80		if (algorithms++)
81			sbuf_printf(&sb, " ");
82		sbuf_printf(&sb, "%s", fpops->type);
83	}
84	sbuf_finish(&sb);
85	error = SYSCTL_OUT(req, sbuf_data(&sb), sbuf_len(&sb));
86	sbuf_delete(&sb);
87	return (error);
88}
89
90/**
91 * @internal
92 * @brief Consistently identify file encountering errors
93 *
94 * @param imgp		image params to display
95 * @param td		calling thread
96 * @param msg		message to display
97 *
98 * @return String form of the information stored in @p imgp
99 */
100static void
101identify_error (struct image_params *imgp, struct thread *td, const char *msg)
102{
103	struct proc *parent;
104	pid_t ppid, gppid;
105
106	parent = imgp->proc->p_pptr;
107	ppid = (parent != NULL) ? parent->p_pid : 0;
108	gppid = (parent != NULL && parent->p_pptr != NULL) ?
109	    parent->p_pptr->p_pid : 0;
110
111	log(LOG_ERR, MAC_VERIEXEC_FULLNAME ": %s (file=%s fsid=%ju fileid=%ju "
112	    "gen=%lu uid=%u pid=%u ppid=%u gppid=%u)", msg,
113	    (imgp->args != NULL) ? imgp->args->fname : "",
114	    (uintmax_t)imgp->attr->va_fsid, (uintmax_t)imgp->attr->va_fileid,
115	    imgp->attr->va_gen, td->td_ucred->cr_ruid, imgp->proc->p_pid,
116	    ppid, gppid);
117}
118
119/**
120 * @internal
121 * @brief Check the fingerprint type for the given file and evaluate the
122 * fingerprint for that file.
123 *
124 * It is assumed that @p fingerprint has sufficient storage to hold the
125 * resulting fingerprint string.
126 *
127 * @param vp		vnode to check
128 * @param ip		file info from the meta-data store
129 * @param td		calling thread
130 * @param file_size	size of the file to read
131 * @param fingerprint	resulting fingerprint
132 *
133 * @return 0 on success, otherwise an error code.
134 */
135static int
136evaluate_fingerprint(struct vnode *vp, struct mac_veriexec_file_info *ip,
137    struct thread *td, off_t file_size, unsigned char *fingerprint)
138{
139	uint8_t *filebuf;
140	void *ctx;
141	off_t offset;
142	size_t count, nread, resid;
143	int error = EINVAL;
144
145	filebuf = malloc(PAGE_SIZE, M_VERIEXEC, M_WAITOK);
146	ctx = malloc(ip->ops->context_size, M_VERIEXEC, M_WAITOK);
147
148	(ip->ops->init)(ctx);
149	for (offset = 0; offset < file_size; offset += nread) {
150		if ((offset + PAGE_SIZE) > file_size)
151			count = file_size - offset;
152		else
153			count = PAGE_SIZE;
154
155		error = vn_rdwr_inchunks(UIO_READ, vp, filebuf, count, offset,
156		    UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, &resid,
157		    td);
158		if (error)
159			goto failed;
160
161		nread = count - resid;
162		(ip->ops->update)(ctx, filebuf, nread);
163	}
164	(ip->ops->final)(fingerprint, ctx);
165
166#ifdef DEBUG_VERIEXEC_FINGERPRINT
167	for (offset = 0; offset < ip->ops->digest_len; offset++)
168		printf("%02x", fingerprint[offset]);
169	printf("\n");
170#endif
171
172failed:
173	free(ctx, M_VERIEXEC);
174	free(filebuf, M_VERIEXEC);
175	return (error);
176}
177
178/**
179 * @internal
180 * @brief Compare the two given fingerprints to see if they are the same.
181 *
182 * Differing fingerprint methods may have differing lengths which
183 * is handled by this routine.
184 *
185 * @param ip		file info from the meta-data store
186 * @param digest	digest to compare
187 *
188 * @return 0 if the fingerprints match and non-zero if they do not.
189 */
190static int
191fingerprintcmp(struct mac_veriexec_file_info *ip, unsigned char *digest)
192{
193
194	return memcmp(ip->fingerprint, digest, ip->ops->digest_len);
195}
196
197/**
198 * @brief Check if @p fingerprint matches the one associated with the vnode
199 *     @p vp
200 *
201 * @param vp		vnode to check
202 * @param ip		file info from the meta-data store
203 * @param td		calling thread
204 * @param file_size	size of the file to read
205 * @param fingerprint	fingerprint to compare
206 *
207 * @return 0 if they match, otherwise an error code.
208 */
209int
210mac_veriexec_fingerprint_check_vnode(struct vnode *vp,
211    struct mac_veriexec_file_info *ip, struct thread *td, off_t file_size,
212    unsigned char *fingerprint)
213{
214	int error;
215
216	/* reject fingerprint if writers are active */
217	if (vp->v_writecount > 0)
218		return (ETXTBSY);
219
220	if ((vp->v_mount->mnt_flag & MNT_VERIFIED) != 0) {
221		VERIEXEC_DEBUG(2, ("file %ju.%lu on verified %s mount\n",
222		    (uintmax_t)ip->fileid, ip->gen,
223		    vp->v_mount->mnt_vfc->vfc_name));
224
225		/*
226		 * The VFS is backed by a file which has been verified.
227		 * No need to waste time here.
228		 */
229		return (0);
230	}
231
232	error = evaluate_fingerprint(vp, ip, td, file_size, fingerprint);
233	if (error)
234		return (error);
235
236	if (fingerprintcmp(ip, fingerprint) != 0)
237		return (EAUTH);
238
239	return (0);
240}
241
242/**
243 * @brief Check a file signature and validate it.
244 *
245 * @param imgp		parameters for the image to check
246 * @param check_files	if 1, check the files list first, otherwise check the
247 * 			exectuables list first
248 * @param td		calling thread
249 *
250 * @note Called with imgp->vp locked.
251 *
252 * @return 0 if the signature is valid, otherwise an error code.
253 */
254int
255mac_veriexec_fingerprint_check_image(struct image_params *imgp,
256    int check_files, struct thread *td)
257{
258	struct vnode *vp = imgp->vp;
259	int error;
260	fingerprint_status_t status;
261
262	if (!mac_veriexec_in_state(VERIEXEC_STATE_ACTIVE))
263		return 0;
264
265	error = mac_veriexec_metadata_fetch_fingerprint_status(vp, imgp->attr,
266	    td, check_files);
267	if (error && error != EAUTH)
268		return (error);
269
270	/*
271	 * By now status is set.
272	 */
273	status = mac_veriexec_get_fingerprint_status(vp);
274	switch (status) {
275	case FINGERPRINT_INVALID: /* should not happen */
276		identify_error(imgp, td, "got unexpected FINGERPRINT_INVALID");
277		error = EPERM;
278		break;
279
280	case FINGERPRINT_FILE:
281		if (!check_files) {
282			if (prison0.pr_securelevel > 1 ||
283			    mac_veriexec_in_state(VERIEXEC_STATE_ENFORCE))
284				error = EPERM;
285		}
286		break;
287
288	case FINGERPRINT_VALID: /* is ok - report so if debug is on */
289		VERIEXEC_DEBUG(4, ("Fingerprint matches\n"));
290		break;
291
292	case FINGERPRINT_INDIRECT: /* fingerprint ok but need to check
293				      for direct execution */
294		if (!imgp->interpreted) {
295			identify_error(imgp, td, "attempted direct execution");
296			if (prison0.pr_securelevel > 1 ||
297			    mac_veriexec_in_state(VERIEXEC_STATE_ENFORCE))
298				error = EPERM;
299		}
300		break;
301
302	case FINGERPRINT_NOMATCH: /* does not match - whine about it */
303		identify_error(imgp, td,
304		    "fingerprint does not match loaded value");
305		if (prison0.pr_securelevel > 1 ||
306		    mac_veriexec_in_state(VERIEXEC_STATE_ENFORCE))
307			error = EAUTH;
308		break;
309
310	case FINGERPRINT_NOENTRY: /* no entry in the list, complain */
311		identify_error(imgp, td, "no fingerprint");
312		if (prison0.pr_securelevel > 1 ||
313		    mac_veriexec_in_state(VERIEXEC_STATE_ENFORCE))
314			error = EAUTH;
315		break;
316
317	case FINGERPRINT_NODEV: /* no signatures for the device, complain */
318		identify_error(imgp, td, "no signatures for device");
319		if (prison0.pr_securelevel > 1 ||
320		    mac_veriexec_in_state(VERIEXEC_STATE_ENFORCE))
321			error = EAUTH;
322		break;
323
324	default: /* this should never happen. */
325		identify_error(imgp, td, "invalid status field for vnode");
326		error = EPERM;
327	}
328	return error;
329}
330
331/**
332 * @brief Look up the fingerprint operations for a specific digest type
333 *
334 * @return A pointer to fingerprint operations, if found, or else @c NULL.
335 */
336struct mac_veriexec_fpops *
337mac_veriexec_fingerprint_lookup_ops(const char *type)
338{
339	struct mac_veriexec_fpops *fpops;
340
341	if (type == NULL)
342		return (NULL);
343
344	LIST_FOREACH(fpops, &fpops_list, entries) {
345		if (!strcasecmp(type, fpops->type))
346			break;
347	}
348	return (fpops);
349}
350
351/**
352 * @brief Add fingerprint operations for a specific digest type
353 *
354 * Any attempts to add a duplicate digest type results in an error.
355 *
356 * @return 0 if the ops were added successfully, otherwise an error code.
357 */
358int
359mac_veriexec_fingerprint_add_ops(struct mac_veriexec_fpops *fpops)
360{
361
362	/* Sanity check the ops */
363	if (fpops->type == NULL || fpops->digest_len == 0 ||
364	    fpops->context_size == 0 || fpops->init == NULL ||
365	    fpops->update == NULL || fpops->final == NULL)
366		return (EINVAL);
367
368	/* Make sure we do not already have ops for this digest type */
369	if (mac_veriexec_fingerprint_lookup_ops(fpops->type))
370		return (EEXIST);
371
372	/* Add the ops to the list */
373	LIST_INSERT_HEAD(&fpops_list, fpops, entries);
374
375	printf("MAC/veriexec fingerprint module loaded: %s\n", fpops->type);
376
377	return (0);
378}
379
380/**
381 * @brief Initialize the fingerprint operations list
382 */
383void
384mac_veriexec_fingerprint_init(void)
385{
386
387	LIST_INIT(&fpops_list);
388}
389
390/**
391 * @brief Handle fingerprint module events
392 *
393 * This function is called by the @c MAC_VERIEXEC_FPMOD macro.
394 *
395 * @param mod		module information
396 * @param type		event type
397 * @param data		event-specific data
398 *
399 * @return On @c MOD_LOAD, 0 if the fingerprint ops were added successfully,
400 *     otherwise an error code. All other event types result in an error code.
401 */
402int
403mac_veriexec_fingerprint_modevent(module_t mod, int type, void *data)
404{
405	struct mac_veriexec_fpops *fpops;
406	int error;
407
408	error = 0;
409	fpops = (struct mac_veriexec_fpops *) data;
410
411	switch (type) {
412	case MOD_LOAD:
413		/* We do not allow late loading of fingerprint modules */
414		if (mac_veriexec_late) {
415			printf("%s: can't load %s fingerprint module after "
416			    "booting\n", __func__, fpops->type);
417			error = EBUSY;
418			break;
419		}
420		error = mac_veriexec_fingerprint_add_ops(fpops);
421		break;
422	case MOD_UNLOAD:
423		error = EBUSY;
424		break;
425	default:
426		error = EOPNOTSUPP;
427		break;
428	}
429
430	return (error);
431}
432
433/**
434 * @internal
435 * @brief Mark veriexec late initialization flag
436 */
437static void
438mac_veriexec_late_init(void)
439{
440
441	mac_veriexec_late = 1;
442}
443
444SYSINIT(mac_veriexec_late, SI_SUB_MAC_LATE, SI_ORDER_ANY,
445    mac_veriexec_late_init, NULL);
446