From: jfh@rpp386.cactus.org (John F Haugh II) Newsgroups: alt.sources Subject: Shadow Login Suite, version 3 (part 4 of 8) Message-ID: <19298@rpp386.cactus.org> Date: 16 May 91 16:31:49 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: # gpmain.c # chage.c # pwent.c # valid.c # setup.c # entry.c # ttytype.c # port.h # This archive created: Sun Mar 3 13:27:23 1991 # By: John F Haugh II (River Parishes Programming, Austin TX) export PATH; PATH=/bin:/usr/bin:$PATH echo shar: "extracting 'gpmain.c'" '(9448 characters)' if test -f 'gpmain.c' then echo shar: "will not over-write existing file 'gpmain.c'" else sed 's/^X//' << \SHAR_EOF > 'gpmain.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 "pwd.h" X#include X#include X#include X#include X#ifndef BSD X#include X#ifdef SYS3 X#include X#endif X#include X#ifndef SYS3 X#include X#endif X#else X#include X#include X#define strchr index X#define strrchr rindex X#endif X#include "config.h" X X#ifndef PASSLENGTH X#define PASSLENGTH 5 X#endif X X#ifndef lint Xstatic char _sccsid[] = "@(#)gpmain.c 3.4 12:30:43 12/12/90"; X#endif X Xchar name[BUFSIZ]; Xchar pass[BUFSIZ]; Xchar pass2[BUFSIZ]; X Xstruct group grent; X Xchar *Prog; Xchar *user; Xchar *group; Xint aflg; Xint dflg; Xint rflg; Xint Rflg; X X#ifndef RETRIES X#define RETRIES 3 X#endif X Xextern char *l64a (); Xextern char *crypt (); Xextern char *pw_encrypt (); Xextern int errno; Xextern long a64l (); Xextern void entry (); Xextern time_t time (); X X/* X * usage - display usage message X */ X Xvoid Xusage () X{ X fprintf (stderr, "usage: %s [ -r|R ] group\n", Prog); X fprintf (stderr, " %s [ -a user ] group\n", Prog); X fprintf (stderr, " %s [ -d user ] group\n", Prog); X exit (1); X} X X/* X * add_list - add a member to a list of group members X * X * the array of member names is searched for the new member X * name, and if not present it is added to a freshly allocated X * list of users. X */ X Xchar ** Xadd_list (list, member) Xchar **list; Xchar *member; X{ X int i; X int found = 0; X char **tmp; X X for (i = 0;!found && list[i] != (char *) 0;i++) X if (strcmp (list[i], member) == 0) X found++; X X tmp = (char **) malloc ((i + 2) * sizeof member); X X for (i = 0;list[i] != (char *) 0;i++) X tmp[i] = list[i]; X X if (! found) X tmp[i++] = strdup (member); X X tmp[i] = (char *) 0; X return tmp; X} X X/* X * del_list - delete a group member from a list of members X * X * del_list searches a list of group members, copying the X * members which do not match "member" to a newly allocated X * list. X */ X Xchar ** Xdel_list (list, member) Xchar **list; Xchar *member; X{ X int i, j; X char **tmp; X X for (j = i = 0;list[i] != (char *) 0;i++) X if (strcmp (list[i], member)) X j++; X X tmp = (char **) malloc ((j + 1) * sizeof member); X X for (j = i = 0;list[i] != (char *) 0;i++) X if (strcmp (list[i], member) != 0) X tmp[j++] = list[i]; X X tmp[j] = (char *) 0; X X return tmp; X} X Xint Xmain (argc, argv) Xint argc; Xchar **argv; X{ X extern int optind; X extern char *optarg; X int flag; X int i; X void die (); X char *cp; X char *getlogin (); X int amroot; X int retries; X int ruid = getuid(); X struct group *gr; X struct group *getgrnam (); X struct group *sgetgrent (); X struct passwd *pw; X struct passwd *getpwuid (); X struct passwd *getpwnam (); X X /* X * Make a note of whether or not this command was invoked X * by root. This will be used to bypass certain checks X * later on. Also, set the real user ID to match the X * effective user ID. This will prevent the invoker from X * issuing signals which would interfer with this command. X */ X X amroot = getuid () == 0; X setuid (geteuid ()); X Prog = argv[0]; X setbuf (stdout, (char *) 0); X setbuf (stderr, (char *) 0); X X while ((flag = getopt (argc, argv, "a:d:grR")) != EOF) { X switch (flag) { X case 'a': /* add a user */ X aflg++; X user = optarg; X break; X case 'd': /* delete a user */ X dflg++; X user = optarg; X break; X case 'g': /* no-op from normal password */ X break; X case 'r': /* remove group password */ X rflg++; X break; X case 'R': /* restrict group password */ X Rflg++; X break; X default: X usage (); X } X } X X /* X * Make sure exclusive flags are exclusive X */ X X if (aflg + dflg + rflg + Rflg > 1) X usage (); X X /* X * Unless the mode is -a, -d or -r, the input and output must X * both be a tty. The typical keyboard signals are caught X * so the termio modes can be restored. X */ X X if (! aflg && ! dflg && ! rflg && ! Rflg) { X if (! isatty (0) || ! isatty (1)) X exit (1); X X die (0); /* save tty modes */ X X signal (SIGHUP, die); X signal (SIGINT, die); X signal (SIGQUIT, die); X signal (SIGTERM, die); X } X X /* X * Determine the name of the user that invoked this command. X * This is really hit or miss because there are so many ways X * that command can be executed and so many ways to trip up X * the routines that report the user name. X */ X X if ((cp = getlogin ()) && (pw = getpwnam (cp)) && pw->pw_uid == ruid) { X /* need user name */ X (void) strcpy (name, cp); X } else if (pw = getpwuid (ruid)) /* get it from password file */ X strcpy (name, pw->pw_name); X else { /* can't find user name! */ X fprintf (stderr, "Who are you?\n"); X exit (1); X } X if (! (pw = getpwnam (name))) X goto failure; /* can't get my name ... */ X X /* X * Get the name of the group that is being affected. The group X * entry will be completely replicated so it may be modified X * later on. X */ X X if (! (group = argv[optind])) X usage (); X X if (! (gr = getgrnam (group))) { X fprintf (stderr, "unknown group: %s\n", group); X exit (1); X } X grent = *gr; X grent.gr_name = strdup (gr->gr_name); X grent.gr_passwd = strdup (gr->gr_passwd); X X for (i = 0;gr->gr_mem[i];i++) X ; X grent.gr_mem = (char **) malloc ((i + 1) * sizeof (char *)); X for (i = 0;gr->gr_mem[i];i++) X grent.gr_mem[i] = strdup (gr->gr_mem[i]); X grent.gr_mem[i] = (char *) 0; X X /* X * The policy for changing a group is that 1) you must be root X * or 2) you must be the first listed member of the group. The X * first listed member of a group can do anything to that group X * that the root user can. X */ X X if (! amroot) { X if (grent.gr_mem[0] == (char *) 0) X goto failure; X X if (strcmp (grent.gr_mem[0], name) != 0) X goto failure; X } X X /* X * Removing a password is straight forward. Just set the X * password field to a "". X */ X X if (rflg) { X grent.gr_passwd = ""; X goto output; X } else if (Rflg) { X grent.gr_passwd = "!"; X goto output; X } X X /* X * Adding a member to a member list is pretty straightforward X * as well. Call the appropriate routine and split. X */ X X if (aflg) { X if (getpwnam (user) == (struct passwd *) 0) { X fprintf (stderr, "%s: unknown user %s\n", Prog, user); X exit (1); X } X printf ("Adding user %s to group %s\n", user, group); X grent.gr_mem = add_list (grent.gr_mem, user); X goto output; X } X X /* X * Removing a member from the member list is the same deal X * as adding one, except the routine is different. X */ X X if (dflg) { X for (i = 0;grent.gr_mem[i];i++) X if (strcmp (user, grent.gr_mem[i]) == 0) X break; X X if (grent.gr_mem[i] == (char *) 0) { X fprintf (stderr, "%s: unknown member %s\n", Prog, user); X exit (1); X } X printf ("Removing user %s from group %s\n", user, group); X grent.gr_mem = del_list (grent.gr_mem, user); X goto output; X } X X /* X * A new password is to be entered and it must be encrypted, X * etc. The password will be prompted for twice, and both X * entries must be identical. There is no need to validate X * the old password since the invoker is either the group X * owner, or root. X */ X X printf ("Changing the password for group %s\n", group); X X for (retries = 0;retries < RETRIES;retries++) { X if (! (cp = getpass ("New Password:"))) X exit (1); X else X strcpy (pass, cp); X X if (! (cp = getpass ("Re-enter new password:"))) X exit (1); X else X strcpy (pass2, cp); X X if (strcmp (pass, pass2) == 0) X break; X X if (retries + 1 < RETRIES) X puts ("They don't match; try again"); X } X if (retries == RETRIES) { X fprintf (stderr, "%s: Try again later\n", Prog); X exit (1); X } X grent.gr_passwd = pw_encrypt (pass, (char *) 0); X X /* X * This is the common arrival point to output the new group X * file. The freshly crafted entry is in allocated space. X * The group file will be locked and opened for writing. The X * new entry will be output, etc. X */ X Xoutput: X signal (SIGHUP, SIG_IGN); X signal (SIGINT, SIG_IGN); X signal (SIGQUIT, SIG_IGN); X X if (! gr_lock ()) { X fprintf (stderr, "%s: can't get lock\n", Prog); X exit (1); X } X if (! gr_open (O_RDWR)) { X fprintf (stderr, "%s: can't open file\n", Prog); X exit (1); X } X if (! gr_update (&grent)) { X fprintf (stderr, "%s: can't update entry\n", Prog); X exit (1); X } X if (! gr_close ()) { X fprintf (stderr, "%s: can't re-write file\n", Prog); X exit (1); X } X if (! gr_unlock ()) { X fprintf (stderr, "%s: can't unlock file\n", Prog); X exit (1); X } X#ifdef NDBM X if (! gr_dbm_update (&grent)) { X fprintf (stderr, "%s: can't update DBM files\n", Prog); X exit (1); X } X#endif X exit (0); X /*NOTREACHED*/ X Xfailure: X fprintf (stderr, "Permission denied.\n"); X exit (1); X /*NOTREACHED*/ X} X X/* X * die - set or reset termio modes. X * X * die() is called before processing begins. signal() is then X * called with die() as the signal handler. If signal later X * calls die() with a signal number, the terminal modes are X * then reset. X */ X Xvoid die (killed) Xint killed; X{ X#ifdef BSD X static struct sgtty sgtty; X X if (killed) X stty (0, &sgtty); X else X gtty (0, &sgtty); X#else X static struct termio sgtty; X X if (killed) X ioctl (0, TCSETA, &sgtty); X else X ioctl (0, TCGETA, &sgtty); X#endif X if (killed) { X putchar ('\n'); X fflush (stdout); X exit (killed); X } X} SHAR_EOF if test 9448 -ne "`wc -c < 'gpmain.c'`" then echo shar: "error transmitting 'gpmain.c'" '(should have been 9448 characters)' fi fi echo shar: "extracting 'chage.c'" '(15243 characters)' if test -f 'chage.c' then echo shar: "will not over-write existing file 'chage.c'" else sed 's/^X//' << \SHAR_EOF > 'chage.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#include X X#ifndef lint Xstatic char sccsid[] = "%W% %U% %G%"; 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 "shadow.h" X X/* X * Global variables X */ X Xchar *Prog; Xlong mindays; Xlong maxdays; Xlong lastday; Xlong warndays; Xlong inactdays; Xlong expdays; Xvoid cleanup(); X X/* X * External identifiers X */ X Xextern long a64l(); Xextern int pw_lock(), pw_open(), X pw_unlock(), pw_close(), X pw_update(); Xextern struct passwd *pw_locate(); Xextern int spw_lock(), spw_open(), X spw_unlock(), spw_close(), X spw_update(); Xextern struct spwd *spw_locate(); Xextern int optind; Xextern char *optarg; Xextern char *getlogin (); X#ifdef NDBM Xextern int pw_dbm_mode; Xextern int sp_dbm_mode; X#endif 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 (7*DAY) X X#ifdef ITI_AGING X#define SCALE (1) X#else X#define SCALE (DAY) X#endif X X/* X * days and juldays are used to compute the number of days in the X * current month, and the cummulative number of days in the preceding X * months. they are declared so that january is 1, not 0. X */ X Xstatic short days[13] = { 0, X 31, 28, 31, 30, 31, 30, /* JAN - JUN */ X 31, 31, 30, 31, 30, 31 }; /* JUL - DEC */ X Xstatic short juldays[13] = { 0, X 0, 31, 59, 90, 120, 151, /* JAN - JUN */ X 181, 212, 243, 273, 304, 334 }; /* JUL - DEC */ X X/* X * #defines for messages. This facilities foreign language conversion X * since all messages are defined right here. X */ X X#define USAGE \ X"Usage: %s [ -l ] [ -m min_days ] [ -M max_days ] [ -W warn ]\n\ X [ -I inactive ] [ -E expire ] [ -d last_day ] user\n" X#define DBMERROR "Error updating the DBM password entry.\n" X#define DBMERROR2 "error updating DBM shadow entry.\n" X X/* X * usage - print command line syntax and exit X */ X Xvoid Xusage () X{ X fprintf (stderr, USAGE, Prog); X exit (1); X} X X/* X * strtoday - compute the number of days since 1970. X * X * the total number of days prior to the current date is X * computed. january 1, 1970 is used as the origin with X * it having a day number of 0. the gmtime() routine is X * used to prevent confusion regarding time zones. X */ X Xlong Xstrtoday (str) Xchar *str; X{ X char slop[2]; X int month; X int day; X int year; X long total; X X /* X * start by separating the month, day and year. this is X * a chauvanistic program - it only takes date input in X * the standard USA format. X */ X X if (sscanf (str, "%d/%d/%d%c", &month, &day, &year, slop) != 3) X return -1; X X /* X * the month, day of the month, and year are checked for X * correctness and the year adjusted so it falls between X * 1970 and 2069. X */ X X if (month < 1 || month > 12) X return -1; X X if (day < 1) X return -1; X X if ((month != 2 || (year % 4) != 0) && day > days[month]) X return -1; X else if ((month == 2 && (year % 4) == 0) && day > 29) X return -1; X X if (year < 0) X return -1; X else if (year < 69) X year += 2000; X else if (year < 99) X year += 1900; X X if (year < 1970 || year > 2069) X return -1; X X /* X * the total number of days is the total number of days in all X * the whole years, plus the number of leap days, plus the X * number of days in the whole months preceding, plus the number X * of days so far in the month. X */ X X total = ((year - 1970) * 365) + (((year + 1) - 1970) / 4); X total += juldays[month] + (month > 2 && (year % 4) == 0 ? 1:0); X total += day - 1; X X return total; X} X X/* X * new_fields - change the user's password aging information interactively. X * X * prompt the user for all of the password age values. set the fields X * from the user's response, or leave alone if nothing was entered. the X * value (-1) is used to indicate the field should be removed if possible. X * any other negative value is an error. very large positive values will X * be handled elsewhere. X */ X Xint Xnew_fields () X{ X char buf[BUFSIZ]; X char *cp; X long value; X struct tm *tp; X X printf ("Enter the new value, or press return for the default\n\n"); X X sprintf (buf, "%ld", mindays); X change_field (buf, "Minimum Password Age"); X if (((mindays = strtol (buf, &cp, 10)) == 0 && *cp) || mindays < -1) X return 0; X X sprintf (buf, "%ld", maxdays); X change_field (buf, "Maximum Password Age"); X if (((maxdays = strtol (buf, &cp, 10)) == 0 && *cp) || maxdays < -1) X return 0; X X value = lastday * SCALE; X tp = gmtime (&value); X sprintf (buf, "%02d/%02d/%02d", X tp->tm_mon + 1, tp->tm_mday, tp->tm_year); X change_field (buf, "Last Password Change (MM/DD/YY)"); X if (strcmp (buf, "12/31/69") == 0) X lastday = -1; X else if ((lastday = strtoday (buf)) == -1) X return 0; X X sprintf (buf, "%ld", warndays); X change_field (buf, "Password Expiration Warning"); X if (((warndays = strtol (buf, &cp, 10)) == 0 && *cp) || warndays < -1) X return 0; X X sprintf (buf, "%ld", inactdays); X change_field (buf, "Password Inactive"); X if (((inactdays = strtol (buf, &cp, 10)) == 0 && *cp) || inactdays < -1) X return 0; X X value = expdays * SCALE; X tp = gmtime (&value); X sprintf (buf, "%02d/%02d/%02d", X tp->tm_mon + 1, tp->tm_mday, tp->tm_year); X change_field (buf, "Account Expiration Date (MM/DD/YY)"); X if (strcmp (buf, "12/31/69") == 0) X expdays = -1; X else if ((expdays = strtoday (buf)) == -1) X return 0; X X return 1; X} X X/* X * list_fields - display the current values of the expiration fields X * X * display the password age information from the password fields. date X * values will be displayed as a calendar date, or the word "Never" if X * the date is 1/1/70, which is day number 0. X */ X Xvoid Xlist_fields () X{ X struct tm *tp; X char *cp; X long changed; X long expires; X X /* X * Start with the easy numbers - the number of days before the X * password can be changed, the number of days after which the X * password must be chaged, the number of days before the X * password expires that the user is told, and the number of X * days after the password expires that the account becomes X * unusable. X */ X X printf ("Minimum:\t%d\n", mindays); X printf ("Maximum:\t%d\n", maxdays); X printf ("Warning:\t%d\n", warndays); X printf ("Inactive:\t%d\n", inactdays); X X /* X * The "last change" date is either "Never" or the date the X * password was last modified. The date is the number of X * days since 1/1/1970. X */ X X printf ("Last Change:\t\t"); X if (changed <= 0) { X printf ("Never\n"); X } else { X changed = lastday * SCALE; X tp = gmtime (&changed); X cp = asctime (tp); X printf ("%6.6s, %4.4s\n", cp + 4, cp + 20); X } X X /* X * The password expiration date is determined from the last X * change date plus the number of days the password is valid X * for. X */ X X printf ("Password Expires:\t"); X if (changed <= 0 || maxdays >= 10000*(DAY/SCALE) || maxdays <= 0) { X printf ("Never\n"); X } else { X expires = changed + maxdays * SCALE; X tp = gmtime (&expires); X cp = asctime (tp); X printf ("%6.6s, %4.4s\n", cp + 4, cp + 20); X } X X /* X * The account becomes inactive if the password is expired X * for more than "inactdays". The expiration date is calculated X * and the number of inactive days is added. The resulting date X * is when the active will be disabled. X */ X X printf ("Password Inactive:\t"); X if (changed <= 0 || inactdays <= 0 || X maxdays >= 10000*(DAY/SCALE) || maxdays <= 0) { X printf ("Never\n"); X } else { X expires = changed + (maxdays + inactdays) * SCALE; X tp = gmtime (&expires); X cp = asctime (tp); X printf ("%6.6s, %4.4s\n", cp + 4, cp + 20); X } X X /* X * The account will expire on the given date regardless of the X * password expiring or not. X */ X X printf ("Account Expires:\t"); X if (expdays <= 0) { X printf ("Never\n"); X } else { X expires = expdays * SCALE; X tp = gmtime (&expires); X cp = asctime (tp); X printf ("%6.6s, %4.4s\n", cp + 4, cp + 20); X } X} X X/* X * chage - change a user's password aging information X * X * This command controls the password aging information. X * X * The valid options are X * X * -m minimum number of days before password change (*) X * -M maximim number of days before password change (*) X * -d last password change date (*) X * -l last password change date X * -W expiration warning days (*) X * -I password inactive after expiration (*) X * -E account expiration date (*) X * X * (*) requires root permission to execute. X * X * All of the time fields are entered in the internal format X * which is either seconds or days. X */ X Xint Xmain (argc, argv) Xint argc; Xchar **argv; X{ X int flag; X int lflg; X int mflg; X int Mflg; X int dflg; X int Wflg; X int Iflg; X int Eflg; X int ruid = getuid (); X struct passwd *pw; X struct passwd pwent; X struct spwd *sp; X struct spwd spwd; X char name[BUFSIZ]; X X /* X * Get the program name so that error messages can use it. 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#ifdef NDBM X sp_dbm_mode = O_RDWR; X pw_dbm_mode = O_RDWR; X#endif X X /* X * Parse the flags. The difference between password file X * formats includes the number of fields, and whether the X * dates are entered as days or weeks. Shadow password X * file info =must= be entered in days, while regular X * password file info =must= be entered in weeks. X */ X X while ((flag = getopt (argc, argv, "lm:M:W:I:E:d:")) != EOF) { X switch (flag) { X case 'l': X lflg++; X break; X case 'm': X mflg++; X mindays = strtol (optarg, 0, 10); X break; X case 'M': X Mflg++; X maxdays = strtol (optarg, 0, 10); X break; X case 'd': X dflg++; X lastday = strtol (optarg, 0, 10); X break; X case 'W': X Wflg++; X warndays = strtol (optarg, 0, 10); X break; X case 'I': X Iflg++; X inactdays = strtol (optarg, 0, 10); X break; X case 'E': X Eflg++; X expdays = strtol (optarg, 0, 10); X break; X default: X usage (); X } X } X X /* X * Make certain the flags do not conflict and that there is X * a user name on the command line. X */ X X if (argc != optind + 1) X usage (); X X if (lflg && (mflg || Mflg || dflg || Wflg || Iflg || Eflg)) { X fprintf (stderr, "%s: do not include \"l\" with other flags\n", X Prog); X usage (); X } X X /* X * An unprivileged user can ask for their own aging information, X * but only root can change it, or list another user's aging X * information. X */ X X if (ruid != 0 && ! lflg) { X fprintf (stderr, "%s: permission denied\n", Prog); X exit (1); X } X X /* X * Lock and open the password file. This loads all of the X * password file entries into memory. Then we get a pointer X * to the password file entry for the requested user. X */ X X if (! pw_lock ()) { X fprintf (stderr, "%s: can't lock password file\n", Prog); X exit (1); X } X if (! pw_open (ruid != 0 || lflg ? O_RDONLY:O_RDWR)) { X fprintf (stderr, "%s: can't open password file\n", Prog); X cleanup (1); X exit (1); X } X if (! (pw = pw_locate (argv[optind]))) { X fprintf (stderr, "%s: unknown user: %s\n", Prog, argv[optind]); X cleanup (1); X exit (1); X } X X /* X * For shadow password files we have to lock the file and X * read in the entries as was done for the password file. X * The user entries does not have to exist in this case; X * a new entry will be created for this user if one does X * not exist already. X */ X X if (! spw_lock ()) { X fprintf (stderr, "%s: can't lock shadow file\n", Prog); X cleanup (1); X exit (1); X } X if (! spw_open ((ruid != 0 || lflg) ? O_RDONLY:O_RDWR)) { X fprintf (stderr, "%s: can't open shadow file\n", Prog); X cleanup (2); X exit (1); X } X if (sp = spw_locate (argv[optind])) X spwd = *sp; X X strcpy (name, pw->pw_name); X pwent = *pw; X X /* X * Set the fields that aren't being set from the command line X * from the password file. X */ X X if (sp) { X if (! Mflg) X maxdays = spwd.sp_max; X if (! mflg) X mindays = spwd.sp_min; X if (! dflg) X lastday = spwd.sp_lstchg; X if (! Wflg) X warndays = spwd.sp_warn; X if (! Iflg) X inactdays = spwd.sp_inact; X if (! Eflg) X expdays = spwd.sp_expire; X } else X#ifdef ATT_AGE X { X if (pwent.pw_age && strlen (pwent.pw_age) >= 2) { X if (! Mflg) X maxdays = c64i (pwent.pw_age[0]) * (WEEK/SCALE); X if (! mflg) X mindays = c64i (pwent.pw_age[1]) * (WEEK/SCALE); X if (! dflg && strlen (pwent.pw_age) == 4) X lastday = a64l (pwent.pw_age+2) * (WEEK/SCALE); X } else { X mindays = 0; X maxdays = 10000L * (DAY/SCALE); X lastday = -1; X } X warndays = inactdays = expdays = -1; X } X#endif X X /* X * Print out the expiration fields if the user has X * requested the list option. X */ X X if (lflg) { X if (ruid != 0 && ruid != pw->pw_uid) { X fprintf (stderr, "%s: permission denied\n", Prog); X exit (1); X } X list_fields (); X cleanup (2); X exit (0); X } X X /* X * If none of the fields were changed from the command line, X * let the user interactively change them. X */ X X if (! mflg && ! Mflg && ! dflg && ! Wflg && ! Iflg && ! Eflg) { X printf ("Changing the aging information for %s\n", name); X if (! new_fields ()) { X fprintf (stderr, "%s: error changing fields\n", Prog); X cleanup (2); X exit (1); X } X } X X /* X * There was no shadow entry. The new entry will have the X * encrypted password transferred from the normal password X * file along with the aging information. X */ X X if (sp == 0) { X sp = &spwd; X bzero (&spwd, sizeof spwd); X X sp->sp_namp = pw->pw_name; X sp->sp_pwdp = pw->pw_passwd; X sp->sp_flag = -1; X X pwent.pw_passwd = "!"; X#ifdef ATT_AGE X pwent.pw_age = ""; X#endif X if (! pw_update (&pwent)) { X fprintf (stderr, "%s: can't update password file\n", X Prog); X cleanup (2); X exit (1); X } X#if defined(DBM) || defined(NDBM) X (void) pw_dbm_update (&pwent); X#endif X } X X /* X * Copy the fields back to the shadow file entry and X * write the modified entry back to the shadow file. X * Closing the shadow and password files will commit X * any changes that have been made. X */ X X sp->sp_max = maxdays; X sp->sp_min = mindays; X sp->sp_lstchg = lastday; X sp->sp_warn = warndays; X sp->sp_inact = inactdays; X sp->sp_expire = expdays; X X if (! spw_update (sp)) { X fprintf (stderr, "%s: can't update shadow file\n", Prog); X cleanup (2); 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 /* NDBM */ X if (! spw_close ()) { X fprintf (stderr, "%s: can't rewrite shadow file\n", Prog); X cleanup (2); X exit (1); X } X (void) pw_close (); X cleanup (2); X exit (0); X /*NOTREACHED*/ X} X X/* X * cleanup - unlock any locked password files X */ X Xvoid Xcleanup (state) Xint state; X{ X switch (state) { X case 2: X spw_unlock (); X case 1: X pw_unlock (); X case 0: X break; X } X} SHAR_EOF if test 15243 -ne "`wc -c < 'chage.c'`" then echo shar: "error transmitting 'chage.c'" '(should have been 15243 characters)' fi fi echo shar: "extracting 'pwent.c'" '(9715 characters)' if test -f 'pwent.c' then echo shar: "will not over-write existing file 'pwent.c'" else sed 's/^X//' << \SHAR_EOF > 'pwent.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 "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/* X * If AUTOSHADOW is enable, the getpwnam and getpwuid calls will X * fill in the pw_passwd and pw_age fields from the passwd and X * shadow files. X */ X X#if defined(AUTOSHADOW) && !defined(SHADOWPWD) X#undef AUTOSHADOW X#endif X#ifdef AUTOSHADOW X#include "shadow.h" X#endif X X/* X * If DBM or NDBM is enabled, the getpwnam and getpwuid calls will X * go to the database files to look for the requested entries. X */ X X#ifdef DBM X#include X#endif X#ifdef NDBM X#include X#include XDBM *pw_dbm; Xint pw_dbm_mode = -1; X#endif X X/* X * ITI-style aging uses time_t's as the time fields, while X * AT&T-style aging uses long numbers of days. X */ X X#ifdef ITI_AGING X#define WEEK (7L*24L*3600L) X#else X#define WEEK 7 X#endif X X#ifndef lint Xstatic char sccsid[] = "@(#)pwent.c 3.5 12:53:57 12/19/90"; X#endif X X#define SBUFSIZ 64 X#define NFIELDS 7 X Xstatic FILE *pwdfp; Xstatic char pwdbuf[BUFSIZ]; Xstatic char *pwdfile = PWDFILE; X#if defined(DBM) || defined(NDBM) Xstatic int dbmopened; Xstatic int dbmerror; X#endif Xstatic char *pwdfields[NFIELDS]; Xstatic struct passwd pwent; X X#if defined(AUTOSHADOW) && defined(ATT_AGE) X/* X * sptopwage - convert shadow ages to AT&T-style pw_age ages X * X * sptopwage() converts the values in the shadow password X * entry to the format used in the old-style password X * entry. X */ X Xstatic char * Xsptopwage (spwd) Xstruct spwd *spwd; X{ X static char age[5]; X long min; X long max; X long last; X X if ((min = (spwd->sp_min / WEEK)) < 0) X min = 0; X else if (min >= 64) X min = 63; X X if ((max = (spwd->sp_max / WEEK)) < 0) X max = 0; X else if (max >= 64) X max = 63; X X if ((last = (spwd->sp_lstchg / WEEK)) < 0) X last = 0; X else if (last >= 4096) X last = 4095; X X age[0] = i64c (max); X age[1] = i64c (min); X age[2] = i64c (last % 64); X age[3] = i64c (last / 64); X age[4] = '\0'; X return age; X} X#endif X X/* X * sgetpwent - convert a string to a (struct passwd) X * X * sgetpwent() parses a string into the parts required for a password X * structure. Strict checking is made for the UID and GID fields and X * presence of the correct number of colons. Any failing tests result X * in a NULL pointer being returned. X */ X Xstruct passwd * Xsgetpwent (buf) Xchar *buf; X{ X int i; X char *cp; X X /* X * Copy the string to a static buffer so the pointers into X * the password structure remain valid. X */ X X strncpy (pwdbuf, buf, BUFSIZ); X pwdbuf[BUFSIZ-1] = '\0'; X X /* X * Save a pointer to the start of each colon separated X * field. The fields are converted into NUL terminated strings. X */ X X for (cp = pwdbuf, i = 0;i < NFIELDS && cp;i++) { X pwdfields[i] = cp; X if (cp = strchr (cp, ':')) X *cp++ = 0; X } X X /* X * There must be exactly NFIELDS colon separated fields or X * the entry is invalid. Also, the UID and GID must be non-blank. X */ X X if (i != NFIELDS || *pwdfields[2] == '\0' || *pwdfields[3] == '\0') X return 0; X X /* X * Each of the fields is converted the appropriate data type X * and the result assigned to the password structure. If the X * UID or GID does not convert to an integer value, a NULL X * pointer is returned. X */ X X pwent.pw_name = pwdfields[0]; X pwent.pw_passwd = pwdfields[1]; X if ((pwent.pw_uid = strtol (pwdfields[2], &cp, 10)) == 0 && *cp) X return 0; X X if ((pwent.pw_gid = strtol (pwdfields[3], &cp, 10)) == 0 && *cp) X return 0; X#ifdef ATT_AGE X if (cp = strchr (pwent.pw_passwd, ',')) { X pwent.pw_age = cp + 1; X *cp = '\0'; X } else X pwent.pw_age = ""; X#endif X pwent.pw_gecos = pwdfields[4]; X#ifdef ATT_COMMENT X pwent.pw_comment = ""; X#endif X pwent.pw_dir = pwdfields[5]; X pwent.pw_shell = pwdfields[6]; X X return (&pwent); X} X X/* X * fgetpwent - get a password file entry from a stream X * X * fgetpwent() reads the next line from a password file formatted stream X * and returns a pointer to the password structure for that line. X */ X Xstruct passwd * Xfgetpwent (fp) XFILE *fp; X{ X char buf[BUFSIZ]; X X while (fgets (buf, BUFSIZ, fp) != (char *) 0) { X buf[strlen (buf) - 1] = '\0'; X return (sgetpwent (buf)); X } X return 0; X} X X/* X * endpwent - close a password file X * X * endpwent() closes the password file if open. if autoshadowing is X * enabled the system must also end access to the shadow files since X * the user is probably unaware it was ever accessed. X */ X Xint Xendpwent () X{ X if (pwdfp) X if (fclose (pwdfp)) X return -1; X X pwdfp = 0; X#ifdef NDBM X if (dbmopened && pw_dbm) { X dbm_close (pw_dbm); X dbmopened = 0; X dbmerror = 0; X pw_dbm = 0; X } X#endif X#ifdef AUTOSHADOW X endspent (); X#endif X return 0; X} X X/* X * getpwent - get a password entry from the password file X * X * getpwent() opens the password file, if not already opened, and reads X * a single entry. NULL is returned if any errors are encountered reading X * the password file. X */ X Xstruct passwd * Xgetpwent () X{ X if (! pwdfp && setpwent ()) X return 0; X X return fgetpwent (pwdfp); X} X X/* X * getpwuid - locate the password entry for a given UID X * X * getpwuid() locates the first password file entry for the given UID. X * If there is a valid DBM file, the DBM files are queried first for X * the entry. Otherwise, a linear search is begun of the password file X * searching for an entry which matches the provided UID. X */ X Xstruct passwd * Xgetpwuid (uid) Xint uid; X{ X struct passwd *pwd; X#if defined(DBM) || defined(NDBM) X datum key; X datum content; X#endif X#ifdef AUTOSHADOW X struct spwd *spwd; X#endif X X if (setpwent ()) X return 0; X X#if defined(DBM) || defined(NDBM) X X /* X * If the DBM file are now open, create a key for this UID and X * try to fetch the entry from the database. A matching record X * will be unpacked into a static structure and returned to X * the user. X */ X X if (dbmopened) { X pwent.pw_uid = uid; X key.dsize = sizeof pwent.pw_uid; X key.dptr = (char *) &pwent.pw_uid; X#ifdef DBM X content = fetch (key); X#endif X#ifdef NDBM X content = dbm_fetch (pw_dbm, key); X#endif X if (content.dptr != 0) { X memcpy (pwdbuf, content.dptr, content.dsize); X pw_unpack (pwdbuf, content.dsize, &pwent); X#ifdef AUTOSHADOW X if (spwd = getspnam (pwent.pw_name)) { X pwent.pw_passwd = spwd->sp_pwdp; X#ifdef ATT_AGE X pwent.pw_age = sptopwage (spwd); X#endif X } X#endif X return &pwent; X } X } X#endif X /* X * Search for an entry which matches the UID. Return the X * entry when a match is found. X */ X X while (pwd = getpwent ()) X if (pwd->pw_uid == uid) X break; X X#ifdef AUTOSHADOW X if (pwd && (spwd = getspnam (pwd->pw_name))) { X pwd->pw_passwd = spwd->sp_pwdp; X#ifdef ATT_AGE X pwd->pw_age = sptopwage (spwd); X#endif X } X#endif X return pwd; X} X X/* X * getpwnam - locate the password entry for a given name X * X * getpwnam() locates the first password file entry for the given name. X * If there is a valid DBM file, the DBM files are queried first for X * the entry. Otherwise, a linear search is begun of the password file X * searching for an entry which matches the provided name. X */ X Xstruct passwd * Xgetpwnam (name) Xchar *name; X{ X struct passwd *pwd; X#if defined(DBM) || defined(NDBM) X datum key; X datum content; X#endif X#ifdef AUTOSHADOW X struct spwd *spwd; X#endif X X if (setpwent ()) X return 0; X X#if defined(DBM) || defined(NDBM) X X /* X * If the DBM file are now open, create a key for this UID and X * try to fetch the entry from the database. A matching record X * will be unpacked into a static structure and returned to X * the user. X */ X X if (dbmopened) { X key.dsize = strlen (name); X key.dptr = name; X#ifdef DBM X content = fetch (key); X#endif X#ifdef NDBM X content = dbm_fetch (pw_dbm, key); X#endif X if (content.dptr != 0) { X memcpy (pwdbuf, content.dptr, content.dsize); X pw_unpack (pwdbuf, content.dsize, &pwent); X#ifdef AUTOSHADOW X if (spwd = getspnam (pwent.pw_name)) { X pwent.pw_passwd = spwd->sp_pwdp; X#ifdef ATT_AGE X pwent.pw_age = sptopwage (spwd); X#endif X } X#endif X return &pwent; X } X } X#endif X /* X * Search for an entry which matches the name. Return the X * entry when a match is found. X */ X X while (pwd = getpwent ()) X if (strcmp (pwd->pw_name, name) == 0) X break; X X#ifdef AUTOSHADOW X if (pwd && (spwd = getspnam (pwd->pw_name))) { X pwd->pw_passwd = spwd->sp_pwdp; X#ifdef ATT_AGE X pwd->pw_age = sptopwage (spwd); X#endif X } X#endif X return pwd; X} X X/* X * setpwent - open the password file X * X * setpwent() opens the system password file, and the DBM password files X * if they are present. The system password file is rewound if it was X * open already. X */ X Xint Xsetpwent () X{ X#ifdef NDBM X int mode; X#endif X X if (! pwdfp) { X if (! (pwdfp = fopen (pwdfile, "r"))) X return -1; X } else { X if (fseek (pwdfp, 0L, 0) != 0) X return -1; X } X X /* X * Attempt to open the DBM files if they have never been opened X * and an error has never been returned. X */ X X#if defined (DBM) || defined (NDBM) X if (! dbmerror && ! dbmopened) { X char dbmfiles[BUFSIZ]; X X strcpy (dbmfiles, pwdfile); X strcat (dbmfiles, ".pag"); X#ifdef NDBM X if (pw_dbm_mode == -1) X mode = O_RDONLY; X else X mode = (pw_dbm_mode == O_RDONLY || X pw_dbm_mode == O_RDWR) ? pw_dbm_mode:O_RDONLY; X#endif X#ifdef DBM X if (access (dbmfiles, 0) || dbminit (pwdfile)) X#endif X#ifdef NDBM X if (access (dbmfiles, 0) || X (! (pw_dbm = dbm_open (pwdfile, mode, 0)))) X#endif X dbmerror = 1; X else X dbmopened = 1; X } X#endif X return 0; X} SHAR_EOF if test 9715 -ne "`wc -c < 'pwent.c'`" then echo shar: "error transmitting 'pwent.c'" '(should have been 9715 characters)' fi fi echo shar: "extracting 'valid.c'" '(2336 characters)' if test -f 'valid.c' then echo shar: "will not over-write existing file 'valid.c'" else sed 's/^X//' << \SHAR_EOF > 'valid.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 "pwd.h" 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[] = "@(#)valid.c 3.3 08:00:20 2/6/91"; X#endif X X/* X * valid - compare encrypted passwords X * X * Valid() compares the DES encrypted password from the password file X * against the password which the user has entered after it has been X * encrypted using the same salt as the original. Entries which do X * not have a password file entry have a NULL pw_name field and this X * is used to indicate that a dummy salt must be used to encrypt the X * password anyway. X */ X Xint valid (password, entry) Xchar *password; Xstruct passwd *entry; X{ X char *encrypt; X char *salt; X char *pw_encrypt (); X char *shell; X X /* X * Start with blank or empty password entries. Always encrypt X * a password if no such user exists. Only if the ID exists and X * the password is really empty do you return quickly. This X * routine is meant to waste CPU time. X */ X X if (entry->pw_name && ! entry->pw_passwd[0]) { X if (! password[0]) X return (1); /* user entered nothing */ X else X return (0); /* user entered something! */ X } X X /* X * If there is no entry then we need a salt to use. X */ X X if (entry->pw_name == (char *) 0 || entry->pw_passwd[0] == '\0') X salt = "xx"; X else X salt = entry->pw_passwd; X X /* X * Now, perform the encryption using the salt from before on X * the users input. Since we always encrypt the string, it X * should be very difficult to determine if the user exists by X * looking at execution time. X */ X X encrypt = pw_encrypt (password, salt); X X /* X * One last time we must deal with there being no password file X * entry for the user. We use the pw_passwd == NULL idiom to X * cause non-existent users to not be validated. X */ X X if (entry->pw_name && strcmp (encrypt, entry->pw_passwd) == 0) X return (1); X else X return (0); X} SHAR_EOF if test 2336 -ne "`wc -c < 'valid.c'`" then echo shar: "error transmitting 'valid.c'" '(should have been 2336 characters)' fi fi echo shar: "extracting 'setup.c'" '(3560 characters)' if test -f 'setup.c' then echo shar: "will not over-write existing file 'setup.c'" else sed 's/^X//' << \SHAR_EOF > 'setup.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 X#ifdef BSD X#include X#define strchr index X#else X#include X#include X#endif X X#include "config.h" X#include "pwd.h" X X#ifndef lint Xstatic char sccsid[] = "@(#)setup.c 3.3 07:46:41 2/6/91"; X#endif X X#ifndef PATH X#define PATH "PATH=/bin:/usr/bin" X#endif X X#ifndef SUPATH X#define SUPATH "PATH=/bin:/usr/bin:/etc" X#endif X X#ifndef MAILDIR X#define MAILDIR "/usr/spool/mail" X#endif X X#ifndef TTYPERM X#define TTYPERM 0622 X#endif X X#ifndef SU Xextern struct utmp utent; X#endif X X#ifdef QUOTAS Xlong strtol (); X#ifdef ULIMIT Xlong ulimit (); X#endif X#endif X Xvoid addenv (); X X/* X * setup - initialize login environment X * X * setup() performs the following steps - X * X * set the login tty to be owned by the new user ID with TTYPERM modes X * change to the user's home directory X * set the process nice, ulimit, and umask from the password file entry X * set the group ID to the value from the password file entry X * set the user ID to the value from the password file entry X * set the HOME, SHELL, MAIL, PATH, and LOGNAME environmental variables X */ X Xvoid setup (info) Xstruct passwd *info; X{ X extern int errno; X char buf[BUFSIZ]; X#ifndef SU X char tty[30]; X#endif X char *cp; X int i; X long l; X X#ifndef SU X (void) strcat (strcpy (tty, "/dev/"), utent.ut_line); X if (chown (tty, info->pw_uid, info->pw_gid) || chmod (tty, TTYPERM)) { X (void) sprintf (buf, "Unable to change tty %s", tty); X syslog (LOG_WARN, "unable to change tty `%s' for user `%s'", X tty, info->pw_name); X perror (buf); X exit (errno); X } X#endif X if (chdir (info->pw_dir) == -1) { X (void) sprintf (buf, "Unable to cd to \"%s\"", info->pw_dir); X syslog (LOG_WARN, "unable to cd to `%s' for user `%s'", X info->pw_dir, info->pw_name); X perror (buf); X exit (errno); X } X#ifdef QUOTAS X for (cp = info->pw_gecos;cp != (char *) 0;cp = strchr (cp, ',')) { X if (*cp == ',') X cp++; X X if (strncmp (cp, "pri=", 4) == 0) { X i = atoi (cp + 4); X if (i >= -20 && i <= 20) X (void) nice (i); X X continue; X } X#ifdef ULIMIT X if (strncmp (cp, "ulimit=", 7) == 0) { X l = strtol (cp + 7, (char **) 0, 10); X (void) ulimit (2, l); X X continue; X } X#endif X if (strncmp (cp, "umask=", 6) == 0) { X i = strtol (cp + 6, (char **) 0, 8) & 0777; X (void) umask (i); X X continue; X } X } X#endif X if (setgid (info->pw_gid) == -1) { X puts ("Bad group id"); X syslog (LOG_WARN, "bad group ID `%d' for user `%s'", X info->pw_gid, info->pw_name); X exit (errno); X } X#ifndef BSD X if (setuid (info->pw_uid)) X#else X if (setreuid (info->pw_uid, info->pw_uid)) X#endif X { X puts ("Bad user id"); X syslog (LOG_WARN, "bad user ID `%d' for user `%s'", X info->pw_uid, info->pw_name); X exit (errno); X } X (void) strcat (strcpy (buf, "HOME="), info->pw_dir); X addenv (buf); X X if (info->pw_shell == (char *) 0) X info->pw_shell = "/bin/sh"; X X (void) strcat (strcpy (buf, "SHELL="), info->pw_shell); X addenv (buf); X X if (info->pw_uid == 0) X addenv (SUPATH); X else X addenv (PATH); X X (void) strcat (strcpy (buf, "LOGNAME="), info->pw_name); X addenv (buf); X X (void) strcat (strcat (strcpy (buf, "MAIL="), MAILDIR), info->pw_name); X addenv (buf); X} SHAR_EOF if test 3560 -ne "`wc -c < 'setup.c'`" then echo shar: "error transmitting 'setup.c'" '(should have been 3560 characters)' fi fi echo shar: "extracting 'entry.c'" '(1946 characters)' if test -f 'entry.c' then echo shar: "will not over-write existing file 'entry.c'" else sed 's/^X//' << \SHAR_EOF > 'entry.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 "pwd.h" X#ifndef BSD X#include X#else X#include X#define strchr index X#define strrchr rindex X#endif X#include "config.h" X#ifdef SHADOWPWD X#include "shadow.h" X#endif X X#ifndef lint Xstatic char sccsid[] = "@(#)entry.c 3.2 12:30:39 12/12/90"; X#endif X Xstruct passwd *fgetpwent (); X Xvoid entry (name, pwent) Xchar *name; Xstruct passwd *pwent; X{ X struct passwd *passwd; X#ifdef SHADOWPWD X struct spwd *spwd; X char *l64a (); X#endif X char *cp; X X if (! (passwd = getpwnam (name))) { X pwent->pw_name = (char *) 0; X return; X } else { X pwent->pw_name = strdup (passwd->pw_name); X pwent->pw_uid = passwd->pw_uid; X pwent->pw_gid = passwd->pw_gid; X#ifdef ATT_COMMENT X pwent->pw_comment = strdup (passwd->pw_comment); X#endif X pwent->pw_gecos = strdup (passwd->pw_gecos); X pwent->pw_dir = strdup (passwd->pw_dir); X pwent->pw_shell = strdup (passwd->pw_shell); X#if defined(SHADOWPWD) && !defined(AUTOSHADOW) X setspent (); X if (spwd = getspnam (name)) { X pwent->pw_passwd = strdup (spwd->sp_pwdp); X#ifdef ATT_AGE X pwent->pw_age = malloc (5); X X if (spwd->sp_max > (63*7)) X spwd->sp_max = (63*7); X if (spwd->sp_min > (63*7)) X spwd->sp_min = (63*7); X X pwent->pw_age[0] = i64c (spwd->sp_max / 7); X pwent->pw_age[1] = i64c (spwd->sp_min / 7); X X cp = l64a (spwd->sp_lstchg / 7); X pwent->pw_age[2] = cp[0]; X pwent->pw_age[3] = cp[1]; X X pwent->pw_age[4] = '\0'; X#endif X endspent (); X return; X } X endspent (); X#endif X pwent->pw_passwd = strdup (passwd->pw_passwd); X#ifdef ATT_AGE X pwent->pw_age = strdup (passwd->pw_age); X#endif X } X} SHAR_EOF if test 1946 -ne "`wc -c < 'entry.c'`" then echo shar: "error transmitting 'entry.c'" '(should have been 1946 characters)' fi fi echo shar: "extracting 'ttytype.c'" '(1125 characters)' if test -f 'ttytype.c' then echo shar: "will not over-write existing file 'ttytype.c'" else sed 's/^X//' << \SHAR_EOF > 'ttytype.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#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#ifdef TTYTYPE X#ifndef lint Xstatic char _sccsid[] = "@(#)ttytype.c 2.2 19:24:24 7/29/90"; X#endif X X/* X * ttytype - set ttytype from port to terminal type mapping database X */ X Xvoid ttytype (line) Xchar *line; X{ X FILE *fp; X char buf[BUFSIZ]; X char termvar[BUFSIZ]; X char *cp; X char *type; X char *port; X char *getenv (); X X if (getenv ("TERM")) X return; X X if (! (fp = fopen (TTYTYPE, "r"))) X return; X X while (fgets (buf, BUFSIZ, fp)) { X if (buf[0] == '#') X continue; X X if (cp = strchr (buf, '\n')) X *cp = '\0'; X X if ((type = strtok (buf, " \t")) X && (port = strtok ((char *) 0, " \t"))) { X if (strcmp (line, port) == 0) X break; X } X } X if (! feof (fp) && ! ferror (fp)) { X strcat (strcpy (termvar, "TERM="), type); X addenv (termvar); X } X fclose (fp); X} X#endif SHAR_EOF if test 1125 -ne "`wc -c < 'ttytype.c'`" then echo shar: "error transmitting 'ttytype.c'" '(should have been 1125 characters)' fi fi echo shar: "extracting 'port.h'" '(1743 characters)' if test -f 'port.h' then echo shar: "will not over-write existing file 'port.h'" else sed 's/^X//' << \SHAR_EOF > 'port.h' 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/* X * port.h - structure of /etc/porttime X * X * @(#)port.h 3.1 08:59:36 2/8/91 X * X * Each entry in /etc/porttime consists of a TTY device X * name or "*" to indicate all TTY devices, followed by X * a list of 1 or more user IDs or "*" to indicate all X * user names, followed by a list of zero or more valid X * login times. Login time entries consist of zero or X * more day names (Su, Mo, Tu, We, Th, Fr, Sa, Wk, Al) X * followed by a pair of time values in HHMM format X * separated by a "-". X */ X X/* X * PORTS - Name of system port access time file. X * PORT_IDS - Allowable number of IDs per entry. X * PORT_TTY - Allowable number of TTYs per entry. X * PORT_TIMES - Allowable number of time entries per entry. X * PORT_DAY - Day of the week to a bit value (0 = Sunday). X */ X X#define PORTS "/etc/porttime" X#define PORT_IDS 64 X#define PORT_TTY 64 X#define PORT_TIMES 24 X#define PORT_DAY(day) (1<<(day)) X X/* X * pt_names - pointer to array of device names in /dev/ X * pt_users - pointer to array of applicable user IDs. X * pt_times - pointer to list of allowable time periods. X */ X Xstruct port { X char **pt_names; X char **pt_users; X struct pt_time *pt_times; X}; X X/* X * t_days - bit array for each day of the week (0 = Sunday) X * t_start - starting time for this entry X * t_end - ending time for this entry X */ X Xstruct pt_time { X short t_days; X short t_start; X short t_end; X}; SHAR_EOF if test 1743 -ne "`wc -c < 'port.h'`" then echo shar: "error transmitting 'port.h'" '(should have been 1743 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."