root/usr/src/common/secflags/secflags.c
/*
 * 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);
        }
}