[alt.sources] SCO 'su' replacement

em@dce.ie (Eamonn McManus) (04/11/91)

slootman@dri.nl (Paul Slootman) writes:
>Me too! I wonder why this "luid" business is forced on us; even commands
>such as "ct" don't work (you get a login, but then it complains about
>the user id... *&^*$#^%#&^%#  :-(  )

Yes, the luid as shipped is unquestionably the stupidest of the many
mistakes embodied in SCO `security'.

Here is our replacement su again, unchanged since the last time it was
posted.  As I understand it, it's now rendered largely unnecessary by
the free Support Level Supplement "unx257", entitled "UNIX Security
Supplement".  If someone in Europe wants to UUCP this from SCO and send
it to me, I won't complain.  Meanwhile our su is still a workable
solution.

I'm also including a replacement newgrp.  The standard one, in 3.2.0 at
least, suffers from the bug whereby set_auth_parameters() trashes the
umask.  Hence standard newgrp leaves you with a umask of 077.  We don't
bother doing any C2-ish stuff in newgrp, so we don't get this bug.

Remember, you use this software at your own risk.  We have no reason to
suppose that it is insecure, but if you are concerned you should not run
it.

,
Eamonn

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  README Makefile su.c oksetluid.c uxnlist.c newgrp.c
# Wrapped by em@dce.ie on Thu Apr 11 12:30:42 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f README -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"README\"
else
echo shar: Extracting \"README\" \(2207 characters\)
sed "s/^X//" >README <<'END_OF_README'
XREADME for SCO Unix $Revision: 1.1 $.
X
XThis is a replacement su for SCO Unix.  It replaces the complicated rules
Xfor who can su to whom with just two: all except superuser must supply the
Xcorrect password to su to another user; and if a group `wheel' exists then
Xyou must be a member of it to su to root.  (For the wheel to be effective
Xyou should restrict root logins; see login(M).)
X
XThis su also sets the login uid to the new user.  This should not be necessary,
Xbut a number of programs such as `at' and `crontab' refuse to run if the login
Xuid is different from the real uid.  To add to this stupidity, the setluid(S)
Xcall cannot change the login uid once set, even if called as superuser.  Our
Xreplacement in oksetluid.c changes the login uid by hitting it in /dev/kmem.
XThis means that YOU MUST RECOMPILE SU FOR NEW OS RELEASES.  The offset of the
Xlogin uid might change.
X
XThe configuration file /etc/default/su is read for the settings SULOG and
XCONSOLE.  See the SCO manual page.  Other settings are ignored.
X
XThis program compiles with the GNU C compiler (gcc), the Microsoft compiler
X(cc) and the AT&T compiler (rcc).  The Microsoft compiler will produce three
Xwarnings: one is due to incorrect handling of ?: (use gcc); another is due
Xto an incorrect prototype in <pwd.h> (fix the include file); and the third
Xis due to a wrong definition of offsetof() in <stddef.h> (use gcc or fix the
Xinclude file).  These warnings can be ignored.
X
XIt should be possible to compile su on other Unix systems, if you give
X-DSecureWare=0 on the command line.  We have compiled but not tested it
Xon some other systems.
X
XWe have done our best to make su secure.  However, if you are very concerned
Xabout security this program is not for you; you probably want the SCO C2
Xsecurity, even though it is severely misguided in many ways.  THE AUTHORS
XACCEPT NO RESPONSIBILITY FOR DAMAGE OR LOSS RESULTING FROM THIS PROGRAM.
X
XThe file uxnlist.c contains a function that is useful in its own right.
XSee the comments there for details.
X
XThis program is not copyrighted and may be used freely.  We hope you find
Xit useful.
X
XEamonn McManus <em@dce.ie>
XCharles Bryant <ch@dce.ie>
XDatacode Communications Ltd, Dublin, Ireland.
END_OF_README
if test 2207 -ne `wc -c <README`; then
    echo shar: \"README\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f Makefile -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"Makefile\"
else
echo shar: Extracting \"Makefile\" \(547 characters\)
sed "s/^X//" >Makefile <<'END_OF_Makefile'
X# Makefile - for su and newgrp
X# $Id: Makefile,v 1.2 91/04/11 13:17:43 em Exp Locker: em $
X
XCC = gcc -Wall
XCFLAGS = -O
XDESTDIR = /usr/local
XLIBS = -lprot -lc_s
X
Xall: su newgrp
X
Xsu: su.o oksetluid.o uxnlist.o
X	$(CC) $(CFLAGS) su.o oksetluid.o uxnlist.o $(LIBS) -o su
Xnewgrp: newgrp.o
X	$(CC) $(CFLAGS) newgrp.o -o newgrp
X
Xinstall: $(DESTDIR)/su $(DESTDIR)/newgrp
X	chown root $(DESTDIR)/su $(DESTDIR)/newgrp
X	chmod u+s $(DESTDIR)/su $(DESTDIR)/newgrp
X	touch install
X$(DESTDIR)/su: su
X	cp su $(DESTDIR)
X$(DESTDIR)/newgrp: newgrp
X	cp newgrp $(DESTDIR)
END_OF_Makefile
if test 547 -ne `wc -c <Makefile`; then
    echo shar: \"Makefile\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f su.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"su.c\"
