git: 1554ba03b651 - main - Add mac_grantbylabel
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 25 Aug 2023 00:43:08 UTC
The branch main has been updated by sjg: URL: https://cgit.FreeBSD.org/src/commit/?id=1554ba03b651319ab0e1cde8492ea4516afc648b commit 1554ba03b651319ab0e1cde8492ea4516afc648b Author: Simon J. Gerraty <sjg@FreeBSD.org> AuthorDate: 2023-08-25 00:41:22 +0000 Commit: Simon J. Gerraty <sjg@FreeBSD.org> CommitDate: 2023-08-25 00:42:11 +0000 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 --- etc/mtree/BSD.include.dist | 2 + include/Makefile | 4 +- lib/libveriexec/Makefile | 4 +- lib/libveriexec/exec_script.c | 159 +++++++ lib/libveriexec/gbl_check.c | 125 ++++++ lib/libveriexec/libveriexec.h | 13 +- lib/libveriexec/veriexec_get.c | 174 +++++++- sbin/veriexec/Makefile.depend | 2 +- sbin/veriexec/veriexec.8 | 17 +- sbin/veriexec/veriexec.c | 58 ++- sys/conf/files | 1 + sys/conf/options | 1 + sys/security/mac_grantbylabel/mac_grantbylabel.c | 506 +++++++++++++++++++++++ sys/security/mac_grantbylabel/mac_grantbylabel.h | 63 +++ 14 files changed, 1090 insertions(+), 39 deletions(-) diff --git a/etc/mtree/BSD.include.dist b/etc/mtree/BSD.include.dist index dffce2469f33..a243f085a1be 100644 --- a/etc/mtree/BSD.include.dist +++ b/etc/mtree/BSD.include.dist @@ -333,6 +333,8 @@ .. mac_bsdextended .. + mac_grantbylabel + .. mac_lomac .. mac_mls diff --git a/include/Makefile b/include/Makefile index f3c9230f6d40..1d7651216cf9 100644 --- a/include/Makefile +++ b/include/Makefile @@ -67,7 +67,9 @@ LSUBDIRS= dev/acpica dev/agp dev/ciss dev/filemon dev/firewire \ netinet/netdump \ netinet/tcp_stacks \ 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_veriexec \ sys/disk \ diff --git a/lib/libveriexec/Makefile b/lib/libveriexec/Makefile index aa1a60640f0c..154605142074 100644 --- a/lib/libveriexec/Makefile +++ b/lib/libveriexec/Makefile @@ -8,8 +8,10 @@ INCS= libveriexec.h WARNS?= 2 SRCS= \ + exec_script.c \ + gbl_check.c \ veriexec_check.c \ - veriexec_get.c + veriexec_get.c \ .include <bsd.lib.mk> diff --git a/lib/libveriexec/exec_script.c b/lib/libveriexec/exec_script.c new file mode 100644 index 000000000000..cdbbbab54a03 --- /dev/null +++ b/lib/libveriexec/exec_script.c @@ -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 diff --git a/lib/libveriexec/gbl_check.c b/lib/libveriexec/gbl_check.c new file mode 100644 index 000000000000..c0da93d0857c --- /dev/null +++ b/lib/libveriexec/gbl_check.c @@ -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 diff --git a/lib/libveriexec/libveriexec.h b/lib/libveriexec/libveriexec.h index 2d726e76af01..32b2f20d6123 100644 --- a/lib/libveriexec/libveriexec.h +++ b/lib/libveriexec/libveriexec.h @@ -1,7 +1,7 @@ /*- * 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. * * 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 *); int veriexec_check_path_label(const char *, 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__ */ diff --git a/lib/libveriexec/veriexec_get.c b/lib/libveriexec/veriexec_get.c index 8d12d0a9890c..4cfa81de210c 100644 --- a/lib/libveriexec/veriexec_get.c +++ b/lib/libveriexec/veriexec_get.c @@ -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 * @li 0 if successful @@ -79,9 +79,119 @@ veriexec_get_path_params(const char *file, 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 * + * @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 * @li 0 if no * @li 1 if yes @@ -90,20 +200,13 @@ 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 && + (n = strlen(want)) > 0 && veriexec_get_pid_params(pid, ¶ms) == 0) { - /* Does label contain [,]<want>[,] ? */ - 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 check_label_want(params.label, params.labellen, + want, n); } 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 * + * @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 * @li 0 if no * @li 1 if yes @@ -119,20 +230,13 @@ int veriexec_check_path_label(const char *file, const char *want) { struct mac_veriexec_syscall_params params; - char *cp; size_t n; if (want != NULL && file != NULL && + (n = strlen(want)) > 0 && veriexec_get_path_params(file, ¶ms) == 0) { - /* Does label contain [,]<want>[,] ? */ - 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 check_label_want(params.label, params.labellen, + want, n); } return 0; /* no */ } @@ -167,13 +271,19 @@ main(int argc, char *argv[]) { struct mac_veriexec_syscall_params params; pid_t pid; + char buf[BUFSIZ]; + const char *cp; char *want = NULL; + int lflag = 0; int pflag = 0; int error; int c; - while ((c = getopt(argc, argv, "pw:")) != -1) { + while ((c = getopt(argc, argv, "lpw:")) != -1) { switch (c) { + case 'l': + lflag = 1; + break; case 'p': pflag = 1; break; @@ -188,6 +298,12 @@ main(int argc, char *argv[]) if (pflag) { 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) { error = veriexec_check_pid_label(pid, want); printf("pid=%d want='%s': %d\n", @@ -196,6 +312,20 @@ main(int argc, char *argv[]) } error = veriexec_get_pid_params(pid, ¶ms); } 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); } if (error) { diff --git a/sbin/veriexec/Makefile.depend b/sbin/veriexec/Makefile.depend index 9307e12bc8dd..cbe57ebe4cf8 100644 --- a/sbin/veriexec/Makefile.depend +++ b/sbin/veriexec/Makefile.depend @@ -1,7 +1,6 @@ # Autogenerated - do NOT edit! DIRDEPS = \ - gnu/lib/csu \ include \ include/xlocale \ lib/${CSU_DIR} \ @@ -10,6 +9,7 @@ DIRDEPS = \ lib/libcompiler_rt \ lib/libsecureboot \ lib/libveriexec \ + usr.bin/yacc.host \ .include <dirdeps.mk> diff --git a/sbin/veriexec/veriexec.8 b/sbin/veriexec/veriexec.8 index c325f267689d..734b1cda40f6 100644 --- a/sbin/veriexec/veriexec.8 +++ b/sbin/veriexec/veriexec.8 @@ -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 .\" modification, are permitted provided that the following conditions @@ -22,7 +24,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE .\" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd July 8, 2022 +.Dd August 8, 2023 .Dt VERIEXEC 8 .Os .Sh NAME @@ -39,6 +41,9 @@ .Nm .Fl i Ar state .Nm +.Fl l +.Ar file ... +.Nm .Fl x .Ar file ... .Sh DESCRIPTION @@ -67,6 +72,14 @@ and with to query the current .Ar state . .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 .Fl x is used to test whether diff --git a/sbin/veriexec/veriexec.c b/sbin/veriexec/veriexec.c index 3899a781625a..0619b261665e 100644 --- a/sbin/veriexec/veriexec.c +++ b/sbin/veriexec/veriexec.c @@ -1,7 +1,7 @@ /*- * 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 * modification, are permitted provided that the following conditions @@ -53,7 +53,7 @@ static int veriexec_usage(void) { printf("%s", - "Usage:\tveriexec [-h] [-i state] [-C] [-xv state|verbosity] [path]\n"); + "Usage:\tveriexec [-C path] [-hlxv] [-[iz] state] [path]\n"); return (0); } @@ -135,6 +135,45 @@ veriexec_state_modify(const char *arg_text) 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 main(int argc, char *argv[]) { @@ -147,7 +186,7 @@ main(int argc, char *argv[]) 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) { case 'h': /* Print usage info */ @@ -173,6 +212,11 @@ main(int argc, char *argv[]) exit((x & state) == 0); break; +#ifdef HAVE_VERIEXEC_GET_PATH_LABEL + case 'l': + veriexec_check_labels(argc, argv); + break; +#endif case 'S': /* Strictly enforce certificate validity */ ve_enforce_validity_set(1); @@ -188,13 +232,7 @@ main(int argc, char *argv[]) /* * -x says all other args are paths to check. */ - for (x = EX_OK; optind < argc; optind++) { - if (veriexec_check_path(argv[optind])) { - warn("%s", argv[optind]); - x = 2; - } - } - exit(x); + veriexec_check_paths(argc, argv); break; case 'z': /* Modify the state */ diff --git a/sys/conf/files b/sys/conf/files index 8d38b9cc8a2e..2d429c11f523 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -5147,6 +5147,7 @@ security/mac_priority/mac_priority.c optional mac_priority security/mac_seeotheruids/mac_seeotheruids.c optional mac_seeotheruids security/mac_stub/mac_stub.c optional mac_stub 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/veriexec_fingerprint.c optional mac_veriexec security/mac_veriexec/veriexec_metadata.c optional mac_veriexec diff --git a/sys/conf/options b/sys/conf/options index 56c1a33216d5..ae94d1b60492 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -168,6 +168,7 @@ MAC_SEEOTHERUIDS opt_dontuse.h MAC_STATIC opt_mac.h MAC_STUB opt_dontuse.h MAC_TEST opt_dontuse.h +MAC_GRANTBYLABEL opt_dontuse.h MAC_VERIEXEC opt_dontuse.h MAC_VERIEXEC_SHA1 opt_dontuse.h MAC_VERIEXEC_SHA256 opt_dontuse.h diff --git a/sys/security/mac_grantbylabel/mac_grantbylabel.c b/sys/security/mac_grantbylabel/mac_grantbylabel.c new file mode 100644 index 000000000000..848131e54590 --- /dev/null +++ b/sys/security/mac_grantbylabel/mac_grantbylabel.c @@ -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); *** 459 LINES SKIPPED ***