home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright 1989, 1990, 1991, John F. Haugh II
- * All rights reserved.
- *
- * Permission is granted to copy and create derivative works for any
- * non-commercial purpose, provided this copyright notice is preserved
- * in all copies of source code, or included in human readable form
- * and conspicuously displayed on all copies of object code or
- * distribution media.
- */
-
- #include <sys/types.h>
- #include <stdio.h>
-
- #ifndef lint
- static char sccsid[] = "%W% %U% %G%";
- #endif
-
- /*
- * Set up some BSD defines so that all the BSD ifdef's are
- * kept right here
- */
-
- #ifndef BSD
- #include <string.h>
- #include <memory.h>
- #define bzero(a,n) memset(a, 0, n)
- #include <termio.h>
- #else
- #include <strings.h>
- #include <sgtty.h>
- #define strchr index
- #define strrchr rindex
- #endif
-
- #include <signal.h>
- #include <syslog.h>
- #include "config.h"
- #include "lastlog.h"
- #include "pwd.h"
- #include "shadow.h"
-
- /*
- * Password aging constants
- *
- * DAY - seconds in a day
- * WEEK - seconds in a week
- * SCALE - convert from clock to aging units
- */
-
- #define DAY (24L*3600L)
- #define WEEK (7L*DAY)
-
- #ifdef ITI_AGING
- #define SCALE (1)
- #else
- #define SCALE DAY
- #endif
-
- /*
- * Assorted #defines to control su's behavior
- */
-
- #ifndef MAXENV
- #define MAXENV 128
- #endif
-
- #ifndef PATH
- #define PATH ":/bin:/usr/bin"
- #endif
-
- #ifndef SUPATH
- #define SUPATH ":/bin:/usr/bin:/etc"
- #endif
-
- /*
- * Global variables
- */
-
- #ifdef HUSHLOGIN
- char hush[BUFSIZ];
- int hushed;
- #endif
-
- char name[BUFSIZ];
- char pass[BUFSIZ];
- char home[BUFSIZ];
- char prog[BUFSIZ];
- char mail[BUFSIZ];
- char oldname[BUFSIZ];
- char *newenvp[MAXENV];
- char *Prog;
- int newenvc = 0;
- int maxenv = MAXENV;
- struct passwd pwent;
-
- #ifdef TZ
- FILE *tzfile;
- char tzbuf[16] = TZ;
- #endif
-
- /*
- * External identifiers
- */
-
- extern void addenv ();
- extern void entry ();
- extern void sulog ();
- extern void subsystem ();
- extern void setup ();
- extern void motd ();
- extern void mailcheck ();
- extern void shell ();
- extern char *ttyname ();
- extern char *getenv ();
- extern char *getpass ();
- extern struct passwd *getpwuid ();
- extern struct passwd *getpwnam ();
- extern struct spwd *getspnam ();
- extern char **environ;
-
- /*
- * die - set or reset termio modes.
- *
- * die() is called before processing begins. signal() is then
- * called with die() as the signal handler. If signal later
- * calls die() with a signal number, the terminal modes are
- * then reset.
- */
-
- void die (killed)
- int killed;
- {
- #ifdef BSD
- static struct sgtty sgtty;
-
- if (killed)
- stty (0, &sgtty);
- else
- gtty (0, &sgtty);
- #else
- static struct termio sgtty;
-
- if (killed)
- ioctl (0, TCSETA, &sgtty);
- else
- ioctl (0, TCGETA, &sgtty);
- #endif
- if (killed) {
- closelog ();
- exit (killed);
- }
- }
-
- /*
- * su - switch user id
- *
- * su changes the user's ids to the values for the specified user.
- * if no new user name is specified, "root" is used by default.
- *
- * The only valid option is a "-" character, which is interpreted
- * as requiring a new login session to be simulated.
- *
- * Any additional arguments are passed to the user's shell. In
- * particular, the argument "-c" will cause the next argument to
- * be interpreted as a command by the common shell programs.
- */
-
- int main (argc, argv, envp)
- int argc;
- char **argv;
- char **envp;
- {
- void (*oldsig)();
- char *cp;
- char *tty = 0; /* Name of tty SU is run from */
- int doshell = 0;
- int fakelogin = 0;
- int amroot = 0;
- struct passwd *pw = 0;
- struct spwd *spwd = 0;
-
- /*
- * Get the program name. The program name is used as a
- * prefix to most error messages. It is also used as input
- * to the openlog() function for error logging.
- */
-
- if (Prog = strrchr (argv[0], '/'))
- Prog++;
- else
- Prog = argv[0];
-
- openlog (Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
-
- /*
- * Get the tty name. Entries will be logged indicating that
- * the user tried to change to the named new user from the
- * current terminal.
- */
-
- if (isatty (0) && (cp = ttyname (0))) {
- if (strncmp (cp, "/dev/", 5) == 0)
- tty = cp + 5;
- else
- tty = cp;
- } else
- tty = "???";
-
- /*
- * Process the command line arguments.
- */
-
- argc--; argv++; /* shift out command name */
-
- if (argc > 0 && argv[0][0] == '-' && argv[0][1] == '\0') {
- fakelogin = 1;
- argc--; argv++; /* shift ... */
- }
-
- /*
- * If a new login is being set up, the old environment will
- * be ignored and a new one created later on.
- */
-
- if (! fakelogin)
- while (*envp)
- addenv (*envp++);
-
- #ifdef TZ
-
- /*
- * The timezone will be reset to the login value if required.
- */
-
- if (fakelogin) {
- if (tzbuf[0] == '/') {
- if ((tzfile = fopen (tzbuf, "r")) != (FILE *) 0) {
- if (fgets (tzbuf, sizeof tzbuf, tzfile)) {
- tzbuf[strlen (tzbuf) - 1] = '\0';
- addenv (tzbuf);
- }
- fclose (tzfile);
- }
- } else {
- addenv (tzbuf);
- }
- }
- #endif /* TZ */
- #ifdef HZ
-
- /*
- * The clock frequency will be reset to the login value if required
- */
-
- if (fakelogin)
- addenv (HZ); /* set the default $HZ, if one */
- #endif /* HZ */
-
- /*
- * The next argument must be either a user ID, or some flag to
- * a subshell. Pretty sticky since you can't have an argument
- * which doesn't start with a "-" unless you specify the new user
- * name. Any remaining arguments will be passed to the user's
- * login shell.
- */
-
- if (argc > 0 && argv[0][0] != '-') {
- (void) strcpy (name, argv[0]); /* use this login id */
- argc--; argv++; /* shift ... */
- }
- if (! name[0]) /* use default user ID */
- (void) strcpy (name, "root");
-
- doshell = argc == 0; /* any arguments remaining? */
-
- /*
- * Get the user's real name. The current UID is used to determine
- * who has executed su. That user ID must exist.
- */
-
- if (pw = getpwuid (getuid ())) /* need old user name */
- (void) strcpy (oldname, pw->pw_name);
- else { /* user ID MUST exist */
- syslog (LOG_CRIT, "Unknown UID: %d\n", getuid ());
- goto failure;
- }
- amroot = getuid () == 0; /* currently am super user */
-
- top:
- /*
- * This is the common point for validating a user whose name
- * is known. It will be reached either by normal processing,
- * or if the user is to be logged into a subsystem root.
- *
- * The password file entries for the user is gotten and the
- * accont validated.
- */
-
- if (pw = getpwnam (name)) {
- if (spwd = getspnam (name))
- pw->pw_passwd = spwd->sp_pwdp;
- } else {
- (void) fprintf (stderr, "Unknown id: %s\n", name);
- closelog ();
- exit (1);
- }
- pwent = *pw;
-
- #ifdef NOLOGIN
-
- /*
- * See if the account is usable for anything but login.
- */
-
- if (strcmp (pwent.pw_shell, NOLOGIN) == 0)
- pwent.pw_shell = getenv ("SHELL");
- #endif /* NOLOGIN */
-
- /*
- * Set the default shell.
- */
-
- if (pwent.pw_shell == 0 || pwent.pw_shell[0] == '\0')
- pwent.pw_shell = "/bin/sh";
-
- /*
- * Set up a signal handler in case the user types QUIT.
- */
-
- die (0);
- oldsig = signal (SIGQUIT, die);
-
- /*
- * Get the password from the invoker
- */
-
- if (! amroot && pwent.pw_passwd[0]) {
- if (! (cp = getpass ("Password:"))) {
- syslog (pwent.pw_uid ? LOG_WARN:LOG_CRIT,
- "Unable to get password for %s\n", name);
- goto failure;
- } else
- strncpy (pass, cp, sizeof pass);
- } else
- bzero (pass, sizeof pass);
-
- /*
- * check encrypted passwords ...
- */
-
- if (! amroot && ((pass[0] != '\0' || pwent.pw_passwd[0] != '\0') &&
- strcmp (pwent.pw_passwd,
- pw_encrypt (pass, pwent.pw_passwd)) != 0)) {
- syslog (pwent.pw_uid ? LOG_WARN:LOG_CRIT,
- "Invalid password for %s\n", name);
- failure: sulog (0); /* log failed attempt */
- syslog (pwent.pw_uid ? LOG_INFO:LOG_CRIT,
- "- %s %s-%s\n", tty ? tty:"???",
- oldname[0] ? oldname:"???", name[0] ? name:"???");
- puts ("Sorry.");
- closelog ();
- exit (1);
- }
- signal (SIGQUIT, oldsig);
-
- /*
- * Check to see if the account is expired. root gets to
- * ignore any expired accounts, but normal users can't become
- * a user with an expired password.
- */
-
- if (! amroot) {
- if (spwd) {
- if (isexpired (&pwent, spwd)) {
- syslog (pwent.pw_uid ? LOG_WARN:LOG_CRIT,
- "Expired account %s\n", name);
- goto failure;
- }
- }
- #ifdef ATT_AGE
- else if (pwent.pw_age[0] &&
- isexpired (&pwent, (struct spwd *) 0)) {
- syslog (pwent.pw_uid ? LOG_WARN:LOG_CRIT,
- "Expired account %s\n", name);
- goto failure;
- }
- #endif /* ATT_AGE */
- }
- if (pwent.pw_uid == 0)
- addenv (SUPATH);
- else
- addenv (PATH);
-
- environ = newenvp; /* make new environment active */
-
- if (getenv ("IFS")) /* don't export user IFS ... */
- addenv ("IFS= \t\n"); /* ... instead, set a safe IFS */
-
- if (doshell && pwent.pw_shell[0] == '*') { /* subsystem root required */
- subsystem (&pwent); /* figure out what to execute */
- endpwent ();
- endspent ();
- goto top;
- }
-
- sulog (1); /* save SU information */
- syslog (LOG_INFO, "+ %s %s-%s\n", tty ? tty:"???",
- oldname[0] ? oldname:"???", name[0] ? name:"???");
-
- if (fakelogin)
- setup (&pwent); /* set UID, GID, HOME, etc ... */
- else {
- if (setgid (pwent.pw_gid) || setuid (pwent.pw_uid)) {
- perror ("Can't set ID");
- syslog (LOG_CRIT, "Unable to set uid = %d, gid = %d\n",
- pwent.pw_uid, pwent.pw_gid);
- closelog ();
- exit (1);
- }
- }
- if (! doshell) { /* execute arguments as command */
- if (cp = getenv ("SHELL"))
- pwent.pw_shell = cp;
- argv[-1] = pwent.pw_shell;
- (void) execv (pwent.pw_shell, &argv[-1]);
- (void) fprintf (stderr, "No shell\n");
- syslog (LOG_WARN, "Cannot execute %s\n", pwent.pw_shell);
- closelog ();
- exit (1);
- }
- if (fakelogin) {
- #ifdef HUSHLOGIN
- sprintf (hush, "%s/.hushlogin", pwent.pw_dir);
- hushed = access (hush, 0) != -1;
- #endif /* HUSHLOGIN */
- #ifdef MOTD
- motd (); /* print the message of the day */
- #endif /* MOTD */
- #ifdef MAILCHECK
- if (! hushed)
- mailcheck (); /* report on the status of mail */
- #endif /* MAILCHECK */
- shell (pwent.pw_shell, "-su"); /* exec the shell finally. */
- } else {
- if (cp = strrchr (pwent.pw_shell, '/'))
- cp++;
- else
- cp = pwent.pw_shell;
-
- shell (pwent.pw_shell, cp);
- }
- syslog (LOG_WARN, "Cannot execute %s\n", pwent.pw_shell);
-
- /*NOTREACHED*/
- }
-