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 */
63 static LIST_HEAD(fpopshead, mac_veriexec_fpops) fpops_list;
64
65 static int mac_veriexec_late;
66
67 static int sysctl_mac_veriexec_algorithms(SYSCTL_HANDLER_ARGS);
68
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");
73
74 static int
sysctl_mac_veriexec_algorithms(SYSCTL_HANDLER_ARGS)75 sysctl_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 */
104 static void
identify_error(struct image_params * imgp,struct thread * td,const char * msg)105 identify_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 */
139 static int
evaluate_fingerprint(struct vnode * vp,struct mac_veriexec_file_info * ip,struct thread * td,off_t file_size,unsigned char * fingerprint)140 evaluate_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
176 failed:
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 */
194 static int
fingerprintcmp(struct mac_veriexec_file_info * ip,unsigned char * digest)195 fingerprintcmp(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 */
213 int
mac_veriexec_fingerprint_check_vnode(struct vnode * vp,struct mac_veriexec_file_info * ip,struct thread * td,off_t file_size,unsigned char * fingerprint)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)
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 */
258 int
mac_veriexec_fingerprint_check_image(struct image_params * imgp,int check_files,struct thread * td)259 mac_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 */
358 struct mac_veriexec_fpops *
mac_veriexec_fingerprint_lookup_ops(const char * type)359 mac_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 */
380 int
mac_veriexec_fingerprint_add_ops(struct mac_veriexec_fpops * fpops)381 mac_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 */
405 void
mac_veriexec_fingerprint_init(void)406 mac_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 */
424 int
mac_veriexec_fingerprint_modevent(module_t mod,int type,void * data)425 mac_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 */
459 static void
mac_veriexec_late_init(void)460 mac_veriexec_late_init(void)
461 {
462
463 mac_veriexec_late = 1;
464 }
465
466 SYSINIT(mac_veriexec_late, SI_SUB_MAC_LATE, SI_ORDER_ANY,
467 mac_veriexec_late_init, NULL);
468