1/*
2 * $FreeBSD$
3 *
4 * Copyright (c) 2011-2013, 2015, 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#include <sys/cdefs.h>
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/buf.h>
33#include <sys/conf.h>
34#include <sys/errno.h>
35#include <sys/fcntl.h>
36#include <sys/file.h>
37#include <sys/filedesc.h>
38#include <sys/ioccom.h>
39#include <sys/jail.h>
40#include <sys/kernel.h>
41#include <sys/lock.h>
42#include <sys/malloc.h>
43#include <sys/mdioctl.h>
44#include <sys/mount.h>
45#include <sys/mutex.h>
46#include <sys/namei.h>
47#include <sys/proc.h>
48#include <sys/queue.h>
49#include <sys/vnode.h>
50
51#include <security/mac_veriexec/mac_veriexec.h>
52#include <security/mac_veriexec/mac_veriexec_internal.h>
53
54#include "veriexec_ioctl.h"
55
56/*
57 * We need a mutex while updating lists etc.
58 */
59extern struct mtx ve_mutex;
60
61/*
62 * Handle the ioctl for the device
63 */
64static int
65verifiedexecioctl(struct cdev *dev __unused, u_long cmd, caddr_t data,
66    int flags, struct thread *td)
67{
68	struct nameidata nid;
69	struct vattr vattr;
70	struct verified_exec_params *params;
71	int error = 0;
72
73	params = (struct verified_exec_params *)data;
74	switch (cmd) {
75	case VERIEXEC_ACTIVE:
76		mtx_lock(&ve_mutex);
77		if (mac_veriexec_in_state(VERIEXEC_STATE_LOADED))
78			mac_veriexec_set_state(VERIEXEC_STATE_ACTIVE);
79		else
80			error = EINVAL;
81		mtx_unlock(&ve_mutex);
82		break;
83	case VERIEXEC_DEBUG_ON:
84		mtx_lock(&ve_mutex);
85		{
86			int *ip = (int *)data;
87
88			mac_veriexec_debug++;
89			if (ip) {
90				if (*ip > 0)
91					mac_veriexec_debug = *ip;
92				*ip = mac_veriexec_debug;
93			}
94		}
95		mtx_unlock(&ve_mutex);
96		break;
97	case VERIEXEC_DEBUG_OFF:
98		mac_veriexec_debug = 0;
99		break;
100	case VERIEXEC_ENFORCE:
101		mtx_lock(&ve_mutex);
102		if (mac_veriexec_in_state(VERIEXEC_STATE_LOADED))
103			mac_veriexec_set_state(VERIEXEC_STATE_ACTIVE |
104			    VERIEXEC_STATE_ENFORCE);
105		else
106			error = EINVAL;
107		mtx_unlock(&ve_mutex);
108		break;
109	case VERIEXEC_GETSTATE:
110		{
111			int *ip = (int *)data;
112
113			if (ip)
114				*ip = mac_veriexec_get_state();
115			else
116			    error = EINVAL;
117		}
118		break;
119	case VERIEXEC_LOCK:
120		mtx_lock(&ve_mutex);
121		mac_veriexec_set_state(VERIEXEC_STATE_LOCKED);
122		mtx_unlock(&ve_mutex);
123		break;
124	case VERIEXEC_LOAD:
125	    	if (prison0.pr_securelevel > 0)
126			return (EPERM);	/* no updates when secure */
127
128		/* FALLTHROUGH */
129	case VERIEXEC_SIGNED_LOAD:
130		/*
131		 * If we use a loader that will only use a
132		 * digitally signed hash list - which it verifies.
133		 * We can load fingerprints provided veriexec is not locked.
134		 */
135	    	if (prison0.pr_securelevel > 0 &&
136		    !mac_veriexec_in_state(VERIEXEC_STATE_LOADED)) {
137			/*
138			 * If securelevel has been raised and we
139			 * do not have any fingerprints loaded,
140			 * it would dangerous to do so now.
141			 */
142			return (EPERM);
143		}
144		if (mac_veriexec_in_state(VERIEXEC_STATE_LOCKED))
145			error = EPERM;
146		else {
147			int flags = FREAD;
148			int override = (cmd == VERIEXEC_SIGNED_LOAD);
149
150			/*
151			 * Get the attributes for the file name passed
152			 * stash the file's device id and inode number
153			 * along with it's fingerprint in a list for
154			 * exec to use later.
155			 */
156			/*
157			 * FreeBSD seems to copy the args to kernel space
158			 */
159                        NDINIT(&nid, LOOKUP, FOLLOW, UIO_SYSSPACE,
160                               params->file, td);
161			if ((error = vn_open(&nid, &flags, 0, NULL)) != 0)
162				return (error);
163
164			error = VOP_GETATTR(nid.ni_vp, &vattr, td->td_ucred);
165			if (error != 0) {
166				mac_veriexec_set_fingerprint_status(nid.ni_vp,
167				    FINGERPRINT_INVALID);
168				VOP_UNLOCK(nid.ni_vp, 0);
169				(void) vn_close(nid.ni_vp, FREAD, td->td_ucred,
170				    td);
171				return (error);
172			}
173			if (override) {
174				/*
175				 * If the file is on a "verified" filesystem
176				 * someone may be playing games.
177				 */
178				if ((nid.ni_vp->v_mount->mnt_flag &
179				    MNT_VERIFIED) != 0)
180					override = 0;
181			}
182
183			/*
184			 * invalidate the node fingerprint status
185			 * which will have been set in the vn_open
186			 * and would always be FINGERPRINT_NOTFOUND
187			 */
188			mac_veriexec_set_fingerprint_status(nid.ni_vp,
189			    FINGERPRINT_INVALID);
190			VOP_UNLOCK(nid.ni_vp, 0);
191			(void) vn_close(nid.ni_vp, FREAD, td->td_ucred, td);
192
193			mtx_lock(&ve_mutex);
194			error = mac_veriexec_metadata_add_file(
195			    ((params->flags & VERIEXEC_FILE) != 0),
196			    vattr.va_fsid, vattr.va_fileid, vattr.va_gen,
197			    params->fingerprint, params->flags,
198			    params->fp_type, override);
199
200			mac_veriexec_set_state(VERIEXEC_STATE_LOADED);
201			mtx_unlock(&ve_mutex);
202		}
203		break;
204	default:
205		error = ENODEV;
206	}
207	return (error);
208}
209
210struct cdevsw veriexec_cdevsw = {
211	.d_version =	D_VERSION,
212	.d_ioctl =	verifiedexecioctl,
213	.d_name =	"veriexec",
214};
215
216static void
217veriexec_drvinit(void *unused __unused)
218{
219
220	make_dev(&veriexec_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "veriexec");
221}
222
223SYSINIT(veriexec, SI_SUB_PSEUDO, SI_ORDER_ANY, veriexec_drvinit, NULL);
224MODULE_DEPEND(veriexec, mac_veriexec, 1, 1, 1);
225