root/usr/src/cmd/newgrp/newgrp.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 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/*        All Rights Reserved   */

/*
 * newgrp [-l | -] [group]
 *
 * rules
 *      if no arg, group id in password file is used
 *      else if group id == id in password file
 *      else if login name is in member list
 *      else if password is present and user knows it
 *      else too bad
 */
#include <stdio.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <crypt.h>
#include <string.h>
#include <stdlib.h>
#include <locale.h>
#include <syslog.h>
#include <unistd.h>

#include <bsm/adt_event.h>

#define SHELL   "/usr/bin/sh"

#define PATH    "PATH=:/usr/bin:"
#define SUPATH  "PATH=:/usr/sbin:/usr/bin"
#define ELIM    128

char    PW[] = "newgrp: Password: ";
char    NG[] = "newgrp: Sorry";
char    PD[] = "newgrp: Permission denied";
char    UG[] = "newgrp: Unknown group";
char    NS[] = "newgrp: You have no shell";

char *homedir;
char *logname;

char *envinit[ELIM];
extern char **environ;
char *path = PATH;
char *supath = SUPATH;

void error(char *s) __NORETURN;
static void warn(char *s);
void usage(void);

int
main(int argc, char *argv[])
{
        struct passwd *p;
        gid_t chkgrp();
        int eflag = 0;
        int flag;
        uid_t uid;
        char *shell, *dir, *name;
        size_t len;

#ifdef  DEBUG
        chroot(".");
#endif

        (void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN)               /* Should be defined by cc -D */
#define TEXT_DOMAIN     "SYS_TEST"      /* Use this only if it weren't */
#endif
        (void) textdomain(TEXT_DOMAIN);

        if ((p = getpwuid(getuid())) == NULL)
                error(NG);
        endpwent();

        while ((flag = getopt(argc, argv, "l")) != EOF) {
                switch (flag) {
                case 'l':
                        eflag++;
                        break;

                default:
                        usage();
                        break;
                }
        }

        argc -= optind;
        argv = &argv[optind];

        if (argc > 0 && *argv[0] == '-') {
                if (eflag)
                        usage();
                eflag++;
                argv++;
                --argc;
        }

        if (argc > 0)
                p->pw_gid = chkgrp(argv[0], p);

        uid = p->pw_uid;

        len = strlen(p->pw_dir) + 1;
        if ((dir = (char *)malloc(len)) == NULL)
                error("newgrp: Memory request failed");
        (void) strncpy(dir, p->pw_dir, len);
        len = strlen(p->pw_name) + 1;
        if ((name = (char *)malloc(len)) == NULL)
                error("newgrp: Memory request failed");
        (void) strncpy(name, p->pw_name, len);

        if (setgid(p->pw_gid) < 0 || setuid(getuid()) < 0)
                error(NG);

        if (!*p->pw_shell) {
                if ((shell = getenv("SHELL")) != NULL) {
                        p->pw_shell = shell;
                } else {
                        p->pw_shell = SHELL;
                }
        }

        if (eflag) {
                char *simple;

                len = strlen(dir) + 6;
                if ((homedir = (char *)malloc(len)) == NULL)
                        error("newgrp: Memory request failed");
                (void) snprintf(homedir, len, "HOME=%s", dir);
                len = strlen(name) + 9;
                if ((logname = (char *)malloc(len)) == NULL)
                        error("newgrp: Memory request failed");
                (void) snprintf(logname, len, "LOGNAME=%s", name);


                envinit[2] = logname;
                (void) chdir(dir);
                envinit[0] = homedir;
                if (uid == 0)
                        envinit[1] = supath;
                else
                        envinit[1] = path;
                envinit[3] = NULL;
                environ = envinit;

                len = strlen(p->pw_shell) + 2;
                if ((shell = (char *)malloc(len)) == NULL)
                        error("newgrp: Memory request failed");
                (void) snprintf(shell, len, "-%s", p->pw_shell);
                simple = strrchr(shell, '/');
                if (simple) {
                        *(shell+1) = '\0';
                        shell = strcat(shell, ++simple);
                }
        }
        else
                shell = p->pw_shell;

        (void) execl(p->pw_shell, shell, NULL);
        warn(NS);
        return (1);
}

static void
warn(char *s)
{
        (void) fprintf(stderr, "%s\n", gettext(s));
}

void
error(char *s)
{
        warn(s);
        exit(1);
}

void
put_event(char *gname, int sorf)
{
        adt_session_data_t      *ah;
        adt_event_data_t        *event;

        if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) {
                syslog(LOG_AUTH | LOG_ALERT,
                    "adt_start_session(ADT_newgrp_login): %m");
        }
        if ((event = adt_alloc_event(ah, ADT_newgrp_login)) == NULL) {
                syslog(LOG_AUTH | LOG_ALERT,
                    "adt_alloc_event(ADT_newgrp_login): %m");
        } else {
                event->adt_newgrp_login.groupname = gname;
        }

        if (adt_put_event(event, sorf, sorf) != 0) {
                syslog(LOG_AUTH | LOG_ALERT,
                    "adt_put_event(ADT_newgrp, %d): %m", sorf);
        }
        adt_free_event(event);
        (void) adt_end_session(ah);
}

gid_t
chkgrp(gname, p)
char    *gname;
struct  passwd *p;
{
        char **t;
        struct group *g;

        g = getgrnam(gname);
        endgrent();
        if (g == NULL) {
                warn(UG);
                put_event(gname, ADT_FAILURE);
                return (getgid());
        }
        if (p->pw_gid == g->gr_gid || getuid() == 0) {
                put_event(gname, ADT_SUCCESS);
                return (g->gr_gid);
        }
        for (t = g->gr_mem; *t; ++t) {
                if (strcmp(p->pw_name, *t) == 0) {
                        put_event(gname, ADT_SUCCESS);
                        return (g->gr_gid);
                }
        }
        if (*g->gr_passwd) {
                if (!isatty(fileno(stdin))) {
                        put_event(gname, ADT_FAILURE);
                        error(PD);
                }
                if (strcmp(g->gr_passwd,
                    crypt(getpassphrase(PW), g->gr_passwd)) == 0) {
                        put_event(gname, ADT_SUCCESS);
                        return (g->gr_gid);
                }
        }
        put_event(gname, ADT_FAILURE);
        warn(NG);
        return (getgid());
}

void
usage(void)
{
        (void) fprintf(stderr, gettext(
            "usage: newgrp [-l | -] [group]\n"));
        exit(2);
}