else
echo shar: Extracting \"su.c\" \(13518 characters\)
sed "s/^X//" >su.c <<'END_OF_su.c'
X/* su.c - proper su for horrible value-added SCO Unix. */
X
X/* By Eamonn McManus, Datacode Communications Ltd <em@dce.ie>, August 1990.
X   This program is not copyrighted.
X
X   Usage: su [-s shell] [-] [user [shell-args]].
X
X   Sets user id to the named user (default root) and execs the named shell
X   (default their login shell).  If the invoking user is not root, the
X   specified user's login password must be supplied, and the -s option is
X   not allowed.  The shell-args if supplied are usually -c "commandline"
X   to do just that one command under control of the shell.
X
X   Usage differs from standard su in that you cannot say, e.g., "su -c cmd";
X   you must specify a user to su to except if there are no shell-args.
X
X   If a group called `wheel' exists in /etc/group, only members of that
X   group can su to root.  The name `wheel' dates from TOPS-20 at least,
X   but I don't know what it means.  4.3 BSD re-invented it.
X
X   Standard su clears the environment to a few standard entries when you
X   do `su -'.  We don't bother doing that, but just change a few key
X   entries.
X*/
X
X#ifndef __STDC__
X#define const /* nothing */
X#endif
X
X#ifndef lint
Xstatic const char rcsid[] =
X	"$Id: su.c,v 1.1 90/09/28 16:22:21 em Release $";
X#endif
X
X#ifndef SecureWare
X#define SecureWare 1	/* Appears to be necessary, if regrettable. */
X#endif
X
X#include <stdio.h>
X#ifdef __STDC__
X#include <stdlib.h>
X#endif
X#include <string.h>
X#include <ctype.h>
X#include <pwd.h>
X#include <grp.h>
X#include <sys/types.h>
X#include <sys/wait.h>
X#if SecureWare
X#include <sys/security.h>
X#include <prot.h>
X#endif
X#include <errno.h>
X
X#define DEFAULTS_FILE "/etc/default/su"
X
X#ifdef __STDC__	/* <prototypes.h> is worthless, conflicts with <stdlib.h>. */
X
Xextern time_t time(long *tloc);
Xextern const char *ctime(const time_t *clock);
Xextern const char *ttyname(int fildes);
Xextern const char *oksetluid(int uid);
Xextern char *getpasswd(const char *prompt, int max_size);
Xextern char *crypt(const char *key, const char *salt);
X/* Declare everything for gcc -Wall. */
Xextern int getuid(void), fork(void), setgid(int gid), setuid(int uid);
Xextern int putenv(const char *envstring);
Xextern unsigned sleep(unsigned seconds);
Xextern int execv(const char *path, const char *const* arg);
Xextern pid_t waitpid(pid_t pid, int *status, int options);
Xextern int umask(int cmask);
Xextern int chdir(const char *dir);
X
Xtypedef void *pointer;
X
Xstatic void su(const struct passwd *pwd, int islogin, const char *shell,
X	       const char **shargs);
Xstatic void crash(const char *why, const char *what);
Xstatic void logsu(const char *sutype, const char *sumsg, const char *suarg);
Xstatic int readdefault(const char *defaultname);
Xstatic const char *defaultvalue(const char *settingname);
Xstatic char *xstrdup(const char *str);
Xstatic pointer xmalloc(int size);
Xstatic void xputenv(const char *name, const char *value);
Xstatic struct passwd *xgetpwnam(const char *name);
Xstatic struct passwd *xgetpwuid(int uid);
Xstatic struct passwd *whoami(void);
X
X#else	/* !STDC */
X
Xextern char *getpass(), *getpasswd();
Xextern char *crypt();
Xextern char *ttyname();
Xextern char *oksetluid();
Xextern char *malloc();
X
Xtypedef char *pointer;
X
Xstatic void su(), logsu(), crash(), xputenv();
Xstatic char *defaultvalue(), *xstrdup();
Xstatic pointer xmalloc();
Xstatic struct passwd *xgetpwnam(), *xgetpwuid(), *whoami();
X
X#endif	/* !STDC */
X
X
Xconst char *progname = "su";
X
X
Xint main(argc, argv)
Xint argc;
Xconst char **const argv;
X{
X    int i;
X    int login = 0;
X    struct passwd *pwd;
X    const char *user = "root", *shell = NULL;
X    if (argc > 0)
X	progname = argv[0];
X    (void) readdefault(DEFAULTS_FILE);
X    for (i = 1; i < argc && argv[i][0] == '-'; i++) {
X	switch (argv[i][1]) {
X	case '\0':
X	    login = 1; break;
X	case 's':	/* Specify different shell from user's own. */
X	    if (argv[i][2])
X		shell = &argv[i][2];
X	    else {
X		if (++i >= argc)
X		    goto usage;
X		shell = argv[i];
X	    }
X	    if (getuid() != 0) {
X		logsu("BADSU", "tried to say -s ", shell);
X		crash("*-s", "only superuser can specify -s");
X	    }
X	    break;
X	default:
Xusage:
X	    fprintf(stderr, "Usage: %s [-s shell] [-] [user [shell-args]]\n",
X		    progname);
X	    exit(1);
X	}
X    }
X    if (i < argc) {
X	user = argv[i];
X	i++;
X    }
X    if ((pwd = xgetpwnam(user)) == NULL)
X	crash("*no such user", user);
X    /* Root isn't asked for a password; everyone else is unless su'ing
X       to themselves. */
X    if (getuid() != 0 && getuid() != pwd->pw_uid) {
X	char *pass, *crpass, *realpass;
X#if SecureWare
X	struct pr_passwd *ugh;
X	int origumask;
X#endif
X	/* If becoming root, make sure we're on the wheel (whatever that
X	   means). */
X	if (pwd->pw_uid == 0) {
X	    struct group *grp = getgrnam("wheel");
X	    if (grp != NULL) {
X		struct passwd *me = whoami();
X		char **mem;
X		for (mem = grp->gr_mem; *mem != NULL; mem++)
X		    if (strcmp(*mem, me->pw_name) == 0)
X			break;
X		if (*mem == NULL) {
X		    logsu("BADSU", "to root ", "but not on wheel");
X		    fprintf(stderr, "Not in wheel group\n");
X		    exit(2);
X		}
X	    }
X	}
X#if !SecureWare	/* Standard su, untested. */
X	if ((pass = getpass("Password:")) == NULL)
X	    crash("getpass", user);
X	if ((crpass = crypt(pass, pwd->pw_passwd)) == NULL)
X	    crash("crypt", user);
X	realpass = pwd->pw_passwd;
X#else /* SecureWare */
X	origumask = umask(0); (void) umask(origumask);
X	set_auth_parameters(argc, argv);
X	/* OBNOXIOUS MISFEATURE: above call sets the umask to 077.  If I want
X	   the umask to be changed, I'll ASK for it to be changed.  Grrr.  */
X	(void) umask(origumask);
X	if ((ugh = getprpwnam(user)) == NULL)
X	    crash("get protected password", user);
X	if ((pass = getpasswd("Password:", AUTH_MAX_PASSWD_LENGTH)) == NULL)
X	    crash("getpasswd", user);
X	/* Use the undocumented bigcrypt() routine which crypts a password
X	   in pieces if it is longer than 8 characters. */
X	if ((crpass = bigcrypt(pass, ugh->ufld.fd_encrypt)) == NULL)
X	    crash("crypt", user);
X	    /* I don't think crypt can fail, but may as well test. */
X#if 0	/* DEBUG */
Xfprintf(stderr, "user %s entered %s crypted %s really %s\n",
X	user, pass, crpass, ugh->ufld.fd_encrypt);
X#endif
X	realpass = ugh->ufld.fd_encrypt;
X#endif /* SecureWare */
X	if (strcmp(crpass, realpass) != 0) {
X	    logsu("BADSU", "wrong password for ", user);
X	    sleep(1);	/* Make dictionary searches etc harder. */
X	    fprintf(stderr, "Sorry\n");
X	    exit(2);
X	}
X    }
X    logsu("SU", "to ", user);
X    /* We overwrite argv[i - 1] with the name of the shell so we can use
X       the rest of the array as an argument to execv().
X       The stupid Microsoft compiler warns about the ?: below.  Ignore it.
X       Better still, use GNU C. */
X    su(pwd, login, shell ? shell : pwd->pw_shell, argv + i - 1);
X    return 0;	/* Not reached. */
X}
X
X
X/* su to the named user and uid.  If islogin, pretend we are logging in,
X   to the extent of setting HOME and LOGNAME environment variables,
X   prefixing argv[0] of the shell with "-", and changing to the new
X   user's directory.  */
Xstatic void su(pwd, islogin, shell, shargs)
Xconst struct passwd *pwd;
Xint islogin;
Xconst char *shell;
Xconst char **shargs;
X{
X    if (shell == NULL || *shell == '\0')
X	shell = "/bin/sh";
X    shargs[0] = strrchr(shell, '/');
X    if (shargs[0] != NULL)
X	shargs[0]++;
X    else shargs[0] = shell;
X    if (islogin) {	/* su - user */
X	if (strcmp(shargs[0], "sh") == 0)
X	    shargs[0] = "-su";	/* As per man page. */
X	else {
X	    char *p = xmalloc(strlen(shargs[0]) + sizeof("-"));
X	    (void) sprintf(p, "-%s", shargs[0]);
X	    shargs[0] = p;
X	}
X	/* Change environment; probably not necessary as shell will do
X	   this because it's invoked with the leading "-".  */
X	xputenv("LOGNAME", pwd->pw_name);
X    }
X    xputenv("HOME", pwd->pw_dir);
X    xputenv("SHELL", shell);
X#if SecureWare
X    {
X	const char *err;
X	if ((err = oksetluid(pwd->pw_uid)) != NULL)
X	    crash("setluid", err);
X    }
X#endif
X    if (setgid(pwd->pw_gid) < 0)
X	crash("setgid", pwd->pw_name);
X    if (setuid(pwd->pw_uid) < 0)
X	crash("setuid", pwd->pw_name);
X    if (islogin && chdir(pwd->pw_dir) < 0)
X	perror(pwd->pw_dir);	/* But su anyway. */
X    execv(shell, shargs);
X    crash("execl", shell);
X}
X
X
X/* Log an su attempt.  The three fields are printed as well as the current
X   user, their tty, and the time. */
Xstatic void logsu(sutype, sumsg, suarg)
Xconst char *sutype, *sumsg, *suarg;
X{
X    const char *const*logp;
X    static const char *const logfiles[] = {"SULOG", "CONSOLE", NULL};
X    struct passwd *pwd;
X    char *user;
X    if ((pwd = whoami()) == NULL)
X	user = "(unknown)";
X    else user = pwd->pw_name;
X    for (logp = logfiles; *logp != NULL; logp++) {
X	FILE *f;
X	const char *p;
X	time_t now;
X	if ((p = defaultvalue(*logp)) == NULL)
X	    continue;
X	if ((f = fopen(p, "a")) == NULL) {
X	    /* This isn't a fatal error, lest we lock ourselves out of su.
X	       Giving the message to the su'ing user is imperfect but will
X	       do.  If you want to know why your log isn't getting any su
X	       messages you can try su. */
X	    (void) fprintf(stderr, "%s: append to log ", progname);
X	    perror(p);
X	    continue;
X	}
X	(void) fprintf(f, "%s: %s%s: user %s", sutype, sumsg, suarg, user);
X	if ((p = ttyname(0)) != NULL)
X	    (void) fprintf(f, " on %s", p);
X	(void) time(&now);
X	(void) fprintf(f, ", %s", ctime(&now));	/* ctime() has trailing \n. */
X	(void) fclose(f);
X    }
X}
X
X
X/* Print message "why: what" to stderr.  The what string is printed with
X   perror unless the why string begins with *.  The * is not printed.
X   Exits the program.  */
Xstatic void crash(why, what)
Xconst char *why, *what;
X{
X    fprintf(stderr, "%s: ", progname);
X    if (*why == '*') {
X	fprintf(stderr, "%s", why + 1);
X	if (what)
X	    fprintf(stderr, ": %s", what);
X	putc('\n', stderr);
X    } else if (what) {
X	fprintf(stderr, "%s: ", why);
X	perror(what);
X    } else perror(why);
X    exit(1);
X}
X
X
Xstatic const struct setting {
X	const char *name, *value;
X	const struct setting *next;
X} *settingp;
X
X
X/* Read the configuration file.  Lines in the file can be blank, comments
X   beginning with #, or assigments looking like WORD=VALUE.  Spaces around
X   the WORD and the VALUE are stripped.  We store (word, value) pairs
X   in a linked list which we can consult later for settings of
X   interest.  If we don't succeed in opening the file we don't care; there
X   will just be no settings.  Return status is <0 if there is an error,
X   though this doesn't include not being able to open the file. */
Xstatic int readdefault(configname)
Xconst char *configname;
X{
X    FILE *f;
X    char buf[512];
X    int i;
X    char *p;
X    struct setting *sp;
X    if ((f = fopen(configname, "r")) == NULL)
X	return 0;
X    while (fgets(buf, sizeof buf, f) != NULL) {
X	for (i = strlen(buf); i > 0 && isspace(buf[i - 1]); i--) ;
X	buf[i] = '\0';	/* i == 0 => just a \n. */
X	for (i = 0; isspace(buf[i]); i++) ;
X	if (buf[i] == '\0' || buf[i] == '#')
X	    continue;
X	p = xstrdup(buf + i);
X	for (i = 0; p[i] != '='; i++) {
X	    if (isspace(p[i])) {
X		p[i] = '\0';
X		do i++; while (isspace(p[i]));
X		if (p[i] != '=') {
X		    (void) fclose(f);
X		    (void) fprintf(stderr,
X				   "%s: format error in defaults file:\n%s\n",
X				   progname, buf);
X		    return -1;
X		}
X		break;
X	    }
X	}
X	p[i] = '\0';
X	sp = (struct setting *) xmalloc((int) sizeof *sp);
X	sp->name = p;
X	do i++; while (isspace(p[i]));
X	sp->value = p + i;
X	sp->next = settingp;
X	settingp = sp;
X    }
X    (void) fclose(f);
X    return 0;
X}
X
X
X/* Return the value of a default setting. */
Xstatic const char *defaultvalue(settingname)
Xconst char *settingname;
X{
X    const struct setting *sp;
X    for (sp = settingp; sp != NULL; sp = sp->next)
X	if (strcmp(sp->name, settingname) == 0)
X	    return sp->value;
X    return NULL;
X}
X
X
X/* Safe malloc, 'cos it crashes if it fails.  (That's safe.) */
Xstatic pointer xmalloc(size)
Xint size;
X{
X    pointer p;
X    if ((p = malloc((size_t) size)) == NULL)
X	crash("malloc", (char *) NULL);
X    return p;
X}
X
X
X/* Likewise for strdup.  Since strdup isn't standard we write it by hand. */
Xstatic char *xstrdup(str)
Xconst char *str;
X{
X    char *p = xmalloc(strlen(str) + 1);
X    return strcpy(p, str);
X}
X
X
X/* Put name=value into the environment. */
Xstatic void xputenv(name, value)
Xconst char *name, *value;
X{
X    int len = strlen(name) + strlen(value) + sizeof("=");
X    char *p = xmalloc(len);
X    (void) sprintf(p, "%s=%s", name, value);
X    if (putenv(p) < 0)
X	crash("putenv", p);
X}
X
X
X/* Wrappers for getpw* that copy the returned structure into a malloced
X   buffer, so it doesn't get overwritten by subsequent calls. */
Xstatic struct passwd *copypasswd(pwd)
Xstruct passwd *pwd;
X{
X    struct passwd *p;
X    if (pwd == NULL)
X	return NULL;
X    p = (struct passwd *) xmalloc(sizeof *p);
X    *p = *pwd;
X    /* We only dup the fields we're interested in here; the other ones will
X       get splatted the next time you call getpwthing() because the stuff
X       they point to will be overwritten. */
X    p->pw_name = xstrdup(p->pw_name);
X    p->pw_dir = xstrdup(p->pw_dir);
X    p->pw_shell = xstrdup(p->pw_shell);
X    return p;
X}
X
X
Xstatic struct passwd *xgetpwnam(name)
Xconst char *name;
X{
X    /* You may get a warning from the next line, if you have an inadequate
X       <pwd.h> that declares getpwnam(char *name) rather than
X       getpwnam(const char *name). */
X    return copypasswd(getpwnam(name));
X}
X
X
Xstatic struct passwd *xgetpwuid(uid)
Xint uid;
X{
X    return copypasswd(getpwuid(uid));
X}
X
X
Xstatic struct passwd *whoami()
X{
X    static struct passwd *iam;
X    if (iam != NULL)
X	return iam;
X    else return iam = xgetpwuid(getuid());
X}
END_OF_su.c
if test 13518 -ne `wc -c <su.c`; then
    echo shar: \"su.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f oksetluid.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"oksetluid.c\"
