git: 1554ba03b651 - main - Add mac_grantbylabel

From: Simon J. Gerraty <sjg_at_FreeBSD.org>
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, &params) == 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, &params) == 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, &params) == 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, &params) == 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, &params);
 		} 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], &params);
 		}
 		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 ***