Add a new ioctl for the larger params struct that includes the label.

We need to make the find_veriexec_file() function available publicly, so
rename it to mac_veriexec_metadata_find_file_info() and make it non-static.

Bump the version of the veriexec device interface so user space will know
the labelized version of fingerprint loading is available.

Approved by:	sjg
Obtained from:	Juniper Networks, Inc.
Differential Revision:	https://reviews.freebsd.org/D20295
This commit is contained in:
Stephen J. Kiernan 2019-05-17 19:27:07 +00:00
parent 910013c6a8
commit 942886743b
5 changed files with 140 additions and 57 deletions

View File

@ -46,6 +46,11 @@ struct verified_exec_params {
unsigned char fingerprint[MAXFINGERPRINTLEN];
};
struct verified_exec_label_params {
struct verified_exec_params params;
char label[MAXLABELLEN];
};
#define VERIEXEC_LOAD _IOW('S', 0x1, struct verified_exec_params)
#define VERIEXEC_ACTIVE _IO('S', 0x2) /* start checking */
#define VERIEXEC_ENFORCE _IO('S', 0x3) /* fail exec */
@ -55,6 +60,7 @@ struct verified_exec_params {
#define VERIEXEC_GETSTATE _IOR('S', 0x7, int) /* get state */
#define VERIEXEC_SIGNED_LOAD _IOW('S', 0x8, struct verified_exec_params)
#define VERIEXEC_GETVERSION _IOR('S', 0x9, int) /* get version */
#define VERIEXEC_LABEL_LOAD _IOW('S', 0xa, struct verified_exec_label_params)
#define _PATH_DEV_VERIEXEC _PATH_DEV "veriexec"

View File

@ -68,6 +68,7 @@ verifiedexecioctl(struct cdev *dev __unused, u_long cmd, caddr_t data,
{
struct nameidata nid;
struct vattr vattr;
struct verified_exec_label_params *lparams;
struct verified_exec_params *params;
int error = 0;
@ -102,7 +103,12 @@ verifiedexecioctl(struct cdev *dev __unused, u_long cmd, caddr_t data,
if (error)
return (error);
params = (struct verified_exec_params *)data;
lparams = (struct verified_exec_label_params *)data;
if (cmd == VERIEXEC_LABEL_LOAD)
params = &lparams->params;
else
params = (struct verified_exec_params *)data;
switch (cmd) {
case VERIEXEC_ACTIVE:
mtx_lock(&ve_mutex);
@ -158,6 +164,7 @@ verifiedexecioctl(struct cdev *dev __unused, u_long cmd, caddr_t data,
return (EPERM); /* no updates when secure */
/* FALLTHROUGH */
case VERIEXEC_LABEL_LOAD:
case VERIEXEC_SIGNED_LOAD:
/*
* If we use a loader that will only use a
@ -176,8 +183,9 @@ verifiedexecioctl(struct cdev *dev __unused, u_long cmd, caddr_t data,
if (mac_veriexec_in_state(VERIEXEC_STATE_LOCKED))
error = EPERM;
else {
size_t labellen = 0;
int flags = FREAD;
int override = (cmd == VERIEXEC_SIGNED_LOAD);
int override = (cmd != VERIEXEC_LOAD);
/*
* Get the attributes for the file name passed
@ -221,13 +229,18 @@ verifiedexecioctl(struct cdev *dev __unused, u_long cmd, caddr_t data,
FINGERPRINT_INVALID);
VOP_UNLOCK(nid.ni_vp, 0);
(void) vn_close(nid.ni_vp, FREAD, td->td_ucred, td);
if (params->flags & VERIEXEC_LABEL)
labellen = strnlen(lparams->label,
sizeof(lparams->label) - 1) + 1;
mtx_lock(&ve_mutex);
error = mac_veriexec_metadata_add_file(
((params->flags & VERIEXEC_FILE) != 0),
vattr.va_fsid, vattr.va_fileid, vattr.va_gen,
params->fingerprint, params->flags,
params->fp_type, override);
params->fingerprint,
(params->flags & VERIEXEC_LABEL) ?
lparams->label : NULL, labellen,
params->flags, params->fp_type, override);
mac_veriexec_set_state(VERIEXEC_STATE_LOADED);
mtx_unlock(&ve_mutex);

View File

@ -1,7 +1,7 @@
/*
* $FreeBSD$
*
* Copyright (c) 2011, 2012, 2013, 2015, 2016, Juniper Networks, Inc.
* Copyright (c) 2011, 2012, 2013, 2015, 2016, 2019, Juniper Networks, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -49,6 +49,7 @@
* Enough room for the largest signature...
*/
#define MAXFINGERPRINTLEN 64 /* enough room for largest signature */
#define MAXLABELLEN 128
/*
* Types of veriexec inodes we can have
@ -57,8 +58,8 @@
#define VERIEXEC_FILE (1<<1) /* Fingerprint of a plain file */
#define VERIEXEC_NOTRACE (1<<2) /**< PTRACE not allowed */
#define VERIEXEC_TRUSTED (1<<3) /**< Safe to write /dev/mem */
/* XXX these are currently unimplemented */
#define VERIEXEC_NOFIPS (1<<4) /**< Not allowed in FIPS mode */
#define VERIEXEC_LABEL (1<<5) /**< We have a label */
#define VERIEXEC_STATE_INACTIVE 0 /**< Ignore */
#define VERIEXEC_STATE_LOADED (1<<0) /**< Sigs have been loaded */
@ -71,7 +72,7 @@
/**
* Version of the MAC/veriexec module
*/
#define MAC_VERIEXEC_VERSION 1
#define MAC_VERIEXEC_VERSION 2
/* Valid states for the fingerprint flag - if signed exec is being used */
typedef enum fingerprint_status {
@ -152,7 +153,8 @@ int mac_veriexec_fingerprint_modevent(module_t mod, int type, void *data);
*/
int mac_veriexec_metadata_add_file(int file_dev, dev_t fsid, long fileid,
unsigned long gen, unsigned char fingerprint[MAXFINGERPRINTLEN],
int flags, const char *fp_type, int override);
char *label, size_t labellen, int flags, const char *fp_type,
int override);
int mac_veriexec_metadata_has_file(dev_t fsid, long fileid,
unsigned long gen);
int mac_veriexec_proc_is_trusted(struct ucred *cred, struct proc *p);

View File

@ -1,7 +1,7 @@
/*
* $FreeBSD$
*
* Copyright (c) 2011, 2012, 2013, 2015, 2016, Juniper Networks, Inc.
* Copyright (c) 2011, 2012, 2013, 2015, 2016, 2019, Juniper Networks, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -54,6 +54,8 @@ struct mac_veriexec_file_info
unsigned long gen;
struct mac_veriexec_fpops *ops;
unsigned char fingerprint[MAXFINGERPRINTLEN];
char *label;
size_t labellen;
LIST_ENTRY(mac_veriexec_file_info) entries;
};
@ -76,6 +78,9 @@ int mac_veriexec_metadata_get_executable_flags(struct ucred *cred,
struct proc *p, int *flags, int check_files);
int mac_veriexec_metadata_get_file_flags(dev_t fsid, long fileid,
unsigned long gen, int *flags, int check_files);
struct mac_veriexec_file_info *
mac_veriexec_metadata_get_file_info(dev_t fsid, long fileid,
unsigned long gen, int *found_dev, int check_files);
void mac_veriexec_metadata_init(void);
void mac_veriexec_metadata_print_db(struct sbuf *sbp);
int mac_veriexec_metadata_unmounted(dev_t fsid, struct thread *td);

View File

@ -1,7 +1,7 @@
/*
* $FreeBSD$
*
* Copyright (c) 2011, 2012, 2013, 2015, 2016, Juniper Networks, Inc.
* Copyright (c) 2011, 2012, 2013, 2015, 2016, 2019, Juniper Networks, Inc.
* All rights reserved.
*
* Originally derived from:
@ -138,6 +138,8 @@ get_veriexec_file(struct veriexec_devhead *head, dev_t fsid, long fileid,
break;
/* we need to garbage collect */
LIST_REMOVE(ip, entries);
if (ip->label)
free(ip->label, M_VERIEXEC);
free(ip, M_VERIEXEC);
}
}
@ -150,48 +152,6 @@ get_veriexec_file(struct veriexec_devhead *head, dev_t fsid, long fileid,
return (ip);
}
/**
* @internal
* @brief Search the meta-data store for information on the specified file.
*
* @param fsid file system identifier to look for
* @param fileid file to look for
* @param gen generation of file
* @param found_dev indicator that an entry for the file system was found
* @param check_files if 1, check the files list first, otherwise check the
* exectuables list first
*
* @return A pointer to the meta-data inforation if meta-data exists for
* the specified file identifier, otherwise @c NULL
*/
static struct mac_veriexec_file_info *
find_veriexec_file(dev_t fsid, long fileid, unsigned long gen, int *found_dev,
int check_files)
{
struct veriexec_devhead *search[3];
struct mac_veriexec_file_info *ip;
int x;
/* Determine the order of the lists to search */
if (check_files) {
search[0] = &veriexec_file_dev_head;
search[1] = &veriexec_dev_head;
} else {
search[0] = &veriexec_dev_head;
search[1] = &veriexec_file_dev_head;
}
search[2] = NULL;
VERIEXEC_DEBUG(3, ("%s: searching for dev %ju, file %lu\n",
__func__, (uintmax_t)fsid, fileid));
/* Search for the specified file */
for (ip = NULL, x = 0; ip == NULL && search[x]; x++)
ip = get_veriexec_file(search[x], fsid, fileid, gen, found_dev);
return (ip);
}
/**
* @internal
* @brief Display the fingerprint for each entry in the device list
@ -270,7 +230,7 @@ int
mac_veriexec_metadata_has_file(dev_t fsid, long fileid, unsigned long gen)
{
return (find_veriexec_file(fsid, fileid, gen, NULL,
return (mac_veriexec_metadata_get_file_info(fsid, fileid, gen, NULL,
VERIEXEC_FILES_FIRST) != NULL);
}
@ -312,6 +272,8 @@ free_veriexec_dev(dev_t fsid, struct veriexec_devhead *head)
for (ip = LIST_FIRST(&(lp->file_head)); ip != NULL; ip = nip) {
nip = LIST_NEXT(ip, entries);
LIST_REMOVE(ip, entries);
if (ip->label)
free(ip->label, M_VERIEXEC);
free(ip, M_VERIEXEC);
}
@ -392,6 +354,38 @@ find_veriexec_dev(dev_t fsid, struct veriexec_devhead *head)
return (lp);
}
/**
* @internal
* @brief Allocate and initialize label record with the provided data.
*
* @param labelp Location to store the initialized label
* @param src Pointer to label string to copy
* @param srclen Length of label string to copy
*
* @return Length of resulting label
*
* @note Called with ve_mutex locked.
*/
static size_t
mac_veriexec_init_label(char **labelp, size_t labellen, char *src,
size_t srclen)
{
char *label;
label = *labelp;
if (labellen < srclen) {
mtx_unlock(&ve_mutex);
if (label != NULL)
free(label, M_VERIEXEC);
label = malloc(srclen, M_VERIEXEC, M_WAITOK);
mtx_lock(&ve_mutex);
labellen = srclen;
*labelp = label;
}
memcpy(label, src, srclen);
return labellen;
}
/**
* @brief When a device is unmounted, we want to toss the signatures recorded
* against it.
@ -446,7 +440,8 @@ mac_veriexec_metadata_get_file_flags(dev_t fsid, long fileid, unsigned long gen,
struct mac_veriexec_file_info *ip;
int found_dev;
ip = find_veriexec_file(fsid, fileid, gen, &found_dev, check_files);
ip = mac_veriexec_metadata_get_file_info(fsid, fileid, gen, &found_dev,
check_files);
if (ip == NULL)
return (ENOENT);
@ -518,8 +513,8 @@ mac_veriexec_metadata_fetch_fingerprint_status(struct vnode *vp,
status = mac_veriexec_get_fingerprint_status(vp);
if (status == FINGERPRINT_INVALID || status == FINGERPRINT_NODEV) {
found_dev = 0;
ip = find_veriexec_file(vap->va_fsid, vap->va_fileid,
vap->va_gen, &found_dev, check_files);
ip = mac_veriexec_metadata_get_file_info(vap->va_fsid,
vap->va_fileid, vap->va_gen, &found_dev, check_files);
if (ip == NULL) {
status = (found_dev) ? FINGERPRINT_NOENTRY :
FINGERPRINT_NODEV;
@ -611,7 +606,7 @@ mac_veriexec_metadata_fetch_fingerprint_status(struct vnode *vp,
int
mac_veriexec_metadata_add_file(int file_dev, dev_t fsid, long fileid,
unsigned long gen, unsigned char fingerprint[MAXFINGERPRINTLEN],
int flags, const char *fp_type, int override)
char *label, size_t labellen, int flags, const char *fp_type, int override)
{
struct mac_veriexec_fpops *fpops;
struct veriexec_dev_list *lp;
@ -619,6 +614,10 @@ mac_veriexec_metadata_add_file(int file_dev, dev_t fsid, long fileid,
struct mac_veriexec_file_info *ip;
struct mac_veriexec_file_info *np = NULL;
/* Label and labellen must be set if VERIEXEC_LABEL is set */
if ((flags & VERIEXEC_LABEL) != 0 && (label == NULL || labellen == 0))
return (EINVAL);
/* Look up the device entry */
if (file_dev)
head = &veriexec_file_dev_head;
@ -652,6 +651,15 @@ mac_veriexec_metadata_add_file(int file_dev, dev_t fsid, long fileid,
ip->ops = fpops;
memcpy(ip->fingerprint, fingerprint,
fpops->digest_len);
if (flags & VERIEXEC_LABEL) {
ip->labellen = mac_veriexec_init_label(
&ip->label, ip->labellen, label,
labellen);
} else if (ip->labellen > 0) {
free(ip->label, M_VERIEXEC);
ip->labellen = 0;
ip->label = NULL;
}
} else if ((flags & (VERIEXEC_INDIRECT|VERIEXEC_FILE)))
ip->flags |= flags;
@ -697,6 +705,13 @@ mac_veriexec_metadata_add_file(int file_dev, dev_t fsid, long fileid,
ip->fileid = fileid;
ip->gen = gen;
memcpy(ip->fingerprint, fingerprint, fpops->digest_len);
if (flags & VERIEXEC_LABEL)
ip->labellen = mac_veriexec_init_label(&ip->label,
ip->labellen, label, labellen);
else {
ip->label = NULL;
ip->labellen = 0;
}
VERIEXEC_DEBUG(3, ("add file %ju.%lu (files=%d)\n",
(uintmax_t)ip->fileid,
@ -717,6 +732,48 @@ mac_veriexec_metadata_add_file(int file_dev, dev_t fsid, long fileid,
return (0);
}
/**
* @brief Search the meta-data store for information on the specified file.
*
* @param fsid file system identifier to look for
* @param fileid file to look for
* @param gen generation of file
* @param found_dev indicator that an entry for the file system was found
* @param check_files if 1, check the files list first, otherwise check the
* exectuables list first
*
* @return A pointer to the meta-data inforation if meta-data exists for
* the specified file identifier, otherwise @c NULL
*/
struct mac_veriexec_file_info *
mac_veriexec_metadata_get_file_info(dev_t fsid, long fileid, unsigned long gen,
int *found_dev, int check_files)
{
struct veriexec_devhead *search[3];
struct mac_veriexec_file_info *ip;
int x;
/* Determine the order of the lists to search */
if (check_files) {
search[0] = &veriexec_file_dev_head;
search[1] = &veriexec_dev_head;
} else {
search[0] = &veriexec_dev_head;
search[1] = &veriexec_file_dev_head;
}
search[2] = NULL;
VERIEXEC_DEBUG(3, ("%s: searching for dev %ju, file %lu\n",
__func__, (uintmax_t)fsid, fileid));
/* Search for the specified file */
for (ip = NULL, x = 0; ip == NULL && search[x]; x++)
ip = get_veriexec_file(search[x], fsid, fileid, gen, found_dev);
return (ip);
}
/**
* @brief Intialize the meta-data store
*/