Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F103436059
D41431.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
34 KB
Referenced Files
None
Subscribers
None
D41431.diff
View Options
diff --git a/etc/mtree/BSD.include.dist b/etc/mtree/BSD.include.dist
--- 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
--- a/include/Makefile
+++ b/include/Makefile
@@ -67,7 +67,9 @@
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
--- a/lib/libveriexec/Makefile
+++ b/lib/libveriexec/Makefile
@@ -8,8 +8,10 @@
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
--- /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
--- /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
--- 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 @@
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
--- a/lib/libveriexec/veriexec_get.c
+++ b/lib/libveriexec/veriexec_get.c
@@ -59,7 +59,7 @@
}
/**
- * @brief get veriexec params for a process
+ * @brief get veriexec params for a path
*
* @return
* @li 0 if successful
@@ -79,9 +79,119 @@
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 @@
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 @@
/**
* @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 @@
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 @@
{
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 @@
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 @@
}
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
--- 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 @@
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
--- 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 @@
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
--- 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 @@
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 @@
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 @@
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 @@
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 @@
/*
* -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
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -5147,6 +5147,7 @@
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
--- a/sys/conf/options
+++ b/sys/conf/options
@@ -168,6 +168,7 @@
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/lib/libveriexec/libveriexec.h b/sys/security/mac_grantbylabel/mac_grantbylabel.h
copy from lib/libveriexec/libveriexec.h
copy to sys/security/mac_grantbylabel/mac_grantbylabel.h
--- a/lib/libveriexec/libveriexec.h
+++ b/sys/security/mac_grantbylabel/mac_grantbylabel.h
@@ -1,7 +1,7 @@
-/*-
+/*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2011, 2012, 2013, 2015, Juniper Networks, Inc.
+ * Copyright (c) 2018-2023, Juniper Networks, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -26,21 +26,38 @@
* SUCH DAMAGE.
*/
-#ifndef __LIBVERIEXEC_H__
-#define __LIBVERIEXEC_H__
+#ifndef _SECURITY_MAC_GRANTBYLABEL_H
+#define _SECURITY_MAC_GRANTBYLABEL_H
-struct mac_veriexec_syscall_params;
+#include <security/mac_veriexec/mac_veriexec.h>
-int veriexec_check_fd_mode(int, unsigned int);
-int veriexec_check_path_mode(const char *, unsigned int);
-int veriexec_check_fd(int);
-int veriexec_check_path(const char *);
-int veriexec_get_pid_params(pid_t, struct mac_veriexec_syscall_params *);
-int veriexec_get_path_params(const char *,
- struct mac_veriexec_syscall_params *);
-int veriexec_check_path_label(const char *, const char *);
-int veriexec_check_pid_label(pid_t, const char *);
+#define MAC_GRANTBYLABEL_NAME "mac_grantbylabel"
-#define HAVE_VERIEXEC_CHECK_PID_LABEL 1
+/* 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
-#endif /* __LIBVERIEXEC_H__ */
+/* 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
diff --git a/sys/security/mac_grantbylabel/mac_grantbylabel.c b/sys/security/mac_grantbylabel/mac_grantbylabel.c
new file mode 100644
--- /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);
+ 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);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Nov 26, 12:20 AM (21 h, 43 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14850851
Default Alt Text
D41431.diff (34 KB)
Attached To
Mode
D41431: Add mac_grantbylabel
Attached
Detach File
Event Timeline
Log In to Comment