367 lines
11 KiB
C
367 lines
11 KiB
C
/* $NetBSD: kern_verifiedexec.c,v 1.7 2003/11/18 13:13:03 martin Exp $ */
|
|
|
|
/*-
|
|
* Copyright (c) 2001, 2002 The NetBSD Foundation, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
* by Brett Lymn and Jason R Fink
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the NetBSD
|
|
* Foundation, Inc. and its contributors.
|
|
* 4. Neither the name of The NetBSD Foundation nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: kern_verifiedexec.c,v 1.7 2003/11/18 13:13:03 martin Exp $");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/mount.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/vnode.h>
|
|
#include <sys/exec.h>
|
|
#include <sys/md5.h>
|
|
#include <sys/sha1.h>
|
|
#include <sys/verified_exec.h>
|
|
|
|
/* Set the buffer to a single page for md5 and sha1 */
|
|
#define BUF_SIZE PAGE_SIZE
|
|
|
|
extern LIST_HEAD(veriexec_devhead, veriexec_dev_list) veriexec_dev_head;
|
|
|
|
static int
|
|
md5_fingerprint(struct vnode *vp, struct veriexec_inode_list *ip,
|
|
struct proc *p, u_quad_t file_size, char *fingerprint);
|
|
|
|
static int
|
|
sha1_fingerprint(struct vnode *vp, struct veriexec_inode_list *ip,
|
|
struct proc *p, u_quad_t file_size, char *fingerprint);
|
|
|
|
|
|
/*
|
|
* md5_fingerprint:
|
|
* Evaluate the md5 fingerprint of the given file.
|
|
*
|
|
*/
|
|
static int
|
|
md5_fingerprint(struct vnode *vp, struct veriexec_inode_list *ip,
|
|
struct proc *p, u_quad_t file_size, char *fingerprint)
|
|
{
|
|
u_quad_t j;
|
|
MD5_CTX md5context;
|
|
char *filebuf;
|
|
size_t resid;
|
|
int count, error;
|
|
|
|
filebuf = malloc(BUF_SIZE, M_TEMP, M_WAITOK);
|
|
MD5Init(&md5context);
|
|
|
|
for (j = 0; j < file_size; j+= BUF_SIZE) {
|
|
if ((j + BUF_SIZE) > file_size) {
|
|
count = file_size - j;
|
|
} else
|
|
count = BUF_SIZE;
|
|
|
|
error = vn_rdwr(UIO_READ, vp, filebuf, count, j,
|
|
UIO_SYSSPACE, 0, p->p_ucred, &resid, p);
|
|
|
|
if (error) {
|
|
free(filebuf, M_TEMP);
|
|
return error;
|
|
}
|
|
|
|
MD5Update(&md5context, filebuf, (unsigned int) count);
|
|
|
|
}
|
|
|
|
MD5Final(fingerprint, &md5context);
|
|
free(filebuf, M_TEMP);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
sha1_fingerprint(struct vnode *vp, struct veriexec_inode_list *ip,
|
|
struct proc *p, u_quad_t file_size, char *fingerprint)
|
|
{
|
|
u_quad_t j;
|
|
SHA1_CTX sha1context;
|
|
char *filebuf;
|
|
size_t resid;
|
|
int count, error;
|
|
|
|
filebuf = malloc(BUF_SIZE, M_TEMP, M_WAITOK);
|
|
SHA1Init(&sha1context);
|
|
|
|
for (j = 0; j < file_size; j+= BUF_SIZE) {
|
|
if ((j + BUF_SIZE) > file_size) {
|
|
count = file_size - j;
|
|
} else
|
|
count = BUF_SIZE;
|
|
|
|
error = vn_rdwr(UIO_READ, vp, filebuf, count, j,
|
|
UIO_SYSSPACE, 0, p->p_ucred, &resid, p);
|
|
|
|
if (error) {
|
|
free(filebuf, M_TEMP);
|
|
return error;
|
|
}
|
|
|
|
SHA1Update(&sha1context, filebuf, (unsigned int) count);
|
|
|
|
}
|
|
|
|
SHA1Final(fingerprint, &sha1context);
|
|
free(filebuf, M_TEMP);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* evaluate_fingerprint:
|
|
* Check the fingerprint type for the given file and evaluate the
|
|
* fingerprint for that file. It is assumed that fingerprint has sufficient
|
|
* storage to hold the resulting fingerprint string.
|
|
*
|
|
*/
|
|
int
|
|
evaluate_fingerprint(struct vnode *vp, struct veriexec_inode_list *ip,
|
|
struct proc *p, u_quad_t file_size, char *fingerprint)
|
|
{
|
|
int error;
|
|
|
|
switch (ip->fp_type) {
|
|
case FINGERPRINT_TYPE_MD5:
|
|
error = md5_fingerprint(vp, ip, p, file_size, fingerprint);
|
|
break;
|
|
|
|
case FINGERPRINT_TYPE_SHA1:
|
|
error = sha1_fingerprint(vp, ip, p, file_size, fingerprint);
|
|
break;
|
|
|
|
default:
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* fingerprintcmp:
|
|
* Compare the two given fingerprints to see if they are the same
|
|
* Differing fingerprint methods may have differing lengths which
|
|
* is handled by this routine. This function follows the convention
|
|
* of other cmp functions and returns 0 if the fingerprints match and
|
|
* 1 if they don't.
|
|
*/
|
|
int
|
|
fingerprintcmp(struct veriexec_inode_list *ip, unsigned char *digest)
|
|
{
|
|
switch(ip->fp_type) {
|
|
case FINGERPRINT_TYPE_MD5:
|
|
return memcmp(ip->fingerprint, digest, MD5_FINGERPRINTLEN);
|
|
break;
|
|
|
|
case FINGERPRINT_TYPE_SHA1:
|
|
return memcmp(ip->fingerprint, digest, SHA1_FINGERPRINTLEN);
|
|
break;
|
|
|
|
default:
|
|
/* unknown fingerprint type, just fail it after whining */
|
|
printf("fingerprintcmp: unknown fingerprint type\n");
|
|
return 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* get_veriexec_inode:
|
|
* Search the given verified exec fingerprint list for the given
|
|
* fileid. If it exists then return a pointer to the list entry,
|
|
* otherwise return NULL. If found_dev is non-NULL set this to true
|
|
* iff the given device has a fingerprint list.
|
|
*
|
|
*/
|
|
struct veriexec_inode_list *
|
|
get_veriexec_inode(struct veriexec_devhead *head, long fsid, long fileid,
|
|
char *found_dev)
|
|
{
|
|
struct veriexec_dev_list *lp;
|
|
struct veriexec_inode_list *ip;
|
|
|
|
ip = NULL;
|
|
if (found_dev != NULL)
|
|
*found_dev = 0;
|
|
|
|
#ifdef VERIFIED_EXEC_DEBUG
|
|
printf("searching for file %lu on device %lu\n", fileid, fsid);
|
|
#endif
|
|
|
|
for (lp = LIST_FIRST(head); lp != NULL; lp = LIST_NEXT(lp, entries))
|
|
if (lp->id == fsid)
|
|
break;
|
|
|
|
if (lp != NULL) {
|
|
#ifdef VERIFIED_EXEC_DEBUG
|
|
printf("found matching dev number %lu\n", lp->id);
|
|
#endif
|
|
if (found_dev != NULL)
|
|
*found_dev = 1;
|
|
|
|
for (ip = LIST_FIRST(&(lp->inode_head)); ip != NULL;
|
|
ip = LIST_NEXT(ip, entries))
|
|
if (ip->inode == fileid)
|
|
break;
|
|
}
|
|
|
|
return ip;
|
|
}
|
|
|
|
/*
|
|
* check veriexec:
|
|
* check a file signature and return a status to check_exec.
|
|
*/
|
|
int
|
|
check_veriexec(struct proc *p, struct vnode *vp, struct exec_package *epp,
|
|
int direct_exec)
|
|
{
|
|
int error;
|
|
char digest[MAXFINGERPRINTLEN], found_dev;
|
|
struct veriexec_inode_list *ip;
|
|
|
|
error = 0;
|
|
found_dev = 0;
|
|
if (vp->fp_status == FINGERPRINT_INVALID) {
|
|
|
|
#ifdef VERIFIED_EXEC_DEBUG
|
|
printf("looking for loaded signature\n");
|
|
#endif
|
|
ip = get_veriexec_inode(&veriexec_dev_head, epp->ep_vap->va_fsid,
|
|
epp->ep_vap->va_fileid, &found_dev);
|
|
|
|
if (found_dev == 0) {
|
|
#ifdef VERIFIED_EXEC_DEBUG
|
|
printf("No device entry found\n");
|
|
#endif
|
|
vp->fp_status = FINGERPRINT_NODEV;
|
|
}
|
|
|
|
if (ip != NULL) {
|
|
#ifdef VERIFIED_EXEC_DEBUG
|
|
printf("found matching inode number %lu\n", ip->inode);
|
|
#endif
|
|
error = evaluate_fingerprint(vp, ip, p,
|
|
epp->ep_vap->va_size,
|
|
digest);
|
|
if (error)
|
|
return error;
|
|
|
|
if (fingerprintcmp(ip, digest) == 0) {
|
|
if (ip->type == VERIEXEC_DIRECT) {
|
|
#ifdef VERIFIED_EXEC_DEBUG
|
|
printf("Evaluated fingerprint matches\n");
|
|
#endif
|
|
vp->fp_status = FINGERPRINT_VALID;
|
|
} else {
|
|
#ifdef VERIFIED_EXEC_DEBUG
|
|
printf("Evaluated indirect fingerprint matches\n");
|
|
#endif
|
|
vp->fp_status = FINGERPRINT_INDIRECT;
|
|
}
|
|
} else {
|
|
#ifdef VERIFIED_EXEC_DEBUG
|
|
printf("Evaluated fingerprint match failed\n");
|
|
#endif
|
|
vp->fp_status = FINGERPRINT_NOMATCH;
|
|
}
|
|
} else {
|
|
#ifdef VERIFIED_EXEC_DEBUG
|
|
printf("No fingerprint entry found\n");
|
|
#endif
|
|
vp->fp_status = FINGERPRINT_NOENTRY;
|
|
}
|
|
}
|
|
|
|
switch (vp->fp_status) {
|
|
case FINGERPRINT_INVALID: /* should not happen */
|
|
printf("Got unexpected FINGERPRINT_INVALID!!!\n");
|
|
error = EPERM;
|
|
break;
|
|
case FINGERPRINT_VALID: /* is ok - report so if debug is on */
|
|
#ifdef VERIFIED_EXEC_DEBUG
|
|
printf("Fingerprint matches\n");
|
|
#endif
|
|
break;
|
|
|
|
case FINGERPRINT_INDIRECT: /* fingerprint ok but need to check
|
|
for direct execution */
|
|
if (direct_exec == 1) {
|
|
printf("Attempt to execute %s (dev %lu, inode %lu) "
|
|
"directly by pid %u (ppid %u, gppid %u)\n",
|
|
epp->ep_name, epp->ep_vap->va_fsid,
|
|
epp->ep_vap->va_fileid, p->p_pid,
|
|
p->p_pptr->p_pid, p->p_pptr->p_pptr->p_pid);
|
|
if (securelevel > 1)
|
|
error = EPERM;
|
|
}
|
|
break;
|
|
|
|
case FINGERPRINT_NOMATCH: /* does not match - whine about it */
|
|
printf("Fingerprint for %s (dev %lu, inode %lu) does not "
|
|
"match loaded value\n",
|
|
epp->ep_name, epp->ep_vap->va_fsid,
|
|
epp->ep_vap->va_fileid);
|
|
if (securelevel > 1)
|
|
error = EPERM;
|
|
break;
|
|
|
|
case FINGERPRINT_NOENTRY: /* no entry in the list, complain */
|
|
printf("No fingerprint for %s (dev %lu, inode %lu)\n",
|
|
epp->ep_name, epp->ep_vap->va_fsid,
|
|
epp->ep_vap->va_fileid);
|
|
if (securelevel > 1)
|
|
error = EPERM;
|
|
break;
|
|
|
|
case FINGERPRINT_NODEV: /* no signatures for the device, complain */
|
|
#ifdef VERIFIED_EXEC_DEBUG
|
|
printf("No signatures for device %lu\n",
|
|
epp->ep_vap->va_fsid);
|
|
#endif
|
|
if (securelevel > 1)
|
|
error = EPERM;
|
|
break;
|
|
|
|
default: /* this should never happen. */
|
|
printf("Invalid fp_status field for vnode %p\n", vp);
|
|
error = EPERM;
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|