root/usr/src/common/tsol/ltos.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#if !defined(_KERNEL)
#include <errno.h>
#endif /* !defined(_KERNEL) */

#include <sys/types.h>
#include <sys/mman.h>
#include <sys/tsol/label_macro.h>

#include <sys/tsol/label.h>

#if !defined(_KERNEL)
#include "clnt.h"
#include "labeld.h"
#endif /* !defined(_KERNEL) */

#if defined(_KERNEL)
#include <sys/systm.h>
#include <sys/sunddi.h>
#else
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#endif



static _mac_label_impl_t low;
static _mac_label_impl_t high;
static int      inited = 0;

#if defined(_KERNEL)
#define malloc(l)       kmem_alloc(l, KM_NOSLEEP)
#define freeit(a, l)            kmem_free(a, l)
#else /* defined(_KERNEL) */
#define freeit(a, l)            free(a)
#endif /* defined(_KERNEL) */

/* 0x + Classification + '-' + ll + '-' + Compartments + end of string */
#define _HEX_SIZE 2+(sizeof (Classification_t)*2)+4+\
        (sizeof (Compartments_t)*2)+1

/* 0x + Classification + '-' + ll + '-' + end of string */
#define _MIN_HEX (2 + (sizeof (Classification_t)*2) + 4 + 1)

static char digits[] = "0123456789abcdef";

#define HEX(h, i, l, s) \
        for (; i < s; /* */) {\
        h[i++] = digits[(unsigned int)(*l >> 4)];\
        h[i++] = digits[(unsigned int)(*l++&0xF)]; }

static int
__hex(char **s, const m_label_t *l)
{
        char    *hex;
        int     i = 0;
        uchar_t *hl;
        int     hex_len;
        uchar_t *len;

        hl = (uchar_t  *)&(((_mac_label_impl_t *)l)->_c_len);
        len = hl;

        if (*len == 0) {
                /* old binary label */
                hex_len = _HEX_SIZE;
        } else {
                hex_len = _MIN_HEX + (*len * sizeof (uint32_t) * 2);
        }

        if ((hex = malloc(hex_len)) == NULL) {
                return (-1);
        }

        /* header */

        hex[i++] = '0';
        hex[i++] = 'x';

        /* classification */

        hl++;           /* start at classification */
        HEX(hex, i, hl, 6);

        /* Add compartments length */
        hex[i++] = '-';
        HEX(hex, i, len, 9);
        hex[i++] = '-';

        /* compartments */
        HEX(hex, i, hl, hex_len-1);
        hex[i] = '\0';

        /* truncate trailing zeros */

        while (hex[i-1] == '0' && hex[i-2] == '0') {
                i -= 2;
        }
        hex[i] = '\0';

        if ((*s = strdup(hex)) == NULL) {
                freeit(hex, hex_len);
                return (-1);
        }

        freeit(hex, hex_len);
        return (0);

}

int
l_to_str_internal(const m_label_t *l, char **s)
{
        if (inited == 0) {
                inited = 1;
                _BSLLOW(&low);
                _BSLHIGH(&high);
        }

        if (!(_MTYPE(l, SUN_MAC_ID) || _MTYPE(l, SUN_UCLR_ID))) {
#if !defined(_KERNEL)
                errno = EINVAL;
#endif /* !defined(_KERNEL) */
                *s = NULL;
                return (-1);
        }
        if (_MEQUAL(&low, (_mac_label_impl_t *)l)) {
                if ((*s = strdup(ADMIN_LOW)) == NULL) {
                        return (-1);
                }
                return (0);
        }
        if (_MEQUAL(&high, (_mac_label_impl_t *)l)) {
                if ((*s = strdup(ADMIN_HIGH)) == NULL) {
                        return (-1);
                }
                return (0);
        }

        return (__hex(s, l));
}

#if !defined(_KERNEL)
/*
 * label_to_str -- convert a label to the requested type of string.
 *
 *      Entry   l = label to convert;
 *              t = type of conversion;
 *              f = flags for conversion type;
 *
 *      Exit    *s = allocated converted string;
 *                   Caller must call free() to free.
 *
 *      Returns 0, success.
 *              -1, error, errno set; *s = NULL.
 *
 *      Calls   labeld
 */