else
echo shar: Extracting \"oksetluid.c\" \(1523 characters\)
sed "s/^X//" >oksetluid.c <<'END_OF_oksetluid.c'
X/* oksetluid.c - version of setluid() that always works for root. */
X
X/* By Charles Bryant, Datacode Communications Ltd <ch@dce.ie>, August 1990.
X   This program is not copyrighted. */
X
X#ifndef __STDC__
X#define const /* nothing */
X#endif
X
X#ifndef lint
Xstatic const char rcsid[] =
X	"$Id: oksetluid.c,v 1.1 90/09/28 16:21:11 em Release $";
X#endif
X
X#include <fcntl.h>
X#define _PTRDIFF_T
X#include <sys/types.h>
X#include <nlist.h>
X#define SecureWare
X#include <sys/security.h>
X#include <stddef.h>
X
Xstatic struct nlist nlsym[2] = {
X	{"sip"},
X	{""},
X};
X
Xextern void uxnlist();
Xextern int lseek(), read(), open(), write(), close(), setluid();
X
Xchar *oksetluid(id)
Xunsigned short id;
X{
X struct security_info *secp;
X long secpaddr;
X int fd, i;
X char *err=0;
X if (!setluid(id)) return 0;
X uxnlist(nlsym);
X if (!(secpaddr = nlsym[0].n_value) )
X	return "Can't read nlist from /unix";
X if ( (fd=open("/dev/kmem", O_RDWR)) < 0)
X	 return "Can't open /dev/kmem for read/write";
X if (lseek(fd, secpaddr, 0) != secpaddr)
X	  err = "Can't seek to pointer in /dev/kmem";
X else if (read(fd, &secp, sizeof(secp)) != sizeof(secp))
X	err = "Can't read pointer from /dev/kmem";
X else	{
X	 /* Stupid definition of offsetof() in <stddef.h> produces warning from
X	    Microsoft compiler. */
X	 i = (long) secp + offsetof(struct security_info, si_luid);
X	 if (lseek(fd, (long) i, 0) != i)
X		 err = "Can't seek to info in /dev/kmem";
X	 else if (write(fd, &id, sizeof(id)) != sizeof(id))
X		 err = "Can't write luid to /dev/kmem";
X	}
X close(fd);
X return err;
X}
END_OF_oksetluid.c
if test 1523 -ne `wc -c <oksetluid.c`; then
    echo shar: \"oksetluid.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f uxnlist.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"uxnlist.c\"
