#! /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 'login.c' <<'END_OF_FILE' X/* ta=4 tabs are set to 4. vi: "set ts=4" */ X/**************************************************************************** X* l o g i n . c * X* * X* login replacement for AT&T SysV/386 3.2 and Interactive Unix. * X* * X* tony field to...@ajfcal.cuc.ab.ca * X****************************************************************************/ X/* X * $Id: login.c,v 2.0 1991/11/10 23:01:36 ajf Exp $ X * $Log: login.c,v $ X * Revision 2.0 1991/11/10 23:01:36 ajf X * 1. added slip, /etc/nologin, general cleanup and bugfix. X * 2. added /etc/netlogin support (for use with rlogind). X * 3. bug fix plus "!" from Uwe Doering (gem...@geminix.in-berlin.de) X * X * Revision 1.2 1991/04/04 05:06:17 ajf X * added to rcs X * X*/ X/* If compiled for Interactive unix with TCP/IP, /etc/netlogin X should be a link to /bin/login. X X Wollongong (on AT&T) rlogind uses an incompatible format for the utmp X entries: do NOT use this login for /usr/etc/netlogin! Wollongong X puts "ttypxx.hostname" into utmp. X XCalling Parameters: X called by getty: login X called by telnet: login -p (-p = preserve environment) X called by rlogind: login -r X X If called by rlogind, read from the socket to get the X remote user, local user and terminal type/speed as null X terminated strings: X X ajf ajf AT386-M/9600 X XMakefile manifests: X -DTCPIP if compilation for use with tcp/ip X -DWOLLONGONG if for AT&T SysV/386 + Wollongong TCP/IP X -DUSRACCESS if /etc/usrtime file is to be used X -DNOISY if login user id errors are reported to user X -DNEEDUTIME if /usr/include/utime.h is missing X -DSLIP="xx" name of slip login binary (e.g. "sllogin") X XFor "large" systems with many users, /etc/usrtime should be a dbm Xfile to speed user lookups. X*/ X X#include X#include X#ifdef NEEDUTIME Xstruct utimbuf /* missing from /usr/include */ X{ time_t actime; /* access time */ X time_t modtime; /* modification time */ X} ; X#else X#include X#include X#endif X#include X#include X#include X#include X X#include X X#include X#include X#include X#include X#include X#include X X#ifdef __GNUC__ X#define alloca __builtin_alloca X#else /* not __GNUC__ */ Xchar *alloca (); X#endif X X#define TIMEOUT 120 /* seconds before login aborted*/ X#define MAXBAD 5 /* max allowed login attempts */ X#define BADLOGINTIME 200 /* wait time before allowing new logins */ X#define MAXUSERNAME 25 /* size of user name */ X X#define DEFAULTSHELL "/bin/sh" X#define USERPATH "/bin:/usr/bin" X#define ROOTPATH "/bin:/etc:/usr/bin" X#define MAIL "/usr/mail" X#define DEFAULTTZ "EST5EDT" X X#define BINPASSWD "/bin/passwd" X#define DIALUPTTY "/etc/dialups" X#define DIALUPPWD "/etc/d_passwd" X#define SHADOW "/etc/shadow" X#define DEFAULTFILE "/etc/default/login" X#define TTYTYPE "/etc/ttytype" X#define WTMP "/etc/wtmp" X#define USRTIME "/etc/usrtime" X#define NOLOGIN "/etc/nologin" X#define TRUSTED "/etc/trustme" X#define LOGINLOG "/usr/adm/loginlog" X#define LASTLOGIN ".lastlogin" X X#ifndef SLIP X#define SLIP "/usr/ucb/sllogin" X#endif X X#define ROOT 0 X X/* user is forbidden to modify these with login command line options */ Xchar *forbidden[] = {"HOME=", "PATH=", "SHELL=", "MAIL=", "IFS=", X "HZ=", "CDPATH=", "LOGNAME=", NULL }; X X#define MAXPATHLEN 300 X#define LONGLINE 500 X#define SHORTLINE 100 X X X#ifdef USRACCESS Xstruct usrtime /* fields within /etc/usrtime */ X{ char *enable; X char *ttyline; X char *days; X char *timerange; X} ; X#endif X Xlong shadow_lstchg, shadow_min, shadow_max; X X#ifdef __STDC__ X/* login.c */ Xint main(int argc, char **argv); Xint dialup_pwd(char *ttyfname, char *pwdfname, char *tty_name, struct passwd *pwd); Xvoid setenv(char *this, char *val); Xvoid check_putenv(char *val); Xchar *get_shadow(char *fname, char *user, int allow); Xchar *search_ttytype(char *tty); Xint get_defaults(char *fname, char *tz, char *hz, char **console, long *ulim, int *passreq, int *altshell, char **path, char **supath, long *timeout, int *cmask, int *idleweeks); Xvoid trim(char *s); Xint atoo(char *s); Xvoid user_timeout(void); Xint tscan(char s[], char t[]); Xint qtscan(char s[], char t[]); Xvoid get_remote(char *buf, int cnt); Xint get_remote_user(char *host); Xvoid set_tcp_ioctl(); Xvoid slow_exit (int rc); Xvoid etc_nologin(char *c); X#ifdef USRACCESS Xint get_usrtime(char *fname, char *user, char *shell, char *tty); Xint check_user(char *note, long now, char *tty, struct usrtime *usr); Xint check_tty(char *note, char *tty, char *list); Xint check_day(char *note, long now, char *list); Xint check_time(char *note, long now, char *list, int timerange); X#endif /* USERACCESS */ X#else Xint main(); Xint dialup_pwd(); Xvoid setenv(); Xvoid check_putenv(); Xchar *get_shadow(); Xchar *search_ttytype(); Xint get_defaults(); Xvoid trim(); Xint atoo(); Xvoid user_timeout(); Xint tscan(); Xint qtscan(); Xvoid get_remote(); Xint get_remote_user(); Xvoid set_tcp_ioctl(); Xvoid slow_exit (); Xvoid etc_nologin(); X#ifdef USRACCESS Xint get_usrtime(); Xint check_user(); Xint check_tty(); Xint check_day(); Xint check_time(); X#endif /* USRACCESS */ X#endif /* __STDC__ */ X X Xextern char *getpass(), *crypt(), *malloc(); Xextern int errno; Xextern char *sys_errlist[]; Xlong timeout; /* TIMEOUT from /etc/default/login */ Xlong atol(); Xint atoi(); X Xchar rname[100], lname[100], tname[100]; Xstruct passwd *pwd; Xchar *tty_name; Xchar *hostname; Xint last_match; /* qtscan last character matched */ X Xmain (argc, argv) Xint argc; Xchar **argv; X{ X char *login_name; /* user login id */ X char user_name[SHORTLINE+1]; X char *tty_n, *ttyname(); /* tty name for user */ X int i; X struct passwd *getnam(); X char shell[MAXPATHLEN + 1]; X char term[SHORTLINE + 1]; X char buffer[MAXPATHLEN+1], *p; X char *shadow, *my_pass; X char *ctime(); X long ulimit_blocks; /* ULIMIT from /etc/default/login */ X char tz[50]; /* TZ " */ X char hz[50]; /* HZ " */ X char *console; /* CONSOLE " */ X int passreq; /* PASSREQ " */ X int altshell; /* ALTSHELL " */ X char *path; /* PATH " */ X char *supath; /* SUPATH " */ X int cmask; /* UMASK " */ X int idleweeks; /* IDLEWEEKS " */ X int passwd_needed = 0; X int null_user_count; X int lcreate; X char bad_user[MAXBAD][MAXUSERNAME+1]; X extern char **environ, *optarg; X extern int optind, getopt(); X int c; X char *domain, *environ_init[1]; X int preserve = 0; X X X /* To be really safe, two binaries for login should exist. X For /bin/login, the user must be at the lowest shell for X any login attempt. For /etc/netlogin, the uid must be ROOT X and the ppid must be != 1. Unfortunately, we cannot check X argv[0] since getty, rlogind and telnetd force "login" to X be the argv[0] name. The following test could be #ifdef'ed X out for TCPIP. For /bin/login, the test should be X for getppid() != 1 only. X */ X if (getuid() && getppid () != 1) /* bypassed by rlogind */ X { fprintf (stdout, "%s must be exec'ed from lowest level shell.\n", argv[0]); X fflush (stdout); X exit (1); X } X X#ifdef TCPIP X gethostname(buffer, sizeof(buffer)); X domain = strchr (buffer, '.'); X#endif X hostname = NULL; X X tty_name = ttyname (0); X if (tty_name == NULL || *tty_name == '\0') X tty_name = "/dev/tty??"; X if ((tty_n = strrchr (tty_name, '/'))) X tty_n++; X else X tty_n = tty_name; X X while ((c = getopt(argc, argv, "pr:")) != -1) X { X switch (c) X { X case 'p': /* preserve environment */ X preserve = 1; X break; X X#ifdef TCPIP X case 'r': /* rlogind initiated this call, remote host name */ X if (getuid()) X { fprintf (stdout, "%s: -r for superuser only\n", argv[0]); X slow_exit (1); X } X if (domain && (p = strchr (optarg, '.')) && X strcasecmp(p, domain) == 0) X *p = 0; X hostname = optarg; X if (get_remote_user (hostname)) X slow_exit (1); X break; X#endif X X default: break; X } X } X X if (hostname) X login_name = lname; X else if (optind < argc) X login_name = argv[optind++]; X else X login_name = NULL; X if (login_name) X etc_nologin (login_name); X X /* get defaults from /etc/default/login */ X X console = path = supath = NULL; X *tz = *hz = '\0'; X idleweeks = timeout = cmask = -1; X ulimit_blocks = 0; X passreq = 1; X altshell = 1; X get_defaults (DEFAULTFILE, tz, hz, &console, &ulimit_blocks, &passreq, X &altshell, &path, &supath, &timeout, &cmask, &idleweeks); X X if (path == NULL) X path = USERPATH; X if (supath == NULL) X supath = ROOTPATH; X if (*tz == '\0') X strcpy (tz, DEFAULTTZ); X if (timeout == -1) X timeout = TIMEOUT; X X#ifdef __STDC__ X signal (SIGALRM, (void (*)(int)) user_timeout); X#else X signal (SIGALRM, (void (*)()) user_timeout); X#endif X alarm (timeout); X signal (SIGQUIT, SIG_IGN); X signal (SIGINT, SIG_IGN); X X /* accept a user's login user name and password */ X X shadow_lstchg = -1; X for (null_user_count = i = 0; i < MAXBAD; i++) X { char *salt, *pass, *pass_crypt; X X if (hostname) /* network rlogin already ok */ X { strcpy (user_name, login_name); X break; X } X X if (login_name) X { strcpy (user_name, login_name); X login_name = NULL; X } X else X { fprintf (stdout, "login: "); X fgets (user_name, SHORTLINE, stdin); X user_name[SHORTLINE] = '\0'; X trim (user_name); X user_name[MAXUSERNAME] = '\0'; X strcpy (bad_user[i], user_name); X if (*user_name == '\0') X { null_user_count++; X continue; X } X } X X pwd = getpwnam (user_name); X if (pwd) X { if (pwd->pw_uid) X etc_nologin (user_name); X if (*(pwd->pw_shell) == '*') X { /* see 'real' man page for login for * in shell field. X must change the root directory, then rerun login X with the new root looking at new password file... X HOWEVER, i cannot see any use of this feature! X */ X if (chroot (pwd->pw_dir)) X { perror ("Could not change to your root directory: "); X slow_exit (1); X } X fprintf (stdout, "Subsystem root = %s\n", pwd->pw_dir); X execlp ("login", "login", 0); X perror ("No login on subsystem: "); X slow_exit (1); X } X X /* valid user. shadow file may or may not exist */ X if ((strcmp (pwd->pw_passwd, "x") == 0) && (shadow = get_shadow (SHADOW, user_name, 0))) X { salt = shadow; X my_pass = shadow; X } X else X my_pass = salt = pwd->pw_passwd; X } X else /* invalid user */ X { my_pass = salt = "xx"; X etc_nologin ("invalid user name"); X } X X if (*my_pass == '\0') X { /* bypass prompting if zero length */ X if (passreq) X passwd_needed = 1; /* ask user later */ X#ifdef USRACCESS X if (get_usrtime (USRTIME, user_name, pwd->pw_shell, tty_name)) X slow_exit (1); X#endif X break; X } X X pass = getpass ("password: "); X if (pwd) X { /* get regular + dialup password + restrict time access */ X pass_crypt = crypt (pass, salt); X if ( !strcmp (pass_crypt, my_pass) X && !dialup_pwd (DIALUPTTY, DIALUPPWD, tty_name, pwd) X#ifdef USRACCESS X && !get_usrtime (USRTIME, user_name, pwd->pw_shell, tty_name) X#endif X ) X break; X } X#ifdef NOISY X if (i > 1) X sleep (i); X fprintf (stdout, "Login incorrect: try again.\n"); X fflush (stdout); X#endif X } X alarm (0); /* turn of pending alarm */ X X if (i == MAXBAD) X { /* write the bad attempt to /usr/adm/loginlog. */ X FILE *fp; X time_t now; X X if (null_user_count < 3) X { if ((fp = fopen (LOGINLOG, "a"))) X { now = time (0L); X for (i = 0; i < MAXBAD; i++) X { if (*bad_user[i]) X fprintf (fp, "%s:%s: %s", bad_user[i], tty_name, ctime (&now)); X } X fclose (fp); X } X sleep (BADLOGINTIME); /* discourage further abuse */ X } X slow_exit (1); X } X X /* if CONSOLE= is defined in /etc/default/login, then root may X root may log in only on the CONSOLE=device X */ X if (pwd->pw_uid == ROOT && console && *console) X { strcpy (buffer, tty_name); X strcat (buffer, ":"); X if (qtscan (console, buffer) < 0) X { fprintf (stdout, "Superuser must be on %s\n", console); X slow_exit (1); X } X } X X if (*(pwd->pw_shell) == '\0') X strcpy (shell, DEFAULTSHELL); X else X strcpy (shell, pwd->pw_shell); X X if (chdir (pwd->pw_dir) < 0) X { perror ("Could not locate login directory"); X slow_exit (1); X } X X { /* update utmp and wtmp (if wtmp exists) X For an original tty login, utmp will contain: X LOGIN /dev/vt01 X For exec login: X ajf vt01 X Must scan for either getutline() form. X */ X struct utmp *utmp; X struct utmp line, id; X struct utmp *getutline(); X X /* first check for /dev/tty?? format */ X X memset ((char *)&line, '\0', sizeof(line)); X strncpy (line.ut_line, tty_name, sizeof(line.ut_line)); X if ((utmp = getutline (&line)) == NULL) X { X /* for tty?? format */ X setutent(); X memset ((char *)&line, '\0', sizeof(line)); X strncpy (line.ut_line, tty_n, sizeof(line.ut_line)); X if ((utmp = getutline (&line)) == NULL) X { fprintf (stdout, "No utmp entry.\n"); X slow_exit (1); X } X } X memset ((char *)&id, '\0', sizeof(id)); X strncpy (id.ut_id, utmp->ut_id, sizeof(id.ut_id)); X (void) time (&id.ut_time); X strncpy (id.ut_name, user_name, sizeof(id.ut_name)); X strncpy (id.ut_line, tty_n, sizeof(id.ut_line)); X id.ut_pid = getpid(); X id.ut_type = USER_PROCESS; X pututline (&id); X X if (access (WTMP, W_OK) == 0) X { FILE *wtmp; X if ((wtmp = fopen (WTMP, "a"))) X { fwrite (&id, sizeof(id), 1, wtmp); X fclose (wtmp); X } X } X } X X if (pwd->pw_uid != ROOT && ulimit_blocks) X { if (ulimit (2, ulimit_blocks) == -1) X fprintf (stdout, "Could not set ULIMIT=%ld\n", ulimit_blocks); X } X X /* set user context: tty owner/group, uid/gid and environment */ X X if (strcmp (tty_name, "/dev/tty??") != 0) X { chown (tty_name, pwd->pw_uid, pwd->pw_gid); X chmod (tty_name, 0620); X } X X if (setgid (pwd->pw_gid)) X { fprintf(stdout, "Could not set gid=%d: %s\n", pwd->pw_gid, sys_errlist[errno]); X slow_exit (1); X } X#ifdef POSIX_GROUPS X initgroups (user_name, pwd->pw_gid); /* posix supplimentary groups */ X#endif X if (setuid (pwd->pw_uid)) X { fprintf(stdout, "Could not set uid=%d: %s\n", pwd->pw_uid, sys_errlist[errno]); X slow_exit (1); X } X X if (passwd_needed) X { fprintf (stdout, "A password is needed to log into this system. Choose one.\n"); X if (system (BINPASSWD)) X slow_exit (1); X } X else if (shadow_lstchg >= 0) X { if ((time(NULL) / (24L*60*60) - shadow_lstchg) > shadow_max) X { fprintf (stdout, "Your password has expired. Choose a new one.\n"); X if (system (BINPASSWD)) X slow_exit (1); X } X } X X /* lastlogin magic seems to use a 2 second difference in X the modified and access times of the .lastlogin file. X This is a guess....but it seems to work. X */ X sprintf (buffer, "%s/%s", pwd->pw_dir, LASTLOGIN); X if (access (buffer, 0)) X { FILE *fp; X fprintf (stdout, "Warning: creating .lastlogin\n"); X lcreate = 1; X if (fp = fopen (buffer, "w")) X { fclose (fp); X chmod (buffer, 0400); X } X } X else X lcreate = 0; X if (access (buffer, 0) == 0) X { struct stat sb; X struct utimbuf timset; X time_t now; X X if (stat (buffer, &sb) == 0) X { if (lcreate == 0) X { if (sb.st_mtime - sb.st_atime < 2) X fprintf (stdout, "Warning: .lastlogin has been modified since your previous login.\n"); X fprintf (stdout, "last login: %s", ctime (&sb.st_mtime)); X } X now = time (0L); X if (idleweeks > 0 && pwd->pw_uid != ROOT X && ((now - sb.st_atime) / (24L * 60 * 60 * 7 * (long) idleweeks)) >= idleweeks) X { fprintf (stdout, "Login disabled due to lack of usage.\n"); X slow_exit (1); X } X timset.actime = now - 2; X timset.modtime = now; X utime (buffer, &timset); X } X } X else X fprintf (stdout, "Warning: could not find .lastlogin\n"); X X if (cmask != -1) X umask (cmask); X X if (!preserve) X { environ_init[0] = NULL; X environ = environ_init; X if (hostname == NULL) X { strncpy(term, search_ttytype(tty_n), sizeof(term)); X term[sizeof(term) - 1] = 0; X } X else X { char *c, *strchr(); X c = strchr (tname, '/'); X *c = '\0'; X strcpy (term, tname); X } X setenv ("TERM", term); X } X sprintf (buffer, "%s/%s", MAIL, user_name); X setenv ("MAIL", buffer); X setenv ("HOME", pwd->pw_dir); X setenv ("LOGNAME", user_name); X if (altshell) X setenv ("SHELL", shell); X if (pwd->pw_uid == ROOT) X setenv ("PATH", supath); X else X setenv ("PATH", path); X if (*tz) X setenv ("TZ", tz); X X for (i = optind; i < argc; i++) /* environment from command line */ X check_putenv (argv[i]); X X signal (SIGALRM, SIG_DFL); X signal (SIGQUIT, SIG_DFL); X signal (SIGINT, SIG_DFL); X#ifdef SIGTSTP X /* Interactive has job control signals, AT&T does not */ X signal (SIGTSTP, SIG_IGN); X#endif X X /* exec the desired shell */ X buffer[0] = '-'; X strcpy (buffer + 1, (p = strrchr (shell, '/')) ? p + 1 : shell); X execlp (shell, buffer, 0); X fprintf (stdout, "%s: no shell: ", argv[0]); X perror (shell); X slow_exit (1); X} X X/**************************************************************************** X* read /etc/dialups & /etc/d_passwd, if they exist * X* Return 0 if user has access, 1 if no access * X****************************************************************************/ X Xint dialup_pwd (ttyfname, pwdfname, tty_name, pwd) Xchar *ttyfname; /* /etc/dialups */ Xchar *pwdfname; /* /etc/d_passwd */ Xchar *tty_name; /* user's tty line */ Xstruct passwd *pwd; /* user;s /etc/passwd file entry */ X{ FILE *fp; X char dtty[SHORTLINE+1]; X char *dpass, *dcrypt, *salt, *shell, *dialpass, *crypt(); X X if (pwd == NULL) X return (0); X X if (*(pwd->pw_shell)) X shell = pwd->pw_shell; X else X shell = DEFAULTSHELL; X if (access (ttyfname, 0)) X return (0); X if (fp = fopen (ttyfname, "r")) X { while (fgets (dtty, SHORTLINE, fp)) X { dtty[SHORTLINE] = 0; X trim (dtty); X if (strcmp (dtty, tty_name) == 0) X { fclose (fp); X if (dialpass = get_shadow (DIALUPPWD, shell, 1)) X { if (*dialpass == '\0') /* no password needed */ X return (0); X salt = dialpass; X } X else X return (1); X dpass = getpass ("dialup password: "); X dcrypt = crypt (dpass, salt); X if (strcmp (dcrypt, dialpass)) X return (1); X else X return (0); X } X } X fclose (fp); X } X return (0); X} X X#ifdef USRACCESS X/**************************************************************************** X* process /etc/usrtime, if it exists * X* Return 0 if user has access, 1 if no access * X****************************************************************************/ X Xint get_usrtime (fname, user, shell, tty) Xchar *fname; /* /etc/usrtime */ Xchar *user; /* user login name */ Xchar *shell; /* user's shell (from /etc/passwd) */ Xchar *tty; /* user's login tty line */ X{ X struct usrtime all, uucp, slip, interact, genuser, thisuser, *which; X char line[LONGLINE+1]; X FILE *fp; X char *var[5], *colon; X char *uname, save_letter; X int i; X long now; X char *uucico; X X if (strcmp (user, "root") == 0) X return (0); X X if ((fp = fopen (fname, "r")) == NULL) X return (0); X X uucico = "uucico"; X all.enable = uucp.enable = slip.enable = interact.enable X = thisuser.enable = genuser.enable = NULL; X X uname = alloca (strlen (user) + 3); X strcpy (uname, user); X strcat (uname, ","); X X while (fgets (line, LONGLINE, fp)) X { line[LONGLINE] = '\0'; X if (*line == '#') X continue; X trim (line); X for (colon = line, i = 0; i < 5; i++) X { var[i] = colon; X colon = strchr (colon, ':'); X if (colon == NULL) X break; X *colon++ = '\0'; X } X if (colon == NULL) /* need min of 5 colons */ X continue; X which = NULL; X X /* select first instance of lines */ X X if (strcmp (var[0], "ALL") == 0) X { if (all.enable) X continue; X which = &all; X } X else if (strcmp (var[0], "UUCP") == 0) X { if (uucp.enable) X continue; X which = &uucp; X } X else if (strcmp (var[0], "SLIP") == 0) X { if (slip.enable) X continue; X which = &slip; X } X else if (strcmp (var[0], "INTERACTIVE") == 0) X { if (interact.enable) X continue; X which = &interact; X } X else if (var[0][0] == '\0') X { /* general default user setup */ X if (genuser.enable) X continue; X which = &genuser; X } X X if (which) X { /* ALL, UUCP, SLIP, INTERACTIVE or default user lines */ X which->enable = alloca (strlen (var[1]) + 2); X which->ttyline = alloca (strlen (var[2]) + 2); X which->days = alloca (strlen (var[3]) + 2); X which->timerange = alloca (strlen (var[4]) + 2); X strcpy (which->enable, var[1]); X strcpy (which->ttyline, var[2]); X strcpy (which->days, var[3]); X strcpy (which->timerange, var[4]); X } X else X { /* standard user line, could be list of users */ X i = strlen (var[0]); X save_letter = var[1][0]; X strcat (var[0], ","); X if (tscan (var[0], uname) >= 0) X { var[0][i] = '\0'; /* undo comma */ X var[1][0] = save_letter; X thisuser.enable = var[1]; X thisuser.ttyline = var[2]; X thisuser.days = var[3]; X thisuser.timerange = var[4]; X break; /* got user exactly */ X } X } X } X fclose (fp); X now = time (NULL); X X if ( (all.enable X && check_user ("All", now, tty, &all)) X || (uucp.enable X && tscan (shell, uucico) >= 0 X && check_user ("Uucp", now, tty, &uucp)) X || (slip.enable X && tscan (shell, SLIP) >= 0 X && check_user ("Slip", now, tty, &slip)) X || (interact.enable X && tscan (shell, uucico) < 0 X && tscan (shell, SLIP) < 0 X && check_user ("Interactive", now, tty, &interact)) X || (genuser.enable X && thisuser.enable == NULL X && check_user ("General", now, tty, &genuser)) X || (thisuser.enable X && check_user ("Your", now, tty, &thisuser))) X { return (1); /* user rejected */ X } X return (0); /* user accepted */ X} X X/**************************************************************************** X* usrtime: check fields in sequence to see if user has access permission * X****************************************************************************/ X Xint check_user (note, now, tty, usr) Xchar *note; /* comment to user */ Xchar *tty; /* user's tty line */ Xlong now; /* current time of day (now=time(NULL)) */ Xstruct usrtime *usr; /* various values from /etc/usrtime */ X{ X if (strcmp (usr->enable, "NOLOGIN") == 0) X { fprintf (stdout, "%s logins are disabled.\n", note); X return (1); X } X return ( check_time (note, now, usr->enable, 0) /* yymmdd */ X || check_tty (note, tty, usr->ttyline) X || check_day (note, now, usr->days) X || check_time (note, now, usr->timerange, 1)); /* hhmm */ X} X X/**************************************************************************** X* usrtime: ensure usr is on acceptable tty line * X****************************************************************************/ X Xint check_tty (note, tty, list) Xchar *note; /* comment to user */ Xchar *tty; /* user's tty line */ Xchar *list; /* list of valid tty lines */ X{ X char *tt, *ls, *short_tt; X int inverse = 0; X X if (*list == '\0') X return (0); X if (*list == '!') X { inverse = 1; X list++; X } X tt = alloca (strlen (tty) + 2); X strcpy (tt, tty); X strcat (tt, ","); X if (short_tt = strrchr (tt, '/')) X tt = short_tt + 1; X ls = alloca (strlen (list) + 2); X strcpy (ls, list); X strcat (ls, ","); X if (qtscan (ls, tt) >= 0 && last_match == ',') X { if (!inverse) X return (0); X } X else if (inverse) X return (0); X fprintf (stdout, "%s logins are %s on %s.\n", X note, inverse ? "not enabled" : "enabled", list); X return (1); X} X X/**************************************************************************** X* usrtime: ensure usr is on acceptable week day * X****************************************************************************/ X Xint check_day (note, now, list) Xchar *note; /* comment to user */ Xlong now; /* current time = time(NULL) */ Xchar *list; /* list of days (Mon, Tue...) */ X{ X char weekday[50]; X int inverse = 0; X X if (*list == '\0') X return (0); X if (*list == '!') X { inverse = 1; X list++; X } X cftime (weekday, "%a", &now); X if (tscan (list, weekday) >= 0) X { X if (!inverse) X return (0); X } X else if (inverse) X return (0); X fprintf (stdout, "%s logins are %s on %s.\n", X note, inverse ? "not enabled" : "enabled", list); X return (1); X} X X/**************************************************************************** X* usrtime: ensure usr is on acceptable time or date range * X****************************************************************************/ X Xint check_time (note, now, list, timerange) Xchar *note; /* comment to user */ Xlong now; /* current time = time(NULL) */ Xchar *list; /* hhmm-hhmm or yymmdd-yymmdd list */ Xint timerange; /* 0 = check yyyymmdd, 1 = check hhmm */ X{ X char clock[50]; X char *begin, *end; X char *ls; X long present, first, last; X int inverse = 0; X X if (*list == '!') X { inverse = 1; X list++; X } X if (*list < '0' || *list > '9' || strchr (list, '-') == NULL) X return (0); X ls = end = alloca (strlen (list) + 2); X strcpy (end, list); X if (timerange) X cftime (clock, "%H%M", &now); X else X cftime (clock, "%Y%m%d", &now); X present = atol (clock); X while (*end) X { begin = end; X if ((end = strchr (begin, '-')) == NULL) X break; X *end++ = '\0'; X first = atol (begin); X last = atol (end); X if (first <= present && present <= last) X { if (!inverse) X return (0); X else X break; X } X if ((end = strchr (end, ',')) == NULL) X break; X end++; X } X if ((inverse && end) || (!inverse && end == NULL)) X { if (inverse) X end = "are not"; X else X end = "are"; X if (timerange) X fprintf (stdout, "%s logins %s enabled between %s hours.\n", note, end, list); X else X fprintf (stdout, "%s logins %s enabled for dates %s.\n", note, end, list); X return (1); X } X return (0); X} X X#endif X X/**************************************************************************** X* set the users environment with values. * X****************************************************************************/ X Xvoid setenv (this, val) Xchar *this; Xchar *val; X{ X char s[MAXPATHLEN+1]; X char *ss; X X sprintf (s, "%s=%s", this, val); X ss = malloc (strlen(s) + 1); X strcpy (ss, s); X putenv (ss); X} X X/**************************************************************************** X* user-provided environment variables must be checked * X****************************************************************************/ X Xvoid check_putenv (val) Xchar *val; X{ static count = 0; X char *env; X char **forbid; X X /* check forbidden environment variables */ X for (forbid = forbidden; *forbid; forbid++) X { if (strncmp (*forbid, val, strlen (*forbid)) == 0) X return; X } X X /* variables without an '=' sign assume the form of "Lnn=val" */ X if (strchr (val, '=') == NULL) X { env = malloc (strlen (val)+ 6); X sprintf (env, "L%d=%s", count++, val); X val = env; X } X putenv (val); X} X X/**************************************************************************** X* get the encrypted password from /etc/shadow or /etc/d_passwd * X* allow "null" user for /etc/d_passwd general shell password. * X* Return encrypted password if found, else null. * X****************************************************************************/ X X/* could have used getspnam(user) except for /etc/d_passwd */ X Xchar *get_shadow (fname, user, allow) Xchar *fname; /* /etc/shadow or /etc/d_passwd */ Xchar *user; /* user's login name */ Xint allow; /* 0 =must have pwr entry, 1 =success even if no entry */ X{ FILE *shadow; X char line[SHORTLINE+1]; X char *pwd, *colon; X static char pass[50]; X X *pass = '\0'; X if (shadow = fopen (fname, "r")) X { while (fgets (line, SHORTLINE, shadow)) X { line[SHORTLINE] = '\0'; X if (pwd = strchr (line, ':')) X { *pwd++ = '\0'; X if (colon = strchr (pwd, ':')) X { *colon++ = '\0'; X if (*line) X { if (strcmp (user, line) == 0) X { fclose (shadow); X strcpy (pass, pwd); X if (allow == 0 && colon) X { shadow_lstchg = atol (colon); X colon = strchr (colon, ':') + 1; X shadow_min = atol (colon); X colon = strchr (colon, ':') + 1; X shadow_max = atol (colon); X } X return (pass); X } X } X else if (allow) X strcpy (pass, pwd); X } X } X } X } X fclose (shadow); X if (allow && *pass) X return (pass); X return (NULL); X} X X/**************************************************************************** X* search for tty type in /etc/ttytype * X****************************************************************************/ X Xchar *search_ttytype (tty) Xchar *tty; /* user's tty line */ X{ FILE *fp; X char line[SHORTLINE+1]; X static char *tname; X char *dev; X X if (fp = fopen (TTYTYPE, "r")) X { while (fgets (line, SHORTLINE, fp)) X { line[SHORTLINE] = '\0'; X trim (line); X dev = line; X while (*dev) /* skip tty name */ X { if (*dev <= ' ') X break; X dev++; X } X while (*dev) /* locate device */ X { if (*dev > ' ') X break; X *dev++ = '\0'; X } X if (strcmp (dev, tty) == 0) X { fclose (fp); X tname = malloc (strlen(line)+1); X strcpy (tname, line); X return (tname); X } X } X } X fclose (fp); X return ("UNKNOWN"); X} X X X/**************************************************************************** X* read /etc/default/login file * X****************************************************************************/ X Xint get_defaults (fname, tz, hz, console, ulim, passreq, altshell, X path, supath, timeout, cmask, idleweeks) Xchar *fname; /* /etc/default/login */ Xchar *tz; /* TIMEZONE=xxx should be same as in /etc/TIMEZONE */ Xchar *hz; /* HZ=nn */ Xchar **console; /* CONSOLE=xxx root must be on this device if given*/ Xlong *ulim; /* ULIMIT=xxx default ulimit for users */ Xint *passreq; /* PASSREQ=YES/NO is password required */ Xint *altshell; /* ALTSHELL=YES/NO YES==set shell name in environment */ Xchar **path; /* PATH=xxx default user path */ Xchar **supath; /* SUPATH=xxx defaut root path */ Xlong *timeout; /* TIMEOUT=nn login abort timeout in seconds */ Xint *cmask; /* UMASK=ooo default umask for users */ Xint *idleweeks; /* IDLEWEEKS=nn idle weeks */ X{ X FILE *fp; X char line[LONGLINE+1]; X char *equal; X X if ((fp = fopen (fname, "r")) == NULL) X return (2); X while ((fgets (line, LONGLINE, fp))) X { if (*line == '#') /* must be first character */ X continue; X line[LONGLINE] = '\0'; X trim (line); X if (equal = strchr (line, '=')) X { *equal++ = '\0'; X if (strcmp (line, "TIMEZONE") == 0) X strcpy (tz, equal); X else if (strcmp (line, "HZ") == 0) X strcpy (hz, equal); X else if (strcmp (line, "CONSOLE") == 0) X { if (*console == NULL) X { *console = malloc (strlen (equal) + 2); X strcpy (*console, equal); X if ((*console)[strlen(*console) - 1] != ':') X strcat (*console, ":"); X } X } X else if (strcmp (line, "ULIMIT") == 0) X *ulim = atol (equal); X else if (strcmp (line, "PATH") == 0) X { if (*path == NULL) X { *path = malloc (strlen (equal) + 1); X strcpy (*path, equal); X } X } X else if (strcmp (line, "SUPATH") == 0) X { if (*supath == NULL) X { *supath = malloc (strlen (equal) + 1); X strcpy (*supath, equal); X } X } X else if (strcmp (line, "TIMEOUT") == 0) X *timeout = atol (equal); X else if (strcmp (line, "UMASK") == 0) X *cmask = atoo (equal); X else if (strcmp (line, "PASSREQ") == 0) X { if (toupper (*equal) == 'Y') X *passreq = 1; X else X *passreq = 0; X } X else if (strcmp (line, "ALTSHELL") == 0) X { if (toupper (*equal) == 'Y') X *altshell = 1; X else X *altshell = 0; X } X else if (strcmp (line, "IDLEWEEKS") == 0) X *idleweeks = atoi (equal); X } X } X fclose (fp); X return (0); X} X X/**************************************************************************** X* check for and display /etc/nologin file * X****************************************************************************/ X Xvoid etc_nologin (trustme) Xchar *trustme; X{ X int fd, nchars; X FILE *fp; X char why[8192]; X static int msg_done = 0; X X if (strcmp (trustme, "root") == 0 || msg_done) X return; X if ((fd = open (NOLOGIN, O_RDONLY, 0)) >= 0) X { while ((nchars = read(fd, why, sizeof(why))) > 0) X write (fileno(stdout), why, nchars); X close (fd); X if (fp = fopen (TRUSTED, "r")) X { fgets (why, 100, fp); X fclose (fp); X trim (why); X X /* trusted user can bypass nologin to allow the sysadmin X to login on the network (he cannot be root - usually) X */ X if (strcmp (why, trustme)) X slow_exit (0); X } X else slow_exit (0); X } X msg_done = 1; X} X X/**************************************************************************** X* sleep before exit so network does not close before msg is out. * X****************************************************************************/ X Xvoid slow_exit (rc) Xint rc; X{ X#ifdef TCPIP X fflush (stdout); X fflush (stdout); X sleep (1); /* maybe this should be longer? */ X#endif X exit (rc); X} X X/**************************************************************************** X* trim line at end * X****************************************************************************/ X Xvoid trim (s) Xchar *s; X{ char *t; X X t = s + strlen (s); X do X { if (*t > ' ') X break; X *t = '\0'; X } while (t-- != s); X} X X/**************************************************************************** X* ascii to octal * X****************************************************************************/ X Xint atoo (s) Xchar *s; X{ X unsigned int v; X X v = 0; X while (*s) X v = v * 8 + (*s++ - '0'); X return (v); X} X X/**************************************************************************** X* timeout error message if user takes too long to log in. * X****************************************************************************/ X Xvoid user_timeout() X{ X fprintf (stdout, "login timed out after %d seconds\n", timeout); X slow_exit(1); X} X X X/**************************************************************************** X* search for string t in string s. return -1 if it does not exist * X* otherwise return an integer, the first byte of s that matches t. * X****************************************************************************/ X Xint tscan (s, t) Xchar s[], t[]; X{ X int i, j, k; X for (i = 0; s[i] != '\0'; i++) X { for (j = i, k=0; t[k] != '\0' && s[j] == t[k]; j++, k++) X ; X if (t[k] == '\0') X return (i); X } X return (-1); X} X X/**************************************************************************** X* search for string t in string s. return -1 if it does not exist * X* otherwise return an integer, the first byte of s that matches t. * X* Any '?' in s is a wild card match for t * X****************************************************************************/ X Xint qtscan (s, t) Xchar s[], t[]; X{ X int i, j, k; X for (i = 0; s[i] != '\0'; i++) X { for (j = i, k=0; t[k] != '\0' && ((s[j] == t[k]) || (s[j] == '?')) ; j++, k++) X last_match = s[j]; X if (t[k] == '\0') X return (i); X } X last_match = 0; X return (-1); X} X X#ifdef TCPIP X/**************************************************************************** X* read remotename, localname tty info from rlogind * X****************************************************************************/ X Xvoid get_remote (buf, cnt) /* read null terminated string */ Xchar *buf; Xint cnt; X{ X char c; X X do X { if (read (0, &c, 1) != 1) X slow_exit(1); X if (--cnt < 0) X slow_exit(1); /* should never happen! */ X *buf++ = c; X } while (c != 0); X} X Xint get_remote_user (host) /* read from rlogind */ Xchar *host; X{ int ruserok(); X X if (host == NULL || *host == '\0') X return (-1); X get_remote (rname, sizeof (rname)); /* remote user name */ X get_remote (lname, sizeof (lname)); /* desired local user name */ X get_remote (tname, sizeof (tname)); /* tty / baud rate */ X set_tcp_ioctl (); /* clean up socket. */ X pwd = getpwnam (lname); X X /* check hosts.equiv and all those good things */ X if (pwd == NULL || ruserok (host, (pwd->pw_uid == 0), rname, lname)) X { fprintf (stdout, "Who is %s@%s?\n", rname, host); X return (-1); X } X#ifdef USRACCESS X if (get_usrtime (USRTIME, lname, pwd->pw_shell, tty_name)) X return (-1); X#endif /* USRACCESS */ X return (0); X} X X X/**************************************************************************** X* set up the tty line ioctl for network connection * X* * X* The default ioctl() conditions are for Interactive Unix. If WOLLONGONG * X* is defined, then the ioctls for AT&T + Wollongong rlogind are set. * X****************************************************************************/ X Xchar *rates[] = { "0", "50", "75", "110", "134", "150", "200", "300", X "600", "1200", "1800", "2400", "4800", "9600", X "19200", "38400" X }; X#define NRATE (sizeof (rates) / sizeof (rates[0])) X Xvoid set_tcp_ioctl () X{ struct termio tio; X char *baudrate; X int i; X int t; X X ioctl(0, TCGETA, &tio); X if (baudrate = strchr (tname, '/')) X { baudrate++; X for (i = 0; i < NRATE; i++) X { if (strcmp (baudrate, rates[i]) == 0) X { X#ifdef WOLLONGONG X tio.c_cflag = (B0 + i) | CS8; X#else X tio.c_cflag = (B0 + i) | HUPCL | CREAD | CS8; X#endif X break; X } X } X } X#ifdef WOLLONGONG X tio.c_iflag = ICRNL | ISTRIP | IGNPAR; X tio.c_oflag = TAB3 | ONLCR | OPOST; X tio.c_lflag = ECHO | ECHOE | ECHOK | ISIG | ICANON; X#else X tio.c_iflag = IXON | IXANY | ICRNL | ISTRIP | IGNPAR | BRKINT; X tio.c_oflag = ONLCR | OPOST; X tio.c_lflag = ECHO | ECHOE | ECHOK | ISIG | ICANON; X#endif X ioctl (0, TCSETA, &tio); X X for (t = getdtablesize(); t > 2; t--) X close(t); X X} X#endif /* TCPIP */ END_OF_FILE if test 37527 -ne `wc -c <'login.c'`; then echo shar: \"'login.c'\" unpacked with wrong size! fi # end of 'login.c' fi echo shar: End of archive 2 \(of 2\). cp /dev/null ark2isdone MISSING="" for I in 1 2 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked both archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0 exit 0 # Just in case...