Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F144805795
D45145.id138469.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
12 KB
Referenced Files
None
Subscribers
None
D45145.id138469.diff
View Options
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -288,6 +288,7 @@
mac_biba.4 \
mac_bsdextended.4 \
mac_ddb.4 \
+ mac_do.4 \
mac_ifoff.4 \
mac_ipacl.4 \
mac_lomac.4 \
diff --git a/share/man/man4/mac_do.4 b/share/man/man4/mac_do.4
new file mode 100644
--- /dev/null
+++ b/share/man/man4/mac_do.4
@@ -0,0 +1,60 @@
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.\" Copyright (c) 2024 Baptiste Daroussin <bapt@FreeBSD.org>
+.Dd May 9, 2024
+.Dt MAC_DO 4
+.Os
+.Sh NAME
+.Nm mac_do
+.Nd "policy allowing user to execute program as another user"
+.Sh SYNOPSIS
+To compile the mac_do policy into your kernel, place the following lines
+in your kernel configruation file:
+.Bd -ragged -offset indent
+.Cd "options MAC"
+.Cd "options MAC_DO"
+.Ed
+.Pp
+.Sh DESCRIPTION
+The
+.Nm
+policy grants some user the ability to run process as another user
+according to rules.
+.Pp
+The exact set of kernel privileges granted are:
+.Bl -inset -compact -offset indent
+.It Dv PRIV_CRED_SETGROUPS
+.It Dv PROV_CRET_SETUID
+.El
+The following
+.Xr sysctl 8
+MIBs are available:
+.Bl -tag -width indent
+.It Va security.mac.do.enabled
+Enable the
+.Nm
+policy.
+(Default: 1).
+.It Va security.mac.do.rules
+The set of rules.
+.El
+.Pp
+The rules consist in a list of element separated by
+.So , Sc .
+Each elements is in the following form
+.Dq [uid|gid]=<fid>:<tid>
+Where
+.Ar fid
+is the uid or the gid depending on the modifier specicied of the users allowed
+to become
+.Ar tid . and
+.Ar tid is the uid of the targetted user.
+2 special form are accepted for
+.Ar tid :
+.Va any
+or
+.Va *
+which allows to target any users.
+.Sh SEE ALSO
+.Xr mac 4 ,
+.Xr mdo 1
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -226,6 +226,7 @@
${_mac_biba} \
${_mac_bsdextended} \
${_mac_ddb} \
+ ${_mac_do} \
${_mac_ifoff} \
${_mac_ipacl} \
${_mac_lomac} \
@@ -587,6 +588,7 @@
.if ${KERN_OPTS:MDDB} || defined(ALL_MODULES)
_mac_ddb= mac_ddb
.endif
+_mac_do= mac_do
_mac_ifoff= mac_ifoff
_mac_ipacl= mac_ipacl
_mac_lomac= mac_lomac
diff --git a/sys/modules/mac_do/Makefile b/sys/modules/mac_do/Makefile
new file mode 100644
--- /dev/null
+++ b/sys/modules/mac_do/Makefile
@@ -0,0 +1,6 @@
+.PATH: ${SRCTOP}/sys/security/mac_do
+
+KMOD= mac_do
+SRCS= mac_do.c vnode_if.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/security/mac_do/mac_do.c b/sys/security/mac_do/mac_do.c
new file mode 100644
--- /dev/null
+++ b/sys/security/mac_do/mac_do.c
@@ -0,0 +1,335 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright(c) 2024 Baptiste Daroussin <bapt@FreeBSD.org
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+#include <sys/ucred.h>
+#include <sys/proc.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/priv.h>
+#include <sys/vnode.h>
+
+#include <security/mac/mac_policy.h>
+
+SYSCTL_DECL(_security_mac);
+
+static SYSCTL_NODE(_security_mac, OID_AUTO, do,
+ CTLFLAG_RW|CTLFLAG_MPSAFE, 0, "mac_do policy controls");
+
+static int do_enabled = 1;
+SYSCTL_INT(_security_mac_do, OID_AUTO, enabled, CTLFLAG_RWTUN,
+ &do_enabled, 0, "Enforce do policy");
+
+static MALLOC_DEFINE(M_DO, "do_rule", "Rules for mac_do");
+
+#define MAC_RULE_STRING_LEN 1024
+
+static struct mtx rule_mtx;
+static char rule_string[MAC_RULE_STRING_LEN];
+
+#define RULE_UID 1
+#define RULE_GID 2
+#define RULE_ANY 3
+
+struct rule {
+ int from_type;
+ union {
+ uid_t f_uid;
+ gid_t f_gid;
+ };
+ int to_type;
+ uid_t t_uid;
+ TAILQ_ENTRY(rule) r_entries;
+};
+
+static TAILQ_HEAD(rulehead, rule) rule_head;
+
+static void
+toast_rules(struct rulehead *head)
+{
+ struct rule *r;
+
+ while ((r = TAILQ_FIRST(head)) != NULL) {
+ TAILQ_REMOVE(head, r, r_entries);
+ free(r, M_DO);
+ }
+}
+
+static int
+parse_rule_element(char *element, struct rule **rule)
+{
+ int error = 0;
+ char *type, *id, *p;
+ struct rule *new;
+
+ new = malloc(sizeof(*new), M_DO, M_ZERO|M_WAITOK);
+
+ type = strsep(&element, "=");
+ if (type == NULL) {
+ error = EINVAL;
+ goto out;
+ }
+ if (strcmp(type, "uid") == 0) {
+ new->from_type = RULE_UID;
+ } else if (strcmp(type, "gid") == 0) {
+ new->from_type = RULE_GID;
+ } else {
+ error = EINVAL;
+ goto out;
+ }
+ id = strsep(&element, ":");
+ if (id == NULL) {
+ error = EINVAL;
+ goto out;
+ }
+ if (new->from_type == RULE_UID)
+ new->f_uid = strtol(id, &p, 10);
+ if (new->from_type == RULE_GID)
+ new->f_gid = strtol(id, &p, 10);
+ if (*p != '\0') {
+ error = EINVAL;
+ goto out;
+ }
+ if (*element == '\0') {
+ error = EINVAL;
+ goto out;
+ }
+ if (strcmp(element, "any") == 0 || strcmp(element, "*") == 0) {
+ new->to_type = RULE_ANY;
+ } else {
+ new->to_type = RULE_UID;
+ new->t_uid = strtol(element, &p, 10);
+ if (*p != '\0') {
+ error = EINVAL;
+ goto out;
+ }
+ }
+out:
+ if (error != 0) {
+ free(new, M_DO);
+ *rule = NULL;
+ } else
+ *rule = new;
+ return (error);
+}
+
+static int
+parse_rules(char *string, struct rulehead *head)
+{
+ struct rule *new;
+ char *element;
+ int error = 0;
+
+ while ((element = strsep(&string, ",")) != NULL) {
+ if (strlen(element) == 0)
+ continue;
+ error = parse_rule_element(element, &new);
+ if (error)
+ goto out;
+ TAILQ_INSERT_TAIL(head, new, r_entries);
+ }
+out:
+ if (error != 0)
+ toast_rules(head);
+ return (error);
+}
+
+static int
+sysctl_rules(SYSCTL_HANDLER_ARGS)
+{
+ char *copy_string, *new_string;
+ struct rulehead head, saved_head;
+ int error;
+
+ if (req->newptr == NULL)
+ return (sysctl_handle_string(oidp, rule_string, MAC_RULE_STRING_LEN, req));
+
+ new_string = malloc(MAC_RULE_STRING_LEN, M_DO,
+ M_WAITOK|M_ZERO);
+ mtx_lock(&rule_mtx);
+ strcpy(new_string, rule_string);
+ mtx_unlock(&rule_mtx);
+
+ error = sysctl_handle_string(oidp, new_string, MAC_RULE_STRING_LEN, req);
+ if (error)
+ goto out;
+
+ copy_string = strdup(new_string, M_DO);
+ TAILQ_INIT(&head);
+ error = parse_rules(copy_string, &head);
+ free(copy_string, M_DO);
+ if (error)
+ goto out;
+ TAILQ_INIT(&saved_head);
+ mtx_lock(&rule_mtx);
+ TAILQ_CONCAT(&saved_head, &rule_head, r_entries);
+ TAILQ_CONCAT(&rule_head, &head, r_entries);
+ strcpy(rule_string, new_string);
+ mtx_unlock(&rule_mtx);
+ toast_rules(&saved_head);
+
+out:
+ free(new_string, M_DO);
+ return (error);
+}
+
+SYSCTL_PROC(_security_mac_do, OID_AUTO, rules,
+ CTLTYPE_STRING|CTLFLAG_RW|CTLFLAG_MPSAFE,
+ 0, 0, sysctl_rules, "A",
+ "Rules");
+
+static void
+destroy(struct mac_policy_conf *mpc)
+{
+ mtx_destroy(&rule_mtx);
+ toast_rules(&rule_head);
+}
+
+static void
+init(struct mac_policy_conf *mpc)
+{
+ mtx_init(&rule_mtx, "rule_mtx", NULL, MTX_DEF);
+ TAILQ_INIT(&rule_head);
+}
+
+static bool
+rule_is_valid(struct ucred *cred, struct rule *r)
+{
+ if (r->from_type == RULE_UID && r->f_uid == cred->cr_uid)
+ return (true);
+ if (r->from_type == RULE_GID && r->f_gid == cred->cr_gid)
+ return (true);
+ return (false);
+}
+
+static int
+priv_grant(struct ucred *cred, int priv)
+{
+ struct rule *r;
+
+ if (do_enabled == 0)
+ return (EPERM);
+
+
+ mtx_lock(&rule_mtx);
+ TAILQ_FOREACH(r, &rule_head, r_entries) {
+ if (rule_is_valid(cred, r)) {
+ switch (priv) {
+ case PRIV_CRED_SETGROUPS:
+ case PRIV_CRED_SETUID:
+ mtx_unlock(&rule_mtx);
+ return (0);
+ default:
+ break;
+ }
+ }
+ }
+ mtx_unlock(&rule_mtx);
+ return (EPERM);
+}
+
+static int
+check_setgroups(struct ucred *cred, int ngrp, gid_t *groups)
+{
+ struct rule *r;
+ char *fullpath = NULL;
+ char *freebuf = NULL;
+
+ if (do_enabled == 0)
+ return (0);
+ if (cred->cr_uid == 0)
+ return (0);
+
+ if (vn_fullpath(curproc->p_textvp, &fullpath, &freebuf) != 0)
+ return (EPERM);
+ if (strcmp(fullpath, "/usr/bin/mdo") != 0) {
+ free(freebuf, M_TEMP);
+ return (EPERM);
+ }
+ free(freebuf, M_TEMP);
+
+ mtx_lock(&rule_mtx);
+ TAILQ_FOREACH(r, &rule_head, r_entries) {
+ if (rule_is_valid(cred, r)) {
+ mtx_unlock(&rule_mtx);
+ return (0);
+ }
+ }
+ mtx_unlock(&rule_mtx);
+
+ return (EPERM);
+}
+
+static int
+check_setuid(struct ucred *cred, uid_t uid)
+{
+ struct rule *r;
+ int error;
+ char *fullpath = NULL;
+ char *freebuf = NULL;
+
+ if (do_enabled == 0)
+ return (0);
+ if (cred->cr_uid == uid || cred->cr_uid == 0)
+ return (0);
+
+ if (vn_fullpath(curproc->p_textvp, &fullpath, &freebuf) != 0)
+ return (EPERM);
+ if (strcmp(fullpath, "/usr/bin/mdo") != 0) {
+ free(freebuf, M_TEMP);
+ return (EPERM);
+ }
+ free(freebuf, M_TEMP);
+
+ error = EPERM;
+ mtx_lock(&rule_mtx);
+ TAILQ_FOREACH(r, &rule_head, r_entries) {
+ if (r->from_type == RULE_UID) {
+ if (cred->cr_uid != r->f_uid)
+ continue;
+ if (r->to_type == RULE_ANY) {
+ error = 0;
+ break;
+ }
+ if (r->to_type == RULE_UID && uid == r->t_uid) {
+ error = 0;
+ break;
+ }
+ }
+ if (r->from_type == RULE_GID) {
+ if (cred->cr_gid != r->f_gid)
+ continue;
+ if (r->to_type == RULE_ANY) {
+ error = 0;
+ break;
+ }
+ if (r->to_type == RULE_UID && uid == r->t_uid) {
+ error = 0;
+ break;
+ }
+ }
+ }
+ mtx_unlock(&rule_mtx);
+ return (error);
+}
+
+static struct mac_policy_ops do_ops =
+{
+ .mpo_destroy = destroy,
+ .mpo_init = init,
+ .mpo_cred_check_setuid = check_setuid,
+ .mpo_cred_check_setgroups = check_setgroups,
+ .mpo_priv_grant = priv_grant,
+};
+
+MAC_POLICY_SET(&do_ops, mac_do, "MAC/do",
+ MPC_LOADTIME_FLAG_UNLOADOK, NULL);
+MODULE_VERSION(mac_do, 1);
diff --git a/usr.bin/Makefile b/usr.bin/Makefile
--- a/usr.bin/Makefile
+++ b/usr.bin/Makefile
@@ -87,6 +87,7 @@
lzmainfo \
m4 \
mandoc \
+ mdo \
mesg \
ministat \
mkdep \
diff --git a/usr.bin/mdo/Makefile b/usr.bin/mdo/Makefile
new file mode 100644
--- /dev/null
+++ b/usr.bin/mdo/Makefile
@@ -0,0 +1,4 @@
+PROG= mdo
+SRCS= mdo.c
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/mdo/mdo.1 b/usr.bin/mdo/mdo.1
new file mode 100644
--- /dev/null
+++ b/usr.bin/mdo/mdo.1
@@ -0,0 +1,42 @@
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.\" Copyright(c) 2024 Baptiste Daroussin <bapt@FreeBSD.org
+.Dd May 9, 2024
+.Dt MDO 1
+.Os
+.Sh NAME
+.Nm mdo
+.Nd execute commands as another user
+.Sh SYNOPSIS
+.Nm
+.Op Fl u Ar username
+.Op Fl i
+.Op command Op args
+.Sh DESCRIPTION
+The
+.Nm
+utility executes the specified
+.Ar command
+as user
+.Ar username .
+.Pp
+If no
+.Ar username
+is provided it defaults on the
+.Va root
+user.
+If no
+.Ar command
+is specificied, it will execute the shell specificed as
+.Va SHELL
+environnement variable, falling back on
+.Pa /bin/sh .
+.Pp
+The
+.Fl i
+option can be used to only call
+.Fn setuid
+and keep the group from the calling user.
+.Sh SEE ALSO
+.Xr su 1
+.Xr mac_do 4
diff --git a/usr.bin/mdo/mdo.c b/usr.bin/mdo/mdo.c
new file mode 100644
--- /dev/null
+++ b/usr.bin/mdo/mdo.c
@@ -0,0 +1,76 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright(c) 2024 Baptiste Daroussin <bapt@FreeBSD.org
+ */
+
+#include <sys/limits.h>
+
+#include <err.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: mdo [-u username] [-i] [--] [command [args]]\n");
+ exit(EXIT_FAILURE);
+}
+
+int
+main(int argc, char **argv)
+{
+ struct passwd *pw;
+ const char *username = "root";
+ bool uidonly = false;
+ int ch;
+
+ while ((ch = getopt(argc, argv, "u:i")) != -1) {
+ switch (ch) {
+ case 'u':
+ username = optarg;
+ break;
+ case 'i':
+ uidonly = true;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if ((pw = getpwnam(username)) == NULL) {
+ if (strspn(username, "0123456789") == strlen(username)) {
+ const char *errp = NULL;
+ uid_t uid = strtonum(username, 0, UID_MAX, &errp);
+ if (errp != NULL)
+ err(EXIT_FAILURE, "%s", errp);
+ pw = getpwuid(uid);
+ }
+ if (pw == NULL)
+ err(EXIT_FAILURE, "Invalid username '%s'", username);
+ }
+ if (!uidonly) {
+ if (initgroups(pw->pw_name, pw->pw_gid) == -1)
+ err(EXIT_FAILURE, "Impossible to call initgroups");
+ if (setgid(pw->pw_gid) == -1)
+ err(EXIT_FAILURE, "Impossible to call setgid");
+ }
+ if (setuid(pw->pw_uid) == -1)
+ err(EXIT_FAILURE, "Impossible to call setuid");
+ if (*argv == NULL) {
+ const char *sh = getenv("SHELL");
+ if (sh == NULL)
+ sh = _PATH_BSHELL;
+ execlp(sh, sh, "-i", NULL);
+ } else {
+ execvp(argv[0], argv);
+ }
+ err(EXIT_FAILURE, "exec failed");
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Feb 13, 8:50 PM (4 h, 49 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28680886
Default Alt Text
D45145.id138469.diff (12 KB)
Attached To
Mode
D45145: mac_do: add a new MAC/do policy and mdo(1) utility
Attached
Detach File
Event Timeline
Log In to Comment