From: jfh@rpp386.cactus.org (John F Haugh II) Newsgroups: alt.sources Subject: Shadow Login Suite, version 3 (part 2 of 8) Message-ID: <19296@rpp386.cactus.org> Date: 16 May 91 16:31:34 GMT #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # passwd.c # port.c # lmain.c # mkpasswd.c # sulogin.c # pwpack.c # dialup.c # sulog.c # getpass.c # This archive created: Sun Mar 3 13:27:16 1991 # By: John F Haugh II (River Parishes Programming, Austin TX) export PATH; PATH=/bin:/usr/bin:$PATH echo shar: "extracting 'passwd.c'" '(17138 characters)' if test -f 'passwd.c' then echo shar: "will not over-write existing file 'passwd.c'" else sed 's/^X//' << \SHAR_EOF > 'passwd.c' X/* X * Copyright 1989, 1990, John F. Haugh II X * All rights reserved. X * X * Permission is granted to copy and create derivative works for any X * non-commercial purpose, provided this copyright notice is preserved X * in all copies of source code, or included in human readable form X * and conspicuously displayed on all copies of object code or X * distribution media. X */ X X#include X#include X#include X#include X#include X#include X X#ifndef lint Xstatic char sccsid[] = "@(#)passwd.c 3.1 09:00:47 2/8/91"; X#endif X X/* X * Set up some BSD defines so that all the BSD ifdef's are X * kept right here X */ X X#ifndef BSD X#include X#include X#define bzero(a,n) memset(a, 0, n) X#else X#include X#define strchr index X#define strrchr rindex X#endif X X#include "config.h" X#include "pwd.h" X#include "lastlog.h" X#include "shadow.h" X X/* X * Password aging constants X * X * DAY - seconds in a day X * WEEK - seconds in a week X * SCALE - convert from clock to aging units X */ X X#define DAY (24L*3600L) X#define WEEK (7L*DAY) X X#ifdef ITI_AGING X#define SCALE (1) X#else X#define SCALE DAY X#endif X X/* X * Global variables X */ X Xchar name[32]; /* The user's name */ Xchar *Prog; /* Program name */ Xint amroot; /* The real UID was 0 */ X X/* X * External identifiers X */ X Xextern char *getpass(); Xextern char *pw_encrypt(); Xextern char *getlogin(); Xextern int optind; /* Index into argv[] for current option */ Xextern char *optarg; /* Pointer to current option value */ X#ifdef NDBM Xextern int sp_dbm_mode; Xextern int pw_dbm_mode; X#endif X X/* X * #defines for messages. This facilities foreign language conversion X * since all messages are defined right here. X */ X X#define USAGE "usage: %s [ -f | -s ] [ name ]\n" X#define ADMUSAGE \ X " %s [ -x max ] [ -n min ] [ -w warn ] [ -i inact ] name\n" X#define ADMUSAGE2 \ X " %s { -l | -d | -S } name\n" X#define OLDPASS "Old Password:" X#define NEWPASSMSG \ X"Enter the new password (minimum of 5 characters)\n\ XPlease use a combination of upper and lower case letters and numbers.\n" X#define NEWPASS "New Password:" X#define NEWPASS2 "Re-enter new password:" X#define WRONGPWD "Incorrect password for %s.\n" X#define WRONGPWD2 "incorrect password for `%s'\n" X#define NOMATCH "They don't match; try again.\n" X#define CANTCHANGE "The password for %s cannot be changed.\n" X#define CANTCHANGE2 "password locked for `%s'\n" X#define TOOSOON "Sorry, the password for %s cannot be changed yet.\n" X#define TOOSOON2 "now < sp_min for `%s'\n" X#define EXECFAILED "%s: Cannot execute %s" X#define EXECFAILED2 "cannot execute %s\n" X#define WHOAREYOU "%s: Cannot determine you user name.\n" X#define UNKUSER "%s: Unknown user %s\n" X#define NOPERM "You may not change the password for %s.\n" X#define NOPERM2 "can't change pwd for `%s'\n" X#define UNCHANGED "The password for %s is unchanged.\n" X#define SPWDBUSY "Cannot lock the password file; try again later.\n" X#define SPWDBUSY2 "can't lock /etc/shadow\n" X#define OPNERROR "Cannot open the password file.\n" X#define OPNERROR2 "can't open /etc/shadow\n" X#define UPDERROR "Error updating the password entry.\n" X#define UPDERROR2 "error updating shadow entry\n" X#define DBMERROR "Error updating the DBM password entry.\n" X#define DBMERROR2 "error updating DBM shadow entry.\n" X#define NOTROOT "Cannot change ID to root.\n" X#define NOTROOT2 "can't setuid(0).\n" X#define CLSERROR "Cannot commit shadow file changes.\n" X#define CLSERROR2 "can't rewrite /etc/shadow.\n" X#define UNLKERROR "Cannot unlock the shadow file.\n" X#define UNLKERROR2 "can't unlock /etc/shadow.\n" X#define TRYAGAIN "Try again.\n" X#define CHGPASSWD "changed password for `%s'\n" X X/* X * usage - print command usage and exit X */ X Xvoid Xusage () X{ X fprintf (stderr, USAGE, Prog); X if (amroot) { X fprintf (stderr, ADMUSAGE, Prog); X fprintf (stderr, ADMUSAGE2, Prog); X } X exit (1); X} X X/* X * new_password - validate old password and replace with new X */ X Xint Xnew_password (pw, sp) Xstruct passwd *pw; Xstruct spwd *sp; X{ X char *clear; /* Pointer to clear text */ X char *cipher; /* Pointer to cipher text */ X char *cp; /* Pointer to getpass() response */ X char orig[BUFSIZ]; /* Original password */ X char pass[BUFSIZ]; /* New password */ X int i; /* Counter for retries */ X X /* X * Authenticate the user. The user will be prompted for their X * own password. X */ X X if (! amroot && sp->sp_pwdp[0]) { X bzero (orig, sizeof orig); X X if (! (clear = getpass (OLDPASS))) X return -1; X X cipher = pw_encrypt (clear, sp->sp_pwdp); X if (strcmp (cipher, sp->sp_pwdp) != 0) { X sleep (1); X fprintf (stderr, WRONGPWD, sp->sp_namp); X syslog (LOG_WARN, WRONGPWD2, sp->sp_namp); X return -1; X } X strcpy (orig, clear); X } X X /* X * Get the new password. The user is prompted for the new password X * and has three tries to get it right. The password will be tested X * for strength, unless it is the root user. This provides an escape X * for initial login passwords. X */ X X printf (NEWPASSMSG); X for (i = 0;i < 3;i++) { X if (! (cp = getpass (NEWPASS))) X return -1; X else X strcpy (pass, cp); X X if (! amroot && ! obscure (orig, pass)) { X printf (TRYAGAIN); X continue; X } X if (! (cp = getpass (NEWPASS2))) X return -1; X X if (strcmp (cp, pass)) X fprintf (stderr, NOMATCH); X else X break; X } X if (i == 3) X return -1; X X /* X * Encrypt the password. The new password is encrypted and X * the shadow password structure updated to reflect the change. X */ X X sp->sp_pwdp = pw_encrypt (pass, (char *) 0); X sp->sp_lstchg = time ((time_t *) 0) / SCALE; X X return 0; X} X X/* X * check_password - test a password to see if it can be changed X * X * check_password() sees if the invoker has permission to change the X * password for the given user. X */ X Xvoid Xcheck_password (pw, sp) Xstruct passwd *pw; Xstruct spwd *sp; X{ X time_t now = time ((time_t *) 0) / SCALE; X X /* X * Root can change any password any time. X */ X X if (amroot) X return; X X /* X * Expired accounts cannot be changed ever. Passwords X * which are locked may not be changed. Passwords where X * min > max may not be changed. Passwords which have X * been inactive too long cannot be changed. X */ X X if ((sp->sp_expire > 0 && now >= sp->sp_expire) || X (sp->sp_inact >= 0 && sp->sp_max >= 0 && X now >= (sp->sp_lstchg + sp->sp_inact + sp->sp_max)) || X strcmp (sp->sp_pwdp, "!") == 0 || X sp->sp_min > sp->sp_max) { X fprintf (stderr, CANTCHANGE, sp->sp_namp); X syslog (LOG_WARN, CANTCHANGE2, sp->sp_namp); X exit (1); X } X X /* X * Passwords may only be changed after sp_min time is up. X */ X X if (sp->sp_min >= 0 && now < (sp->sp_lstchg + sp->sp_min)) { X fprintf (stderr, TOOSOON, sp->sp_namp); X syslog (LOG_WARN, TOOSOON2, sp->sp_namp); X exit (1); X } X} X X/* X * pwd_to_spwd - create entries for new spwd structure X * X * pwd_to_spwd() creates a new (struct spwd) containing the X * information in the pointed-to (struct passwd). X */ X Xvoid Xpwd_to_spwd (pw, sp) Xstruct passwd *pw; Xstruct spwd *sp; X{ X time_t t; X X /* X * Nice, easy parts first. The name and passwd map directly X * from the old password structure to the new one. X */ X X sp->sp_namp = strdup (pw->pw_name); X sp->sp_pwdp = strdup (pw->pw_passwd); X#ifdef ATT_AGE X X /* X * AT&T-style password aging maps the sp_min, sp_max, and X * sp_lstchg information from the pw_age field, which appears X * after the encrypted password. X */ X X if (pw->pw_age[0]) { X t = (c64i (pw->pw_age[0]) * WEEK) / SCALE; X sp->sp_max = t; X X if (pw->pw_age[1]) { X t = (c64i (pw->pw_age[1]) * WEEK) / SCALE; X sp->sp_min = t; X } else X sp->sp_min = (10000L * DAY) / SCALE; X X if (pw->pw_age[1] && pw->pw_age[2]) { X t = (a64l (pw->pw_age + 2) * WEEK) / SCALE; X sp->sp_lstchg = t; X } else X sp->sp_lstchg = time ((time_t *) 0) / SCALE; X } else { X sp->sp_min = 0; X sp->sp_max = (10000L * DAY) / SCALE; X sp->sp_lstchg = time ((time_t *) 0) / SCALE; X } X#else X /* X * BSD does not use the pw_age field and has no aging information X * anywheres. The default values are used to initialize the X * fields which are in the missing pw_age field; X */ X X sp->sp_min = 0; X sp->sp_max = (10000L * DAY) / SCALE; X sp->sp_lstchg = time ((time_t *) 0) / SCALE; X#endif X X /* X * These fields have no corresponding information in the password X * file. They are set to uninitialized values. X */ X X sp->sp_warn = -1; X sp->sp_inact = -1; X sp->sp_expire = -1; X sp->sp_flag = -1; X} X X/* X * print_status - print current password status X */ X Xvoid Xprint_status (sp) Xstruct spwd *sp; X{ X struct tm *tm; X time_t time; X X time = sp->sp_lstchg * SCALE; X tm = gmtime (&time); X X printf ("%s ", sp->sp_namp); X printf ("%s ", X sp->sp_pwdp[0] ? (sp->sp_pwdp[0] == '!' ? "L":"P"):"NP"); X printf ("%02.2d/%02.2d/%02.2d ", X tm->tm_mon + 1, tm->tm_mday, tm->tm_year % 100); X printf ("%d %d %d %d\n", X (sp->sp_min * SCALE) / DAY, (sp->sp_max * SCALE) / DAY, X (sp->sp_warn * SCALE) / DAY, (sp->sp_inact * SCALE) / DAY); X} X X/* X * passwd - change a user's password file information X * X * This command controls the password file and commands which are X * used to modify it. X * X * The valid options are X * X * -l lock the named account (*) X * -d delete the password for the named account (*) X * -x # set sp_max to # days (*) X * -n # set sp_min to # days (*) X * -w # set sp_warn to # days (*) X * -i # set sp_inact to # days (*) X * -S show password status of named account (*) X * -g execute gpasswd command to interpret flags X * -f execute chfn command to interpret flags X * -s execute chsh command to interpret flags X * X * (*) requires root permission to execute. X * X * All of the time fields are entered in days and converted to the X * appropriate internal format. For finer resolute the chage X * command must be used. X */ X Xint Xmain (argc, argv) Xint argc; Xchar **argv; X{ X char buf[BUFSIZ]; /* I/O buffer for messages, etc. */ X char *cp; /* Miscellaneous character pointing */ X time_t min; /* Minimum days before change */ X time_t max; /* Maximum days until change */ X time_t warn; /* Warning days before change */ X time_t inact; /* Days without change before locked */ X int i; /* Loop control variable */ X int flag; /* Current option to process */ X int lflg = 0; /* -l - lock account option */ X int dflg = 0; /* -d - delete password option */ X int xflg = 0; /* -x - set maximum days */ X int nflg = 0; /* -n - set minimum days */ X int wflg = 0; /* -w - set warning days */ X int iflg = 0; /* -i - set inactive days */ X int Sflg = 0; /* -S - show password status */ X struct passwd *pw; /* Password file entry for user */ X struct spwd *sp; /* Shadow file entry for user */ X struct spwd tspwd; /* New shadow file entry if none */ X X /* X * The program behaves differently when executed by root X * than when executed by a normal user. X */ X X amroot = getuid () == 0; X#ifdef NDBM X sp_dbm_mode = O_RDWR; X pw_dbm_mode = O_RDWR; X#endif X X /* X * Get the program name. The program name is used as a X * prefix to most error messages. It is also used as input X * to the openlog() function for error logging. X */ X X if (Prog = strrchr (argv[0], '/')) X Prog++; X else X Prog = argv[0]; X X openlog (Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH); X X /* X * Start with the flags which cause another command to be X * executed. The effective UID will be set back to the X * real UID and the new command executed with the flags X */ X X if (argc > 1 && argv[1][0] == '-' && strchr ("gfs", argv[1][1])) { X setuid (getuid ()); X switch (argv[1][1]) { X case 'g': X argv[1] = "gpasswd"; X execv ("/bin/gpasswd", &argv[1]); X break; X case 'f': X argv[1] = "chfn"; X execv ("/bin/chfn", &argv[1]); X break; X case 's': X argv[1] = "chsh"; X execv ("/bin/chsh", &argv[1]); X break; X default: X usage (); X } X sprintf (buf, EXECFAILED, Prog, argv[1]); X perror (buf); X syslog (LOG_CRIT, EXECFAILED2, argv[1]); X exit (1); X } X X /* X * The remaining arguments will be processed one by one and X * executed by this command. The name is the last argument X * if it does not begin with a "-", otherwise the name is X * determined from the environment and must agree with the X * real UID. Also, the UID will be checked for any commands X * which are restricted to root only. X */ X X while ((flag = getopt (argc, argv, "ldx:n:w:i:S")) != EOF) { X switch (flag) { X case 'x': X max = strtol (optarg, &cp, 10); X if (*cp || getuid ()) X usage (); X X xflg++; X break; X case 'n': X min = strtol (optarg, &cp, 10); X if (*cp || getuid ()) X usage (); X X nflg++; X break; X case 'w': X warn = strtol (optarg, &cp, 10); X if (*cp || getuid ()) X usage (); X X wflg++; X break; X case 'i': X inact = strtol (optarg, &cp, 10); X if (*cp || getuid ()) X usage (); X X iflg++; X break; X case 'S': X if (getuid ()) X usage (); X X Sflg++; X break; X case 'd': X dflg++; X break; X case 'l': X lflg++; X break; X default: X usage (); X } X } X X /* X * If any of the flags were given, a user name must be supplied X * on the command line. Only an unadorned command line doesn't X * require the user's name be given. Also, on -x, -n, -m, and X * -i may appear with each other. -d, -l and -S must appear alone. X */ X X if ((dflg || lflg || xflg || nflg || X wflg || iflg || Sflg) && optind >= argc) X usage (); X X if ((dflg + lflg + (xflg || nflg || wflg || iflg) + Sflg) > 1) X usage (); X X /* X * Now I have to get the user name. The name will be gotten X * from the command line if possible. Otherwise it is figured X * out from the environment. X */ X X if (optind < argc) { X strncpy (name, argv[optind], sizeof name); X name[sizeof name - 1] = '\0'; X } else if (cp = getlogin ()) { X strncpy (name, cp, sizeof name); X name[sizeof name - 1] = '\0'; X } else { X fprintf (stderr, WHOAREYOU, Prog); X exit (1); X } X X /* X * Now I have a name, let's see if the UID for the name X * matches the current real UID. X */ X X if (! (pw = getpwnam (name))) { X fprintf (stderr, UNKUSER, Prog, name); X exit (1); X } X if (! amroot && pw->pw_uid != getuid ()) { X fprintf (stderr, NOPERM, name); X syslog (LOG_WARN, NOPERM2, name); X exit (1); X } X X /* X * The user name is valid, so let's get the shadow file X * entry. X */ X X if (! (sp = getspnam (name))) X pwd_to_spwd (pw, sp = &tspwd); X X /* X * Save the shadow entry off to the side so it doesn't X * get changed by any of the following code. X */ X X if (sp != &tspwd) { X tspwd = *sp; X sp = &tspwd; X } X tspwd.sp_namp = strdup (sp->sp_namp); X tspwd.sp_pwdp = strdup (sp->sp_pwdp); X X if (Sflg) { X print_status (sp); X exit (0); X } X X /* X * If there are no other flags, just change the password. X */ X X if (! (dflg || lflg || xflg || nflg || wflg || iflg)) { X X /* X * See if the user is permitted to change the password. X * Otherwise, go ahead and set a new password. X */ X X check_password (pw, sp); X X if (new_password (pw, sp)) { X fprintf (stderr, UNCHANGED, name); X exit (1); X } X } X X /* X * The other options are incredibly simple. Just modify the X * field in the shadow file entry. X */ X X if (dflg) /* Set password to blank */ X sp->sp_pwdp = ""; X X if (lflg) /* Set password to "locked" value */ X sp->sp_pwdp = "!"; X X if (xflg) X sp->sp_max = (max * DAY) / SCALE; X X if (nflg) X sp->sp_min = (min * DAY) / SCALE; X X if (wflg) X sp->sp_warn = (warn * DAY) / SCALE; X X if (iflg) X sp->sp_inact = (inact * DAY) / SCALE; X X /* X * Before going any further, raise the ulimit to prevent X * colliding into a lowered ulimit, and set the real UID X * to root to protect against unexpected signals. Any X * keyboard signals are set to be ignored. X */ X X ulimit (2, 30000); X if (setuid (0)) { X fprintf (stderr, NOTROOT); X syslog (LOG_ERR, NOTROOT2); X exit (1); X } X signal (SIGHUP, SIG_IGN); X signal (SIGINT, SIG_IGN); X signal (SIGQUIT, SIG_IGN); X#ifdef SIGTSTP X signal (SIGTSTP, SIG_IGN); X#endif X X /* X * The shadow entry is now ready to be committed back to X * the shadow file. Get a lock on the file and open it. X */ X X for (i = 0;i < 30;i++) X if (spw_lock ()) X break; X X if (i == 30) { X fprintf (stderr, SPWDBUSY); X syslog (LOG_WARN, SPWDBUSY2); X exit (1); X } X if (! spw_open (O_RDWR)) { X fprintf (stderr, OPNERROR); X syslog (LOG_ERR, OPNERROR2); X (void) spw_unlock (); X exit (1); X } X X /* X * Update the shadow file entry. If there is a DBM file, X * update that entry as well. X */ X X if (! spw_update (sp)) { X fprintf (stderr, UPDERROR); X syslog (LOG_ERR, UPDERROR2); X (void) spw_unlock (); X exit (1); X } X#ifdef NDBM X if (access ("/etc/shadow.pag", 0) == 0 && ! sp_dbm_update (sp)) { X fprintf (stderr, DBMERROR); X syslog (LOG_ERR, DBMERROR2); X (void) spw_unlock (); X exit (1); X } X#endif X X /* X * Changes have all been made, so commit them and unlock the X * file. X */ X X if (! spw_close ()) { X fprintf (stderr, CLSERROR); X syslog (LOG_ERR, CLSERROR2); X (void) spw_unlock (); X exit (1); X } X if (! spw_unlock ()) { X fprintf (stderr, UNLKERROR); X syslog (LOG_ERR, UNLKERROR2); X exit (1); X } X syslog (LOG_INFO, CHGPASSWD, name); X exit (0); X} SHAR_EOF if test 17138 -ne "`wc -c < 'passwd.c'`" then echo shar: "error transmitting 'passwd.c'" '(should have been 17138 characters)' fi fi echo shar: "extracting 'port.c'" '(8978 characters)' if test -f 'port.c' then echo shar: "will not over-write existing file 'port.c'" else sed 's/^X//' << \SHAR_EOF > 'port.c' X/* X * Copyright 1989, 1990, 1991, John F. Haugh II X * All rights reserved. X * X * Permission is granted to copy and create derivative works for any X * non-commercial purpose, provided this copyright notice is preserved X * in all copies of source code, or included in human readable form X * and conspicuously displayed on all copies of object code or X * distribution media. X */ X X#include X#include X#include X#include X#include X#ifndef BSD X#include X#else X#include X#define strchr index X#define strrchr rindex X#endif X#include "port.h" X X#ifndef lint Xstatic char _sccsid[] = "@(#)port.c 3.1 08:59:32 2/8/91"; X#endif X Xextern int errno; X Xstatic FILE *ports; X X/* X * setttyent - open /etc/porttime file or rewind X * X * the /etc/porttime file is rewound if already open, or X * opened for reading. X */ X Xvoid Xsetttyent () X{ X if (ports) X rewind (ports); X else X ports = fopen (PORTS, "r"); X} X X/* X * endttyent - close the /etc/porttime file X * X * the /etc/porttime file is closed and the ports variable set X * to NULL to indicate that the /etc/porttime file is no longer X * open. X */ X Xvoid Xendttyent () X{ X if (ports) X fclose (ports); X X ports = (FILE *) 0; X} X X/* X * getttyent - read a single entry from /etc/porttime X * X * the next line in /etc/porttime is converted to a (struct port) X * and a pointer to a static (struct port) is returned to the X * invoker. NULL is returned on either EOF or error. errno is X * set to EINVAL on error to distinguish the two conditions. X */ X Xstruct port * Xgetttyent () X{ X static struct port port; /* static struct to point to */ X static char buf[BUFSIZ]; /* some space for stuff */ X static char *ttys[PORT_TTY+1]; /* some pointers to tty names */ X static char *users[PORT_IDS+1]; /* some pointers to user ids */ X static struct pt_time times[PORT_TIMES+1]; /* time ranges */ X char *cp; /* pointer into line */ X int time; /* scratch time of day */ X int i, j; X int saveerr = errno; /* errno value on entry */ X X /* X * If the ports file is not open, open the file. Do not rewind X * since we want to search from the beginning each time. X */ X X if (! ports) X setttyent (); X X if (! ports) { X errno = saveerr; X return 0; X } X X /* X * Common point for beginning a new line - X * X * - read a line, and NUL terminate X * - skip lines which begin with '#' X * - parse off the tty names X * - parse off a list of user names X * - parse off a list of days and times X */ X Xagain: X X /* X * Get the next line and remove the last character, which X * is a '\n'. Lines which begin with '#' are all ignored. X */ X X if (fgets (buf, BUFSIZ, ports) == 0) { X errno = saveerr; X return 0; X } X if (buf[0] == '#') X goto again; X X /* X * Get the name of the TTY device. It is the first colon X * separated field, and is the name of the TTY with no X * leading "/dev". The entry '*' is used to specify all X * TTY devices. X */ X X buf[strlen (buf) - 1] = 0; X X port.pt_names = ttys; X for (cp = buf, j = 0;j < PORT_TTY;j++) { X port.pt_names[j] = cp; X while (*cp && *cp != ':' && *cp != ',') X cp++; X X if (! *cp) X goto again; /* line format error */ X X if (*cp == ':') /* end of tty name list */ X break; X X if (*cp == ',') /* end of current tty name */ X *cp++ = '\0'; X } X *cp++ = 0; X port.pt_names[j + 1] = (char *) 0; X X /* X * Get the list of user names. It is the second colon X * separated field, and is a comma separated list of user X * names. The entry '*' is used to specify all usernames. X * The last entry in the list is a (char *) 0 pointer. X */ X X if (*cp != ':') { X port.pt_users = users; X port.pt_users[0] = cp; X X for (j = 1;*cp != ':';cp++) { X if (*cp == ',' && j < PORT_IDS) { X *cp++ = 0; X port.pt_users[j++] = cp; X } X } X port.pt_users[j] = 0; X } else X port.pt_users = 0; X X if (*cp != ':') X goto again; X X *cp++ = 0; X X /* X * Get the list of valid times. The times field is the third X * colon separated field and is a list of days of the week and X * times during which this port may be used by this user. The X * valid days are 'Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', and 'Sa'. X * X * In addition, the value 'Al' represents all 7 days, and 'Wk' X * represents the 5 weekdays. X * X * Times are given as HHMM-HHMM. The ending time may be before X * the starting time. Days are presumed to wrap at 0000. X */ X X if (*cp == '\0') { X port.pt_times = 0; X return &port; X } X X port.pt_times = times; X X /* X * Get the next comma separated entry X */ X X for (j = 0;*cp && j < PORT_TIMES;j++) { X X /* X * Start off with no days of the week X */ X X port.pt_times[j].t_days = 0; X X /* X * Check each two letter sequence to see if it is X * one of the abbreviations for the days of the X * week or the other two values. X */ X X for (i = 0;cp[i] && cp[i + 1] && isalpha (cp[i]);i += 2) { X switch ((cp[i] << 8) | (cp[i + 1])) { X case ('S' << 8) | 'u': X port.pt_times[j].t_days |= 01; X break; X case ('M' << 8) | 'o': X port.pt_times[j].t_days |= 02; X break; X case ('T' << 8) | 'u': X port.pt_times[j].t_days |= 04; X break; X case ('W' << 8) | 'e': X port.pt_times[j].t_days |= 010; X break; X case ('T' << 8) | 'h': X port.pt_times[j].t_days |= 020; X break; X case ('F' << 8) | 'r': X port.pt_times[j].t_days |= 040; X break; X case ('S' << 8) | 'a': X port.pt_times[j].t_days |= 0100; X break; X case ('W' << 8) | 'k': X port.pt_times[j].t_days |= 076; X break; X case ('A' << 8) | 'l': X port.pt_times[j].t_days |= 0177; X break; X default: X errno = EINVAL; X return 0; X } X } X X /* X * The default is 'Al' if no days were seen. X */ X X if (i == 0) X port.pt_times[j].t_days = 0177; X X /* X * The start and end times are separated from each X * other by a '-'. The times are four digit numbers X * representing the times of day. X */ X X for (time = 0;cp[i] && isdigit (cp[i]);i++) X time = time * 10 + cp[i] - '0'; X X if (cp[i] != '-' || time > 2400 || time % 100 > 59) X goto again; X port.pt_times[j].t_start = time; X cp = cp + i + 1; X X for (time = i = 0;cp[i] && isdigit (cp[i]);i++) X time = time * 10 + cp[i] - '0'; X X if ((cp[i] != ',' && cp[i]) || time > 2400 || time % 100 > 59) X goto again; X X port.pt_times[j].t_end = time; X cp = cp + i + 1; X } X X /* X * The end of the list is indicated by a pair of -1's for the X * start and end times. X */ X X port.pt_times[j].t_start = port.pt_times[j].t_end = -1; X X return &port; X} X X/* X * getttyuser - get ports information for user and tty X * X * getttyuser() searches the ports file for an entry with a TTY X * and user field both of which match the supplied TTY and X * user name. The file is searched from the beginning, so the X * entries are treated as an ordered list. X */ X Xstruct port * Xgetttyuser (tty, user) Xchar *tty; Xchar *user; X{ X int i, j; X struct port *port; X X setttyent (); X X while (port = getttyent ()) { X if (port->pt_names == 0 || port->pt_users == 0) X continue; X X for (i = 0;port->pt_names[i];i++) X if (strcmp (port->pt_names[i], tty) == 0 || X strcmp (port->pt_names[i], "*") == 0) X break; X X if (port->pt_names[i] == 0) X continue; X X for (j = 0;port->pt_users[j];j++) X if (strcmp (user, port->pt_users[j]) == 0 || X strcmp (port->pt_users[j], "*") == 0) X break; X X if (port->pt_users[j] != 0) X break; X } X endttyent (); X return port; X} X X/* X * isttytime - tell if a given user may login at a particular time X * X * isttytime searches the ports file for an entry which matches X * the user name and TTY given. X */ X Xint Xisttytime (id, port, clock) Xchar *id; Xchar *port; Xlong clock; X{ X int i; X int time; X struct port *pp; X struct tm *tm, X *localtime(); X X /* X * Try to find a matching entry for this user. Default to X * letting the user in - there are pleny of ways to have an X * entry to match all users. X */ X X if (! (pp = getttyuser (port, id))) X return 1; X X /* X * The entry is there, but has not time entries - don't X * ever let them login. X */ X X if (pp->pt_times == 0) X return 0; X X /* X * The current time is converted to HHMM format for X * comparision against the time values in the TTY entry. X */ X X tm = localtime (&clock); X time = tm->tm_hour * 100 + tm->tm_min; X X /* X * Each time entry is compared against the current X * time. For entries with the start after the end time, X * the comparision is made so that the time is between X * midnight and either the start or end time. X */ X X for (i = 0;pp->pt_times[i].t_start != -1;i++) { X if (! (pp->pt_times[i].t_days & PORT_DAY(tm->tm_wday))) X continue; X X if (pp->pt_times[i].t_start <= pp->pt_times[i].t_end) { X if (time >= pp->pt_times[i].t_start && X time <= pp->pt_times[i].t_end) X return 1; X } else { X if (time >= pp->pt_times[i].t_start || X time <= pp->pt_times[i].t_end) X return 1; X } X } X X /* X * No matching time entry was found, user shouldn't X * be let in right now. X */ X X return 0; X} SHAR_EOF if test 8978 -ne "`wc -c < 'port.c'`" then echo shar: "error transmitting 'port.c'" '(should have been 8978 characters)' fi fi echo shar: "extracting 'lmain.c'" '(13146 characters)' if test -f 'lmain.c' then echo shar: "will not over-write existing file 'lmain.c'" else sed 's/^X//' << \SHAR_EOF > 'lmain.c' X/* X * Copyright 1989, 1990, John F. Haugh II X * All rights reserved. X * X * Permission is granted to copy and create derivative works for any X * non-commercial purpose, provided this copyright notice is preserved X * in all copies of source code, or included in human readable form X * and conspicuously displayed on all copies of object code or X * distribution media. X */ X X#include X#include X#include X#include "pwd.h" X#include X#include X#include X#include X#ifndef BSD X#include X#include X#define bzero(a,n) memset(a, 0, n); X#else X#include X#define strchr index X#define strrchr rindex X#endif X#ifndef BSD X#include X#else X#include X#endif X#include "config.h" X#include "lastlog.h" X#include "faillog.h" X#include "shadow.h" X X#ifndef lint Xstatic char sccsid[] = "%W% %U% %G%"; X#endif X X#ifndef ERASECHAR X#define ERASECHAR '\b' /* backspace */ X#endif X X#ifndef KILLCHAR X#define KILLCHAR '\025' /* control U */ X#endif X X#ifdef UT_HOST Xchar host[BUFSIZ]; X#endif X#ifdef HUSHLOGIN Xint hushed; X#endif X Xstruct passwd pwent; Xstruct utmp utent; Xstruct lastlog lastlog; Xint pflg; Xint rflg; Xint fflg; Xint hflg; X#ifndef BSD Xstruct termio termio; X#endif X X#ifndef MAXENV X#define MAXENV 64 X#endif X X/* X * Global variables. X */ X Xchar *newenvp[MAXENV]; Xchar *Prog; Xint newenvc = 0; Xint maxenv = MAXENV; X X/* X * External identifiers. X */ X Xextern char *getenv (); Xextern char *getpass (); Xextern void checkutmp (); Xextern void addenv (); Xextern void setenv (); Xextern unsigned alarm (); Xextern void login (); Xextern void entry (); Xextern void setutmp (); Xextern void subsystem (); Xextern void log (); Xextern void setup (); Xextern int expire (); Xextern void motd (); Xextern void mailcheck (); Xextern void shell (); Xextern long a64l (); Xextern int c64i (); Xextern int optind; Xextern char *optarg; Xextern char **environ; X X#ifdef TZ XFILE *tzfile; Xchar tzbuf[32] = TZ; X#endif X X#ifndef ALARM X#define ALARM 60 X#endif X X#ifndef RETRIES X#define RETRIES 3 X#endif X X#ifdef FAILLOG Xstruct faillog faillog; X#endif X X#ifdef FTMP Xstruct utmp failent; X#endif X X#ifndef UMASK X#define UMASK 0 X#endif X X#ifndef ULIMIT X#define ULIMIT (1L<<21) X#endif X X#define NO_SHADOW "no shadow password for `%s' on `%s'\n" X#define BAD_PASSWD "invalid password for `%s' on `%s'\n" X#define BAD_DIALUP "invalid dialup password for `%s' on `%s'\n" X#define BAD_TIME "invalid login time for `%s' on `%s'\n" X#define BAD_ROOT_LOGIN "ILLEGAL ROOT LOGIN ON TTY `%s'\n" X#define ROOT_LOGIN "ROOT LOGIN ON TTY `%s'\n" X#define FAILURE_CNT "exceeded failure limit for `%s' on `%s'\n" X#define NOT_A_TTY "not a tty\n" X#define NOT_ROOT "-r or -f flag and not ROOT on `%s'\n" X X/* X * usage - print login command usage and exit X */ X Xusage () X{ X fprintf (stderr, "usage: login [ -p ] [ name ]\n"); X#ifdef UT_HOST X fprintf (stderr, " login -r name\n"); X fprintf (stderr, " login [ -p ] -f name [ -h host ]\n"); X#else X fprintf (stderr, " login [ -p ] -f name\n"); X#endif X exit (1); X} X X/* X * login - create a new login session for a user X * X * login is typically called by getty as the second step of a X * new user session. getty is responsible for setting the line X * characteristics to a reasonable set of values and getting X * the name of the user to be logged in. login may also be X * called to create a new user session on a pty for a variety X * of reasons, such as X servers or network logins. X * X * the flags which login supports are X * X * -p - preserve the environment X * -r - perform autologin protocol for rlogin X * -f - do not perform authentication, user is preauthenticated X * -h - the name of the remote host X */ X Xint Xmain (argc, argv, envp) Xint argc; Xchar **argv; Xchar **envp; X{ X char name[32]; X char pass[32]; X char hush[BUFSIZ]; X char tty[BUFSIZ]; X int retries; X int failed; X int flag; X int subroot = 0; X char *cp; X struct passwd *pwd; X struct spwd *spwd; X struct spwd *getspnam(); X#ifdef CONSOLE X int conflag; X char console[BUFSIZ]; X FILE *fp; X struct stat statbuf; X#endif /* CONSOLE */ X X /* X * Some quick initialization. X */ X X name[0] = '\0'; X X /* X * Get the utmp file entry and get the tty name from it. The X * current process ID must match the process ID in the utmp X * file if there are no additional flags on the command line. X */ X X checkutmp (argc > 1 && argv[1][0] != '-'); X strncpy (tty, utent.ut_line, sizeof tty); X tty[sizeof tty - 1] = '\0'; X X if (Prog = strrchr (argv[0], '/')) X Prog++; X else X Prog = argv[0]; X X#ifdef UT_HOST X while ((flag = getopt (argc, argv, "pr:f:h:")) != EOF) X#else X while ((flag = getopt (argc, argv, "pf:")) != EOF) X#endif X { X switch (flag) { X case 'p': pflg++; X break; X case 'f': fflg++; X strncpy (name, optarg, sizeof name); X break; X#ifdef UT_HOST X case 'r': rflg++; X strncpy (name, optarg, sizeof name); X break; X case 'h': hflg++; X strncpy (host, optarg, sizeof host); X strncpy (utmp.ut_host, host, X sizeof utmp.ut_host); X break; X#endif X default: X usage (); X } X } X X /* X * The -r option is not valid with any other flags X */ X X if (rflg && (hflg || fflg || pflg)) X usage (); X X openlog (Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH); X X /* X * The -r and -f flags both require the real UID to be X * zero. No authentication may be required for these X * flags, so the user must already be root. X */ X X if ((rflg || fflg) && getuid () != 0) X exit (1); /* only root can use -r or -f */ X X if (! isatty (0) || ! isatty (1) || ! isatty (2)) X exit (1); /* must be a terminal */ X X#ifndef BSD X (void) ioctl (0, TCGETA, &termio); /* get terminal characteristics */ X X /* X * Add your favorite terminal modes here ... X */ X X termio.c_lflag |= ISIG; X X termio.c_cc[VERASE] = ERASECHAR; X termio.c_cc[VKILL] = KILLCHAR; X (void) ioctl (0, TCSETAF, &termio); /* set erase and kill characters */ X#endif /* !BSD */ X umask (UMASK); /* set the default umask */ X ulimit (2, (long) ULIMIT); /* set the default ulimit */ X X /* X * The entire environment will be preserved if the -p flag X * is used. X */ X X if (pflg) X while (*envp) /* add inherited environment, */ X addenv (*envp++); /* some variables change later */ X X#ifdef TZ X if (! pflg) { X if (tzbuf[0] == '/') { X if ((tzfile = fopen (tzbuf, "r")) != (FILE *) 0) { X if (fgets (tzbuf, sizeof tzbuf, tzfile)) { X tzbuf[strlen (tzbuf) - 1] = '\0'; X addenv (tzbuf); X } X fclose (tzfile); X } X } else { X addenv (tzbuf); X } X } X#endif /* TZ */ X#ifdef HZ X if (! pflg) X addenv (HZ); /* set the default $HZ, if one */ X#endif /* HZ */ X if (optind < argc) { /* now set command line variables */ X if (optind + 1 < argc) X setenv (argc - optind - 1, &argv[optind + 1]); X X (void) strncpy (name, argv[optind], sizeof name); X } Xtop: X (void) alarm (ALARM); /* only allow ALARM sec. for login */ X X retries = RETRIES; X while (1) { /* repeatedly get login/password pairs */ X pass[0] = '\0'; X X if (! name[0]) { /* need to get a login id */ X if (subroot) X exit (1); X X rflg = fflg = 0; X login (name); X continue; X } X if (! (pwd = getpwnam (name))) X pwent.pw_name = (char *) 0; X else X pwent = *pwd; X X if (pwent.pw_name) { X if (! (spwd = getspnam (name))) X syslog (LOG_WARN, NO_SHADOW, name, tty); X else X pwent.pw_passwd = spwd->sp_pwdp; X failed = 0; /* hasn't failed validation yet */ X } else X failed = 1; /* will never pass validation */ X X /* X * The -r and -f flags provide a name which has already X * been authenticated by some server. X */ X X if (pwent.pw_name && (rflg || fflg)) X goto have_name; X X /* X * Get the user's password. One will only be prompted for X * if the pw_passwd (or sp_passwd) field is non-blank. It X * will then be checked against the password entry, along X * with other options which prevent logins. X */ X cp = 0; X if ((! pwent.pw_name || (strlen (pwent.pw_passwd) > 0)) X && ! (cp = getpass ("Password:"))) X continue; X X if (cp) X strncpy (pass, cp, sizeof pass); X X if (! valid (pass, &pwent)) { /* check encrypted passwords */ X syslog (LOG_WARN, BAD_PASSWD, name, tty); X failed = 1; X } X bzero (pass, sizeof pass); X X /* X * This is the point where password-authenticated users X * wind up. If you reach this far, your password has X * been authenticated and so on. X */ X Xhave_name: X#ifdef DIALUP X alarm (30); X if (pwent.pw_name && ! dialcheck (tty, X pwent.pw_shell[0] ? pwent.pw_shell:"/bin/sh")) { X syslog (LOG_WARN, BAD_DIALUP, name, tty); X failed = 1; X } X#endif /* DIALUP */ X#ifdef PORTTIME X if (pwent.pw_name && X ! isttytime (pwent.pw_name, tty, time ((time_t *) 0))) { X syslog (LOG_WARN, BAD_TIME, name, tty); X failed = 1; X } X#endif /* PORTTIME */ X#ifdef CONSOLE X if (! failed && pwent.pw_name && X pwent.pw_uid == 0 && stat (CONSOLE, &statbuf) == 0) { X if ((statbuf.st_mode & S_IFMT) == S_IFREG) { X fp = fopen (CONSOLE, "r"); X while (fp && fgets (console, BUFSIZ, fp) X == console) { X console[strlen (console) - 1] = '\0'; X if (! strcmp (console, tty)) X break; X } X if (! fp || feof (fp)) X failed = 1; X X fclose (fp); X } else { X if (strcmp (CONSOLE, tty)) X failed = 1; X } X if (failed) X syslog (LOG_CRIT, BAD_ROOT_LOGIN, tty); X } X#endif /* CONSOLE */ X#ifdef FAILLOG X if (pwent.pw_name && X ! failcheck (pwent.pw_uid, &faillog, failed)) { X syslog (LOG_CRIT, FAILURE_CNT, name, tty); X failed = 1; X } X#endif /* FAILLOG */ X if (! failed) X break; X X puts ("Login incorrect"); X if (rflg || fflg) X exit (1); X#ifdef FAILLOG X if (pwent.pw_name) /* don't log non-existent users */ X failure (pwent.pw_uid, tty, &faillog); X#endif /* FAILLOG */ X#ifdef FTMP X failent = utent; X X if (pwent.pw_name) X strncpy (failent.ut_name, X pwent.pw_name, sizeof failent.ut_name); X else X#ifdef UNKNOWNS X strcpy (failent.ut_name, name); X#else /* !UNKNOWNS */ X strcpy (failent.ut_name, "UNKNOWN"); X#endif /* UNKNOWNS */ X time (&failent.ut_time); X failent.ut_type = USER_PROCESS; X X failtmp (&failent); X#endif /* FTMP */ X if (--retries <= 0) /* only allow so many failures */ X exit (1); X X bzero (name, sizeof name); X bzero (pass, sizeof pass); X } X (void) alarm (0); /* turn off alarm clock */ X#ifdef NOLOGINS X /* X * Check to see if system is turned off for non-root users. X * This would be useful to prevent users from logging in X * during system maintenance. X */ X X if (pwent.pw_uid != 0 && access (NOLOGINS, 0) == 0) { X FILE *fp; X int c; X X if (fp = fopen (NOLOGINS, "r")) { X while ((c = getc (fp)) != EOF) { X if (c == '\n') X putchar ('\r'); X X putchar (c); X } X fflush (stdout); X fclose (fp); X } else X printf ("\r\nSystem closed for routine maintenance\n"); X X exit (0); X } X#endif /* NOLOGINS */ X environ = newenvp; /* make new environment active */ X X if (getenv ("IFS")) /* don't export user IFS ... */ X addenv ("IFS= \t\n"); /* ... instead, set a safe IFS */ X X setutmp (name, tty); /* make entry in utmp & wtmp files */ X if (pwent.pw_shell[0] == '*') { /* subsystem root */ X subsystem (&pwent); /* figure out what to execute */ X subroot++; /* say i was here again */ X endpwent (); /* close all of the file which were */ X endgrent (); /* open in the original rooted file */ X endspent (); /* system. they will be re-opened */ X endsgent (); /* in the new rooted file system */ X goto top; /* go do all this all over again */ X } X X#ifdef LASTLOG X log (); /* give last login and log this one */ X#endif /* LASTLOG */ X setup (&pwent); /* set UID, GID, HOME, etc ... */ X#ifdef AGING X if (spwd) { /* check for age of password */ X if (expire (&pwent, spwd)) { X spwd = getspnam (name); X pwd = getpwnam (name); X pwent = *pwd; X } X } X#ifdef ATT_AGE X else if (pwent.pw_age && pwent.pw_age[0]) { X if (expire (&pwent, (void *) 0)) { X pwd = getpwnam (name); X pwent = *pwd; X } X } X#endif /* ATT_AGE */ X#endif /* AGING */ X#ifdef HUSHLOGIN X sprintf (hush, "%s/.hushlogin", pwent.pw_dir, 0); X hushed = access (hush, 0) == 0; X#endif /* HUSHLOGIN */ X#ifdef MOTD X if (! hushed) X motd (); /* print the message of the day */ X#endif X#ifdef FAILLOG X if (faillog.fail_cnt != 0) X failprint (pwent.pw_uid, &faillog); X#endif /* FAILLOG */ X#ifdef LASTLOG X if (lastlog.ll_time != 0 && ! hushed) X printf ("Last login: %.19s on %s\n", X ctime (&lastlog.ll_time), lastlog.ll_line); X#endif /* LASTLOG */ X#ifdef AGING X if (! hushed) X agecheck (&pwent, spwd); X#endif /* AGING */ X#ifdef MAILCHECK X if (! hushed) X mailcheck (); /* report on the status of mail */ X#endif /* MAILCHECK */ X#ifdef TTYTYPE X if (! pflg) X ttytype (tty); X#endif /* TTYTYPE */ X signal (SIGINT, SIG_DFL); /* default interrupt signal */ X signal (SIGQUIT, SIG_DFL); /* default quit signal */ X signal (SIGTERM, SIG_DFL); /* default terminate signal */ X signal (SIGALRM, SIG_DFL); /* default alarm signal */ X X endpwent (); /* stop access to password file */ X endgrent (); /* stop access to group file */ X endspent (); /* stop access to shadow passwd file */ X endsgent (); /* stop access to shadow group file */ X X if (pwent.pw_uid == 0) X syslog (LOG_INFO, ROOT_LOGIN, tty); X X shell (pwent.pw_shell, (char *) 0); /* exec the shell finally. */ X /*NOTREACHED*/ X} SHAR_EOF if test 13146 -ne "`wc -c < 'lmain.c'`" then echo shar: "error transmitting 'lmain.c'" '(should have been 13146 characters)' fi fi echo shar: "extracting 'mkpasswd.c'" '(8207 characters)' if test -f 'mkpasswd.c' then echo shar: "will not over-write existing file 'mkpasswd.c'" else sed 's/^X//' << \SHAR_EOF > 'mkpasswd.c' X/* X * Copyright 1990, John F. Haugh II X * All rights reserved. X * X * Permission is granted to copy and create derivative works for any X * non-commercial purpose, provided this copyright notice is preserved X * in all copies of source code, or included in human readable form X * and conspicuously displayed on all copies of object code or X * distribution media. X */ X X#include "config.h" X#include X#include X#include "pwd.h" X#ifdef BSD X#include X#define strchr index X#define strrchr rindex X#else X#include X#endif X X#if !defined(DBM) && !defined(NDBM) XWhat you are trying to do? You have to have a DBM of some kind ... X#endif X X#ifdef DBM X#include X#endif X#ifdef NDBM X#include X#include X#include "shadow.h" X XDBM *pw_dbm; XDBM *gr_dbm; XDBM *sp_dbm; XDBM *sgr_dbm; X#endif X X#ifndef lint Xstatic char sccsid[] = "@(#)mkpasswd.c 3.4 11:29:08 12/19/90"; Xstatic char copyright[] = "Copyright 1990, John F. Haugh II"; X#endif X Xchar *CANT_OPEN = "%s: cannot open file %s\n"; Xchar *CANT_OVERWRITE = "%s: cannot overwrite file %s\n"; Xchar *CANT_CREATE = "%s: cannot create %s\n"; Xchar *DBM_OPEN_ERR = "%s: cannot open DBM files for %s\n"; Xchar *PARSE_ERR = "%s: error parsing line\n\"%s\"\n"; Xchar *LINE_TOO_LONG = "%s: the beginning with \"%.16s ...\" is too long\n"; Xchar *ADD_REC = "adding record for name \"%s\"\n"; Xchar *ADD_REC_ERR = "%s: error adding record for \"%s\"\n"; Xchar *INFO = "added %d entries, longest was %d\n"; X#ifdef NDBM Xchar *USAGE = "Usage: %s [ -vf ] [ -p|g|sp|sg ] file\n"; X#else Xchar *USAGE = "Usage: %s [ -vf ] file\n"; X#endif X Xchar *Progname; Xint vflg = 0; Xint fflg = 0; X#ifdef NDBM Xint gflg = 0; Xint sflg = 0; Xint pflg = 0; X#endif X Xvoid usage(); X Xextern char *malloc(); Xextern struct passwd *sgetpwent(); X#ifdef NDBM Xextern struct group *sgetgrent(); Xextern struct spwd *sgetspent(); Xextern struct sgrp *sgetsgent(); X#endif X X/* X * mkpasswd - create DBM files for /etc/passwd-like input file X * X * mkpasswd takes an an argument the name of a file in /etc/passwd format X * and creates a DBM file keyed by user ID and name. The output files have X * the same name as the input file, with .dir and .pag appended. X * X * if NDBM is defined this command will also create look-aside files for X * /etc/group, /etc/shadow, and /etc/gshadow. X */ X Xint Xmain (argc, argv) Xint argc; Xchar **argv; X{ X extern int optind; X extern char *optarg; X FILE *fp; /* File pointer for input file */ X char *file; /* Name of input file */ X char *dir; /* Name of .dir file */ X char *pag; /* Name of .pag file */ X char *cp; /* Temporary character pointer */ X int flag; /* Flag for command line option */ X int fd; /* File descriptor of open DBM file */ X int cnt = 0; /* Number of entries in database */ X int longest = 0; /* Longest entry in database */ X int len; /* Length of input line */ X int errors = 0; /* Count of errors processing file */ X char buf[BUFSIZ*8]; /* Input line from file */ X struct passwd *passwd; /* Pointer to password file entry */ X#ifdef NDBM X struct group *group; /* Pointer to group file entry */ X struct spwd *shadow; /* Pointer to shadow passwd entry */ X struct sgrp *gshadow; /* Pointer to shadow group entry */ X DBM *dbm; /* Pointer to new NDBM files */ X#endif X X /* X * Figure out what my name is. I will use this later ... X */ X X if (Progname = strrchr (argv[0], '/')) X Progname++; X else X Progname = argv[0]; X X /* X * Figure out what the flags might be ... X */ X X#ifdef NDBM X while ((flag = getopt (argc, argv, "fvpgs")) != EOF) X#else X while ((flag = getopt (argc, argv, "fv")) != EOF) X#endif X { X switch (flag) { X case 'v': X vflg++; X break; X case 'f': X fflg++; X break; X#ifdef NDBM X case 'g': X gflg++; X if (pflg) X usage (); X X break; X case 's': X sflg++; X break; X case 'p': X pflg++; X if (gflg) X usage (); X X break; X#endif X default: X usage (); X } X } X X /* X * Backwards compatibility fix for -p flag ... X */ X X if (! sflg && ! gflg) X pflg++; X X /* X * The last and only remaining argument must be the file name X */ X X if (argc - 1 != optind) X usage (); X X file = argv[optind]; X X if (! (fp = fopen (file, "r"))) { X fprintf (stderr, CANT_OPEN, Progname, file); X exit (1); X } X X /* X * Make the filenames for the two DBM files. X */ X X dir = malloc (strlen (file) + 5); /* space for .dir file */ X strcat (strcpy (dir, file), ".dir"); X X pag = malloc (strlen (file) + 5); /* space for .pag file */ X strcat (strcpy (pag, file), ".pag"); X X /* X * Remove existing files if requested. X */ X X if (fflg) { X (void) unlink (dir); X (void) unlink (pag); X } X X /* X * Create the two DBM files - it is an error for these files X * to have existed already. X */ X X if (access (dir, 0) == 0) { X fprintf (stderr, CANT_OVERWRITE, Progname, dir); X exit (1); X } X if (access (pag, 0) == 0) { X fprintf (stderr, CANT_OVERWRITE, Progname, pag); X exit (1); X } X X#ifdef NDBM X if (sflg) X umask (077); X else X#endif X umask (0); X#ifdef DBM X if ((fd = open (dir, O_RDWR|O_CREAT|O_EXCL, 0644)) == -1) { X fprintf (stderr, CANT_CREATE, Progname, dir); X exit (1); X } else X close (fd); X X if ((fd = open (pag, O_RDWR|O_CREAT|O_EXCL, 0644)) == -1) { X fprintf (stderr, CANT_CREATE, Progname, pag); X unlink (dir); X exit (1); X } else X close (fd); X#endif X X /* X * Now the DBM database gets initialized X */ X X#ifdef DBM X if (dbminit (file) == -1) { X fprintf (stderr, DBM_OPEN_ERR, Progname, file); X exit (1); X } X#endif X#ifdef NDBM X if (! (dbm = dbm_open (file, O_RDWR|O_CREAT, 0644))) { X fprintf (stderr, DBM_OPEN_ERR, Progname, file); X exit (1); X } X if (gflg) { X if (sflg) X sgr_dbm = dbm; X else X gr_dbm = dbm; X } else { X if (sflg) X sp_dbm = dbm; X else X pw_dbm = dbm; X } X#endif X X /* X * Read every line in the password file and convert it into a X * data structure to be put in the DBM database files. X */ X X#ifdef NDBM X while (fgetsx (buf, BUFSIZ, fp) != NULL) X#else X while (fgets (buf, BUFSIZ, fp) != NULL) X#endif X { X X /* X * Get the next line and strip off the trailing newline X * character. X */ X X buf[sizeof buf - 1] = '\0'; X if (! (cp = strchr (buf, '\n'))) { X fprintf (stderr, LINE_TOO_LONG, Progname, buf); X exit (1); X } X *cp = '\0'; X len = strlen (buf); X X /* X * Parse the password file line into a (struct passwd). X * Erroneous lines cause error messages, but that's X * all. YP lines are ignored completely. X */ X X if (buf[0] == '-' || buf[0] == '+') X continue; X X#ifdef DBM X if (! (passwd = sgetpwent (buf))) X#endif X#ifdef NDBM X if (! (((! sflg && pflg) && (passwd = sgetpwent (buf))) X || ((sflg && pflg) && (shadow = sgetspent (buf))) X || ((! sflg && gflg) && (group = sgetgrent (buf))) X || ((sflg && gflg) && (gshadow = sgetsgent (buf))))) X#endif X { X fprintf (stderr, PARSE_ERR, Progname, buf); X errors++; X continue; X } X#ifdef DBM X if (vflg) X printf (ADD_REC, passwd->pw_name); X X if (! pw_dbm_update (passwd)) X fprintf (stderr, ADD_REC_ERR, X Progname, passwd->pw_name); X#endif X#ifdef NDBM X if (vflg) { X if (!sflg && pflg) printf (ADD_REC, passwd->pw_name); X if (sflg && pflg) printf (ADD_REC, shadow->sp_namp); X if (!sflg && gflg) printf (ADD_REC, group->gr_name); X if (sflg && gflg) printf (ADD_REC, gshadow->sg_name); X } X if (! sflg && pflg && ! pw_dbm_update (passwd)) X fprintf (stderr, ADD_REC_ERR, X Progname, passwd->pw_name); X X if (sflg && pflg && ! sp_dbm_update (shadow)) X fprintf (stderr, ADD_REC_ERR, X Progname, shadow->sp_namp); X X if (! sflg && gflg && ! gr_dbm_update (group)) X fprintf (stderr, ADD_REC_ERR, X Progname, group->gr_name); X X if (sflg && gflg && ! sgr_dbm_update (gshadow)) X fprintf (stderr, ADD_REC_ERR, X Progname, gshadow->sg_name); X#endif X X /* X * Update the longest record and record count X */ X X if (len > longest) X longest = len; X cnt++; X } X X /* X * Tell the user how things went ... X */ X X if (vflg) X printf (INFO, cnt, longest); X X exit (errors); X /*NOTREACHED*/ X} X X/* X * usage - print error message and exit X */ X Xvoid Xusage () X{ X fprintf (stderr, USAGE, Progname); X exit (1); X /*NOTREACHED*/ X} SHAR_EOF if test 8207 -ne "`wc -c < 'mkpasswd.c'`" then echo shar: "error transmitting 'mkpasswd.c'" '(should have been 8207 characters)' fi fi echo shar: "extracting 'sulogin.c'" '(3501 characters)' if test -f 'sulogin.c' then echo shar: "will not over-write existing file 'sulogin.c'" else sed 's/^X//' << \SHAR_EOF > 'sulogin.c' X/* X * Copyright 1989, 1990, John F. Haugh II X * All rights reserved. X * X * Permission is granted to copy and create derivative works for any X * non-commercial purpose, provided this copyright notice is preserved X * in all copies of source code, or included in human readable form X * and conspicuously displayed on all copies of object code or X * distribution media. X */ X X#include X#include X#include "pwd.h" X#include X#ifdef BSD X#include X#define strchr index X#define strrchr rindex X#else X#include X#include X#endif X#include "config.h" X X#ifndef lint Xstatic char sccsid[] = "@(#)sulogin.c 3.3 12:31:35 12/12/90"; X#endif X Xchar name[BUFSIZ]; Xchar pass[BUFSIZ]; Xchar home[BUFSIZ]; Xchar prog[BUFSIZ]; Xchar mail[BUFSIZ]; X Xstruct passwd pwent; Xstruct utmp utent; X X#ifdef TZ XFILE *tzfile; Xchar tzbuf[BUFSIZ] = TZ; X#endif X X#ifndef MAXENV X#define MAXENV 64 X#endif X Xchar *newenvp[MAXENV]; Xint newenvc = 0; Xint maxenv = MAXENV; Xextern char **environ; X X#ifndef ALARM X#define ALARM 60 X#endif X X#ifndef RETRIES X#define RETRIES 3 X#endif X Xint main (argc, argv, envp) Xint argc; Xchar **argv; Xchar **envp; X{ X char *getenv (); X char *ttyname (); X char *cp; X X if (access (PWDFILE, 0) == -1) { /* must be a password file! */ X printf ("No password file\n"); X exit (1); X } X#ifdef NDEBUG X if (getppid () != 1) /* parent must be INIT */ X exit (1); X#endif X if (! isatty (0) || ! isatty (1) || ! isatty (2)) X exit (1); /* must be a terminal */ X X while (*envp) /* add inherited environment, */ X addenv (*envp++); /* some variables change later */ X X#ifdef TZ X if (tzbuf[0] == '/') { X if ((tzfile = fopen (tzbuf, "r")) != (FILE *) 0) { X if (fgets (tzbuf, sizeof tzbuf, tzfile)) { X tzbuf[strlen (tzbuf) - 1] = '\0'; X addenv (tzbuf); X } X fclose (tzfile); X } X } else { X addenv (tzbuf); X } X#endif /* TZ */ X#ifdef HZ X addenv (HZ); /* set the default $HZ, if one */ X#endif /* HZ */ X (void) strcpy (name, "root"); /* KLUDGE!!! */ X X alarm (ALARM); /* only wait so long ... */ X while (1) { /* repeatedly get login/password pairs */ X entry (name, &pwent); /* get entry from password file */ X if (pwent.pw_name == (char *) 0) { X printf ("No password entry for 'root'\n"); X exit (1); X } X X /* X * Here we prompt for the root password, or if no password is X * given we just exit. X */ X X /* get a password for root */ X if (! (cp = getpass ("Type control-d for normal startup,\n\ X(or give root password for system maintenance):"))) X exit (0); X else X strcpy (pass, cp); X X if (valid (pass, &pwent)) /* check encrypted passwords ... */ X break; /* ... encrypted passwords matched */ X X puts ("Login incorrect"); X } X alarm (0); X environ = newenvp; /* make new environment active */ X X puts ("Entering System Maintenance Mode"); X X /* X * Normally there would be a utmp entry for login to mung on X * to get the tty name, date, etc. from. We don't need all that X * stuff because we won't update the utmp or wtmp files. BUT!, X * we do need the tty name so we can set the permissions and X * ownership. X */ X X if (cp = ttyname (0)) { /* found entry in /dev/ */ X if (strrchr (cp, '/') != (char *) 0) X strcpy (utent.ut_line, strrchr (cp, '/') + 1); X else X strcpy (utent.ut_line, cp); X } X if (getenv ("IFS")) /* don't export user IFS ... */ X addenv ("IFS= \t\n"); /* ... instead, set a safe IFS */ X X setup (&pwent); /* set UID, GID, HOME, etc ... */ X X shell (pwent.pw_shell, (char *) 0); /* exec the shell finally. */ X /*NOTREACHED*/ X} SHAR_EOF if test 3501 -ne "`wc -c < 'sulogin.c'`" then echo shar: "error transmitting 'sulogin.c'" '(should have been 3501 characters)' fi fi echo shar: "extracting 'pwpack.c'" '(2994 characters)' if test -f 'pwpack.c' then echo shar: "will not over-write existing file 'pwpack.c'" else sed 's/^X//' << \SHAR_EOF > 'pwpack.c' X/* X * Copyright 1990, John F. Haugh II X * All rights reserved. X * X * Permission is granted to copy and create derivative works for any X * non-commercial purpose, provided this copyright notice is preserved X * in all copies of source code, or included in human readable form X * and conspicuously displayed on all copies of object code or X * distribution media. X */ X X#include X#include "pwd.h" X#ifdef BSD X#include X#define strchr index X#define strrchr rindex X#else X#include X#endif X#include "config.h" X X#ifndef lint Xstatic char sccsid[] = "@(#)pwpack.c 3.3 12:31:23 12/12/90"; X#endif X X/* X * pw_pack - convert a (struct pwd) to a packed record X */ X Xint Xpw_pack (passwd, buf) Xstruct passwd *passwd; Xchar *buf; X{ X char *cp; X X cp = buf; X strcpy (cp, passwd->pw_name); X cp += strlen (cp) + 1; X X strcpy (cp, passwd->pw_passwd); X#ifdef ATT_AGE X if (passwd->pw_age[0]) { X *cp++ = ','; X strcat (cp, passwd->pw_age); X } X#endif X cp += strlen (cp) + 1; X X memcpy (cp, (void *) &passwd->pw_uid, sizeof passwd->pw_uid); X cp += sizeof passwd->pw_uid; X X memcpy (cp, (void *) &passwd->pw_gid, sizeof passwd->pw_gid); X cp += sizeof passwd->pw_gid; X#ifdef BSD_QUOTAS X memcpy (cp, (void *) &passwd->pw_quota, sizeof passwd->pw_quota); X cp += sizeof passwd->pw_quota; X#endif X#ifdef ATT_COMMENT X if (passwd->pw_comment) { X strcpy (cp, passwd->pw_comment); X cp += strlen (cp) + 1; X } else X *cp++ = '\0'; X#endif X strcpy (cp, passwd->pw_gecos); X cp += strlen (cp) + 1; X X strcpy (cp, passwd->pw_dir); X cp += strlen (cp) + 1; X X strcpy (cp, passwd->pw_shell); X cp += strlen (cp) + 1; X X return cp - buf; X} X X/* X * pw_unpack - convert a packed (struct pwd) record to a (struct pwd) X */ X Xint Xpw_unpack (buf, len, passwd) Xchar *buf; Xint len; Xstruct passwd *passwd; X{ X char *org = buf; X char *cp; X X memset ((void *) passwd, 0, sizeof *passwd); X X passwd->pw_name = buf; X buf += strlen (buf) + 1; X if (buf - org > len) X return -1; X X passwd->pw_passwd = buf; X buf += strlen (buf) + 1; X if (buf - org > len) X return -1; X X#ifdef ATT_AGE X if (cp = strchr (passwd->pw_passwd, ',')) { X *cp++ = '\0'; X passwd->pw_age = cp; X } else X passwd->pw_age = ""; X#endif X X memcpy ((void *) &passwd->pw_uid, (void *) buf, sizeof passwd->pw_uid); X buf += sizeof passwd->pw_uid; X if (buf - org > len) X return -1; X X memcpy ((void *) &passwd->pw_gid, (void *) buf, sizeof passwd->pw_gid); X buf += sizeof passwd->pw_gid; X if (buf - org > len) X return -1; X X#ifdef BSD_QUOTAS X memcpy ((void *) &passwd->pw_quota, (void *) buf, X sizeof passwd->pw_quota); X buf += sizeof passwd->pw_quota; X if (buf - org > len) X return -1; X#endif X#ifdef ATT_COMMENT X passwd->pw_comment = buf; X buf += strlen (buf) + 1; X if (buf - org > len) X return -1; X#endif X passwd->pw_gecos = buf; X buf += strlen (buf) + 1; X if (buf - org > len) X return -1; X X passwd->pw_dir = buf; X buf += strlen (buf) + 1; X if (buf - org > len) X return -1; X X passwd->pw_shell = buf; X buf += strlen (buf) + 1; X if (buf - org > len) X return -1; X X return 0; X} SHAR_EOF if test 2994 -ne "`wc -c < 'pwpack.c'`" then echo shar: "error transmitting 'pwpack.c'" '(should have been 2994 characters)' fi fi echo shar: "extracting 'dialup.c'" '(2631 characters)' if test -f 'dialup.c' then echo shar: "will not over-write existing file 'dialup.c'" else sed 's/^X//' << \SHAR_EOF > 'dialup.c' X/* X * Copyright 1989, 1990, John F. Haugh II X * All rights reserved. X * X * Permission is granted to copy and create derivative works for any X * non-commercial purpose, provided this copyright notice is preserved X * in all copies of source code, or included in human readable form X * and conspicuously displayed on all copies of object code or X * distribution media. X */ X X#include X#ifndef BSD X#include X#else X#include X#define strchr index X#define strrchr rindex X#endif X#include "dialup.h" X X#ifndef lint Xstatic char sccsid[] = "@(#)dialup.c 3.3 19:44:20 12/10/90"; X#endif X Xstatic FILE *dialpwd; X Xvoid Xsetduent () X{ X if (dialpwd) X rewind (dialpwd); X else X dialpwd = fopen (DIALPWD, "r"); X} X Xvoid Xendduent () X{ X if (dialpwd) X fclose (dialpwd); X X dialpwd = (FILE *) 0; X} X Xstruct dialup * Xfgetduent (fp) XFILE *fp; X{ X static struct dialup dialup; /* static structure to point to */ X static char shell[64]; /* some space for a login shell */ X static char passwd[16]; /* some space for dialup password */ X char buf[BUFSIZ]; X char *cp; X X if (! fp) X return 0; X X if (! fp || feof (fp)) X return ((struct dialup *) 0); X X while (fgets (buf, BUFSIZ, fp) == buf && buf[0] == '#') X ; X X if (feof (fp)) X return ((struct dialup *) 0); X X cp = strchr (buf, ':'); X if (cp - buf > sizeof shell) /* something is fishy ... */ X return ((struct dialup *) 0); X X (void) strncpy (shell, buf, cp - buf); X shell[cp - buf] = '\0'; X X if (strlen (cp + 1) > sizeof passwd) /* something is REALLY fishy */ X return ((struct dialup *) 0); X X (void) strcpy (passwd, cp + 1); X passwd[strlen (passwd) - 1] = '\0'; X if (cp = strchr (passwd, ':')) X *cp = '\0'; X X dialup.du_shell = shell; X dialup.du_passwd = passwd; X X return (&dialup); X} X Xstruct dialup * Xgetduent () X{ X if (! dialpwd) X setduent (); X X return fgetduent (dialpwd); X} X Xstruct dialup *getdushell (shell) Xchar *shell; X{ X struct dialup *dialup; X X while (dialup = getduent ()) { X if (strcmp (shell, dialup->du_shell) == 0) X return (dialup); X X if (strcmp (dialup->du_shell, "*") == 0) X return (dialup); X } X return ((struct dialup *) 0); X} X Xint isadialup (tty) Xchar *tty; X{ X FILE *fp; X char buf[BUFSIZ]; X int dialup = 0; X X if (! (fp = fopen (DIALUPS, "r"))) X return (0); X X while (fgets (buf, BUFSIZ, fp) == buf) { X if (buf[0] == '#') X continue; X X buf[strlen (buf) - 1] = '\0'; X X if (strcmp (buf, tty) == 0) { X dialup = 1; X break; X } X } X fclose (fp); X X return (dialup); X} X Xint Xputduent (dial, fp) Xstruct dialup *dial; XFILE *fp; X{ X if (! fp || ! dial) X return -1; X X if (fprintf (fp, "%s:%s\n", dial->du_shell, dial->du_passwd) == EOF) X return -1; X X return 0; X} SHAR_EOF if test 2631 -ne "`wc -c < 'dialup.c'`" then echo shar: "error transmitting 'dialup.c'" '(should have been 2631 characters)' fi fi echo shar: "extracting 'sulog.c'" '(1168 characters)' if test -f 'sulog.c' then echo shar: "will not over-write existing file 'sulog.c'" else sed 's/^X//' << \SHAR_EOF > 'sulog.c' X/* X * Copyright 1989, 1990, John F. Haugh II X * All rights reserved. X * X * Use, duplication, and disclosure prohibited without X * the express written permission of the author. X */ X X#include X#include X#include X#ifndef BSD X#include X#include X#else X#include X#define strchr index X#define strrchr rindex X#endif X#include "config.h" X X#ifndef lint Xstatic char sccsid[] = "@(#)sulog.c 3.1 09:22:38 11/21/90"; X#endif X Xextern char name[]; Xextern char oldname[]; X Xtime_t time (); X Xvoid sulog (success) Xint success; X{ X#ifdef SULOG X char *tty; X char *cp; X char *ttyname (); X time_t clock; X struct tm *tm; X struct tm *localtime (); X FILE *fp; X X if ((fp = fopen (SULOG, "a+")) == (FILE *) 0) X return; /* can't open or create logfile */ X X (void) time (&clock); X tm = localtime (&clock); X X if (isatty (0) && (cp = ttyname (0))) { X if (tty = strrchr (cp, '/')) X tty++; X else X tty = cp; X } else X tty = "???"; X X (void) fprintf (fp, "SU %.02d/%0.2d %.02d:%.02d %c %.6s %s-%s\n", X tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, X success ? '+':'-', tty, oldname, name); X X fflush (fp); X fclose (fp); X#endif X} SHAR_EOF if test 1168 -ne "`wc -c < 'sulog.c'`" then echo shar: "error transmitting 'sulog.c'" '(should have been 1168 characters)' fi fi echo shar: "extracting 'getpass.c'" '(3390 characters)' if test -f 'getpass.c' then echo shar: "will not over-write existing file 'getpass.c'" else sed 's/^X//' << \SHAR_EOF > 'getpass.c' X/* X * Copyright 1990, John F. Haugh II X * All rights reserved. X * X * Permission is granted to copy and create derivative works for any X * non-commercial purpose, provided this copyright notice is preserved X * in all copies of source code, or included in human readable form X * and conspicuously displayed on all copies of object code or X * distribution media. X */ X X#include X#include X#include "config.h" X X#ifdef BSD X#include X#include X#else X#include X#include X#endif X X#ifndef lint Xstatic char sccsid[] = "@(#)getpass.c 3.2 08:19:03 2/5/91"; X#endif X X/* X * limits.h may be kind enough to specify the length of a prompted X * for password. X */ X X#if defined(__STDC__) || defined(_POSIX_SOURCE) X#include X#endif X X/* X * This is really a giant mess. On the one hand, it would be nice X * if PASS_MAX were real big so that DOUBLESIZE isn't needed. But X * if it is defined we must honor it because some idiot might use X * this in a routine expecting some standard behavior. X */ X X#ifndef PASS_MAX X#ifdef DOUBLESIZE X#define PASS_MAX 16 X#else X#define PASS_MAX 8 X#endif X#endif X X#ifdef BSD X#define STTY(fd,termio) stty(fd, termio) X#define GTTY(fd,termio) gtty(fd, termio) X#define TERMIO struct sgttyb X#define INDEX index X#else X#define STTY(fd,termio) ioctl(fd, TCSETA, termio) X#define GTTY(fd,termio) ioctl(fd, TCGETA, termio) X#define TERMIO struct termio X#define INDEX strchr X#endif X Xstatic int sig_caught; X Xstatic void Xsig_catch () X{ X sig_caught = 1; X} X Xchar * Xgetpass (prompt) Xchar *prompt; X{ X static char input[PASS_MAX+1]; X char *return_value = 0; X char *cp; X FILE *fp; X int tty_opened = 0; X void (*old_signal)(); X TERMIO new_modes; X TERMIO old_modes; X X /* X * set a flag so the SIGINT signal can be re-sent if it X * is caught X */ X X sig_caught = 0; X X /* X * if /dev/tty can't be opened, getpass() needs to read X * from stdin instead. X */ X X if ((fp = fopen ("/dev/tty", "r")) == 0) { X fp = stdin; X setbuf (fp, (char *) 0); X } else { X tty_opened = 1; X } X X /* X * the current tty modes must be saved so they can be X * restored later on. echo will be turned off, except X * for the newline character (BSD has to punt on this) X */ X X if (GTTY (fileno (fp), &new_modes)) X return 0; X X old_modes = new_modes; X old_signal = signal (SIGINT, sig_catch); X X#ifdef BSD X new_modes.sg_flags &= ~ECHO ; X#else X new_modes.c_lflag &= ~(ECHO|ECHOE|ECHOK); X new_modes.c_lflag |= ECHONL; X#endif X X if (STTY (fileno (fp), &new_modes)) X goto out; X X /* X * the prompt is output, and the response read without X * echoing. the trailing newline must be removed. if X * the fgets() returns an error, a NULL pointer is X * returned. X */ X X if (fputs (prompt, stdout) == EOF) X goto out; X X if (fgets (input, sizeof input, fp) == input) { X if (cp = INDEX (input, '\n')) X *cp = '\0'; X else X input[sizeof input - 1] = '\0'; X X return_value = input; X#ifdef BSD X putc ('\n', stdout); X#endif X } Xout: X /* X * the old SIGINT handler is restored after the tty X * modes. then /dev/tty is closed if it was opened in X * the beginning. finally, if a signal was caught it X * is sent to this process for normal processing. X */ X X if (STTY (fileno (fp), &old_modes)) X return_value = 0; X X signal (SIGINT, old_signal); X X if (tty_opened) X fclose (fp); X X if (sig_caught) { X kill (getpid (), SIGINT); X return_value = 0; X } X return return_value; X} SHAR_EOF if test 3390 -ne "`wc -c < 'getpass.c'`" then echo shar: "error transmitting 'getpass.c'" '(should have been 3390 characters)' fi fi exit 0 # End of shell archive -- John F. Haugh II | Distribution to | UUCP: ...!cs.utexas.edu!rpp386!jfh Ma Bell: (512) 255-8251 | GEnie PROHIBITED :-) | Domain: jfh@rpp386.cactus.org "If liberals interpreted the 2nd Amendment the same way they interpret the rest of the Constitution, gun ownership would be mandatory."