else
echo shar: Extracting \"uxnlist.c\" \(4530 characters\)
sed "s/^X//" >uxnlist.c <<'END_OF_uxnlist.c'
X/* uxnlist.c - optimised nlist("/unix"). */
X
X/* By Charles Bryant, Datacode Communications Ltd <ch@dce.ie>, August 1990.
X   This program is not copyrighted. */
X
X#ifndef __STDC__
X#define const /* nothing */
X#endif
X
X#ifndef lint
Xstatic const char rcsid[] =
X	"$Id: uxnlist.c,v 1.1 90/09/28 16:22:31 em Release $";
X#endif
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <stdio.h>
X#include <nlist.h>
X#include <malloc.h>
X#include <string.h>
X#include <memory.h>
X#ifdef __STDC__	/* Keep gcc -Wall happy. */
Xextern int stat(const char *file, struct stat *buf);
Xextern int nlist(const char *file, struct nlist *nl);
Xextern int chmod(const char *file, int mode);
Xextern int chown(const char *file, int owner, int group);
X#endif
X
X#define UNIX n_unix
X#define SYMS n_unixsyms
X
Xstatic const char n_unix[]="/unix", n_unixsyms[]="/unix.syms";
X
X/* Optimised version of nlist("/unix", nl).  The symbols are first looked
X   up in /unix.syms provided that is more recent than /unix.  Any symbols
X   not found in /unix.syms are looked up with nlist on /unix and then
X   appended to /unix.syms.  If /unix.syms does not exist it is created
X   with owner root and group mem (same group as /dev/kmem).  The assumption
X   is that /unix symbols are useful only to programs that grovel in
X   /dev/kmem, which are typically setgid to mem.
X
X   The format of each symbol entry in /unix.syms is like this:
X	struct {
X		int namelength;
X		char name[namelength];	(Not null-terminated.)
X		struct nlist entry;
X	};
X   The reason for not using a text format is that struct nlist is opaque;
X   it is not explained in the manual and differs between systems.
X */
Xchar *uxnlist(nl)
Xstruct nlist *nl;
X{
X struct stat uxstat, symstat;
X struct nlist *toget;
X register struct nlist *np, *np2;
X static char cant_malloc[] = "*Can't malloc";
X int numtodo;
X FILE *sf;
X for (np=nl; np->n_name && *np->n_name; np++) ;
X numtodo = np+1-nl;
X toget = malloc(numtodo * sizeof (*nl));
X if (!toget) return cant_malloc;
X memcpy(toget, nl, numtodo * sizeof(*nl));
X if (stat(UNIX, &uxstat)<0)
X	{
X	 free(toget);
X	 return "Cannot stat /unix";
X	}
X if (stat(SYMS, &symstat) < 0
X	 || uxstat.st_mtime > symstat.st_mtime)
X		{
X		 sf=fopen(SYMS,"w+");
X		 if (!sf)
X			{
X		panic:
X			 free(toget);
X			 return nlist(UNIX, nl) ? "*nlist() failed" : 0;
X			}
X		 stat("/dev/kmem", &uxstat);
X		 chmod(SYMS, 0664);
X		 chown(SYMS, 0, uxstat.st_gid);
X		}
X else if ( !(sf=fopen(SYMS, "a+")) )
X		goto panic;
X else {
X	 {
X	  struct nlist this;
X	  int maxlen=16, thislen, numgot=0;
X	  char *thisnam = malloc(maxlen);
X	  if (!thisnam)
X		{
X		 free(toget);
X		 fclose(sf);
X		 return cant_malloc;
X		}
X	  for (;;)
X		{	/* as suggested by EMcM */
X		 if (numgot==numtodo-1) break;	/* stop if only sentinel left */
X		 if (!fread(&thislen, sizeof(thislen), 1, sf)) break;
X		 if (thislen >= maxlen)
X			if ( !(thisnam = realloc(thisnam, thislen)) )
X				{
X				 fclose(sf);
X				 free(toget);
X				 return "*realloc failed";
X				}
X			 else maxlen = thislen+1;
X		 fread(thisnam, thislen, 1, sf);
X		 thisnam[thislen] = 0;
X		 fread((char *) &this, sizeof(this), 1, sf);
X		 for (np=toget; np-toget < numtodo-1; np++)
X			if (!strcmp(np->n_name, thisnam))
X				{
X				 *np = this;
X				 np->n_name = 0;	/* mark as got */
X				 break;
X				}
X		}
X	  free(thisnam);
X	 }
X	 /* move all to be got up to top */
X	 /* and copy ones we have got into the users buffer */
X	 for (np2=np=toget; np2-toget < numtodo-1; np2++)
X		if (np2->n_name) *np++ = *np2;
X		else	{
X			 np2->n_name = nl[np2-toget].n_name;  /* replace mark*/
X			 nl[np2-toget] = *np2;
X			}
X	 if ( !(numtodo = np-toget) )
X		{
X		 free(toget);
X		 fclose(sf);
X		 return 0;	/* got them all */
X		}
X	 np->n_name = 0;	/* flag end of names */
X	}
Xif (nlist(UNIX, toget))
X	{
X	 free(toget);
X	 fclose(sf);
X	 return "*nlist failed";
X	}
X /* now find where the originals are and copy the data */
X np2=nl;	/* note that the lists are in the same order */
X for (np=toget; np-toget < numtodo; np++)
X	{
X	 char *this, *t2;
X	 int len;
X	 for (; np2->n_name != np->n_name; np2++)
X		if (!np2->n_name || !*np2->n_name)
X			/* NB: don't free! mem is suspect */
X			return "*Internal memory corruption";
X	 *np2 = *np;
X	 len = strlen(np2->n_name);
X	 this = malloc(sizeof(int) + len + sizeof(*np));
X	 if (!this)
X		{
X		 free(toget);
X		 fclose(sf);
X		 return cant_malloc;
X		}
X	 t2 = this;
X	 *(int *) t2 = len;
X	 t2 = (char *) (1 + (int *) this);
X	 memcpy(t2, np2->n_name, len);
X	 t2 += len;
X	 memcpy(t2, np, sizeof(*np));
X	 fwrite(this, sizeof(*np) + t2-this, 1, sf);
X	 free(this);
X	}
X free(toget);
X fclose(sf);
X return 0;
X}
END_OF_uxnlist.c
if test 4530 -ne `wc -c <uxnlist.c`; then
    echo shar: \"uxnlist.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f newgrp.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"newgrp.c\"