int
label_to_str(const m_label_t *l, char **s, const m_label_str_t t, uint_t f)
{
        labeld_data_t   call;
        labeld_data_t   *callp = &call;
        size_t  bufsize = sizeof (labeld_data_t);
        size_t  datasize;
        int     err;
        int     string_start = 0;

        if (inited == 0) {
                inited = 1;
                _BSLLOW(&low);
                _BSLHIGH(&high);
        }

#define lscall callp->param.acall.cargs.ls_arg
#define lsret callp->param.aret.rvals.ls_ret
        switch (t) {
        case M_LABEL:
                call.callop = LTOS;
                lscall.label = *l;
                lscall.flags = f;
                datasize = CALL_SIZE(ls_call_t, 0);
                if ((err = __call_labeld(&callp, &bufsize, &datasize)) ==
                    SUCCESS) {
                        if (callp->reterr != 0) {
                                errno = EINVAL;
                                *s = NULL;
                                return (-1);
                        }
                        *s = strdup(lsret.buf);
                        if (callp != &call) {
                                /* release returned buffer */
                                (void) munmap((void *)callp, bufsize);
                        }
                        if (*s == NULL) {
                                return (-1);
                        }
                        return (0);
                }
                switch (err) {
                case NOSERVER:
                        /* server not present */
                        /* special case admin_low and admin_high */

                        if (_MEQUAL(&low, (_mac_label_impl_t *)l)) {
                                if ((*s = strdup(ADMIN_LOW)) == NULL) {
                                        return (-1);
                                }
                                return (0);
                        } else if (_MEQUAL(&high, (_mac_label_impl_t *)l)) {
                                if ((*s = strdup(ADMIN_HIGH)) == NULL) {
                                        return (-1);
                                }
                                return (0);
                        }
                        errno = ENOTSUP;
                        break;
                default:
                        errno = EINVAL;
                        break;
                }
                *s = NULL;
                return (-1);
#undef  lscall
#undef  lsret

        case M_INTERNAL: {
                return (l_to_str_internal(l, s));
        }

#define ccall callp->param.acall.cargs.color_arg
#define cret callp->param.aret.rvals.color_ret
        case M_COLOR:
                datasize = CALL_SIZE(color_call_t, 0);
                call.callop = BLTOCOLOR;
                ccall.label = *l;

                if (__call_labeld(&callp, &bufsize, &datasize) == SUCCESS) {
                        if (callp->reterr != 0) {
                                errno = EINVAL;
                                *s = NULL;
                                return (-1);
                        }
                        *s = strdup(cret.color);
                        if (callp != &call) {
                                /* release returned buffer */
                                (void) munmap((void *)callp, bufsize);
                        }
                        if (*s == NULL) {
                                return (-1);
                        }
                        return (0);
                } else {
                        errno = ENOTSUP;
                        *s = NULL;
                        return (-1);
                }
#undef  ccall
#undef  cret

#define prcall  callp->param.acall.cargs.pr_arg
#define prret   callp->param.aret.rvals.pr_ret
        case PRINTER_TOP_BOTTOM:
                call.callop = PR_TOP;
                break;
        case PRINTER_LABEL:
                call.callop = PR_LABEL;
                break;
        case PRINTER_CAVEATS:
                call.callop = PR_CAVEATS;
                string_start = 1;       /* compensate for leading space */
                break;
        case PRINTER_CHANNELS:
                call.callop = PR_CHANNELS;
                string_start = 1;       /* compensate for leading space */
                break;
        default:
                errno = EINVAL;
                *s = NULL;
                return (-1);
        }
        /* do the common printer calls */
        datasize = CALL_SIZE(pr_call_t, 0);
        prcall.label = *l;
        prcall.flags = f;
        if (__call_labeld(&callp, &bufsize, &datasize) == SUCCESS) {
                if (callp->reterr != 0) {
                        errno = EINVAL;
                        *s = NULL;
                        return (-1);
                }
                *s = strdup(&prret.buf[string_start]);
                if (callp != &call) {
                        /* release returned buffer */
                        (void) munmap((void *)callp, bufsize);
                }
                if (*s == NULL) {
                        return (-1);
                }
                return (0);
        } else {
                errno = ENOTSUP;
                *s = NULL;
                return (-1);
        }
#undef  prcall
#undef  prret
}
#endif /* !defined(_KERNEL) */