blob: 0fd62516d55648c6f621a89d0619cbbf9b4d2c62 [file] [log] [blame] [raw]
/*
* Copyright 2015-2021 Rivoreo
*
* 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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/module.h>
#include <sys/kernel.h>
#include <security/mac/mac_policy.h>
#include <sys/systm.h>
#include <sys/sbuf.h>
#include <sys/sysctl.h>
#include <sys/ucred.h>
#include <sys/priv.h>
#define PRIV_SET_MASK_T unsigned long int
#define PRIV_BITS_PER_MASK (sizeof(PRIV_SET_MASK_T)*8)
struct privilege_set {
PRIV_SET_MASK_T bits[howmany(_PRIV_HIGHEST, PRIV_BITS_PER_MASK)];
};
#define PRIV_SET_GET_MASK(I) ((PRIV_SET_MASK_T)1 << ((I)%PRIV_BITS_PER_MASK))
#define PRIV_ISSET(I,P) ((P)->bits[(I)/PRIV_BITS_PER_MASK] & PRIV_SET_GET_MASK(I))
#define PRIV_CLEAR(I,P) ((P)->bits[(I)/PRIV_BITS_PER_MASK] &= ~PRIV_SET_GET_MASK(I))
#define PRIV_SET(I,P) ((P)->bits[(I)/PRIV_BITS_PER_MASK] |= PRIV_SET_GET_MASK(I))
static int priv_slot;
#define GET_PRIV_SET(L) ((struct privilege_set *)mac_label_get((L),priv_slot))
static MALLOC_DEFINE(M_MAC_PRIV, "mac_priv_pool", "MAC/Privilege privilege set pool");
SYSCTL_DECL(_security_mac);
static SYSCTL_NODE(_security_mac, OID_AUTO, priv, CTLFLAG_RW, NULL,
"mac_priv policy controls");
static int priv_enabled = -1;
SYSCTL_INT(_security_mac_priv, OID_AUTO, enabled, CTLFLAG_RW,
&priv_enabled, 0, "Enable mac_priv policy");
TUNABLE_INT("security.mac.priv.enabled", &priv_enabled);
static char init_priv[256];
SYSCTL_STRING(_security_mac_priv, OID_AUTO, init_priv, CTLFLAG_RDTUN,
init_priv, 0, "Default privilege set for init process");
static const char *privilege_names[_PRIV_HIGHEST] = {
#define PRIVILEGE(N) [PRIV_##N] = #N
PRIVILEGE(ACCT),
PRIVILEGE(MAXFILES),
PRIVILEGE(MAXPROC),
PRIVILEGE(KTRACE),
PRIVILEGE(SETDUMPER),
PRIVILEGE(REBOOT),
PRIVILEGE(SWAPON),
PRIVILEGE(SWAPOFF),
PRIVILEGE(MSGBUF),
PRIVILEGE(IO),
PRIVILEGE(KEYBOARD),
PRIVILEGE(DRIVER),
PRIVILEGE(ADJTIME),
PRIVILEGE(NTP_ADJTIME),
PRIVILEGE(CLOCK_SETTIME),
PRIVILEGE(SETTIMEOFDAY),
PRIVILEGE(AUDIT_CONTROL),
PRIVILEGE(AUDIT_FAILSTOP),
PRIVILEGE(AUDIT_GETAUDIT),
PRIVILEGE(AUDIT_SETAUDIT),
PRIVILEGE(AUDIT_SUBMIT),
PRIVILEGE(CRED_SETUID),
PRIVILEGE(CRED_SETEUID),
PRIVILEGE(CRED_SETGID),
PRIVILEGE(CRED_SETEGID),
PRIVILEGE(CRED_SETGROUPS),
PRIVILEGE(CRED_SETREUID),
PRIVILEGE(CRED_SETREGID),
PRIVILEGE(CRED_SETRESUID),
PRIVILEGE(CRED_SETRESGID),
PRIVILEGE(SEEOTHERGIDS),
PRIVILEGE(SEEOTHERUIDS),
PRIVILEGE(DEBUG_DIFFCRED),
PRIVILEGE(DEBUG_SUGID),
PRIVILEGE(DEBUG_UNPRIV),
PRIVILEGE(DEBUG_DENIED),
PRIVILEGE(DTRACE_KERNEL),
PRIVILEGE(DTRACE_PROC),
PRIVILEGE(DTRACE_USER),
PRIVILEGE(FIRMWARE_LOAD),
PRIVILEGE(JAIL_ATTACH),
PRIVILEGE(JAIL_SET),
PRIVILEGE(JAIL_REMOVE),
PRIVILEGE(KENV_SET),
PRIVILEGE(KENV_UNSET),
PRIVILEGE(KLD_LOAD),
PRIVILEGE(KLD_UNLOAD),
PRIVILEGE(MAC_PARTITION),
PRIVILEGE(MAC_PRIVS),
PRIVILEGE(PROC_LIMIT),
PRIVILEGE(PROC_SETLOGIN),
PRIVILEGE(PROC_SETRLIMIT),
PRIVILEGE(PROC_SETLOGINCLASS),
PRIVILEGE(IPC_READ),
PRIVILEGE(IPC_WRITE),
PRIVILEGE(IPC_ADMIN),
PRIVILEGE(IPC_MSGSIZE),
PRIVILEGE(MQ_ADMIN),
PRIVILEGE(PMC_MANAGE),
PRIVILEGE(PMC_SYSTEM),
PRIVILEGE(SCHED_DIFFCRED),
PRIVILEGE(SCHED_SETPRIORITY),
PRIVILEGE(SCHED_RTPRIO),
PRIVILEGE(SCHED_SETPOLICY),
PRIVILEGE(SCHED_SET),
PRIVILEGE(SCHED_SETPARAM),
PRIVILEGE(SCHED_CPUSET),
PRIVILEGE(SCHED_CPUSET_INTR),
PRIVILEGE(SEM_WRITE),
PRIVILEGE(SIGNAL_DIFFCRED),
PRIVILEGE(SIGNAL_SUGID),
PRIVILEGE(SYSCTL_DEBUG),
PRIVILEGE(SYSCTL_WRITE),
PRIVILEGE(SYSCTL_WRITEJAIL),
PRIVILEGE(TTY_CONSOLE),
PRIVILEGE(TTY_DRAINWAIT),
PRIVILEGE(TTY_DTRWAIT),
PRIVILEGE(TTY_EXCLUSIVE),
PRIVILEGE(TTY_STI),
PRIVILEGE(TTY_SETA),
PRIVILEGE(UFS_EXTATTRCTL),
PRIVILEGE(UFS_QUOTAOFF),
PRIVILEGE(UFS_QUOTAON),
PRIVILEGE(UFS_SETUSE),
PRIVILEGE(ZFS_POOL_CONFIG),
PRIVILEGE(ZFS_INJECT),
PRIVILEGE(ZFS_JAIL),
PRIVILEGE(NFS_DAEMON),
PRIVILEGE(NFS_LOCKD),
PRIVILEGE(VFS_READ),
PRIVILEGE(VFS_WRITE),
PRIVILEGE(VFS_ADMIN),
PRIVILEGE(VFS_EXEC),
PRIVILEGE(VFS_LOOKUP),
PRIVILEGE(VFS_BLOCKRESERVE),
PRIVILEGE(VFS_CHFLAGS_DEV),
PRIVILEGE(VFS_CHOWN),
PRIVILEGE(VFS_CHROOT),
PRIVILEGE(VFS_RETAINSUGID),
PRIVILEGE(VFS_EXCEEDQUOTA),
PRIVILEGE(VFS_EXTATTR_SYSTEM),
PRIVILEGE(VFS_FCHROOT),
PRIVILEGE(VFS_FHOPEN),
PRIVILEGE(VFS_FHSTAT),
PRIVILEGE(VFS_FHSTATFS),
PRIVILEGE(VFS_GENERATION),
PRIVILEGE(VFS_GETFH),
PRIVILEGE(VFS_GETQUOTA),
PRIVILEGE(VFS_LINK),
PRIVILEGE(VFS_MKNOD_BAD),
PRIVILEGE(VFS_MKNOD_DEV),
PRIVILEGE(VFS_MKNOD_WHT),
PRIVILEGE(VFS_MOUNT),
PRIVILEGE(VFS_MOUNT_OWNER),
PRIVILEGE(VFS_MOUNT_EXPORTED),
PRIVILEGE(VFS_MOUNT_PERM),
PRIVILEGE(VFS_MOUNT_SUIDDIR),
PRIVILEGE(VFS_MOUNT_NONUSER),
PRIVILEGE(VFS_SETGID),
PRIVILEGE(VFS_SETQUOTA),
PRIVILEGE(VFS_STICKYFILE),
PRIVILEGE(VFS_SYSFLAGS),
PRIVILEGE(VFS_UNMOUNT),
PRIVILEGE(VFS_STAT),
PRIVILEGE(VM_MADV_PROTECT),
PRIVILEGE(VM_MLOCK),
PRIVILEGE(VM_MUNLOCK),
PRIVILEGE(VM_SWAP_NOQUOTA),
PRIVILEGE(VM_SWAP_NORLIMIT),
PRIVILEGE(DEVFS_RULE),
PRIVILEGE(DEVFS_SYMLINK),
PRIVILEGE(RANDOM_RESEED),
PRIVILEGE(NET_BRIDGE),
PRIVILEGE(NET_GRE),
#ifdef PRIV_NET_PPP
PRIVILEGE(NET_PPP),
#endif
PRIVILEGE(NET_BPF),
PRIVILEGE(NET_RAW),
PRIVILEGE(NET_ROUTE),
PRIVILEGE(NET_TAP),
PRIVILEGE(NET_SETIFMTU),
PRIVILEGE(NET_SETIFFLAGS),
PRIVILEGE(NET_SETIFCAP),
PRIVILEGE(NET_SETIFNAME),
PRIVILEGE(NET_SETIFMETRIC),
PRIVILEGE(NET_SETIFPHYS),
PRIVILEGE(NET_SETIFMAC),
PRIVILEGE(NET_ADDMULTI),
PRIVILEGE(NET_DELMULTI),
PRIVILEGE(NET_HWIOCTL),
PRIVILEGE(NET_SETLLADDR),
PRIVILEGE(NET_ADDIFGROUP),
PRIVILEGE(NET_DELIFGROUP),
PRIVILEGE(NET_IFCREATE),
PRIVILEGE(NET_IFDESTROY),
PRIVILEGE(NET_ADDIFADDR),
PRIVILEGE(NET_DELIFADDR),
PRIVILEGE(NET_LAGG),
PRIVILEGE(NET_GIF),
PRIVILEGE(NET_SETIFVNET),
PRIVILEGE(NET_SETIFDESCR),
PRIVILEGE(NET_SETIFFIB),
PRIVILEGE(NET_VXLAN),
#ifdef PRIV_NET_SETLANPCP
PRIVILEGE(NET_SETLANPCP),
#endif
#ifdef PRIV_NET_SETVLANPCP
PRIVILEGE(NET_SETVLANPCP),
#endif
#ifdef PRIV_NETATALK_RESERVEDPORT
PRIVILEGE(NETATALK_RESERVEDPORT),
#endif
PRIVILEGE(NETATM_CFG),
PRIVILEGE(NETATM_ADD),
PRIVILEGE(NETATM_DEL),
PRIVILEGE(NETATM_SET),
PRIVILEGE(NETBLUETOOTH_RAW),
PRIVILEGE(NETGRAPH_CONTROL),
PRIVILEGE(NETGRAPH_TTY),
PRIVILEGE(NETINET_RESERVEDPORT),
PRIVILEGE(NETINET_IPFW),
PRIVILEGE(NETINET_DIVERT),
PRIVILEGE(NETINET_PF),
PRIVILEGE(NETINET_DUMMYNET),
PRIVILEGE(NETINET_CARP),
PRIVILEGE(NETINET_MROUTE),
PRIVILEGE(NETINET_RAW),
PRIVILEGE(NETINET_GETCRED),
PRIVILEGE(NETINET_IPSEC),
PRIVILEGE(NETINET_REUSEPORT),
PRIVILEGE(NETINET_SETHDROPTS),
PRIVILEGE(NETINET_BINDANY),
#ifdef PRIV_NETINET_HASHKEY
PRIVILEGE(NETINET_HASHKEY),
#endif
#ifdef PRIV_NETIPX_RESERVEDPORT
PRIVILEGE(NETIPX_RESERVEDPORT),
#endif
#ifdef PRIV_NETIPX_RAW
PRIVILEGE(NETIPX_RAW),
#endif
PRIVILEGE(NETNCP),
PRIVILEGE(NETSMB),
PRIVILEGE(DDB_CAPTURE),
PRIVILEGE(NNPFS_DEBUG),
PRIVILEGE(CPUCTL_WRMSR),
PRIVILEGE(CPUCTL_UPDATE),
PRIVILEGE(AFS_ADMIN),
PRIVILEGE(AFS_DAEMON),
PRIVILEGE(RCTL_GET_RACCT),
PRIVILEGE(RCTL_GET_RULES),
PRIVILEGE(RCTL_GET_LIMITS),
PRIVILEGE(RCTL_ADD_RULE),
PRIVILEGE(RCTL_REMOVE_RULE),
PRIVILEGE(KMEM_READ),
PRIVILEGE(KMEM_WRITE),
#undef PRIVILEGE
};
static void priv_destroy(struct mac_policy_conf *conf) {
}
static void priv_init(struct mac_policy_conf *conf) {
if(priv_enabled < 0) priv_enabled = 1;
}
static void priv_init_label(struct label *label) {
//uprintf("function: priv_init_label(%p)\n", label);
mac_label_set(label, priv_slot,
(intptr_t)malloc(sizeof(struct privilege_set), M_MAC_PRIV, M_WAITOK | M_ZERO));
}
static void priv_destroy_label(struct label *label) {
//uprintf("function: priv_destroy_label(%p)\n", label);
free(GET_PRIV_SET(label), M_MAC_PRIV);
mac_label_set(label, priv_slot, 0);
}
static void priv_cred_create_swapper(struct ucred *cred) {
struct privilege_set *priv_set = GET_PRIV_SET(cred->cr_label);
memset(priv_set, 0xff, sizeof *priv_set);
}
static int get_privilege_by_name(const char *name) {
unsigned int i = _PRIV_LOWEST;
do {
const char *s = privilege_names[i];
if(s && strcasecmp(s, name) == 0) return i;
} while(++i < _PRIV_HIGHEST);
return -1;
}
static int priv_internalize_label_internal(struct label *label, char *element_data) {
struct privilege_set *priv_set = GET_PRIV_SET(label);
memset(priv_set, 0, sizeof(struct privilege_set));
char *p = element_data, *end_p;
while(*p) {
int n = strtol(p, &end_p, 0);
if(*end_p && *end_p != '/') {
end_p = strchr(p, '/');
if(end_p) *end_p = 0;
n = get_privilege_by_name(p);
} else if(!*end_p) {
end_p = NULL;
} else if(end_p == p) {
return EINVAL;
}
if(n < 0) return EINVAL;
PRIV_SET(n, priv_set);
if(!end_p) break;
p = end_p + 1;
}
return 0;
}
static void priv_cred_create_init(struct ucred *cred) {
struct privilege_set *priv_set = GET_PRIV_SET(cred->cr_label);
char *priv = malloc(4096, M_TEMP, M_WAITOK);
if(TUNABLE_STR_FETCH("security.mac.priv.init_priv", priv, 4096) && strcasecmp(priv, "ALL")) {
int e = priv_internalize_label_internal(cred->cr_label, priv);
if(e) {
printf("mac_priv: Failed to internalize privilege set '%s' for init process, error %d\n",
priv, e);
} else {
strlcpy(init_priv, priv, sizeof init_priv);
free(priv, M_TEMP);
if(bootverbose) {
printf("mac_priv: Set init process privilege set for from tunable\n");
}
return;
}
} else {
strcpy(init_priv, "ALL");
}
free(priv, M_TEMP);
unsigned int i = _PRIV_LOWEST;
do {
const char *s = privilege_names[i];
if(s) PRIV_SET(i, priv_set);
else PRIV_CLEAR(i, priv_set);
} while(++i < _PRIV_HIGHEST);
}
static int priv_externalize_label(struct label *label, char *element_name, struct sbuf *sb, int *claimed) {
uprintf("function: priv_externalize_label(%p, %p<%s>, %p, %p<%d>\n",
label, element_name, element_name, sb, claimed, claimed);
if(!label) return 0;
if(strcmp(element_name, "priv")) return 0;
(*claimed)++;
struct privilege_set *priv_set = GET_PRIV_SET(label);
unsigned int i = _PRIV_LOWEST, more = 0;
do {
if(!PRIV_ISSET(i, priv_set)) continue;
if(more) sbuf_putc(sb, '/');
const char *s = privilege_names[i];
if(s) sbuf_cat(sb, s); else sbuf_printf(sb, "%d", i);
more = 1;
} while(++i < _PRIV_HIGHEST);
return 0;
}
static int priv_internalize_label(struct label *label, char *element_name, char *element_data, int *claimed) {
uprintf("function: priv_internalize_label(%p, %p<%s>, %p<%s>, %p<%d>)\n",
label, element_name, element_name, element_data, element_data, claimed, claimed);
if(!label) return 0;
if(strcmp(element_name, "priv")) return 0;
(*claimed)++;
return priv_internalize_label_internal(label, element_data);
}
static void priv_copy_label(struct label *src, struct label *dest) {
if(!src) return;
struct privilege_set *src_set = GET_PRIV_SET(src);
struct privilege_set *dest_set = GET_PRIV_SET(dest);
*dest_set = *src_set;
}
static int has_more_privilege(const struct ucred *cred, struct label *new_label) {
struct privilege_set *old_set = cred->cr_label ? GET_PRIV_SET(cred->cr_label) : NULL;
struct privilege_set *new_set = GET_PRIV_SET(new_label);
int i = howmany(_PRIV_HIGHEST, PRIV_BITS_PER_MASK);
if(old_set) do {
if(~old_set->bits[i] & new_set->bits[i]) return 1;
} while(--i > 0); else do {
if(new_set->bits[i]) return 1;
} while(--i > 0);
return 0;
}
static int priv_cred_check_relabel(struct ucred *cred, struct label *new_label) {
if(!has_more_privilege(cred, new_label)) return 0;
return priv_check_cred(cred, PRIV_MAC_PRIVS, 0);
}
static void priv_cred_relabel(struct ucred *cred, struct label *new_label) {
uprintf("function: priv_cred_relabel(%p, %p)\n", cred, new_label);
struct privilege_set *old_set = GET_PRIV_SET(cred->cr_label);
struct privilege_set *new_set = GET_PRIV_SET(new_label);
*old_set = *new_set;
}
static int priv_priv_check(struct ucred *cred, int priv) {
struct privilege_set *priv_set;
if(priv_enabled <= 0) return 0;
if(!cred->cr_label || !(priv_set = GET_PRIV_SET(cred->cr_label))) {
return priv_enabled < 2 ? 0 : EPERM;
}
return PRIV_ISSET(priv, priv_set) ? 0 : EPERM;
}
static int priv_priv_grant(struct ucred *cred, int priv) {
if(priv_enabled <= 0) return EPERM;
if(!cred->cr_label) return EPERM;
struct privilege_set *priv_set = GET_PRIV_SET(cred->cr_label);
return priv_set && PRIV_ISSET(priv, priv_set) ? 0 : EPERM;
}
/*
* Register functions with MAC Framework policy entry points.
*/
static struct mac_policy_ops priv_ops = {
.mpo_destroy = priv_destroy,
.mpo_init = priv_init,
.mpo_cred_init_label = priv_init_label,
.mpo_cred_destroy_label = priv_destroy_label,
.mpo_cred_create_swapper = priv_cred_create_swapper,
.mpo_cred_create_init = priv_cred_create_init,
.mpo_cred_externalize_label = priv_externalize_label,
.mpo_cred_internalize_label = priv_internalize_label,
.mpo_cred_copy_label = priv_copy_label,
.mpo_cred_check_relabel = priv_cred_check_relabel,
.mpo_cred_relabel = priv_cred_relabel,
.mpo_priv_check = priv_priv_check,
.mpo_priv_grant = priv_priv_grant,
};
MAC_POLICY_SET(&priv_ops, mac_priv, "MAC/Privilege", MPC_LOADTIME_FLAG_UNLOADOK, &priv_slot);