diff options
Diffstat (limited to 'usr/src/common/secflags/secflags.c')
-rw-r--r-- | usr/src/common/secflags/secflags.c | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/usr/src/common/secflags/secflags.c b/usr/src/common/secflags/secflags.c new file mode 100644 index 0000000000..8bcedbc328 --- /dev/null +++ b/usr/src/common/secflags/secflags.c @@ -0,0 +1,241 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* Copyright 2015, Richard Lowe. */ + +#include <sys/secflags.h> +#include <sys/types.h> + +#if defined(_KERNEL) +#include <sys/kmem.h> +#include <sys/sunddi.h> +#else +#include "lint.h" /* libc header */ +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#endif + +secflagset_t +secflag_to_bit(secflag_t secflag) +{ + return (1 << secflag); +} + +boolean_t +secflag_isset(secflagset_t flags, secflag_t sf) +{ + return ((flags & secflag_to_bit(sf)) != 0); +} + +void +secflag_clear(secflagset_t *flags, secflag_t sf) +{ + *flags &= ~secflag_to_bit(sf); +} + +void +secflag_set(secflagset_t *flags, secflag_t sf) +{ + *flags |= secflag_to_bit(sf); +} + +boolean_t +secflags_isempty(secflagset_t flags) +{ + return (flags == 0); +} + +void +secflags_zero(secflagset_t *flags) +{ + *flags = 0; +} + +void +secflags_fullset(secflagset_t *flags) +{ + *flags = PROC_SEC_MASK; +} + +void +secflags_copy(secflagset_t *dst, const secflagset_t *src) +{ + *dst = *src; +} + +boolean_t +secflags_issubset(secflagset_t a, secflagset_t b) +{ + return (!(a & ~b)); +} + +boolean_t +secflags_issuperset(secflagset_t a, secflagset_t b) +{ + return (secflags_issubset(b, a)); +} + +boolean_t +secflags_intersection(secflagset_t a, secflagset_t b) +{ + return (a & b); +} + +void +secflags_union(secflagset_t *a, const secflagset_t *b) +{ + *a |= *b; +} + +void +secflags_difference(secflagset_t *a, const secflagset_t *b) +{ + *a &= ~*b; +} + +boolean_t +psecflags_validate_delta(const psecflags_t *sf, const secflagdelta_t *delta) +{ + if (delta->psd_ass_active) { + /* + * If there's a bit in lower not in args, or a bit args not in + * upper + */ + if (!secflags_issubset(delta->psd_assign, sf->psf_upper) || + !secflags_issuperset(delta->psd_assign, sf->psf_lower)) { + return (B_FALSE); + } + + if (!secflags_issubset(delta->psd_assign, PROC_SEC_MASK)) + return (B_FALSE); + } else { + /* If we're adding a bit not in upper */ + if (!secflags_isempty(delta->psd_add)) { + if (!secflags_issubset(delta->psd_add, sf->psf_upper)) { + return (B_FALSE); + } + } + + /* If we're removing a bit that's in lower */ + if (!secflags_isempty(delta->psd_rem)) { + if (secflags_intersection(delta->psd_rem, + sf->psf_lower)) { + return (B_FALSE); + } + } + + if (!secflags_issubset(delta->psd_add, PROC_SEC_MASK) || + !secflags_issubset(delta->psd_rem, PROC_SEC_MASK)) + return (B_FALSE); + } + + return (B_TRUE); +} + +boolean_t +psecflags_validate(const psecflags_t *sf) +{ + if (!secflags_issubset(sf->psf_lower, PROC_SEC_MASK) || + !secflags_issubset(sf->psf_inherit, PROC_SEC_MASK) || + !secflags_issubset(sf->psf_effective, PROC_SEC_MASK) || + !secflags_issubset(sf->psf_upper, PROC_SEC_MASK)) + return (B_FALSE); + + if (!secflags_issubset(sf->psf_lower, sf->psf_inherit)) + return (B_FALSE); + if (!secflags_issubset(sf->psf_lower, sf->psf_upper)) + return (B_FALSE); + if (!secflags_issubset(sf->psf_inherit, sf->psf_upper)) + return (B_FALSE); + + return (B_TRUE); +} + +void +psecflags_default(psecflags_t *sf) +{ + secflags_zero(&sf->psf_effective); + secflags_zero(&sf->psf_inherit); + secflags_zero(&sf->psf_lower); + secflags_fullset(&sf->psf_upper); +} + +static struct flagdesc { + secflag_t value; + const char *name; +} flagdescs[] = { + { PROC_SEC_ASLR, "aslr" }, + { PROC_SEC_FORBIDNULLMAP, "forbidnullmap" }, + { PROC_SEC_NOEXECSTACK, "noexecstack" }, + { 0x0, NULL } +}; + +boolean_t +secflag_by_name(const char *str, secflag_t *ret) +{ + struct flagdesc *fd; + + for (fd = flagdescs; fd->name != NULL; fd++) { + if (strcasecmp(str, fd->name) == 0) { + *ret = fd->value; + return (B_TRUE); + } + } + + return (B_FALSE); +} + +const char * +secflag_to_str(secflag_t sf) +{ + struct flagdesc *fd; + + for (fd = flagdescs; fd->name != NULL; fd++) { + if (sf == fd->value) + return (fd->name); + } + + return (NULL); +} + +void +secflags_to_str(secflagset_t flags, char *buf, size_t buflen) +{ + struct flagdesc *fd; + + if (buflen >= 1) + buf[0] = '\0'; + + if (flags == 0) { + (void) strlcpy(buf, "none", buflen); + return; + } + + for (fd = flagdescs; fd->name != NULL; fd++) { + if (secflag_isset(flags, fd->value)) { + if (buf[0] != '\0') + (void) strlcat(buf, ",", buflen); + (void) strlcat(buf, fd->name, buflen); + } + + secflag_clear(&flags, fd->value); + } + + if (flags != 0) { /* unknown flags */ + char hexbuf[19]; /* 0x%16 PRIx64 */ + + (void) snprintf(hexbuf, sizeof (hexbuf), "0x%16" PRIx64, flags); + if (buf[0] != '\0') + (void) strlcat(buf, ",", buflen); + (void) strlcat(buf, hexbuf, buflen); + } +} |