root/usr/src/lib/libtsol/common/zone.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 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include        <stdlib.h>
#include        <strings.h>
#include        <zone.h>
#include        <errno.h>
#include        <sys/types.h>
#include        <sys/tsol/label_macro.h>

/*
 * Get label from zone name
 */
m_label_t *
getzonelabelbyname(const char *zone)
{
        zoneid_t        zoneid;

        if ((zoneid = getzoneidbyname(zone)) == -1) {
                errno = EINVAL;
                return (NULL);
        }
        return (getzonelabelbyid(zoneid));
}

/*
 * Get label from zone id
 */
m_label_t *
getzonelabelbyid(zoneid_t zoneid)
{
        m_label_t       *slabel;

        if ((slabel = m_label_alloc(MAC_LABEL)) == NULL)
                return (NULL);

        if (zone_getattr(zoneid, ZONE_ATTR_SLBL, slabel,
            sizeof (m_label_t)) < 0) {
                m_label_free(slabel);
                errno = EINVAL;
                return (NULL);
        }

        return (slabel);
}

/*
 * Get zone id from label
 */

zoneid_t
getzoneidbylabel(const m_label_t *label)
{
        m_label_t       admin_low;
        m_label_t       admin_high;
        zoneid_t        zoneid;
        zoneid_t        *zids;
        uint_t          nzents;
        uint_t          nzents_saved;
        int             i;

        bsllow(&admin_low);
        bslhigh(&admin_high);

        /* Check for admin_low or admin_high; both are global zone */
        if (blequal(label, &admin_low) || blequal(label, &admin_high))
                return (GLOBAL_ZONEID);

        nzents = 0;
        if (zone_list(NULL, &nzents) != 0)
                return (-1);

again:
        if (nzents == 0) {
                errno = EINVAL;
                return (-1);
        }

        /*
         * Add a small amount of padding here to avoid spinning in a tight loop
         * if there's a process running somewhere that's creating lots of zones
         * all at once.
         */
        nzents += 8;
        if ((zids = malloc(nzents * sizeof (zoneid_t))) == NULL)
                return (-1);
        nzents_saved = nzents;

        if (zone_list(zids, &nzents) != 0) {
                free(zids);
                return (-1);
        }
        if (nzents > nzents_saved) {
                /* list changed, try again */
                free(zids);
                goto again;
        }

        for (i = 0; i < nzents; i++) {
                m_label_t       test_sl;

                if (zids[i] == GLOBAL_ZONEID)
                        continue;

                if (zone_getattr(zids[i], ZONE_ATTR_SLBL, &test_sl,
                    sizeof (m_label_t)) < 0)
                        continue;       /* Badly configured zone info */

                if (blequal(label, &test_sl) != 0) {
                        zoneid = zids[i];
                        free(zids);
                        return (zoneid);
                }
        }
        free(zids);
        errno = EINVAL;
        return (-1);
}

/*
 * Get zoneroot for a zoneid
 */

char *
getzonerootbyid(zoneid_t zoneid)
{
        char zoneroot[MAXPATHLEN];

        if (zone_getattr(zoneid, ZONE_ATTR_ROOT, zoneroot,
            sizeof (zoneroot)) == -1) {
                return (NULL);
        }

        return (strdup(zoneroot));
}

/*
 * Get zoneroot for a zonename
 */

char *
getzonerootbyname(const char *zone)
{
        zoneid_t        zoneid;

        if ((zoneid = getzoneidbyname(zone)) == -1)
                return (NULL);
        return (getzonerootbyid(zoneid));
}

/*
 * Get zoneroot for a label
 */

char *
getzonerootbylabel(const m_label_t *label)
{
        zoneid_t        zoneid;

        if ((zoneid = getzoneidbylabel(label)) == -1)
                return (NULL);
        return (getzonerootbyid(zoneid));
}

/*
 * Get label of path relative to global zone
 *
 * This function must be called from the global zone
 */

m_label_t *
getlabelbypath(const char *path)
{
        m_label_t       *slabel;
        zoneid_t        *zids;
        uint_t          nzents;
        uint_t          nzents_saved;
        int             i;

        if (getzoneid() != GLOBAL_ZONEID) {
                errno = EINVAL;
                return (NULL);
        }

        nzents = 0;
        if (zone_list(NULL, &nzents) != 0)
                return (NULL);

again:
        /* Add a small amount of padding to avoid loops */
        nzents += 8;
        zids = malloc(nzents * sizeof (zoneid_t));
        if (zids == NULL)
                return (NULL);

        nzents_saved = nzents;

        if (zone_list(zids, &nzents) != 0) {
                free(zids);
                return (NULL);
        }
        if (nzents > nzents_saved) {
                /* list changed, try again */
                free(zids);
                goto again;
        }

        slabel = m_label_alloc(MAC_LABEL);
        if (slabel == NULL) {
                free(zids);
                return (NULL);
        }

        for (i = 0; i < nzents; i++) {
                char    zoneroot[MAXPATHLEN];
                int     zonerootlen;

                if (zids[i] == GLOBAL_ZONEID)
                        continue;

                if (zone_getattr(zids[i], ZONE_ATTR_ROOT, zoneroot,
                    sizeof (zoneroot)) == -1)
                        continue;       /* Badly configured zone info */

                /*
                 * Need to handle the case for the /dev directory which is
                 * parallel to the zone's root directory.  So we back up
                 * 4 bytes - the strlen of "root".
                 */
                if ((zonerootlen = strlen(zoneroot)) <= 4)
                        continue;       /* Badly configured zone info */
                if (strncmp(path, zoneroot, zonerootlen - 4) == 0) {
                        /*
                         * If we get a match, the file is in a labeled zone.
                         * Return the label of that zone.
                         */
                        if (zone_getattr(zids[i], ZONE_ATTR_SLBL, slabel,
                            sizeof (m_label_t)) < 0)
                                continue;       /* Badly configured zone info */

                        free(zids);
                        return (slabel);
                }
        }
        free(zids);
        bsllow(slabel);
        return (slabel);
}