xref: /freebsd/sys/security/mac_veriexec/veriexec_fingerprint.c (revision 13ea0450a9c8742119d36f3bf8f47accdce46e54)
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  */
60 static LIST_HEAD(fpopshead, mac_veriexec_fpops) fpops_list;
61 
62 static int mac_veriexec_late;
63 
64 static int sysctl_mac_veriexec_algorithms(SYSCTL_HANDLER_ARGS);
65 
66 SYSCTL_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 
70 static int
71 sysctl_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  */
100 static void
101 identify_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  */
135 static int
136 evaluate_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 
172 failed:
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  */
190 static int
191 fingerprintcmp(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  */
209 int
210 mac_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)
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  */
254 int
255 mac_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  */
336 struct mac_veriexec_fpops *
337 mac_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  */
358 int
359 mac_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  */
383 void
384 mac_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  */
402 int
403 mac_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  */
437 static void
438 mac_veriexec_late_init(void)
439 {
440 
441 	mac_veriexec_late = 1;
442 }
443 
444 SYSINIT(mac_veriexec_late, SI_SUB_MAC_LATE, SI_ORDER_ANY,
445     mac_veriexec_late_init, NULL);
446