root/usr/src/lib/libidmap/common/directory_helper.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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
 */

/*
 * Some helper routines for directory lookup.  These offer functions that
 * you could implement yourself on top of the generic routines, but since
 * they're a common request we implement them here.  (Well, OK, we cheat a bit
 * and call an internal routine to do the dirty work to reduce code
 * duplication, but you could still implement them using the generic routines.)
 */

#include <stdio.h>
#include <string.h>
#include <libuutil.h>
#include <rpcsvc/idmap_prot.h>
#include "directory.h"
#include "directory_private.h"
#include "directory_library_impl.h"
#include "sidutil.h"

/*
 * Given a username, return a text-form SID.
 *
 * The SID must be free()ed by the caller.
 *
 * d, if non-NULL, specifies an existing directory-search context.
 * If NULL, a temporary one will be created.
 */
directory_error_t
directory_sid_from_name_common(
    directory_t d,
    char *name,
    char *type,
    char **sid,
    uint64_t *classes)
{
        directory_t d1 = NULL;
        static char *attrs[] = {
                "objectSid",
                "objectClass",
                NULL,
        };
        directory_entry_t *ret_list = NULL;
        directory_error_t de;
        struct ret_sid {
                sid_t **objectSid;
                char **objectClass;
        } *ret_sid;

        /* Prep for error cases. */
        *sid = NULL;
        if (classes != NULL)
                *classes = 0;

        if (d == NULL) {
                de = directory_open(&d1);
                if (de != NULL)
                        goto out;
        } else {
                d1 = d;
        }

        de = directory_get_v(d1, &ret_list, &name, 1, type, attrs);
        if (de != NULL)
                goto out;
        if (ret_list[0].err != NULL) {
                de = ret_list[0].err;
                ret_list[0].err = NULL;
                goto out;
        }

        ret_sid = (struct ret_sid *)ret_list[0].attrs;
        if (ret_sid == NULL)
                goto out;

        if (ret_sid->objectSid != NULL &&
            ret_sid->objectSid[0] != NULL) {
                char text_sid[SID_STRSZ+1];
                sid_from_le(ret_sid->objectSid[0]);
                sid_tostr(ret_sid->objectSid[0], text_sid);
                *sid = strdup(text_sid);
                if (*sid == NULL)
                        goto nomem;
        }

        if (ret_sid->objectClass != NULL &&
            classes != NULL)
                *classes = class_bitmap(ret_sid->objectClass);

        goto out;

nomem:
        de = directory_error("ENOMEM.directory_sid_from_name_common",
            "Insufficient memory retrieving data about SID", NULL);

out:
        directory_free(ret_list);
        if (d == NULL)
                directory_close(d1);
        return (de);
}

directory_error_t
directory_sid_from_name(
    directory_t d,
    char *name,
    char **sid,
    uint64_t *classes)
{
        return (directory_sid_from_name_common(d, name, DIRECTORY_ID_NAME, sid,
            classes));
}

directory_error_t
directory_sid_from_user_name(directory_t d, char *name, char **sid)
{
        return (directory_sid_from_name_common(d, name, DIRECTORY_ID_USER, sid,
            NULL));
}

directory_error_t
directory_sid_from_group_name(directory_t d, char *name, char **sid)
{
        return (directory_sid_from_name_common(d, name, DIRECTORY_ID_GROUP, sid,
            NULL));
}

/*
 * Given a name or text-format SID, return a user@domain.
 *
 * The user@domain returned must be free()ed by the caller.
 *
 * Returns NULL and sets *name to NULL if no error occurred but the specified
 * entity does not exist.
 *
 * d, if non-NULL, specifies an existing directory-search context.
 * If NULL, a temporary one will be created.
 */
static
directory_error_t
directory_canon_common(
    directory_t d,
    char *id,
    char *id_type,
    char **canon,
    uint64_t *classes)
{
        directory_t d1 = NULL;
        directory_entry_t *ret_list = NULL;
        directory_error_t de;
        /*
         * Attributes required to generate a canonical name, in named-list and
         * structure form.
         */
        static char *attrs[] = {
                "x-sun-canonicalName",
                "objectClass",
                NULL,
        };

        struct canon_name_ret {
                char **x_sun_canonicalName;
                char **objectClass;
        } *ret_name;

        /* Prep for error cases. */
        *canon = NULL;
        if (classes != NULL)
                *classes = 0;

        if (d == NULL) {
                de = directory_open(&d1);
                if (de != NULL)
                        goto out;
        } else {
                d1 = d;
        }

        de = directory_get_v(d1, &ret_list, &id, 1, id_type, attrs);
        if (de != NULL)
                goto out;
        if (ret_list[0].err != NULL) {
                de = ret_list[0].err;
                ret_list[0].err = NULL;
                goto out;
        }

        ret_name = (struct canon_name_ret *)ret_list[0].attrs;
        if (ret_name == NULL)
                goto out;

        if (ret_name->x_sun_canonicalName != NULL &&
            ret_name->x_sun_canonicalName[0] != NULL) {
                *canon = strdup(ret_name->x_sun_canonicalName[0]);
                if (*canon == NULL)
                        goto nomem;
        }

        if (ret_name->objectClass != NULL &&
            classes != NULL)
                *classes = class_bitmap(ret_name->objectClass);

        goto out;

nomem:
        de = directory_error("ENOMEM.directory_canon_common",
            "Insufficient memory retrieving data about name", NULL);

out:
        directory_free(ret_list);
        if (d == NULL)
                directory_close(d1);
        return (de);
}

directory_error_t
directory_name_from_sid(
    directory_t d,
    char *sid,
    char **canon,
    uint64_t *classes)
{
        return (directory_canon_common(d, sid, DIRECTORY_ID_SID, canon,
            classes));
}

directory_error_t
directory_canon_from_name(
    directory_t d,
    char *name,
    char **canon,
    uint64_t *classes)
{
        return (directory_canon_common(d, name, DIRECTORY_ID_NAME, canon,
            classes));
}

directory_error_t
directory_canon_from_user_name(directory_t d, char *name, char **canon)
{
        return (
            directory_canon_common(d, name, DIRECTORY_ID_USER, canon, NULL));
}

directory_error_t
directory_canon_from_group_name(directory_t d, char *name, char **canon)
{
        return (
            directory_canon_common(d, name, DIRECTORY_ID_GROUP, canon, NULL));
}

boolean_t
is_in_list(char **list, char *val)
{
        for (; *list != NULL; list++) {
                if (uu_strcaseeq(*list, val))
                        return (B_TRUE);
        }
        return (B_FALSE);
}

uint64_t
class_bitmap(char **objectClass)
{
        uint64_t ret = 0;

        for (; *objectClass != NULL; objectClass++) {
                if (uu_strcaseeq(*objectClass, "user") ||
                    uu_strcaseeq(*objectClass, "posixAccount"))
                        ret |= DIRECTORY_CLASS_USER;

                if (uu_strcaseeq(*objectClass, "group") ||
                    uu_strcaseeq(*objectClass, "posixGroup"))
                        ret |= DIRECTORY_CLASS_GROUP;
        }

        return (ret);
}