else
echo shar: Extracting \"newgrp.c\" \(3986 characters\)
sed "s/^X//" >newgrp.c <<'END_OF_newgrp.c'
X/* newgrp.c - replacement newgrp since SCO has a stupid umask problem. */
X
X/* By Eamonn McManus, Datacode Communications Ltd <em@dce.ie>, January 1991.
X   This program is not copyrighted.
X
X   Usage: newgrp [-] [group]
X*/
X
X#ifndef __STDC__
X#define const /* nothing */
X#endif
X
X#ifndef lint
Xstatic const char rcsid[] =
X	"$Id: newgrp.c,v 1.1 91/04/11 13:29:32 em Exp Locker: em $";
X#endif
X
X#include <stdio.h>
X#ifdef __STDC__
X#include <stdlib.h>
X#endif
X#include <string.h>
X#include <pwd.h>
X#include <grp.h>
X#include <errno.h>
X
X#ifdef __STDC__	/* <prototypes.h> is worthless, conflicts with <stdlib.h>. */
X
X/* Declare everything for gcc -Wall. */
Xextern int getuid(void), setgid(int gid), setuid(int uid);
Xextern int execl(const char *path, const char *arg, ...);
X
Xtypedef void *pointer;
X
Xstatic void newgrp(const char *grpname, int newgid, int islogin);
Xstatic char *groupname(int gid);
Xstatic void crash(const char *why, const char *what);
Xstatic char *xstrdup(const char *str);
Xstatic pointer xmalloc(int size);
X
X#else	/* !STDC */
X
Xextern char *malloc();
X
Xtypedef char *pointer;
X
Xstatic void crash();
Xstatic char *groupname();
Xstatic char *xstrdup();
Xstatic pointer xmalloc();
X
X#endif	/* !STDC */
X
X
Xconst char *progname = "newgrp";
X
X
Xint main(argc, argv)
Xint argc;
Xchar **const argv;
X{
X    int i;
X    int login = 0;
X    struct passwd *pwd;
X    char *group = NULL;
X    if (argc > 0)
X	progname = argv[0];
X    for (i = 1; i < argc && argv[i][0] == '-'; i++) {
X	switch (argv[i][1]) {
X	case '\0':
X	    login = 1; break;
X	default:
Xusage:
X	    fprintf(stderr, "Usage: %s [-] [group]\n", progname);
X	    exit(1);
X	}
X    }
X    if (i < argc) {
X	group = argv[i];
X	if (++i < argc)
X	    goto usage;
X    }
X    if ((pwd = getpwuid(getuid())) == NULL)
X	crash("*you don't exist", (char *) NULL);
X    if (group == NULL)
X	newgrp(groupname(pwd->pw_gid), pwd->pw_gid, login);
X    else {
X	struct group *grp = getgrnam(group);
X	if (grp == NULL)
X	    crash("*no such group", group);
X	if (getuid() != 0) {
X	    char **gnam;
X	    for (gnam = grp->gr_mem; *gnam; gnam++)
X		if (strcmp(*gnam, pwd->pw_name) == 0)
X		    break;
X	    if (*gnam == NULL)
X		crash("*not a member of group", group);
X	}
X	newgrp(group, grp->gr_gid, login);
X    }
X    return 0;	/* Not reached. */
X}
X
X
Xstatic void newgrp(grpname, newgid, islogin)
Xconst char *grpname;
Xint newgid, islogin;
X{
X    char *shell = getenv("SHELL");
X    char *argv0;
X    if (shell == NULL || *shell == '\0')
X	shell = "/bin/sh";
X    argv0 = strrchr(shell, '/');
X    if (argv0 != NULL)
X	argv0++;
X    else argv0 = shell;
X    if (islogin) {	/* newgrp - */
X	char *p = xmalloc(strlen(argv0) + sizeof("-"));
X	(void) sprintf(p, "-%s", argv0);
X	argv0 = p;
X    }
X    if (setgid(newgid) < 0)
X	crash("setgid", grpname);
X    if (setuid(getuid()) < 0)
X	crash("setuid", "to real uid");
X    execl(shell, argv0, (char *) NULL);
X    crash("execl", shell);
X}
X
X
Xstatic char *groupname(gid)
Xint gid;
X{
X    struct group *grp;
X    char gidnum[16];
X    if ((grp = getgrgid(gid)) != NULL)
X	return xstrdup(grp->gr_name);
X    (void) sprintf(gidnum, "%d", gid);
X    return xstrdup(gidnum);
X}
X
X
X/* Print message "why: what" to stderr.  The what string is printed with
X   perror unless the why string begins with *.  The * is not printed.
X   Exits the program.  */
Xstatic void crash(why, what)
Xconst char *why, *what;
X{
X    fprintf(stderr, "%s: ", progname);
X    if (*why == '*') {
X	fprintf(stderr, "%s", why + 1);
X	if (what)
X	    fprintf(stderr, ": %s", what);
X	putc('\n', stderr);
X    } else if (what) {
X	fprintf(stderr, "%s: ", why);
X	perror(what);
X    } else perror(why);
X    exit(1);
X}
X
X
X/* Safe malloc, 'cos it crashes if it fails.  (That's safe.) */
Xstatic pointer xmalloc(size)
Xint size;
X{
X    pointer p;
X    if ((p = malloc((size_t) size)) == NULL)
X	crash("malloc", (char *) NULL);
X    return p;
X}
X
X
X/* Likewise for strdup.  Since strdup isn't standard we write it by hand. */
Xstatic char *xstrdup(str)
Xconst char *str;
X{
X    char *p = xmalloc(strlen(str) + 1);
X    return strcpy(p, str);
X}
END_OF_newgrp.c
if test 3986 -ne `wc -c <newgrp.c`; then
    echo shar: \"newgrp.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of shell archive.
exit 0