diff --git a/lib/libveriexec/Makefile b/lib/libveriexec/Makefile index 2c68faf3356f..84e2b8329967 100644 --- a/lib/libveriexec/Makefile +++ b/lib/libveriexec/Makefile @@ -8,7 +8,9 @@ INCS= libveriexec.h WARNS?= 2 -SRCS= veriexec_check.c +SRCS= \ + veriexec_check.c \ + veriexec_get.c .include diff --git a/lib/libveriexec/libveriexec.h b/lib/libveriexec/libveriexec.h index 42d2c964a174..d186db0ab8d9 100644 --- a/lib/libveriexec/libveriexec.h +++ b/lib/libveriexec/libveriexec.h @@ -29,9 +29,17 @@ #ifndef __LIBVERIEXEC_H__ #define __LIBVERIEXEC_H__ +struct mac_veriexec_syscall_params; + int veriexec_check_fd_mode(int, unsigned int); int veriexec_check_path_mode(const char *, unsigned int); int veriexec_check_fd(int); int veriexec_check_path(const char *); +int veriexec_get_pid_params(pid_t, struct mac_veriexec_syscall_params *); +int veriexec_get_path_params(const char *, + struct mac_veriexec_syscall_params *); +int veriexec_check_pid_label(pid_t, const char *); + +#define HAVE_VERIEXEC_CHECK_PID_LABEL 1 #endif /* __LIBVERIEXEC_H__ */ diff --git a/lib/libveriexec/veriexec_get.c b/lib/libveriexec/veriexec_get.c new file mode 100644 index 000000000000..46df6eecf76e --- /dev/null +++ b/lib/libveriexec/veriexec_get.c @@ -0,0 +1,184 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2021-2023, Juniper Networks, Inc. + * All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 +__FBSDID("$FreeBSD$"); +#include +#include +#include + +#include +#include + +#include + +/** + * @brief get veriexec params for a process + * + * @return + * @li 0 if successful + */ +int +veriexec_get_pid_params(pid_t pid, + struct mac_veriexec_syscall_params *params) +{ + struct mac_veriexec_syscall_params_args args; + + if (params == NULL) + return EINVAL; + + args.u.pid = pid; + args.params = params; + return mac_syscall(MAC_VERIEXEC_NAME, + MAC_VERIEXEC_GET_PARAMS_PID_SYSCALL, &args); +} + +/** + * @brief get veriexec params for a process + * + * @return + * @li 0 if successful + */ +int +veriexec_get_path_params(const char *file, + struct mac_veriexec_syscall_params *params) +{ + struct mac_veriexec_syscall_params_args args; + + if (file == NULL || params == NULL) + return EINVAL; + + args.u.filename = file; + args.params = params; + return mac_syscall(MAC_VERIEXEC_NAME, + MAC_VERIEXEC_GET_PARAMS_PATH_SYSCALL, &args); +} + +/** + * @brief check if label contains what we want + * + * @return + * @li 0 if no + * @li 1 if yes + */ +int +veriexec_check_pid_label(pid_t pid, const char *want) +{ + struct mac_veriexec_syscall_params params; + char *cp; + size_t n; + + if (want != NULL && + veriexec_get_pid_params(pid, ¶ms) == 0) { + /* Does label contain [,][,] ? */ + if (params.labellen > 0 && + (cp = strstr(params.label, want)) != NULL) { + if (cp == params.label || cp[-1] == ',') { + n = strlen(want); + if (cp[n] == '\0' || cp[n] == ',') + return 1; /* yes */ + } + } + } + return 0; /* no */ +} + +#ifdef UNIT_TEST +#include +#include +#include + +static char * +hash2hex(char *type, unsigned char *digest) +{ + static char buf[2*MAXFINGERPRINTLEN+1]; + size_t n; + int i; + + if (strcmp(type, "SHA1") == 0) { + n = 20; + } else if (strcmp(type, "SHA256") == 0) { + n = 32; + } else if (strcmp(type, "SHA384") == 0) { + n = 48; + } + for (i = 0; i < n; i++) { + sprintf(&buf[2*i], "%02x", (unsigned)digest[i]); + } + return buf; +} + +int +main(int argc, char *argv[]) +{ + struct mac_veriexec_syscall_params params; + pid_t pid; + char *want = NULL; + int pflag = 0; + int error; + int c; + + while ((c = getopt(argc, argv, "pw:")) != -1) { + switch (c) { + case 'p': + pflag = 1; + break; + case 'w': + want = optarg; + break; + default: + break; + } + } + for (; optind < argc; optind++) { + + if (pflag) { + pid = atoi(argv[optind]); + if (want) { + error = veriexec_check_pid_label(pid, want); + printf("pid=%d want='%s': %d\n", + pid, want, error); + continue; + } + error = veriexec_get_pid_params(pid, ¶ms); + } else { + error = veriexec_get_path_params(argv[optind], ¶ms); + } + if (error) { + err(2, "%s, error=%d", argv[optind], error); + } + + printf("arg=%s, type=%s, flags=%u, label='%s', fingerprint='%s'\n", + argv[optind], params.fp_type, (unsigned)params.flags, + params.label, + hash2hex(params.fp_type, params.fingerprint)); + } + return 0; +} +#endif diff --git a/sys/dev/veriexec/veriexec_ioctl.h b/sys/dev/veriexec/veriexec_ioctl.h index 2fcccbb3c175..1409ebb9f40f 100644 --- a/sys/dev/veriexec/veriexec_ioctl.h +++ b/sys/dev/veriexec/veriexec_ioctl.h @@ -1,7 +1,7 @@ /* * $FreeBSD$ * - * Copyright (c) 2011-2013, 2015, 2019, Juniper Networks, Inc. + * Copyright (c) 2011-2023, Juniper Networks, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -34,11 +34,8 @@ #ifndef _DEV_VERIEXEC_VERIEXEC_IOCTL_H #define _DEV_VERIEXEC_VERIEXEC_IOCTL_H -#include #include -#define VERIEXEC_FPTYPELEN 16 - struct verified_exec_params { unsigned char flags; char fp_type[VERIEXEC_FPTYPELEN]; /* type of fingerprint */ diff --git a/sys/dev/veriexec/verified_exec.c b/sys/dev/veriexec/verified_exec.c index 1cb3cd75dbbe..c00aa49c2f6c 100644 --- a/sys/dev/veriexec/verified_exec.c +++ b/sys/dev/veriexec/verified_exec.c @@ -99,7 +99,7 @@ verifiedexecioctl(struct cdev *dev __unused, u_long cmd, caddr_t data, * * MAC/veriexec will grant kmem write privs to "trusted" processes. */ - error = priv_check(td, PRIV_KMEM_WRITE); + error = priv_check(td, PRIV_VERIEXEC_CONTROL); if (error) return (error); diff --git a/sys/security/mac_veriexec/mac_veriexec.c b/sys/security/mac_veriexec/mac_veriexec.c index 52202f87f666..bae8c2b9055c 100644 --- a/sys/security/mac_veriexec/mac_veriexec.c +++ b/sys/security/mac_veriexec/mac_veriexec.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2011, 2012, 2013, 2015, 2016, 2019 Juniper Networks, Inc. + * Copyright (c) 2011-2023 Juniper Networks, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -330,7 +330,10 @@ mac_veriexec_proc_check_debug(struct ucred *cred, struct proc *p) if (error != 0) return (0); - return ((flags & VERIEXEC_NOTRACE) ? EACCES : 0); + error = (flags & (VERIEXEC_NOTRACE|VERIEXEC_TRUSTED)) ? EACCES : 0; + MAC_VERIEXEC_DBG(4, "%s flags=%#x error=%d", __func__, flags, error); + + return (error); } /** @@ -406,6 +409,9 @@ mac_veriexec_kld_check_load(struct ucred *cred, struct vnode *vp, * - PRIV_KMEM_WRITE\n * Check if writes to /dev/mem and /dev/kmem are allowed\n * (Only trusted processes are allowed) + * - PRIV_VERIEXEC_CONTROL\n + * Check if manipulating veriexec is allowed\n + * (only trusted processes are allowed) * * @param cred credentials to use * @param priv privilege to check @@ -415,20 +421,30 @@ mac_veriexec_kld_check_load(struct ucred *cred, struct vnode *vp, static int mac_veriexec_priv_check(struct ucred *cred, int priv) { + int error; /* If we are not enforcing veriexec, nothing for us to check */ if ((mac_veriexec_state & VERIEXEC_STATE_ENFORCE) == 0) return (0); + error = 0; switch (priv) { case PRIV_KMEM_WRITE: - if (!mac_veriexec_proc_is_trusted(cred, curproc)) - return (EPERM); + case PRIV_VERIEXEC_CONTROL: + /* + * Do not allow writing to memory or manipulating veriexec, + * unless trusted + */ + if (mac_veriexec_proc_is_trusted(cred, curproc) == 0 && + mac_priv_grant(cred, priv) != 0) + error = EPERM; + MAC_VERIEXEC_DBG(4, "%s priv=%d error=%d", __func__, priv, + error); break; default: break; } - return (0); + return (error); } /** @@ -812,7 +828,24 @@ mac_veriexec_syscall(struct thread *td, int call, void *arg) cap_rights_t rights; struct vattr va; struct file *fp; - int error; + struct mac_veriexec_syscall_params_args pargs; + struct mac_veriexec_syscall_params result; + struct mac_veriexec_file_info *ip; + struct proc *proc; + struct vnode *textvp; + int error, flags, proc_locked; + + nd.ni_vp = NULL; + proc_locked = 0; + textvp = NULL; + switch (call) { + case MAC_VERIEXEC_GET_PARAMS_PID_SYSCALL: + case MAC_VERIEXEC_GET_PARAMS_PATH_SYSCALL: + error = copyin(arg, &pargs, sizeof(pargs)); + if (error) + return error; + break; + } switch (call) { case MAC_VERIEXEC_CHECK_FD_SYSCALL: @@ -863,18 +896,69 @@ mac_veriexec_syscall(struct thread *td, int call, void *arg) NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | LOCKSHARED | AUDITVNODE1, UIO_USERSPACE, arg); - error = namei(&nd); + flags = FREAD; + error = vn_open(&nd, &flags, 0, NULL); if (error != 0) break; NDFREE_PNBUF(&nd); /* Check the fingerprint status of the vnode */ error = mac_veriexec_check_vp(td->td_ucred, nd.ni_vp, VVERIFY); - vput(nd.ni_vp); + /* nd.ni_vp cleaned up below */ + break; + case MAC_VERIEXEC_GET_PARAMS_PID_SYSCALL: + if (pargs.u.pid == 0 || pargs.u.pid == curproc->p_pid) { + proc = curproc; + } else { + proc = pfind(pargs.u.pid); + if (proc == NULL) + return (EINVAL); + proc_locked = 1; + } + textvp = proc->p_textvp; + /* FALLTHROUGH */ + case MAC_VERIEXEC_GET_PARAMS_PATH_SYSCALL: + if (textvp == NULL) { + /* Look up the path to get the vnode */ + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNODE1, + UIO_USERSPACE, pargs.u.filename); + flags = FREAD; + error = vn_open(&nd, &flags, 0, NULL); + if (error != 0) + break; + + NDFREE_PNBUF(&nd); + textvp = nd.ni_vp; + } + error = VOP_GETATTR(textvp, &va, curproc->p_ucred); + if (proc_locked) + PROC_UNLOCK(proc); + if (error != 0) + break; + + error = mac_veriexec_metadata_get_file_info(va.va_fsid, + va.va_fileid, va.va_gen, NULL, &ip, FALSE); + if (error != 0) + break; + + result.flags = ip->flags; + strlcpy(result.fp_type, ip->ops->type, sizeof(result.fp_type)); + result.labellen = ip->labellen; + if (ip->labellen > 0) + strlcpy(result.label, ip->label, sizeof(result.label)); + result.label[result.labellen] = '\0'; + memcpy(result.fingerprint, ip->fingerprint, + ip->ops->digest_len); + + error = copyout(&result, pargs.params, sizeof(result)); break; default: error = EOPNOTSUPP; } + if (nd.ni_vp != NULL) { + VOP_UNLOCK(nd.ni_vp); + vn_close(nd.ni_vp, FREAD, td->td_ucred, td); + } return (error); } diff --git a/sys/security/mac_veriexec/mac_veriexec.h b/sys/security/mac_veriexec/mac_veriexec.h index db5a13bbd06c..e4d336ce4ff4 100644 --- a/sys/security/mac_veriexec/mac_veriexec.h +++ b/sys/security/mac_veriexec/mac_veriexec.h @@ -29,6 +29,8 @@ #ifndef _SECURITY_MAC_VERIEXEC_H #define _SECURITY_MAC_VERIEXEC_H +#include + #ifdef _KERNEL #include #include @@ -42,8 +44,12 @@ #define MAC_VERIEXEC_NAME "mac_veriexec" /* MAC/veriexec syscalls */ -#define MAC_VERIEXEC_CHECK_FD_SYSCALL 1 -#define MAC_VERIEXEC_CHECK_PATH_SYSCALL 2 +#define MAC_VERIEXEC_CHECK_FD_SYSCALL 1 +#define MAC_VERIEXEC_CHECK_PATH_SYSCALL 2 +#define MAC_VERIEXEC_GET_PARAMS_PID_SYSCALL 3 +#define MAC_VERIEXEC_GET_PARAMS_PATH_SYSCALL 4 + +#define VERIEXEC_FPTYPELEN 16 /* hash name */ /** * Enough room for the largest signature... @@ -68,6 +74,23 @@ match signature */ #define VERIEXEC_STATE_LOCKED (1<<3) /**< Do not allow further changes */ +/* for MAC_VERIEXEC_GET_PARAMS_*_SYSCALL */ +struct mac_veriexec_syscall_params { + char fp_type[VERIEXEC_FPTYPELEN]; + unsigned char fingerprint[MAXFINGERPRINTLEN]; + char label[MAXLABELLEN]; + size_t labellen; + unsigned char flags; +}; + +struct mac_veriexec_syscall_params_args { + union { + pid_t pid; + const char *filename; + } u; /* input only */ + struct mac_veriexec_syscall_params *params; /* result */ +}; + #ifdef _KERNEL /** * Version of the MAC/veriexec module diff --git a/sys/security/mac_veriexec/mac_veriexec_internal.h b/sys/security/mac_veriexec/mac_veriexec_internal.h index 6fc963a12393..e69f34df892e 100644 --- a/sys/security/mac_veriexec/mac_veriexec_internal.h +++ b/sys/security/mac_veriexec/mac_veriexec_internal.h @@ -78,9 +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); +int mac_veriexec_metadata_get_file_info(dev_t fsid, long fileid, + unsigned long gen, int *found_dev, + struct mac_veriexec_file_info **ipp, 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); diff --git a/sys/security/mac_veriexec/veriexec_metadata.c b/sys/security/mac_veriexec/veriexec_metadata.c index b5bfe70410d1..9e99f51e7e65 100644 --- a/sys/security/mac_veriexec/veriexec_metadata.c +++ b/sys/security/mac_veriexec/veriexec_metadata.c @@ -231,7 +231,7 @@ mac_veriexec_metadata_has_file(dev_t fsid, long fileid, unsigned long gen) { return (mac_veriexec_metadata_get_file_info(fsid, fileid, gen, NULL, - VERIEXEC_FILES_FIRST) != NULL); + NULL, VERIEXEC_FILES_FIRST) == 0); } /** @@ -438,12 +438,12 @@ mac_veriexec_metadata_get_file_flags(dev_t fsid, long fileid, unsigned long gen, int *flags, int check_files) { struct mac_veriexec_file_info *ip; - int found_dev; + int error; - ip = mac_veriexec_metadata_get_file_info(fsid, fileid, gen, &found_dev, - check_files); - if (ip == NULL) - return (ENOENT); + error = mac_veriexec_metadata_get_file_info(fsid, fileid, gen, NULL, + &ip, check_files); + if (error != 0) + return (error); *flags = ip->flags; return (0); @@ -513,9 +513,9 @@ 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 = mac_veriexec_metadata_get_file_info(vap->va_fsid, - vap->va_fileid, vap->va_gen, &found_dev, check_files); - if (ip == NULL) { + error = mac_veriexec_metadata_get_file_info(vap->va_fsid, + vap->va_fileid, vap->va_gen, &found_dev, &ip, check_files); + if (error != 0) { status = (found_dev) ? FINGERPRINT_NOENTRY : FINGERPRINT_NODEV; VERIEXEC_DEBUG(3, @@ -735,19 +735,20 @@ mac_veriexec_metadata_add_file(int file_dev, dev_t fsid, long fileid, /** * @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 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 + * @param ipp pointer to location to store the info pointer + * @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 * +int mac_veriexec_metadata_get_file_info(dev_t fsid, long fileid, unsigned long gen, - int *found_dev, int check_files) + int *found_dev, struct mac_veriexec_file_info **ipp, int check_files) { struct veriexec_devhead *search[3]; struct mac_veriexec_file_info *ip; @@ -763,14 +764,18 @@ mac_veriexec_metadata_get_file_info(dev_t fsid, long fileid, unsigned long gen, } search[2] = NULL; - VERIEXEC_DEBUG(3, ("%s: searching for dev %ju, file %lu\n", - __func__, (uintmax_t)fsid, fileid)); + VERIEXEC_DEBUG(3, ("%s: searching for dev %#jx, file %lu.%lu\n", + __func__, (uintmax_t)fsid, fileid, gen)); /* 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); + if (ipp != NULL) + *ipp = ip; + if (ip == NULL) + return (ENOENT); + return (0); } /** diff --git a/sys/sys/priv.h b/sys/sys/priv.h index 6574d8c42599..fe2de892f97a 100644 --- a/sys/sys/priv.h +++ b/sys/sys/priv.h @@ -525,11 +525,12 @@ */ #define PRIV_VERIEXEC_DIRECT 700 /* Can override 'indirect' */ #define PRIV_VERIEXEC_NOVERIFY 701 /* Can override O_VERIFY */ +#define PRIV_VERIEXEC_CONTROL 702 /* Can configure veriexec */ /* * Track end of privilege list. */ -#define _PRIV_HIGHEST 702 +#define _PRIV_HIGHEST 703 /* * Validate that a named privilege is known by the privilege system. Invalid