mirror of
https://git.hardenedbsd.org/hardenedbsd/HardenedBSD.git
synced 2024-11-24 17:44:17 +01:00
Add mac_grantbylabel
This module allows controlled privilege escallation via mac labels securely associated with a process via mac_veriexec. There are over 700 PRIV_* but we can compress many of them into a single GBL_* thus constraining the size of gbl labels. The goal is to allow a daemon to run as an unprivileged process while still being able a set of privileged operations needed. We add APIs to libveriexec so that userland processes can check labels and an exec_script API that allows a suitably labeled process to run something like a python interpreter directly if necessary; overcomming the 'indirect' flag applied to the interpreter. Add -l option to sbin/veriexec to report labels. Reviewed by: stevek Sponsored by: Juniper Networks, Inc. Differential Revision: https://reviews.freebsd.org/D41431
This commit is contained in:
parent
52c1066f52
commit
1554ba03b6
@ -333,6 +333,8 @@
|
|||||||
..
|
..
|
||||||
mac_bsdextended
|
mac_bsdextended
|
||||||
..
|
..
|
||||||
|
mac_grantbylabel
|
||||||
|
..
|
||||||
mac_lomac
|
mac_lomac
|
||||||
..
|
..
|
||||||
mac_mls
|
mac_mls
|
||||||
|
@ -67,7 +67,9 @@ LSUBDIRS= dev/acpica dev/agp dev/ciss dev/filemon dev/firewire \
|
|||||||
netinet/netdump \
|
netinet/netdump \
|
||||||
netinet/tcp_stacks \
|
netinet/tcp_stacks \
|
||||||
netlink/route \
|
netlink/route \
|
||||||
security/mac_biba security/mac_bsdextended security/mac_lomac \
|
security/mac_biba security/mac_bsdextended \
|
||||||
|
security/mac_grantbylabel \
|
||||||
|
security/mac_lomac \
|
||||||
security/mac_mls security/mac_partition \
|
security/mac_mls security/mac_partition \
|
||||||
security/mac_veriexec \
|
security/mac_veriexec \
|
||||||
sys/disk \
|
sys/disk \
|
||||||
|
@ -8,8 +8,10 @@ INCS= libveriexec.h
|
|||||||
WARNS?= 2
|
WARNS?= 2
|
||||||
|
|
||||||
SRCS= \
|
SRCS= \
|
||||||
|
exec_script.c \
|
||||||
|
gbl_check.c \
|
||||||
veriexec_check.c \
|
veriexec_check.c \
|
||||||
veriexec_get.c
|
veriexec_get.c \
|
||||||
|
|
||||||
.include <bsd.lib.mk>
|
.include <bsd.lib.mk>
|
||||||
|
|
||||||
|
159
lib/libveriexec/exec_script.c
Normal file
159
lib/libveriexec/exec_script.c
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019-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 <sys/types.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/errno.h>
|
||||||
|
#include <sys/mac.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
|
||||||
|
#include <security/mac_grantbylabel/mac_grantbylabel.h>
|
||||||
|
|
||||||
|
#include "libveriexec.h"
|
||||||
|
|
||||||
|
static char *
|
||||||
|
find_interpreter(const char *script)
|
||||||
|
{
|
||||||
|
static const char ws[] = " \t\n\r";
|
||||||
|
static char buf[MAXPATHLEN+4]; /* allow space for #! etc */
|
||||||
|
char *cp;
|
||||||
|
int fd;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
cp = NULL;
|
||||||
|
if ((fd = open(script, O_RDONLY)) >= 0) {
|
||||||
|
if ((n = read(fd, buf, sizeof(buf))) > 0) {
|
||||||
|
if (strncmp(buf, "#!", 2) == 0) {
|
||||||
|
buf[sizeof(buf) - 1] = '\0';
|
||||||
|
cp = &buf[2];
|
||||||
|
if ((n = strspn(cp, ws)) > 0)
|
||||||
|
cp += n;
|
||||||
|
if ((n = strcspn(cp, ws)) > 0) {
|
||||||
|
cp[n] = '\0';
|
||||||
|
} else {
|
||||||
|
cp = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
return (cp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief exec a python or similar script
|
||||||
|
*
|
||||||
|
* Python and similar scripts must normally be signed and
|
||||||
|
* run directly rather than fed to the interpreter which
|
||||||
|
* is not normally allowed to be run directly.
|
||||||
|
*
|
||||||
|
* If direct execv of script fails due to EAUTH
|
||||||
|
* and process has GBL_VERIEXEC syslog event and run via
|
||||||
|
* interpreter.
|
||||||
|
*
|
||||||
|
* If interpreter is NULL look at first block of script
|
||||||
|
* to find ``#!`` magic.
|
||||||
|
*
|
||||||
|
* @prarm[in] interpreter
|
||||||
|
* if NULL, extract from script if necessary
|
||||||
|
*
|
||||||
|
* @prarm[in] argv
|
||||||
|
* argv for execv(2)
|
||||||
|
* argv[0] must be full path.
|
||||||
|
* Python at least requires argv[1] to also be the script path.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* error on failure usually EPERM or EAUTH
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
execv_script(const char *interpreter, char * const *argv)
|
||||||
|
{
|
||||||
|
const char *script;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
script = argv[0];
|
||||||
|
if (veriexec_check_path(script) == 0) {
|
||||||
|
rc = execv(script, argv);
|
||||||
|
}
|
||||||
|
/* still here? we might be allowed to run via interpreter */
|
||||||
|
if (gbl_check_pid(0) & GBL_VERIEXEC) {
|
||||||
|
if (!interpreter)
|
||||||
|
interpreter = find_interpreter(script);
|
||||||
|
if (interpreter) {
|
||||||
|
syslog(LOG_NOTICE, "running %s via %s",
|
||||||
|
script, interpreter);
|
||||||
|
rc = execv(interpreter, argv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(MAIN) || defined(UNIT_TEST)
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <err.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc __unused, char *argv[])
|
||||||
|
{
|
||||||
|
const char *interp;
|
||||||
|
int c;
|
||||||
|
int s;
|
||||||
|
pid_t child;
|
||||||
|
|
||||||
|
openlog("exec_script", LOG_PID|LOG_PERROR, LOG_DAEMON);
|
||||||
|
|
||||||
|
interp = NULL;
|
||||||
|
while ((c = getopt(argc, argv, "i:")) != -1) {
|
||||||
|
switch (c) {
|
||||||
|
case 'i':
|
||||||
|
interp = optarg;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
errx(1, "unknown option: -%c", c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argc -= optind;
|
||||||
|
argv += optind;
|
||||||
|
/* we need a child */
|
||||||
|
child = fork();
|
||||||
|
if (child < 0)
|
||||||
|
err(2, "fork");
|
||||||
|
if (child == 0) {
|
||||||
|
c = execv_script(interp, argv);
|
||||||
|
err(2, "exec_script(%s,%s)", interp, argv[0]);
|
||||||
|
}
|
||||||
|
c = waitpid(child, &s, 0);
|
||||||
|
printf("%s: exit %d\n", argv[0], WEXITSTATUS(s));
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
#endif
|
125
lib/libveriexec/gbl_check.c
Normal file
125
lib/libveriexec/gbl_check.c
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019-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 <sys/types.h>
|
||||||
|
#include <sys/errno.h>
|
||||||
|
#include <sys/mac.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include <security/mac_grantbylabel/mac_grantbylabel.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief does path have a gbl label
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* @li 0 if no/empty label or module not loaded
|
||||||
|
* @li value of label
|
||||||
|
*/
|
||||||
|
unsigned int
|
||||||
|
gbl_check_path(const char *path)
|
||||||
|
{
|
||||||
|
struct mac_grantbylabel_fetch_gbl_args gbl;
|
||||||
|
int fd;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = 0;
|
||||||
|
if ((fd = open(path, O_RDONLY|O_VERIFY)) >= 0) {
|
||||||
|
gbl.u.fd = fd;
|
||||||
|
if (mac_syscall(MAC_GRANTBYLABEL_NAME,
|
||||||
|
MAC_GRANTBYLABEL_FETCH_GBL,
|
||||||
|
&gbl) == 0) {
|
||||||
|
if (gbl.gbl != GBL_EMPTY)
|
||||||
|
rc = gbl.gbl;
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
return(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief does pid have a gbl label
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* @li 0 if no/empty label or module not loaded
|
||||||
|
* @li value of label
|
||||||
|
*/
|
||||||
|
unsigned int
|
||||||
|
gbl_check_pid(pid_t pid)
|
||||||
|
{
|
||||||
|
struct mac_grantbylabel_fetch_gbl_args gbl;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = 0;
|
||||||
|
gbl.u.pid = pid;
|
||||||
|
if (mac_syscall(MAC_GRANTBYLABEL_NAME,
|
||||||
|
MAC_GRANTBYLABEL_FETCH_PID_GBL, &gbl) == 0) {
|
||||||
|
if (gbl.gbl != GBL_EMPTY)
|
||||||
|
rc = gbl.gbl;
|
||||||
|
}
|
||||||
|
return(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef UNIT_TEST
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <err.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
pid_t pid;
|
||||||
|
int pflag = 0;
|
||||||
|
int c;
|
||||||
|
unsigned int gbl;
|
||||||
|
|
||||||
|
while ((c = getopt(argc, argv, "p")) != -1) {
|
||||||
|
switch (c) {
|
||||||
|
case 'p':
|
||||||
|
pflag = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (; optind < argc; optind++) {
|
||||||
|
|
||||||
|
if (pflag) {
|
||||||
|
pid = atoi(argv[optind]);
|
||||||
|
gbl = gbl_check_pid(pid);
|
||||||
|
} else {
|
||||||
|
gbl = gbl_check_path(argv[optind]);
|
||||||
|
}
|
||||||
|
printf("arg=%s, gbl=%#o\n", argv[optind], gbl);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
@ -1,7 +1,7 @@
|
|||||||
/*-
|
/*-
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*
|
*
|
||||||
* Copyright (c) 2011, 2012, 2013, 2015, Juniper Networks, Inc.
|
* Copyright (c) 2011-2023, Juniper Networks, Inc.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@ -40,7 +40,16 @@ int veriexec_get_path_params(const char *,
|
|||||||
struct mac_veriexec_syscall_params *);
|
struct mac_veriexec_syscall_params *);
|
||||||
int veriexec_check_path_label(const char *, const char *);
|
int veriexec_check_path_label(const char *, const char *);
|
||||||
int veriexec_check_pid_label(pid_t, const char *);
|
int veriexec_check_pid_label(pid_t, const char *);
|
||||||
|
char * veriexec_get_path_label(const char *, char *, size_t);
|
||||||
|
char * veriexec_get_pid_label(pid_t, char *, size_t);
|
||||||
|
unsigned int gbl_check_path(const char *);
|
||||||
|
unsigned int gbl_check_pid(pid_t);
|
||||||
|
int execv_script(const char *, char * const *);
|
||||||
|
|
||||||
#define HAVE_VERIEXEC_CHECK_PID_LABEL 1
|
#define HAVE_GBL_CHECK_PID 1
|
||||||
|
#define HAVE_VERIEXEC_CHECK_PATH_LABEL 1
|
||||||
|
#define HAVE_VERIEXEC_CHECK_PID_LABEL 1
|
||||||
|
#define HAVE_VERIEXEC_GET_PATH_LABEL 1
|
||||||
|
#define HAVE_VERIEXEC_GET_PID_LABEL 1
|
||||||
|
|
||||||
#endif /* __LIBVERIEXEC_H__ */
|
#endif /* __LIBVERIEXEC_H__ */
|
||||||
|
@ -59,7 +59,7 @@ veriexec_get_pid_params(pid_t pid,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief get veriexec params for a process
|
* @brief get veriexec params for a path
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* @li 0 if successful
|
* @li 0 if successful
|
||||||
@ -79,9 +79,119 @@ veriexec_get_path_params(const char *file,
|
|||||||
MAC_VERIEXEC_GET_PARAMS_PATH_SYSCALL, &args);
|
MAC_VERIEXEC_GET_PARAMS_PATH_SYSCALL, &args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief return label associated with a path
|
||||||
|
*
|
||||||
|
* @param[in] file
|
||||||
|
* pathname of file to lookup.
|
||||||
|
*
|
||||||
|
* @prarm[in] buf
|
||||||
|
* if not NULL and big enough copy label to buf.
|
||||||
|
* otherwise return a copy of label.
|
||||||
|
*
|
||||||
|
* @param[in] bufsz
|
||||||
|
* size of buf, must be greater than found label length.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* @li NULL if no label
|
||||||
|
* @li pointer to label
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
veriexec_get_path_label(const char *file, char *buf, size_t bufsz)
|
||||||
|
{
|
||||||
|
struct mac_veriexec_syscall_params params;
|
||||||
|
char *cp;
|
||||||
|
|
||||||
|
cp = NULL;
|
||||||
|
if (veriexec_get_path_params(file, ¶ms) == 0) {
|
||||||
|
/* Does label contain a label */
|
||||||
|
if (params.labellen > 0) {
|
||||||
|
if (buf != NULL && bufsz > params.labellen) {
|
||||||
|
strlcpy(buf, params.label, bufsz);
|
||||||
|
cp = buf;
|
||||||
|
} else
|
||||||
|
cp = strdup(params.label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief return label of a process
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param[in] pid
|
||||||
|
* process id of interest.
|
||||||
|
*
|
||||||
|
* @prarm[in] buf
|
||||||
|
* if not NULL and big enough copy label to buf.
|
||||||
|
* otherwise return a copy of label.
|
||||||
|
*
|
||||||
|
* @param[in] bufsz
|
||||||
|
* size of buf, must be greater than found label length.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* @li NULL if no label
|
||||||
|
* @li pointer to label
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
veriexec_get_pid_label(pid_t pid, char *buf, size_t bufsz)
|
||||||
|
{
|
||||||
|
struct mac_veriexec_syscall_params params;
|
||||||
|
char *cp;
|
||||||
|
|
||||||
|
cp = NULL;
|
||||||
|
if (veriexec_get_pid_params(pid, ¶ms) == 0) {
|
||||||
|
/* Does label contain a label */
|
||||||
|
if (params.labellen > 0) {
|
||||||
|
if (buf != NULL && bufsz > params.labellen) {
|
||||||
|
strlcpy(buf, params.label, bufsz);
|
||||||
|
cp = buf;
|
||||||
|
} else
|
||||||
|
cp = strdup(params.label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* we match
|
||||||
|
* ^want$
|
||||||
|
* ^want,
|
||||||
|
* ,want,
|
||||||
|
* ,want$
|
||||||
|
*
|
||||||
|
* and if want ends with / then we match that prefix too.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
check_label_want(const char *label, size_t labellen,
|
||||||
|
const char *want, size_t wantlen)
|
||||||
|
{
|
||||||
|
char *cp;
|
||||||
|
|
||||||
|
/* Does label contain [,]<want>[,] ? */
|
||||||
|
if (labellen > 0 && wantlen > 0 &&
|
||||||
|
(cp = strstr(label, want)) != NULL) {
|
||||||
|
if (cp == label || cp[-1] == ',') {
|
||||||
|
if (cp[wantlen] == '\0' || cp[wantlen] == ',' ||
|
||||||
|
(cp[wantlen-1] == '/' && want[wantlen-1] == '/'))
|
||||||
|
return 1; /* yes */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0; /* no */
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief check if a process has label that contains what we want
|
* @brief check if a process has label that contains what we want
|
||||||
*
|
*
|
||||||
|
* @param[in] pid
|
||||||
|
* process id of interest.
|
||||||
|
*
|
||||||
|
* @param[in] want
|
||||||
|
* the label we are looking for
|
||||||
|
* if want ends with ``/`` it is assumed a prefix
|
||||||
|
* otherwise we expect it to be followed by ``,`` or end of string.
|
||||||
|
*
|
||||||
* @return
|
* @return
|
||||||
* @li 0 if no
|
* @li 0 if no
|
||||||
* @li 1 if yes
|
* @li 1 if yes
|
||||||
@ -90,20 +200,13 @@ int
|
|||||||
veriexec_check_pid_label(pid_t pid, const char *want)
|
veriexec_check_pid_label(pid_t pid, const char *want)
|
||||||
{
|
{
|
||||||
struct mac_veriexec_syscall_params params;
|
struct mac_veriexec_syscall_params params;
|
||||||
char *cp;
|
|
||||||
size_t n;
|
size_t n;
|
||||||
|
|
||||||
if (want != NULL &&
|
if (want != NULL &&
|
||||||
|
(n = strlen(want)) > 0 &&
|
||||||
veriexec_get_pid_params(pid, ¶ms) == 0) {
|
veriexec_get_pid_params(pid, ¶ms) == 0) {
|
||||||
/* Does label contain [,]<want>[,] ? */
|
return check_label_want(params.label, params.labellen,
|
||||||
if (params.labellen > 0 &&
|
want, n);
|
||||||
(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 */
|
return 0; /* no */
|
||||||
}
|
}
|
||||||
@ -111,6 +214,14 @@ veriexec_check_pid_label(pid_t pid, const char *want)
|
|||||||
/**
|
/**
|
||||||
* @brief check if a path has label that contains what we want
|
* @brief check if a path has label that contains what we want
|
||||||
*
|
*
|
||||||
|
* @param[in] path
|
||||||
|
* pathname of interest.
|
||||||
|
*
|
||||||
|
* @param[in] want
|
||||||
|
* the label we are looking for
|
||||||
|
* if want ends with ``/`` it is assumed a prefix
|
||||||
|
* otherwise we expect it to be followed by ``,`` or end of string.
|
||||||
|
*
|
||||||
* @return
|
* @return
|
||||||
* @li 0 if no
|
* @li 0 if no
|
||||||
* @li 1 if yes
|
* @li 1 if yes
|
||||||
@ -119,20 +230,13 @@ int
|
|||||||
veriexec_check_path_label(const char *file, const char *want)
|
veriexec_check_path_label(const char *file, const char *want)
|
||||||
{
|
{
|
||||||
struct mac_veriexec_syscall_params params;
|
struct mac_veriexec_syscall_params params;
|
||||||
char *cp;
|
|
||||||
size_t n;
|
size_t n;
|
||||||
|
|
||||||
if (want != NULL && file != NULL &&
|
if (want != NULL && file != NULL &&
|
||||||
|
(n = strlen(want)) > 0 &&
|
||||||
veriexec_get_path_params(file, ¶ms) == 0) {
|
veriexec_get_path_params(file, ¶ms) == 0) {
|
||||||
/* Does label contain [,]<want>[,] ? */
|
return check_label_want(params.label, params.labellen,
|
||||||
if (params.labellen > 0 &&
|
want, n);
|
||||||
(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 */
|
return 0; /* no */
|
||||||
}
|
}
|
||||||
@ -167,13 +271,19 @@ main(int argc, char *argv[])
|
|||||||
{
|
{
|
||||||
struct mac_veriexec_syscall_params params;
|
struct mac_veriexec_syscall_params params;
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
char buf[BUFSIZ];
|
||||||
|
const char *cp;
|
||||||
char *want = NULL;
|
char *want = NULL;
|
||||||
|
int lflag = 0;
|
||||||
int pflag = 0;
|
int pflag = 0;
|
||||||
int error;
|
int error;
|
||||||
int c;
|
int c;
|
||||||
|
|
||||||
while ((c = getopt(argc, argv, "pw:")) != -1) {
|
while ((c = getopt(argc, argv, "lpw:")) != -1) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
|
case 'l':
|
||||||
|
lflag = 1;
|
||||||
|
break;
|
||||||
case 'p':
|
case 'p':
|
||||||
pflag = 1;
|
pflag = 1;
|
||||||
break;
|
break;
|
||||||
@ -188,6 +298,12 @@ main(int argc, char *argv[])
|
|||||||
|
|
||||||
if (pflag) {
|
if (pflag) {
|
||||||
pid = atoi(argv[optind]);
|
pid = atoi(argv[optind]);
|
||||||
|
if (lflag) {
|
||||||
|
cp = veriexec_get_pid_label(pid, buf, sizeof(buf));
|
||||||
|
if (cp)
|
||||||
|
printf("pid=%d label='%s'\n", pid, cp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (want) {
|
if (want) {
|
||||||
error = veriexec_check_pid_label(pid, want);
|
error = veriexec_check_pid_label(pid, want);
|
||||||
printf("pid=%d want='%s': %d\n",
|
printf("pid=%d want='%s': %d\n",
|
||||||
@ -196,6 +312,20 @@ main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
error = veriexec_get_pid_params(pid, ¶ms);
|
error = veriexec_get_pid_params(pid, ¶ms);
|
||||||
} else {
|
} else {
|
||||||
|
if (lflag) {
|
||||||
|
cp = veriexec_get_path_label(argv[optind],
|
||||||
|
buf, sizeof(buf));
|
||||||
|
if (cp)
|
||||||
|
printf("path='%s' label='%s'\n",
|
||||||
|
argv[optind], cp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (want) {
|
||||||
|
error = veriexec_check_path_label(argv[optind], want);
|
||||||
|
printf("path='%s' want='%s': %d\n",
|
||||||
|
argv[optind], want, error);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
error = veriexec_get_path_params(argv[optind], ¶ms);
|
error = veriexec_get_path_params(argv[optind], ¶ms);
|
||||||
}
|
}
|
||||||
if (error) {
|
if (error) {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
# Autogenerated - do NOT edit!
|
# Autogenerated - do NOT edit!
|
||||||
|
|
||||||
DIRDEPS = \
|
DIRDEPS = \
|
||||||
gnu/lib/csu \
|
|
||||||
include \
|
include \
|
||||||
include/xlocale \
|
include/xlocale \
|
||||||
lib/${CSU_DIR} \
|
lib/${CSU_DIR} \
|
||||||
@ -10,6 +9,7 @@ DIRDEPS = \
|
|||||||
lib/libcompiler_rt \
|
lib/libcompiler_rt \
|
||||||
lib/libsecureboot \
|
lib/libsecureboot \
|
||||||
lib/libveriexec \
|
lib/libveriexec \
|
||||||
|
usr.bin/yacc.host \
|
||||||
|
|
||||||
|
|
||||||
.include <dirdeps.mk>
|
.include <dirdeps.mk>
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
.\"-
|
.\"-
|
||||||
.\" Copyright (c) 2018, Juniper Networks, Inc.
|
.\" SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
.\"
|
||||||
|
.\" Copyright (c) 2018-2023, Juniper Networks, Inc.
|
||||||
.\"
|
.\"
|
||||||
.\" Redistribution and use in source and binary forms, with or without
|
.\" Redistribution and use in source and binary forms, with or without
|
||||||
.\" modification, are permitted provided that the following conditions
|
.\" modification, are permitted provided that the following conditions
|
||||||
@ -22,7 +24,7 @@
|
|||||||
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
.\" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
.\" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
.\"
|
.\"
|
||||||
.Dd July 8, 2022
|
.Dd August 8, 2023
|
||||||
.Dt VERIEXEC 8
|
.Dt VERIEXEC 8
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@ -39,6 +41,9 @@
|
|||||||
.Nm
|
.Nm
|
||||||
.Fl i Ar state
|
.Fl i Ar state
|
||||||
.Nm
|
.Nm
|
||||||
|
.Fl l
|
||||||
|
.Ar file ...
|
||||||
|
.Nm
|
||||||
.Fl x
|
.Fl x
|
||||||
.Ar file ...
|
.Ar file ...
|
||||||
.Sh DESCRIPTION
|
.Sh DESCRIPTION
|
||||||
@ -67,6 +72,14 @@ and with
|
|||||||
to query the current
|
to query the current
|
||||||
.Ar state .
|
.Ar state .
|
||||||
.Pp
|
.Pp
|
||||||
|
With
|
||||||
|
.Fl l
|
||||||
|
.Nm
|
||||||
|
will report any labels associated with the remaining arguments
|
||||||
|
assumed to be files.
|
||||||
|
If only a single file argument is given, the bare label (if any)
|
||||||
|
will be reported, otherwise the pathname followed by label.
|
||||||
|
.Pp
|
||||||
The final form with
|
The final form with
|
||||||
.Fl x
|
.Fl x
|
||||||
is used to test whether
|
is used to test whether
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*-
|
/*-
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*
|
*
|
||||||
* Copyright (c) 2018, Juniper Networks, Inc.
|
* Copyright (c) 2018-2023, Juniper Networks, Inc.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions
|
* modification, are permitted provided that the following conditions
|
||||||
@ -53,7 +53,7 @@ static int
|
|||||||
veriexec_usage(void)
|
veriexec_usage(void)
|
||||||
{
|
{
|
||||||
printf("%s",
|
printf("%s",
|
||||||
"Usage:\tveriexec [-h] [-i state] [-C] [-xv state|verbosity] [path]\n");
|
"Usage:\tveriexec [-C path] [-hlxv] [-[iz] state] [path]\n");
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
@ -135,6 +135,45 @@ veriexec_state_modify(const char *arg_text)
|
|||||||
return (state);
|
return (state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_VERIEXEC_GET_PATH_LABEL
|
||||||
|
static void
|
||||||
|
veriexec_check_labels(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
char buf[BUFSIZ];
|
||||||
|
char *cp;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
n = (argc - optind);
|
||||||
|
for (; optind < argc; optind++) {
|
||||||
|
cp = veriexec_get_path_label(argv[optind], buf, sizeof(buf));
|
||||||
|
if (cp) {
|
||||||
|
if (n > 1)
|
||||||
|
printf("%s: %s\n", argv[optind], cp);
|
||||||
|
else
|
||||||
|
printf("%s\n", cp);
|
||||||
|
if (cp != buf)
|
||||||
|
free(cp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exit(EX_OK);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void
|
||||||
|
veriexec_check_paths(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int x;
|
||||||
|
|
||||||
|
x = EX_OK;
|
||||||
|
for (; optind < argc; optind++) {
|
||||||
|
if (veriexec_check_path(argv[optind])) {
|
||||||
|
warn("%s", argv[optind]);
|
||||||
|
x = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exit(x);
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, char *argv[])
|
main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
@ -147,7 +186,7 @@ main(int argc, char *argv[])
|
|||||||
|
|
||||||
dev_fd = open(_PATH_DEV_VERIEXEC, O_WRONLY, 0);
|
dev_fd = open(_PATH_DEV_VERIEXEC, O_WRONLY, 0);
|
||||||
|
|
||||||
while ((c = getopt(argc, argv, "hC:i:Sxvz:")) != -1) {
|
while ((c = getopt(argc, argv, "C:hi:lSxvz:")) != -1) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'h':
|
case 'h':
|
||||||
/* Print usage info */
|
/* Print usage info */
|
||||||
@ -173,6 +212,11 @@ main(int argc, char *argv[])
|
|||||||
|
|
||||||
exit((x & state) == 0);
|
exit((x & state) == 0);
|
||||||
break;
|
break;
|
||||||
|
#ifdef HAVE_VERIEXEC_GET_PATH_LABEL
|
||||||
|
case 'l':
|
||||||
|
veriexec_check_labels(argc, argv);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
case 'S':
|
case 'S':
|
||||||
/* Strictly enforce certificate validity */
|
/* Strictly enforce certificate validity */
|
||||||
ve_enforce_validity_set(1);
|
ve_enforce_validity_set(1);
|
||||||
@ -188,13 +232,7 @@ main(int argc, char *argv[])
|
|||||||
/*
|
/*
|
||||||
* -x says all other args are paths to check.
|
* -x says all other args are paths to check.
|
||||||
*/
|
*/
|
||||||
for (x = EX_OK; optind < argc; optind++) {
|
veriexec_check_paths(argc, argv);
|
||||||
if (veriexec_check_path(argv[optind])) {
|
|
||||||
warn("%s", argv[optind]);
|
|
||||||
x = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exit(x);
|
|
||||||
break;
|
break;
|
||||||
case 'z':
|
case 'z':
|
||||||
/* Modify the state */
|
/* Modify the state */
|
||||||
|
@ -5147,6 +5147,7 @@ security/mac_priority/mac_priority.c optional mac_priority
|
|||||||
security/mac_seeotheruids/mac_seeotheruids.c optional mac_seeotheruids
|
security/mac_seeotheruids/mac_seeotheruids.c optional mac_seeotheruids
|
||||||
security/mac_stub/mac_stub.c optional mac_stub
|
security/mac_stub/mac_stub.c optional mac_stub
|
||||||
security/mac_test/mac_test.c optional mac_test
|
security/mac_test/mac_test.c optional mac_test
|
||||||
|
security/mac_grantbylabel/mac_grantbylabel.c optional mac_grantbylabel
|
||||||
security/mac_veriexec/mac_veriexec.c optional mac_veriexec
|
security/mac_veriexec/mac_veriexec.c optional mac_veriexec
|
||||||
security/mac_veriexec/veriexec_fingerprint.c optional mac_veriexec
|
security/mac_veriexec/veriexec_fingerprint.c optional mac_veriexec
|
||||||
security/mac_veriexec/veriexec_metadata.c optional mac_veriexec
|
security/mac_veriexec/veriexec_metadata.c optional mac_veriexec
|
||||||
|
@ -168,6 +168,7 @@ MAC_SEEOTHERUIDS opt_dontuse.h
|
|||||||
MAC_STATIC opt_mac.h
|
MAC_STATIC opt_mac.h
|
||||||
MAC_STUB opt_dontuse.h
|
MAC_STUB opt_dontuse.h
|
||||||
MAC_TEST opt_dontuse.h
|
MAC_TEST opt_dontuse.h
|
||||||
|
MAC_GRANTBYLABEL opt_dontuse.h
|
||||||
MAC_VERIEXEC opt_dontuse.h
|
MAC_VERIEXEC opt_dontuse.h
|
||||||
MAC_VERIEXEC_SHA1 opt_dontuse.h
|
MAC_VERIEXEC_SHA1 opt_dontuse.h
|
||||||
MAC_VERIEXEC_SHA256 opt_dontuse.h
|
MAC_VERIEXEC_SHA256 opt_dontuse.h
|
||||||
|
506
sys/security/mac_grantbylabel/mac_grantbylabel.c
Normal file
506
sys/security/mac_grantbylabel/mac_grantbylabel.c
Normal file
@ -0,0 +1,506 @@
|
|||||||
|
/*-
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018-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 <sys/cdefs.h>
|
||||||
|
|
||||||
|
#include "opt_mac.h"
|
||||||
|
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/capsicum.h>
|
||||||
|
#include <sys/proc.h>
|
||||||
|
#include <sys/vnode.h>
|
||||||
|
#include <sys/kernel.h>
|
||||||
|
#include <sys/module.h>
|
||||||
|
#include <sys/mac.h>
|
||||||
|
#include <sys/namei.h>
|
||||||
|
#include <sys/priv.h>
|
||||||
|
#include <sys/imgact.h>
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
#include <sys/syslog.h>
|
||||||
|
#include <security/mac/mac_policy.h>
|
||||||
|
|
||||||
|
#include "mac_grantbylabel.h"
|
||||||
|
#include <security/mac_veriexec/mac_veriexec_internal.h>
|
||||||
|
|
||||||
|
#define MAC_GRANTBYLABEL_FULLNAME "MAC/grantbylabel"
|
||||||
|
|
||||||
|
SYSCTL_DECL(_security_mac);
|
||||||
|
SYSCTL_NODE(_security_mac, OID_AUTO, grantbylabel, CTLFLAG_RW, 0,
|
||||||
|
"MAC/grantbylabel policy controls");
|
||||||
|
|
||||||
|
#ifdef MAC_DEBUG
|
||||||
|
static int mac_grantbylabel_debug;
|
||||||
|
|
||||||
|
SYSCTL_INT(_security_mac_grantbylabel, OID_AUTO, debug, CTLFLAG_RW,
|
||||||
|
&mac_grantbylabel_debug, 0, "Debug mac_grantbylabel");
|
||||||
|
|
||||||
|
#define GRANTBYLABEL_DEBUG(n, x) if (mac_grantbylabel_debug >= (n)) printf x
|
||||||
|
|
||||||
|
#define MAC_GRANTBYLABEL_DBG(_lvl, _fmt, ...) \
|
||||||
|
do { \
|
||||||
|
GRANTBYLABEL_DEBUG((_lvl), (MAC_GRANTBYLABEL_FULLNAME ": " \
|
||||||
|
_fmt "\n", ##__VA_ARGS__)); \
|
||||||
|
} while(0)
|
||||||
|
#else
|
||||||
|
#define MAC_GRANTBYLABEL_DBG(_lvl, _fmt, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* label token prefix */
|
||||||
|
#define GBL_PREFIX "gbl/"
|
||||||
|
|
||||||
|
static int mac_grantbylabel_slot;
|
||||||
|
|
||||||
|
#define SLOT(l) \
|
||||||
|
mac_label_get((l), mac_grantbylabel_slot)
|
||||||
|
#define SLOT_SET(l, v) \
|
||||||
|
mac_label_set((l), mac_grantbylabel_slot, (v))
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief parse label into bitmask
|
||||||
|
*
|
||||||
|
* We are only interested in tokens prefixed by GBL_PREFIX ("gbl/").
|
||||||
|
*
|
||||||
|
* @return 32bit mask
|
||||||
|
*/
|
||||||
|
static gbl_label_t
|
||||||
|
gbl_parse_label(const char *label)
|
||||||
|
{
|
||||||
|
gbl_label_t gbl;
|
||||||
|
char *cp;
|
||||||
|
|
||||||
|
if (!(label && *label))
|
||||||
|
return GBL_EMPTY;
|
||||||
|
gbl = 0;
|
||||||
|
for (cp = strstr(label, GBL_PREFIX); cp; cp = strstr(cp, GBL_PREFIX)) {
|
||||||
|
/* check we didn't find "fugbl/" */
|
||||||
|
if (cp > label && cp[-1] != ',') {
|
||||||
|
cp += sizeof(GBL_PREFIX);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cp += sizeof(GBL_PREFIX) - 1;
|
||||||
|
switch (*cp) {
|
||||||
|
case 'b':
|
||||||
|
if (strncmp(cp, "bind", 4) == 0)
|
||||||
|
gbl |= GBL_BIND;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
if (strncmp(cp, "daemon", 6) == 0)
|
||||||
|
gbl |= (GBL_BIND|GBL_IPC|GBL_NET|GBL_PROC|
|
||||||
|
GBL_SYSCTL|GBL_VACCESS);
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
if (strncmp(cp, "ipc", 3) == 0)
|
||||||
|
gbl |= GBL_IPC;
|
||||||
|
break;
|
||||||
|
case 'k':
|
||||||
|
if (strncmp(cp, "kmem", 4) == 0)
|
||||||
|
gbl |= GBL_KMEM;
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
if (strncmp(cp, "net", 3) == 0)
|
||||||
|
gbl |= GBL_NET;
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
if (strncmp(cp, "proc", 4) == 0)
|
||||||
|
gbl |= GBL_PROC;
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
if (strncmp(cp, "rtsock", 6) == 0)
|
||||||
|
gbl |= GBL_RTSOCK;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
if (strncmp(cp, "sysctl", 6) == 0)
|
||||||
|
gbl |= GBL_SYSCTL;
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
if (strncmp(cp, "vaccess", 7) == 0)
|
||||||
|
gbl |= GBL_VACCESS;
|
||||||
|
else if (strncmp(cp, "veriexec", 8) == 0)
|
||||||
|
gbl |= GBL_VERIEXEC;
|
||||||
|
break;
|
||||||
|
default: /* ignore unknown? */
|
||||||
|
MAC_GRANTBYLABEL_DBG(1,
|
||||||
|
"ignoring unknown token at %s/%s",
|
||||||
|
GBL_PREFIX, cp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return gbl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief get the v_label for a vnode
|
||||||
|
*
|
||||||
|
* Lookup the label if not already set in v_label
|
||||||
|
*
|
||||||
|
* @return 32bit mask or 0 on error
|
||||||
|
*/
|
||||||
|
static gbl_label_t
|
||||||
|
gbl_get_vlabel(struct vnode *vp, struct ucred *cred)
|
||||||
|
{
|
||||||
|
struct vattr va;
|
||||||
|
const char *label;
|
||||||
|
gbl_label_t gbl;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
gbl = SLOT(vp->v_label);
|
||||||
|
if (gbl == 0) {
|
||||||
|
error = VOP_GETATTR(vp, &va, cred);
|
||||||
|
if (error == 0) {
|
||||||
|
label = mac_veriexec_metadata_get_file_label(va.va_fsid,
|
||||||
|
va.va_fileid, va.va_gen, FALSE);
|
||||||
|
if (label) {
|
||||||
|
MAC_GRANTBYLABEL_DBG(1,
|
||||||
|
"label=%s dev=%ju, file %ju.%lu",
|
||||||
|
label,
|
||||||
|
(uintmax_t)va.va_fsid,
|
||||||
|
(uintmax_t)va.va_fileid,
|
||||||
|
va.va_gen);
|
||||||
|
gbl = gbl_parse_label(label);
|
||||||
|
} else {
|
||||||
|
gbl = GBL_EMPTY;
|
||||||
|
MAC_GRANTBYLABEL_DBG(2, "no label dev=%ju, file %ju.%lu",
|
||||||
|
(uintmax_t)va.va_fsid,
|
||||||
|
(uintmax_t)va.va_fileid,
|
||||||
|
va.va_gen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return gbl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief grant priv if warranted
|
||||||
|
*
|
||||||
|
* If the cred is root, we have nothing to do.
|
||||||
|
* Otherwise see if the current process has a label
|
||||||
|
* that grants it the requested priv.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
mac_grantbylabel_priv_grant(struct ucred *cred, int priv)
|
||||||
|
{
|
||||||
|
gbl_label_t label;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = EPERM; /* default response */
|
||||||
|
|
||||||
|
if ((curproc->p_flag & (P_KPROC|P_SYSTEM)))
|
||||||
|
return rc; /* not interested */
|
||||||
|
|
||||||
|
switch (priv) {
|
||||||
|
case PRIV_KMEM_READ:
|
||||||
|
case PRIV_KMEM_WRITE:
|
||||||
|
break;
|
||||||
|
case PRIV_VERIEXEC_DIRECT:
|
||||||
|
case PRIV_VERIEXEC_NOVERIFY:
|
||||||
|
/* XXX might want to skip in FIPS mode */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (cred->cr_uid == 0)
|
||||||
|
return rc; /* not interested */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
label = (gbl_label_t)(SLOT(curproc->p_textvp->v_label) |
|
||||||
|
SLOT(curproc->p_label));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We look at the extra privs granted
|
||||||
|
* via process label.
|
||||||
|
*/
|
||||||
|
switch (priv) {
|
||||||
|
case PRIV_IPC_READ:
|
||||||
|
case PRIV_IPC_WRITE:
|
||||||
|
if (label & GBL_IPC)
|
||||||
|
rc = 0;
|
||||||
|
break;
|
||||||
|
case PRIV_KMEM_READ:
|
||||||
|
case PRIV_KMEM_WRITE:
|
||||||
|
if (label & GBL_KMEM)
|
||||||
|
rc = 0;
|
||||||
|
break;
|
||||||
|
case PRIV_NETINET_BINDANY:
|
||||||
|
case PRIV_NETINET_RESERVEDPORT: /* socket bind low port */
|
||||||
|
case PRIV_NETINET_REUSEPORT:
|
||||||
|
if (label & GBL_BIND)
|
||||||
|
rc = 0;
|
||||||
|
break;
|
||||||
|
case PRIV_NETINET_ADDRCTRL6:
|
||||||
|
case PRIV_NET_LAGG:
|
||||||
|
case PRIV_NET_SETIFFIB:
|
||||||
|
case PRIV_NET_SETIFVNET:
|
||||||
|
case PRIV_NETINET_SETHDROPTS:
|
||||||
|
case PRIV_NET_VXLAN:
|
||||||
|
case PRIV_NETINET_GETCRED:
|
||||||
|
case PRIV_NETINET_IPSEC:
|
||||||
|
case PRIV_NETINET_RAW:
|
||||||
|
if (label & GBL_NET)
|
||||||
|
rc = 0;
|
||||||
|
break;
|
||||||
|
case PRIV_NETINET_MROUTE:
|
||||||
|
case PRIV_NET_ROUTE:
|
||||||
|
if (label & GBL_RTSOCK)
|
||||||
|
rc = 0;
|
||||||
|
break;
|
||||||
|
case PRIV_PROC_LIMIT:
|
||||||
|
case PRIV_PROC_SETRLIMIT:
|
||||||
|
if (label & GBL_PROC)
|
||||||
|
rc = 0;
|
||||||
|
break;
|
||||||
|
case PRIV_SYSCTL_WRITE:
|
||||||
|
if (label & GBL_SYSCTL)
|
||||||
|
rc = 0;
|
||||||
|
break;
|
||||||
|
case PRIV_VFS_READ:
|
||||||
|
case PRIV_VFS_WRITE:
|
||||||
|
if (label & GBL_KMEM)
|
||||||
|
rc = 0;
|
||||||
|
/* FALLTHROUGH */
|
||||||
|
case PRIV_VFS_ADMIN:
|
||||||
|
case PRIV_VFS_BLOCKRESERVE:
|
||||||
|
case PRIV_VFS_CHOWN:
|
||||||
|
case PRIV_VFS_EXEC: /* vaccess file and accmode & VEXEC */
|
||||||
|
case PRIV_VFS_GENERATION:
|
||||||
|
case PRIV_VFS_LOOKUP: /* vaccess DIR */
|
||||||
|
if (label & GBL_VACCESS)
|
||||||
|
rc = 0;
|
||||||
|
break;
|
||||||
|
case PRIV_VERIEXEC_DIRECT:
|
||||||
|
/*
|
||||||
|
* We are here because we are attempting to direct exec
|
||||||
|
* something with the 'indirect' flag set.
|
||||||
|
* We need to check parent label for this one.
|
||||||
|
*/
|
||||||
|
PROC_LOCK(curproc);
|
||||||
|
label = (gbl_label_t)SLOT(curproc->p_pptr->p_textvp->v_label);
|
||||||
|
if (label & GBL_VERIEXEC) {
|
||||||
|
rc = 0;
|
||||||
|
/*
|
||||||
|
* Of course the only reason to be running an
|
||||||
|
* interpreter this way is to bypass O_VERIFY
|
||||||
|
* so we can run unsigned script.
|
||||||
|
* We set GBL_VERIEXEC on p_label for
|
||||||
|
* PRIV_VERIEXEC_NOVERIFY below
|
||||||
|
*/
|
||||||
|
SLOT_SET(curproc->p_label, GBL_VERIEXEC);
|
||||||
|
}
|
||||||
|
PROC_UNLOCK(curproc);
|
||||||
|
break;
|
||||||
|
case PRIV_VERIEXEC_NOVERIFY:
|
||||||
|
/* we look at p_label! see above */
|
||||||
|
label = (gbl_label_t)SLOT(curproc->p_label);
|
||||||
|
if (label & GBL_VERIEXEC)
|
||||||
|
rc = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
MAC_GRANTBYLABEL_DBG(rc ? 1 : 2,
|
||||||
|
"pid=%d priv=%d, label=%#o rc=%d",
|
||||||
|
curproc->p_pid, priv, label, rc);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If proc->p_textvp does not yet have a label,
|
||||||
|
* fetch file info from mac_veriexec
|
||||||
|
* and set label (if any) else set.
|
||||||
|
* If there is no label set it to GBL_EMPTY.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
mac_grantbylabel_proc_check_resource(struct ucred *cred,
|
||||||
|
struct proc *proc)
|
||||||
|
{
|
||||||
|
gbl_label_t gbl;
|
||||||
|
|
||||||
|
if (!SLOT(proc->p_textvp->v_label)) {
|
||||||
|
gbl = gbl_get_vlabel(proc->p_textvp, cred);
|
||||||
|
if (gbl == 0)
|
||||||
|
gbl = GBL_EMPTY;
|
||||||
|
SLOT_SET(proc->p_textvp->v_label, gbl);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
mac_grantbylabel_syscall(struct thread *td, int call, void *arg)
|
||||||
|
{
|
||||||
|
cap_rights_t rights;
|
||||||
|
struct mac_grantbylabel_fetch_gbl_args gbl_args;
|
||||||
|
struct file *fp;
|
||||||
|
struct proc *proc;
|
||||||
|
int error;
|
||||||
|
int proc_locked;
|
||||||
|
|
||||||
|
switch (call) {
|
||||||
|
case MAC_GRANTBYLABEL_FETCH_GBL:
|
||||||
|
case MAC_GRANTBYLABEL_FETCH_PID_GBL:
|
||||||
|
error = copyin(arg, &gbl_args, sizeof(gbl_args));
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
gbl_args.gbl = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return EOPNOTSUPP;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
proc_locked = 0;
|
||||||
|
switch (call) {
|
||||||
|
case MAC_GRANTBYLABEL_FETCH_GBL:
|
||||||
|
error = getvnode(td, gbl_args.u.fd,
|
||||||
|
cap_rights_init(&rights), &fp);
|
||||||
|
if (error)
|
||||||
|
return (error);
|
||||||
|
|
||||||
|
if (fp->f_type != DTYPE_VNODE) {
|
||||||
|
error = EINVAL;
|
||||||
|
goto cleanup_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
vn_lock(fp->f_vnode, LK_SHARED | LK_RETRY);
|
||||||
|
gbl_args.gbl = gbl_get_vlabel(fp->f_vnode, td->td_ucred);
|
||||||
|
if (gbl_args.gbl == 0)
|
||||||
|
error = EOPNOTSUPP;
|
||||||
|
else
|
||||||
|
error = 0;
|
||||||
|
VOP_UNLOCK(fp->f_vnode);
|
||||||
|
cleanup_file:
|
||||||
|
fdrop(fp, td);
|
||||||
|
break;
|
||||||
|
case MAC_GRANTBYLABEL_FETCH_PID_GBL:
|
||||||
|
error = 0;
|
||||||
|
if (gbl_args.u.pid == 0
|
||||||
|
|| gbl_args.u.pid == curproc->p_pid) {
|
||||||
|
proc = curproc;
|
||||||
|
} else {
|
||||||
|
proc = pfind(gbl_args.u.pid);
|
||||||
|
if (proc == NULL)
|
||||||
|
return (EINVAL);
|
||||||
|
proc_locked = 1;
|
||||||
|
}
|
||||||
|
gbl_args.gbl = (SLOT(proc->p_textvp->v_label) |
|
||||||
|
SLOT(proc->p_label));
|
||||||
|
if (proc_locked)
|
||||||
|
PROC_UNLOCK(proc);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (error == 0) {
|
||||||
|
error = copyout(&gbl_args, arg, sizeof(gbl_args));
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
mac_grantbylabel_proc_init_label(struct label *label)
|
||||||
|
{
|
||||||
|
|
||||||
|
SLOT_SET(label, 0); /* not yet set! */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mac_grantbylabel_vnode_init_label(struct label *label)
|
||||||
|
{
|
||||||
|
|
||||||
|
SLOT_SET(label, 0); /* not yet set! */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief set v_label if needed
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
mac_grantbylabel_vnode_check_exec(struct ucred *cred __unused,
|
||||||
|
struct vnode *vp __unused, struct label *label __unused,
|
||||||
|
struct image_params *imgp, struct label *execlabel __unused)
|
||||||
|
{
|
||||||
|
gbl_label_t gbl;
|
||||||
|
|
||||||
|
gbl = SLOT(vp->v_label);
|
||||||
|
if (gbl == 0) {
|
||||||
|
gbl = gbl_get_vlabel(vp, cred);
|
||||||
|
if (gbl == 0)
|
||||||
|
gbl = GBL_EMPTY;
|
||||||
|
MAC_GRANTBYLABEL_DBG(1, "vnode_check_exec label=%#o", gbl);
|
||||||
|
SLOT_SET(vp->v_label, gbl);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mac_grantbylabel_copy_label(struct label *src, struct label *dest)
|
||||||
|
{
|
||||||
|
SLOT_SET(dest, SLOT(src));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief if interpreting copy script v_label to proc p_label
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
mac_grantbylabel_vnode_execve_will_transition(struct ucred *old,
|
||||||
|
struct vnode *vp, struct label *vplabel,
|
||||||
|
struct label *interpvplabel, struct image_params *imgp,
|
||||||
|
struct label *execlabel)
|
||||||
|
{
|
||||||
|
gbl_label_t gbl;
|
||||||
|
|
||||||
|
if (imgp->interpreted) {
|
||||||
|
gbl = SLOT(interpvplabel);
|
||||||
|
if (gbl) {
|
||||||
|
SLOT_SET(imgp->proc->p_label, gbl);
|
||||||
|
}
|
||||||
|
MAC_GRANTBYLABEL_DBG(1, "execve_will_transition label=%#o", gbl);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct mac_policy_ops mac_grantbylabel_ops =
|
||||||
|
{
|
||||||
|
.mpo_proc_check_resource = mac_grantbylabel_proc_check_resource,
|
||||||
|
.mpo_priv_grant = mac_grantbylabel_priv_grant,
|
||||||
|
.mpo_syscall = mac_grantbylabel_syscall,
|
||||||
|
.mpo_proc_init_label = mac_grantbylabel_proc_init_label,
|
||||||
|
.mpo_vnode_check_exec = mac_grantbylabel_vnode_check_exec,
|
||||||
|
.mpo_vnode_copy_label = mac_grantbylabel_copy_label,
|
||||||
|
.mpo_vnode_execve_will_transition = mac_grantbylabel_vnode_execve_will_transition,
|
||||||
|
.mpo_vnode_init_label = mac_grantbylabel_vnode_init_label,
|
||||||
|
};
|
||||||
|
|
||||||
|
MAC_POLICY_SET(&mac_grantbylabel_ops, mac_grantbylabel,
|
||||||
|
MAC_GRANTBYLABEL_FULLNAME,
|
||||||
|
MPC_LOADTIME_FLAG_NOTLATE, &mac_grantbylabel_slot);
|
||||||
|
MODULE_VERSION(mac_grantbylabel, 1);
|
||||||
|
MODULE_DEPEND(mac_grantbylabel, mac_veriexec, MAC_VERIEXEC_VERSION,
|
||||||
|
MAC_VERIEXEC_VERSION, MAC_VERIEXEC_VERSION);
|
63
sys/security/mac_grantbylabel/mac_grantbylabel.h
Normal file
63
sys/security/mac_grantbylabel/mac_grantbylabel.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018-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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SECURITY_MAC_GRANTBYLABEL_H
|
||||||
|
#define _SECURITY_MAC_GRANTBYLABEL_H
|
||||||
|
|
||||||
|
#include <security/mac_veriexec/mac_veriexec.h>
|
||||||
|
|
||||||
|
#define MAC_GRANTBYLABEL_NAME "mac_grantbylabel"
|
||||||
|
|
||||||
|
/* the bits we use to represent tokens */
|
||||||
|
#define GBL_EMPTY (1<<0)
|
||||||
|
#define GBL_BIND (1<<1)
|
||||||
|
#define GBL_IPC (1<<2)
|
||||||
|
#define GBL_NET (1<<3)
|
||||||
|
#define GBL_PROC (1<<4)
|
||||||
|
#define GBL_RTSOCK (1<<5)
|
||||||
|
#define GBL_SYSCTL (1<<6)
|
||||||
|
#define GBL_VACCESS (1<<7)
|
||||||
|
#define GBL_VERIEXEC (1<<8)
|
||||||
|
#define GBL_KMEM (1<<9)
|
||||||
|
#define GBL_MAX 9
|
||||||
|
|
||||||
|
/* this should suffice for now */
|
||||||
|
typedef uint32_t gbl_label_t;
|
||||||
|
|
||||||
|
#define MAC_GRANTBYLABEL_FETCH_GBL 1
|
||||||
|
#define MAC_GRANTBYLABEL_FETCH_PID_GBL 2
|
||||||
|
|
||||||
|
struct mac_grantbylabel_fetch_gbl_args {
|
||||||
|
union {
|
||||||
|
int fd;
|
||||||
|
pid_t pid;
|
||||||
|
} u;
|
||||||
|
gbl_label_t gbl;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user