root/usr/src/cmd/auths/auths.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) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pwd.h>
#include <string.h>
#include <deflt.h>
#include <libintl.h>
#include <locale.h>
#include <user_attr.h>
#include <prof_attr.h>
#include <auth_attr.h>

#define EXIT_OK         0
#define EXIT_FATAL      1
#define EXIT_NON_FATAL  2

#ifndef TEXT_DOMAIN                     /* Should be defined by cc -D */
#define TEXT_DOMAIN     "SYS_TEST"
#endif

#define INCRAUTHS       512

typedef struct cbs {
        int     auth_cnt;
        int     auth_max;
        char    **auths;
} cbs_t;

static int show_auths(char *, int);
static int add_auth(const char *, void *, void *);
static void free_auths(cbs_t *);
static void simplify(cbs_t *);

static char *progname = "auths";

int
main(int argc, char *argv[])
{
        int             status = EXIT_OK;

        (void) setlocale(LC_ALL, "");
        (void) textdomain(TEXT_DOMAIN);

        switch (argc) {
        case 1:
                status = show_auths(NULL, 0);
                break;
        case 2:
                status = show_auths(argv[argc-1], 0);
                break;
        default:
                while (*++argv) {
                        status = show_auths(*argv, 1);
                        if (status == EXIT_FATAL) {
                                break;
                        }
                }
                break;
        }

        status = (status == EXIT_OK) ? status : EXIT_FATAL;
        return (status);
}

static int
show_auths(char *username, int print_name)
{
        int             status = EXIT_OK;
        struct passwd   *pw;
        int             i;
        cbs_t           cbs = { 0, 0, NULL };

        if (username == NULL) {
                if ((pw = getpwuid(getuid())) == NULL) {
                        status = EXIT_NON_FATAL;
                        (void) fprintf(stderr, "%s: ", progname);
                        (void) fprintf(stderr, gettext("No passwd entry\n"));
                        return (status);
                }
                username = pw->pw_name;
        } else if (getpwnam(username) == NULL) {
                status = EXIT_NON_FATAL;
                (void) fprintf(stderr, "%s: %s : ", progname, username);
                (void) fprintf(stderr, gettext("No such user\n"));
                return (status);
        }

        (void) _enum_auths(username, add_auth, NULL, &cbs);

        if (cbs.auth_cnt == 0)
                status = EXIT_NON_FATAL;

        if (status == EXIT_NON_FATAL) {
                (void) fprintf(stderr, "%s: %s: ", progname, username);
                (void) fprintf(stderr, gettext("No authorizations\n"));
        } else {
                simplify(&cbs);

                if (print_name)
                        (void) printf("%s: ", username);

                /* print out the auths */
                for (i = 0; i < cbs.auth_cnt - 1; i++)
                        (void) printf("%s,", cbs.auths[i]);

                /* print out the last entry, without the comma */
                (void) printf("%s\n", cbs.auths[cbs.auth_cnt - 1]);

                /* free memory allocated for authorizations */
                free_auths(&cbs);
        }

        return (status);
}

/*ARGSUSED*/
static int
add_auth(const char *authname, void *ctxt, void *res)
{
        cbs_t   *cbs = res;

        if (cbs->auth_cnt >= cbs->auth_max) {
                cbs->auth_max += INCRAUTHS;
                cbs->auths = realloc(cbs->auths,
                    cbs->auth_max * sizeof (char *));

                if (cbs->auths == NULL) {
                        (void) fprintf(stderr, "%s: ", progname);
                        (void) fprintf(stderr, gettext("Out of memory\n"));
                        exit(1);
                }
        }

        cbs->auths[cbs->auth_cnt] = strdup(authname);
        cbs->auth_cnt++;

        return (0);
}

static void
free_auths(cbs_t *cbs)
{
        int i;

        for (i = 0; i < cbs->auth_cnt; i++)
                free(cbs->auths[i]);

        free(cbs->auths);
}

/* We have always ignored .grant in auths(1) */
static boolean_t
auth_match(const char *pattern, const char *auth)
{
        size_t len = strlen(pattern);

        if (pattern[len - 1] != KV_WILDCHAR)
                return (B_FALSE);

        return (strncmp(pattern, auth, len - 1) == 0);
}

static int
mstrptr(const void *a, const void *b)
{
        char *const *ap = a;
        char *const *bp = b;

        return (strcmp(*ap, *bp));
}

/*
 * Simplify the returned authorizations: sort and match wildcards;
 * we're using here that "*" sorts before any other character.
 */
static void
simplify(cbs_t *cbs)
{
        int rem, i;

        /* First we sort */
        qsort(cbs->auths, cbs->auth_cnt, sizeof (cbs->auths[0]), mstrptr);

        /*
         * Then we remove the entries which match a later entry.
         * We walk the list, with "i + rem + 1" the cursor for the possible
         * candidate for removal. With "rem" we count the removed entries
         * and we copy while we're looking for duplicate/superfluous entries.
         */
        for (i = 0, rem = 0; i < cbs->auth_cnt - rem - 1; ) {
                if (strcmp(cbs->auths[i], cbs->auths[i + rem + 1]) == 0 ||
                    strchr(cbs->auths[i], KV_WILDCHAR) != NULL &&
                    auth_match(cbs->auths[i], cbs->auths[i + rem + 1])) {
                        free(cbs->auths[i + rem + 1]);
                        rem++;
                } else {
                        i++;
                        if (rem > 0)
                                cbs->auths[i] = cbs->auths[i + rem];
                }
        }

        cbs->auth_cnt -= rem;
}