home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
misc
/
volume26
/
shadow
/
part01
next >
Wrap
Text File
|
1991-11-24
|
56KB
|
2,205 lines
Newsgroups: comp.sources.misc
From: jfh@rpp386.Cactus.ORG (John F Haugh II)
Subject: v26i054: shadow - Shadow Password Suite, Part01/11
Message-ID: <csm-v26i054=shadow.124731@sparky.IMD.Sterling.COM>
X-Md4-Signature: ca4b30ecd506ff36955f1053784a1c9b
Date: Sun, 24 Nov 1991 18:49:19 GMT
Approved: kent@sparky.imd.sterling.com
Submitted-by: jfh@rpp386.Cactus.ORG (John F Haugh II)
Posting-number: Volume 26, Issue 54
Archive-name: shadow/part01
Environment: UNIX
Supersedes: shadow-2: Volume 06, Issue 22-24
This is John F. Haugh II's login replacement, release 3.
New for Release 3:
The objects are being combined into libraries to make maintenance
easier and to encourage developers to use the modules as the
basis for new tools. New tools are planned based on SVR4 commands.
New lint rules have been added to make the code easier to lint.
Files will gradually be fixed so that they lint cleanly.
DBM file access has been added to everything that would tolerate
it. The files /etc/passwd, /etc/group, and /etc/shadow all have
DBM interfaces. The new file, /etc/gshadow, has been added to
support shadowed group information and it too has a DBM interface.
Additional information has been added to the shadow group file to
define the notion of a group administrator.
SVR4 utilities to add and modify user and group information have
been added. The man pages for these commands have been written
as well.
Begin by reading and editing the config.h file. All options are selected
by using #define's. A brief description for each available option appears
in the README. You may want to print this file out as it is LONG and you
will need to refer to it while editting config.h. You will also have to
edit the Makefile. The possible differences are documented there. Pay
close attention to the install: rule. Login now runs on about 30 different
varieties of UNIX that I have been made aware of.
Note that there are MANY options. As distributed most options are turned
on, which produces a really nice package. This is the system as used on
some of the authors' machines. There are many options which may be
selected at run time. You should refer to the login.5 manual page for
more information regarding these options.
Special thanks are due to Chip Rosenthal for his fine testing efforts;
to Steve Simmons for his work in porting this code to BSD; and to Bill
Kennedy for his contributions of LaserJet printer time and energies.
Also, thanks for Dennis L. Mumaugh for the initial shadow password
information and to Tony Walton (olapw@olgb1.oliv.co.uk) for the System
V Release 4 changes. Effort in porting to SunOS has been contributed
by Dr. Michael Newberry (miken@cs.adfa.oz.au).
--------
#! /bin/sh
# This is a shell archive. Remove anything before this line, then feed it
# into a shell via "sh file" or similar. To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix@uunet.uu.net if you want that tool.
# Contents: README lmain.c useradd.c
# Wrapped by kent@sparky on Sun Nov 24 11:03:40 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
echo If this archive is complete, you will see the following message:
echo ' "shar: End of archive 1 (of 11)."'
if test -f 'README' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(9097 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
X[ @(#)README 3.6 08:27:47 10/31/91 ]
X
XThis is the explanatory document for John F. Haugh II's login replacement,
Xrelease 3. This document was last updated 10/31/91.
X
XThis software is copyright 1988, 1989, 1990, 1991, John F. Haugh II. All
Xrights reserved. Use, duplication and disclosure is permitted according
Xto the guidelines listed below.
X
XThis software is being provided as a freely redistributable login clone.
XYou may distribute this software provided you do not charge for other than
Xtransmission costs. You are free to copy this software provided you
Xdo not restrict the rights of the recipients to further copy this software.
X
XTHIS SOFTWARE IS BEING DISTRIBUTED AS-IS. THE AUTHORS DISCLAIM ALL
XLIABILITY FOR ANY CONSEQUENCES OF USE. THE USER IS SOLELY RESPONSIBLE
XFOR THE MAINTENANCE OF THIS SOFTWARE PACKAGE. THE AUTHORS ARE UNDER NO
XOBLIGATION TO PROVIDE MODIFICATIONS OR IMPROVEMENTS. THE USER IS
XENCOURAGE TO TAKE ANY AND ALL STEPS NEEDED TO PROTECT AGAINST ACCIDENTAL
XLOSS OF INFORMATION OR MACHINE RESOURCES.
X
XSpecial thanks are due to Chip Rosenthal for his fine testing efforts;
Xto Steve Simmons for his work in porting this code to BSD; and to Bill
XKennedy for his contributions of LaserJet printer time and energies.
XAlso, thanks for Dennis L. Mumaugh for the initial shadow password
Xinformation and to Tony Walton (olapw@olgb1.oliv.co.uk) for the System
XV Release 4 changes. Effort in porting to SunOS has been contributed
Xby Dr. Michael Newberry (miken@cs.adfa.oz.au).
X
XNew for Release 3:
X The objects are being combined into libraries to make maintenance
X easier and to encourage developers to use the modules as the
X basis for new tools. New tools are planned based on SVR4 commands.
X
X New lint rules have been added to make the code easier to lint.
X Files will gradually be fixed so that they lint cleanly.
X
X DBM file access has been added to everything that would tolerate
X it. The files /etc/passwd, /etc/group, and /etc/shadow all have
X DBM interfaces. The new file, /etc/gshadow, has been added to
X support shadowed group information and it too has a DBM interface.
X Additional information has been added to the shadow group file to
X define the notion of a group administrator.
X
X SVR4 utilities to add and modify user and group information have
X been added. The man pages for these commands have been written
X as well.
X
XBegin by reading and editing the config.h file. All options are selected
Xby using #define's. A brief description for each available option appears
Xbelow. You may want to print this file out as it is LONG and you will
Xneed to refer to it while editting config.h. You will also have to edit
Xthe Makefile. The possible differences are documented there. Pay close
Xattention to the install: rule. Login now runs on about 30 different
Xvarieties of UNIX that I have been made aware of.
X
XNote that there are MANY options. As distributed most options are turned
Xon, which produces a really nice package. This is the system as used on
Xsome of the authors' machines. There are many options which may be
Xselected at run time. You should refer to the login.5 manual page for
Xmore information regarding these options.
X
XLogin Defaults File -
X This option selects the name of the file to read for the
X run-time configurable options. The default value for
X LOGINDEFS is "/etc/login.defs".
X
XShadow [ unreadable ] Password Files -
X This option utilizes an alternate, non-readable file to
X contain the actual encrypted passwords. This is presumed
X to increase system security by increasing the difficulty
X with which system crackers obtain encrypted passwords.
X
X Select this option by defining the SHADOWPWD macro.
X
XShadow Group Files -
X This option utilizes an alternate, non-readable file to
X contain encrypted group passwords and group administrator
X information.
X
X This feature allows one or more users to be defined as
X the administrators of a group for the purpose of adding
X or deleting members and changing the group password.
X
X Select this option by defining the SHADOWGRP macro. You
X must also create an emptry /etc/gshadow file.
X
XDBM Password Files -
X This option utilizes the DBM database access routines to
X increase the performance of user name and ID lookups in the
X password file. You may select the NDBM database instead
X and have DBM-style access to all user information files.
X
X Select this option by defining both the DBM and GETPWENT
X macros. The FGETPWENT macro must also be defined or the
X fgetpwent() library routine must be present.
X
XDouble Length Passwords -
X This option extends the maximum length of a user password
X to 16 characters from eight.
X
X Select this option by defining the DOUBLESIZE macro.
X Credit for this option is due Jonathan Bayer.
X
XPassword Aging -
X This option includes code to perform password aging.
X Password aging is presumed to increase system security
X by forcing users to change passwords on a regular
X basis. The resolution on password age is in weeks for
X non-shadow password systems and in days otherwise.
X
X Select this option by defining the AGING macro.
X
XSyslog -
X This option causes the code to log various errors or
X special conditions to the syslog daemon. The types of
X information that are logged security violations, changes
X to the user database, and program errors.
X
X Select syslog processing by defining the USE_SYSLOG
X macro.
X
XRemote Login -
X This option causes certain network login code to be
X inserted to enable the "rlogin" and "telnet" commands to
X work. To enable network logins, define the RLOGIN macro.
X If your <utmp.h> file includes a ut_host member, you must
X also define the UT_HOST macro.
X
XDirectory Reading Routines -
X Three different macros are defined for opening and reading
X directories. They are DIR_XENIX, DIR_BSD, and DIR_SYSV.
X Refer to config.h for more details.
X
XLibrary Configuration Macros -
X The following macros define the functions which are present
X in your system library:
X
X HAVE_ULIMIT - Define if your UNIX supports ulimit()
X GETPWENT - Define if you want my GETPWENT(3) routines
X GETGRENT - Define if you want my GETGRENT(3) routines
X NEED_AL64 - Define if library does not include a64l()
X NEED_MKDIR - Define if system does not have mkdir()
X NEED_RMDIR - Define if system does not have rmdir()
X NEED_RENAME - Define if system does not have rename()
X NEED_STRSTR - Define if library does not include strstr()
X
XPassword File Information -
X The following macros define the fields which are present in
X your system password file. Because the system was compiled
X to use the password file in its original form, these macros
X must agree with the actual contents of the file.
X
X BSD_QUOTA - the pw_quota field exists
X ATT_AGE - the pw_age field exists
X ATT_COMMENT - the pw_comment field exists
X
XSignal Return Type -
X Because different systems return different data types for
X the signal() system call, you must define SIGTYPE to be
X the data type your system uses. The default is "int", but
X "void" is another popular value.
X
XBSD Notes: Steve Simmons scs@iti.org
X
XThe full port of the shadow package to BSD is not complete; but some
Xof the issues have been worked out. These notes describe the current
Xstate of things:
X
XIn order to make use of password aging under BSD, minor changes to
X/usr/include/pwd.h and getpwent() are needed. These changes are to
Xkeep the password age from messing up the encrypted password when not
Xusing shadow passwords, and involve placing a new field in the password
Xdata structure. To use this, you should apply the following two patches:
X pwd.h.patch
X getpwent.c.patch
Xto the BSD /usr/include/pwd.h and /usr/src/lib/libc/gen/getpwent.c,
Xrespectively. After applying the patches, rebuild your standard C
Xlibrary with the new getpwent. Programs which use the old getpwent
Xwill fail on password checking if they do a strcmp rather than a strncmp.
X[ I do not seem to have these two patches. I have provided an entire
Xgetpwent collection of code which may be useful instead. This code
Xdoes not support Sun Yellow Pages(tm?), which is a shame. -jfh ]
X
XThese changes are based on BSD4.3, not Tahoe
X
XToDo BSD:
X
XI'm working on this in my copious spare time (hah!); any help would
Xbe appreciated. If you decide to help, do these independantly rather
Xthan rework BSD code! Keep it redistributable!
X
XNo dbm functions have been put in place. Dbm functionality is needed
Xfor both /etc/password and /etc/shadow management. [ It is now possible
Xto create /etc/passwd.dir and /etc/passwd.pag using the new mkpasswd
Xcommand. getpwuid and getpwnam both use these files. Also, the
Xcommands chfn, chsh, and chage all update the DBM files. -jfh ]
X
XThe BSD GECOS field gets used for lots more stuff than the USG. At a
Xminimum this functionality should be duplicated under BSD; better is to put
Xit into USG as well; still better would be to make the chfn command for
Xboth systems; best would be site-configurable data to be put into GECOS/chfn.
X
X[ this is now possible using chfn and the -o option. i would appreciate
X anyone who is able to confirm that this code runs reasonably on some
X BSD release. - jfh ]
END_OF_FILE
if test 9097 -ne `wc -c <'README'`; then
echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'lmain.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'lmain.c'\"
else
echo shar: Extracting \"'lmain.c'\" \(14004 characters\)
sed "s/^X//" >'lmain.c' <<'END_OF_FILE'
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 <sys/types.h>
X#include <sys/stat.h>
X#include <stdio.h>
X#include "pwd.h"
X#include <utmp.h>
X#include <time.h>
X#include <signal.h>
X#ifndef BSD
X#include <string.h>
X#include <memory.h>
X#else
X#include <strings.h>
X#define strchr index
X#define strrchr rindex
X#endif
X#ifndef BSD
X#include <termio.h>
X#else
X#include <sgtty.h>
X#endif
X#include "config.h"
X#include "lastlog.h"
X#include "faillog.h"
X#include "shadow.h"
X
X#if !defined(BSD) && !defined(SUN)
X#define bzero(a,n) memset(a, 0, n);
X#endif
X
X#ifdef USE_SYSLOG
X#include <syslog.h>
X
X#ifndef LOG_WARN
X#define LOG_WARN LOG_WARNING
X#endif
X#endif
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)lmain.c 3.15 14:38:18 10/27/91";
X#endif
X
X /* danger - side effects */
X#define STRFCPY(A,B) strncpy((A), (B), sizeof(A)), *((A)+sizeof(A)-1) = '\0'
X
X#if defined(RLOGIN) || defined(UT_HOST)
Xchar host[BUFSIZ];
Xchar term[128] = "TERM=";
X#endif
X
Xstruct passwd pwent;
Xstruct utmp utent;
Xstruct lastlog lastlog;
Xint pflg;
Xint rflg;
Xint fflg;
X#ifdef RLOGIN
Xint hflg;
X#endif
Xint preauth_flag;
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 char *tz ();
Xextern void checkutmp ();
Xextern void addenv ();
Xextern void setenv ();
Xextern unsigned alarm ();
Xextern void login ();
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 char *getdef_str();
Xextern int getdef_bool();
Xextern int getdef_num();
Xextern long getdef_long();
Xextern int optind;
Xextern char *optarg;
Xextern char **environ;
X
X#ifdef HAVE_ULIMIT
Xextern long ulimit();
X#endif
X
X#ifndef ALARM
X#define ALARM 60
X#endif
X
X#ifndef RETRIES
X#define RETRIES 3
X#endif
X
Xstruct faillog faillog;
X
Xstruct utmp failent;
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 * login [ name ]
X * login -r hostname (for rlogind)
X * login -h hostname (for telnetd, etc.)
X * login -f name (for pre-authenticated login: datakit, xterm, etc.)
X */
X
Xusage ()
X{
X fprintf (stderr, "usage: login [ -p ] [ name ]\n");
X#ifdef RLOGIN
X fprintf (stderr, " login [ -p ] -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 /* RLOGIN */
X exit (1);
X}
X
X#ifdef RLOGIN
Xrlogin (remote_host, name, namelen)
Xchar *remote_host;
Xchar *name;
Xint namelen;
X{
X struct passwd *pwd;
X char remote_name[32];
X char *cp;
X
X get_remote_string (remote_name, sizeof remote_name);
X get_remote_string (name, namelen);
X get_remote_string (term + 5, sizeof term - 5);
X if (cp = strchr (term, '/'))
X *cp = '\0';
X
X if (! (pwd = getpwnam (name)))
X return 0;
X
X return ruserok (remote_host, pwd->pw_uid == 0, remote_name, name);
X}
X
Xget_remote_string (buf, size)
Xchar *buf;
Xint size;
X{
X for (;;) {
X if (read (0, buf, 1) != 1)
X exit (1);
X if (*buf == '\0')
X return;
X if (--size > 0)
X ++buf;
X }
X /*NOTREACHED*/
X}
X#endif
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 i;
X int subroot = 0;
X char *fname;
X char *cp;
X struct passwd *pwd;
X struct spwd *spwd;
X struct spwd *getspnam();
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 STRFCPY (tty, utent.ut_line);
X
X if (Prog = strrchr (argv[0], '/'))
X Prog++;
X else
X Prog = argv[0];
X
X#ifdef RLOGIN
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':
X fflg++;
X preauth_flag++;
X STRFCPY (name, optarg);
X break;
X#ifdef RLOGIN
X case 'r':
X rflg++;
X STRFCPY (host, optarg);
X#ifdef UT_HOST
X STRFCPY (utent.ut_host, optarg);
X#endif /*UT_HOST*/
X if (rlogin (host, name, sizeof name))
X preauth_flag++;
X
X break;
X case 'h':
X hflg++;
X STRFCPY (host, optarg);
X#ifdef UT_HOST
X STRFCPY (utent.ut_host, optarg);
X#endif /*UT_HOST*/
X break;
X#endif /*RLOGIN*/
X default:
X usage ();
X }
X }
X
X#ifdef RLOGIN
X /*
X * Neither -h nor -f should be combined with -r.
X */
X
X if (rflg && (hflg || fflg))
X usage ();
X#endif
X
X /*
X * Allow authentication bypass only if real UID is zero.
X */
X
X if ((rflg || fflg) && getuid () != 0) {
X fprintf(stderr, "%s: permission denied\n", Prog);
X exit (1);
X }
X
X#ifdef USE_SYSLOG
X openlog (Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
X#endif
X
X if (! isatty (0) || ! isatty (1) || ! isatty (2)) {
X#ifdef USE_SYSLOG
X closelog ();
X#endif
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] = getdef_num("ERASECHAR", '\b');
X termio.c_cc[VKILL] = getdef_num("KILLCHAR", '\025');
X (void) ioctl (0, TCSETAF, &termio);
X#endif /* !BSD */
X umask (getdef_num("UMASK", 0));
X#ifdef HAVE_ULIMIT
X ulimit (2, getdef_long("ULIMIT", 2097152L));
X#endif
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 RLOGIN
X if (term[5] != '\0') /* see if anything after "TERM=" */
X addenv (term);
X#endif
X if (! getenv("TZ") && (cp = getdef_str("ENV_TZ")))
X addenv (*cp == '/' ? tz(cp) : cp);
X if (! getenv("HZ") && (cp = getdef_str("ENV_HZ")))
X addenv (cp);
X
X if (optind < argc) { /* get the user name */
X if (rflg || fflg)
X usage ();
X
X STRFCPY (name, argv[optind]);
X ++optind;
X }
X if (optind < argc) /* now set command line variables */
X setenv (argc - optind, &argv[optind]);
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#ifdef USE_SYSLOG
X closelog ();
X#endif
X exit (1);
X }
X#ifdef RLOGIN
X preauth_flag = 0;
X#endif
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#ifdef USE_SYSLOG
X syslog (LOG_WARN, NO_SHADOW, name, tty);
X#else
X ;
X#endif
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#ifdef RLOGIN
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 && preauth_flag)
X goto have_name;
X#endif /*RLOGIN*/
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 STRFCPY (pass, cp);
X bzero (cp, strlen (cp));
X }
X if (! valid (pass, &pwent)) { /* check encrypted passwords */
X#ifdef USE_SYSLOG
X syslog (LOG_WARN, BAD_PASSWD, name, tty);
X#endif
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 if (getdef_bool("DIALUPS_CHECK_ENAB")) {
X alarm (30);
X if (pwent.pw_name &&
X ! dialcheck (tty, pwent.pw_shell[0] ?
X pwent.pw_shell:"/bin/sh")) {
X#ifdef USE_SYSLOG
X syslog (LOG_WARN, BAD_DIALUP, name, tty);
X#endif
X failed = 1;
X }
X }
X if (getdef_bool("PORTTIME_CHECKS_ENAB") &&
X pwent.pw_name &&
X ! isttytime (pwent.pw_name, tty, time ((time_t *) 0))
X ) {
X#ifdef USE_SYSLOG
X syslog (LOG_WARN, BAD_TIME, name, tty);
X#endif
X failed = 1;
X }
X if (! failed && pwent.pw_name && pwent.pw_uid == 0 &&
X ! console (tty)) {
X#ifdef USE_SYSLOG
X syslog (LOG_CRIT, BAD_ROOT_LOGIN, tty);
X#endif
X failed = 1;
X }
X if (getdef_bool("FAILLOG_ENAB") && pwent.pw_name &&
X ! failcheck (pwent.pw_uid, &faillog, failed)) {
X#ifdef USE_SYSLOG
X syslog (LOG_CRIT, FAILURE_CNT, name, tty);
X#endif
X failed = 1;
X }
X if (! failed)
X break;
X
X puts ("Login incorrect");
X#ifdef RLOGIN
X if (rflg || fflg) {
X#ifdef USE_SYSLOG
X closelog ();
X#endif
X exit (1);
X }
X#endif /*RLOGIN*/
X
X /* don't log non-existent users */
X if (getdef_bool("FAILLOG_ENAB") && pwent.pw_name)
X failure (pwent.pw_uid, tty, &faillog);
X if (getdef_str("FTMP_FILE") != NULL) {
X failent = utent;
X
X if (pwent.pw_name)
X STRFCPY (failent.ut_name, pwent.pw_name);
X else
X if (getdef_bool("LOG_UNKFAIL_ENAB"))
X STRFCPY (failent.ut_name, name);
X else
X STRFCPY (failent.ut_name, "UNKNOWN");
X time (&failent.ut_time);
X failent.ut_type = USER_PROCESS;
X
X failtmp (&failent);
X }
X
X if (--retries <= 0) { /* only allow so many failures */
X#ifdef USE_SYSLOG
X closelog ();
X#endif
X exit (1);
X }
X bzero (name, sizeof name);
X bzero (pass, sizeof pass);
X }
X (void) alarm (0); /* turn off alarm clock */
X
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 fname = getdef_str("NOLOGINS_FILE");
X if (pwent.pw_uid != 0 && fname != NULL && access (fname, 0) == 0) {
X FILE *nlfp;
X int c;
X
X if (nlfp = fopen (fname, "r")) {
X while ((c = getc (nlfp)) != EOF) {
X if (c == '\n')
X putchar ('\r');
X
X putchar (c);
X }
X fflush (stdout);
X fclose (nlfp);
X } else
X printf ("\r\nSystem closed for routine maintenance\n");
X
X#ifdef USE_SYSLOG
X closelog ();
X#endif
X exit (0);
X }
X
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 if (getdef_bool("LASTLOG_ENAB"))
X log (); /* give last login and log this one */
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 if (! hushed (&pwent)) {
X motd (); /* print the message of the day */
X if (getdef_bool ("FAILLOG_ENAB") && faillog.fail_cnt != 0)
X failprint (&faillog);
X if (getdef_bool ("LASTLOG_ENAB") && lastlog.ll_time != 0)
X printf ("Last login: %.19s on %s\n",
X ctime (&lastlog.ll_time), lastlog.ll_line);
X#ifdef AGING
X agecheck (&pwent, spwd);
X#endif /* AGING */
X mailcheck (); /* report on the status of mail */
X }
X if (getdef_str("TTYTYPE_FILE") != NULL && getenv("TERM") == NULL)
X ttytype (tty);
X
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#ifdef SHADOWGRP
X endsgent (); /* stop access to shadow group file */
X#endif
X#ifdef USE_SYSLOG
X if (pwent.pw_uid == 0)
X syslog (LOG_INFO, ROOT_LOGIN, tty);
X
X closelog ();
X#endif
X shell (pwent.pw_shell, (char *) 0); /* exec the shell finally. */
X /*NOTREACHED*/
X}
END_OF_FILE
if test 14004 -ne `wc -c <'lmain.c'`; then
echo shar: \"'lmain.c'\" unpacked with wrong size!
fi
# end of 'lmain.c'
fi
if test -f 'useradd.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'useradd.c'\"
else
echo shar: Extracting \"'useradd.c'\" \(25685 characters\)
sed "s/^X//" >'useradd.c' <<'END_OF_FILE'
X/*
X * Copyright 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#ifndef lint
Xstatic char sccsid[] = "@(#)useradd.c 3.6 14:38:26 10/27/91";
X#endif
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <stdio.h>
X#include <errno.h>
X#include "pwd.h"
X#include <grp.h>
X#include <ctype.h>
X#include <fcntl.h>
X#include <time.h>
X
X#ifdef BSD
X#include <strings.h>
X#else
X#include <string.h>
X#endif
X
X#include "config.h"
X#include "shadow.h"
X
X#ifdef USE_SYSLOG
X#include <syslog.h>
X
X#ifndef LOG_WARN
X#define LOG_WARN LOG_WARNING
X#endif
X#endif
X
Xgid_t def_group;
Xchar def_home[BUFSIZ];
Xchar def_shell[BUFSIZ];
Xchar def_template[BUFSIZ] = "/etc/skel";
Xlong def_inactive;
Xlong def_expire;
Xchar def_file[] = "/etc/default/useradd";
X
X#ifndef NGROUPS_MAX
X#define NGROUPS_MAX 64
X#endif
X
X#define VALID(s) (strcspn (s, ":\n") == strlen (s))
X
Xchar user_name[BUFSIZ];
Xuid_t user_id;
Xgid_t user_gid;
Xchar user_comment[BUFSIZ];
Xchar user_home[BUFSIZ];
Xchar user_shell[BUFSIZ];
Xlong user_expire;
Xint user_ngroups;
Xgid_t user_groups[NGROUPS_MAX];
X
Xchar *Prog;
X
Xint uflg; /* specify user ID for new account */
Xint oflg; /* permit non-unique user ID to be specified with -u */
Xint gflg; /* primary group ID for new account */
Xint Gflg; /* secondary group set for new account */
Xint dflg; /* home directory for new account */
Xint bflg; /* new default root of home directory */
Xint sflg; /* shell program for new account */
Xint cflg; /* comment (GECOS) field for new account */
Xint mflg; /* create user's home directory if it doesn't exist */
Xint kflg; /* specify a directory to fill new user directory */
Xint fflg; /* days until account with expired password is locked */
Xint eflg; /* days after password changed before it becomes expired */
Xint Dflg; /* set/show new user default values */
X
X#ifdef NDBM
Xextern int pw_dbm_mode;
Xextern int sp_dbm_mode;
Xextern int gr_dbm_mode;
X#ifdef SHADOWGRP
Xextern int sg_dbm_mode;
X#endif
X#endif
Xextern FILE *fopen();
Xextern int fclose();
Xextern char *malloc();
Xextern char *mktemp();
X
Xextern struct group *getgrnam();
Xextern struct group *getgrgid();
Xextern struct group *gr_next();
Xextern struct group *gr_locate();
Xextern int gr_lock();
Xextern int gr_unlock();
Xextern int gr_rewind();
Xextern int gr_open();
X
X#ifdef SHADOWGRP
Xextern struct sgrp *sgr_next();
Xextern int sgr_lock();
Xextern int sgr_unlock();
Xextern int sgr_rewind();
Xextern int sgr_open();
X#endif
X
Xextern struct passwd *getpwnam();
Xextern struct passwd *pw_next();
Xextern int pw_lock();
Xextern int pw_unlock();
Xextern int pw_rewind();
Xextern int pw_open();
X
Xextern int spw_lock();
Xextern int spw_unlock();
Xextern int spw_open();
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#ifdef NEED_RENAME
X/*
X * rename - rename a file to another name
X *
X * rename is provided for systems which do not include the rename()
X * system call.
X */
X
Xint
Xrename (begin, end)
Xchar *begin;
Xchar *end;
X{
X struct stat s1, s2;
X extern int errno;
X int orig_err = errno;
X
X if (stat (begin, &s1))
X return -1;
X
X if (stat (end, &s2)) {
X errno = orig_err;
X } else {
X
X /*
X * See if this is a cross-device link. We do this to
X * insure that the link below has a chance of working.
X */
X
X if (s1.st_dev != s2.st_dev) {
X errno = EXDEV;
X return -1;
X }
X
X /*
X * See if we can unlink the existing destination
X * file. If the unlink works the directory is writable,
X * so there is no need here to figure that out.
X */
X
X if (unlink (end))
X return -1;
X }
X
X /*
X * Now just link the original name to the final name. If there
X * was no file previously, this link will fail if the target
X * directory isn't writable. The unlink will fail if the source
X * directory isn't writable, but life stinks ...
X */
X
X if (link (begin, end) || unlink (begin))
X return -1;
X
X return 0;
X}
X#endif
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.
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 = (long) ((year - 1970) * 365L) + (((year + 1) - 1970) / 4);
X total += (long) juldays[month] + (month > 2 && (year % 4) == 0 ? 1:0);
X total += (long) day - 1;
X
X return total;
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 char **tmp;
X
X /*
X * Scan the list for the new name. Return the original list
X * pointer if it is present.
X */
X
X for (i = 0;list[i] != (char *) 0;i++)
X if (strcmp (list[i], member) == 0)
X return list;
X
X /*
X * Allocate a new list pointer large enough to hold all the
X * old entries, and the new entries as well.
X */
X
X if (! (tmp = (char **) malloc ((i + 2) * sizeof member)))
X return 0;
X
X /*
X * Copy the original list to the new list, then append the
X * new member and NULL terminate the result. This new list
X * is returned to the invoker.
X */
X
X for (i = 0;list[i] != (char *) 0;i++)
X tmp[i] = list[i];
X
X tmp[i++] = strdup (member);
X tmp[i] = (char *) 0;
X
X return tmp;
X}
X
X/*
X * get_defaults - read the defaults file
X *
X * get_defaults() reads the defaults file for this command. It sets
X * the various values from the file, or uses built-in default values
X * if the file does not exist.
X */
X
Xvoid
Xget_defaults ()
X{
X FILE *fp;
X char buf[BUFSIZ];
X char *cp;
X struct group *grp;
X
X /*
X * Open the defaults file for reading.
X */
X
X if (! (fp = fopen (def_file, "r"))) {
X
X /*
X * No defaults file - set up the defaults that are given
X * in the documentation.
X */
X
X def_group = 1;
X strcpy (def_home, "/home");
X def_inactive = 0;
X def_expire = 0;
X return;
X }
X
X /*
X * Read the file a line at a time. Only the lines that have
X * relevant values are used, everything else can be ignored.
X */
X
X while (fgets (buf, BUFSIZ, fp)) {
X if (cp = strrchr (buf, '\n'))
X *cp = '\0';
X
X /*
X * Primary GROUP identifier
X */
X
X if (strncmp ("GROUP=", buf, 6) == 0) {
X cp = buf + 6;
X if (isdigit (*cp))
X def_group = atoi (cp);
X else if (grp = getgrnam (cp))
X def_group = grp->gr_gid;
X else
X fprintf (stderr, "%s: unknown group %s\n",
X Prog, cp);
X }
X
X /*
X * Default HOME filesystem
X */
X
X else if (strncmp ("HOME=", buf, 5) == 0) {
X strncpy (def_home, buf + 5, BUFSIZ);
X }
X
X /*
X * Default Login Shell command
X */
X
X else if (strncmp ("SHELL=", buf, 6) == 0) {
X strncpy (def_shell, buf + 6, BUFSIZ);
X }
X
X /*
X * Default Password Inactive value
X */
X
X else if (strncmp ("INACTIVE=", buf, 9) == 0) {
X def_inactive = atoi (buf + 9);
X }
X
X /*
X * Default Password Expiration value
X */
X
X else if (strncmp ("EXPIRE=", buf, 7) == 0) {
X def_expire = atoi (buf + 7);
X }
X }
X}
X
X/*
X * show_defaults - show the contents of the defaults file
X *
X * show_defaults() displays the values that are used from the default
X * file and the built-in values.
X */
X
Xvoid
Xshow_defaults ()
X{
X printf ("GROUP=%d\n", def_group);
X printf ("HOME=%s\n", def_home);
X printf ("INACTIVE=%d\n", def_inactive);
X printf ("EXPIRE=%d\n", def_expire);
X}
X
X/*
X * set_defaults - write new defaults file
X *
X * set_defaults() re-writes the defaults file using the values that
X * are currently set. Duplicated lines are pruned, missing lines are
X * added, and unrecognized lines are copied as is.
X */
X
Xint
Xset_defaults ()
X{
X FILE *ifp;
X FILE *ofp;
X char buf[BUFSIZ];
X static char new_file[] = "/etc/default/nuaddXXXXXX";
X char *cp;
X int out_group = 0;
X int out_home = 0;
X int out_inactive = 0;
X int out_expire = 0;
X
X /*
X * Create a temporary file to copy the new output to.
X */
X
X mktemp (new_file);
X if (! (ofp = fopen (new_file, "w"))) {
X fprintf (stderr, "%s: cannot create new defaults file\n", Prog);
X return -1;
X }
X
X /*
X * Open the existing defaults file and copy the lines to the
X * temporary files, using any new values. Each line is checked
X * to insure that it is not output more than once.
X */
X
X if (ifp = fopen (def_file, "r")) {
X while (fgets (buf, BUFSIZ, ifp)) {
X if (cp = strrchr (buf, '\n'))
X *cp = '\0';
X
X if (strncmp ("GROUP=", buf, 6) == 0) {
X if (! out_group)
X fprintf (ofp, "GROUP=%d\n", def_group);
X
X out_group++;
X } else if (strncmp ("HOME=", buf, 5) == 0) {
X if (! out_home)
X fprintf (ofp, "HOME=%s\n", def_home);
X
X out_home++;
X } else if (strncmp ("INACTIVE=", buf, 9) == 0) {
X if (! out_inactive)
X fprintf (ofp, "INACTIVE=%d\n",
X def_inactive);
X
X out_inactive++;
X } else if (strncmp ("EXPIRE=", buf, 7) == 0) {
X if (! out_expire)
X fprintf (ofp, "EXPIRE=%d\n",
X def_expire);
X
X out_expire++;
X } else
X fprintf (ofp, "%s\n", buf);
X }
X fclose ((FILE *) ifp);
X }
X
X /*
X * Check each line to insure that every line was output. This
X * causes new values to be added to a file which did not previously
X * have an entry for that value.
X */
X
X if (! out_group)
X fprintf (ofp, "GROUP=%d\n", def_group);
X
X if (! out_home)
X fprintf (ofp, "HOME=%s\n", def_home);
X
X if (! out_inactive)
X fprintf (ofp, "INACTIVE=%d\n", def_inactive);
X
X if (! out_expire)
X fprintf (ofp, "EXPIRE=%d\n", def_expire);
X
X /*
X * Flush and close the file. Check for errors to make certain
X * the new file is intact.
X */
X
X (void) fflush (ofp);
X if (ferror (ofp) || fclose ((FILE *) ofp)) {
X unlink (new_file);
X return -1;
X }
X
X /*
X * Rename the current default file to its backup name.
X */
X
X sprintf (buf, "%s-", def_file);
X if (rename (def_file, buf) && errno != ENOENT) {
X sprintf (buf, "%s: rename: %s", Prog, def_file);
X perror (buf);
X unlink (new_file);
X return -1;
X }
X
X /*
X * Rename the new default file to its correct name.
X */
X
X if (rename (new_file, def_file)) {
X sprintf (buf, "%s: rename: %s", Prog, new_file);
X perror (buf);
X return -1;
X }
X#ifdef USE_SYSLOG
X syslog (LOG_INFO, "defaults: group=%d, home=%s, inactive=%d, expire=%d",
X def_group, def_home, def_inactive, def_expire);
X#endif
X return 0;
X}
X
X/*
X * get_groups - convert a list of group names to an array of group IDs
X *
X * get_groups() takes a comma-separated list of group names and
X * converts it to an array of group ID values. Any unknown group
X * names are reported as errors.
X */
X
Xint
Xget_groups (list)
Xchar *list;
X{
X char *cp;
X struct group *grp;
X int errors = 0;
X
X /*
X * Initialize the list to be empty
X */
X
X user_ngroups = 0;
X
X if (! *list)
X return 0;
X
X /*
X * So long as there is some data to be converted, strip off
X * each name and look it up. A mix of numerical and string
X * values for group identifiers is permitted.
X */
X
X do {
X /*
X * Strip off a single name from the list
X */
X
X if (cp = strchr (list, ','))
X *cp++ = '\0';
X
X /*
X * Names starting with digits are treated as numerical
X * GID values, otherwise the string is looked up as is.
X */
X
X if (isdigit (*list))
X grp = getgrgid (atoi (list));
X else
X grp = getgrnam (list);
X
X /*
X * There must be a match, either by GID value or by
X * string name.
X */
X
X if (! grp) {
X fprintf (stderr, "%s: unknown group %s\n", Prog, list);
X errors++;
X }
X
X /*
X * Add the GID value from the group file to the user's
X * list of groups.
X */
X
X user_groups[user_ngroups++] = grp->gr_gid;
X
X list = cp;
X } while (list);
X
X /*
X * Any errors in finding group names are fatal
X */
X
X if (errors)
X return -1;
X
X return 0;
X}
X
X/*
X * usage - display usage message and exit
X */
X
Xusage ()
X{
X fprintf (stderr,
X "usage:\t%s [-u uid [-o]] [-g group] [-G group,...] \n", Prog);
X fprintf (stderr,
X "\t\t[-d home] [-s shell] [-c comment] [-m [-k template]]\n");
X fprintf (stderr,
X "\t\t[-f inactive] [-e expire] name\n");
X
X fprintf (stderr,
X "\t%s -D [-g group] [-b base] [-f inactive] [-e expire]\n",
X Prog);
X
X exit (1);
X}
X
X/*
X * new_pwent - initialize the values in a password file entry
X *
X * new_pwent() takes all of the values that have been entered and
X * fills in a (struct passwd) with them.
X */
X
Xvoid
Xnew_pwent (pwent)
Xstruct passwd *pwent;
X{
X memset (pwent, 0, sizeof *pwent);
X pwent->pw_name = user_name;
X pwent->pw_passwd = "*";
X pwent->pw_age = "";
X pwent->pw_uid = user_id;
X pwent->pw_gid = user_gid;
X pwent->pw_gecos = user_comment;
X pwent->pw_comment = "";
X pwent->pw_dir = user_home;
X pwent->pw_shell = user_shell;
X}
X
X/*
X * new_spent - initialize the values in a shadow password file entry
X *
X * new_spent() takes all of the values that have been entered and
X * fills in a (struct spwd) with them.
X */
X
Xvoid
Xnew_spent (spent)
Xstruct spwd *spent;
X{
X memset (spent, 0, sizeof *spent);
X spent->sp_namp = user_name;
X spent->sp_pwdp = "!";
X spent->sp_lstchg = 0;
X spent->sp_min = 0;
X spent->sp_max = def_expire;
X spent->sp_warn = 0;
X spent->sp_inact = def_inactive;
X spent->sp_expire = user_expire;
X}
X
X/*
X * grp_update - add user to secondary group set
X *
X * grp_update() takes the secondary group set given in user_groups
X * and adds the user to each group given by that set.
X */
X
Xvoid
Xgrp_update ()
X{
X int i;
X struct group *grp;
X struct sgrp *sgrp;
X
X /*
X * Lock and open the group file. This will load all of the group
X * entries.
X */
X
X if (! gr_lock ()) {
X fprintf (stderr, "%s: error locking group file\n", Prog);
X exit (1);
X }
X if (! gr_open (O_RDWR)) {
X fprintf (stderr, "%s: error opening group file\n", Prog);
X exit (1);
X }
X#ifdef SHADOWGRP
X if (! sgr_lock ()) {
X fprintf (stderr, "%s: error locking shadow group file\n", Prog);
X exit (1);
X }
X if (! sgr_open (O_RDWR)) {
X fprintf (stderr, "%s: error opening shadow group file\n", Prog);
X exit (1);
X }
X#endif
X
X /*
X * Scan through the entire group file looking for the groups that
X * the user is a member of.
X */
X
X for (gr_rewind (), grp = gr_next ();grp;grp = gr_next ()) {
X
X /*
X * See if the user specified this group as one of their
X * concurrent groups.
X */
X
X for (i = 0;i < user_ngroups;i++)
X if (grp->gr_gid == user_groups[i])
X break;
X
X if (i == user_ngroups)
X continue;
X
X /*
X * Add the username to the list of group members and
X * update the group entry to reflect the change.
X */
X
X grp->gr_mem = add_list (grp->gr_mem, user_name);
X if (! gr_update (grp)) {
X fprintf (stderr, "%s: error adding new group entry\n",
X Prog);
X exit (1);
X }
X#ifdef NDBM
X /*
X * Update the DBM group file with the new entry as well.
X */
X
X if (! gr_dbm_update (grp)) {
X fprintf (stderr, "%s: cannot add new dbm group entry\n",
X Prog);
X exit (1);
X }
X endgrent ();
X#endif
X#ifdef USE_SYSLOG
X syslog (LOG_INFO, "add `%s' to group `%s'\n",
X user_name, grp->gr_name);
X#endif
X }
X
X#ifdef SHADOWGRP
X /*
X * Scan through the entire shadow group file looking for the groups
X * that the user is a member of. The administrative list isn't
X * modified.
X */
X
X for (sgr_rewind (), sgrp = sgr_next ();sgrp;sgrp = sgr_next ()) {
X
X /*
X * See if the user specified this group as one of their
X * concurrent groups.
X */
X
X for (i = 0;i < user_ngroups;i++) {
X if (! (grp = gr_locate (sgrp->sg_name)))
X continue;
X
X if (grp->gr_gid == user_groups[i])
X break;
X }
X if (i == user_ngroups)
X continue;
X
X /*
X * Add the username to the list of group members and
X * update the group entry to reflect the change.
X */
X
X sgrp->sg_mem = add_list (sgrp->sg_mem, user_name);
X if (! sgr_update (sgrp)) {
X fprintf (stderr, "%s: error adding new group entry\n",
X Prog);
X exit (1);
X }
X#ifdef NDBM
X /*
X * Update the DBM group file with the new entry as well.
X */
X
X if (! sgr_dbm_update (sgrp)) {
X fprintf (stderr, "%s: cannot add new dbm group entry\n",
X Prog);
X exit (1);
X }
X endsgent ();
X#endif
X#ifdef USE_SYSLOG
X syslog (LOG_INFO, "add `%s' to shadow group `%s'\n",
X user_name, sgrp->sg_name);
X#endif
X }
X#endif
X}
X
X/*
X * find_new_uid - find the next available UID
X *
X * find_new_uid() locates the next highest unused UID in the password
X * file, or checks the given user ID against the existing ones for
X * uniqueness.
X */
X
Xint
Xfind_new_uid ()
X{
X struct passwd *pwd;
X
X /*
X * Start with some UID value if the user didn't provide us with
X * one already.
X */
X
X if (! uflg)
X user_id = 100;
X
X /*
X * Search the entire password file, either looking for this
X * UID (if the user specified one with -u) or looking for the
X * largest unused value.
X */
X
X for (pw_rewind (), pwd = pw_next ();pwd;pwd = pw_next ()) {
X if (strcmp (user_name, pwd->pw_name) == 0) {
X fprintf (stderr, "%s: name %s is not unique\n",
X Prog, user_name);
X exit (1);
X }
X if (uflg && user_id == pwd->pw_uid) {
X fprintf (stderr, "%s: uid %d is not unique\n",
X Prog, user_id);
X exit (1);
X }
X if (! uflg && pwd->pw_uid >= user_id)
X user_id = pwd->pw_uid + 1;
X }
X}
X
X/*
X * process_flags - perform command line argument setting
X *
X * process_flags() interprets the command line arguments and sets
X * the values that the user will be created with accordingly. The
X * values are checked for sanity.
X */
X
Xvoid
Xprocess_flags (argc, argv)
Xint argc;
Xchar **argv;
X{
X extern int optind;
X extern char *optarg;
X struct group *grp;
X int anyflag = 0;
X int arg;
X
X while ((arg = getopt (argc, argv, "Du:og:G:d:s:c:mk:f:e:b:")) != EOF) {
X switch (arg) {
X case 'b':
X if (! VALID (optarg)) {
X fprintf (stderr,
X "%s: invalid field `%s'\n",
X Prog, optarg);
X exit (3);
X }
X bflg++;
X if (! Dflg)
X usage ();
X
X strncpy (def_home, optarg, BUFSIZ);
X break;
X case 'c':
X if (! VALID (optarg)) {
X fprintf (stderr,
X "%s: invalid field `%s'\n",
X Prog, optarg);
X exit (3);
X }
X cflg++;
X strncpy (user_comment, optarg, BUFSIZ);
X break;
X case 'd':
X if (! VALID (optarg)) {
X fprintf (stderr,
X "%s: invalid field `%s'\n",
X Prog, optarg);
X exit (3);
X }
X dflg++;
X strncpy (user_home, optarg, BUFSIZ);
X break;
X case 'D':
X if (anyflag)
X usage ();
X
X Dflg++;
X break;
X case 'e':
X eflg++;
X if (Dflg)
X def_expire = atoi (optarg);
X else {
X user_expire = strtoday (optarg);
X#ifdef ITI_AGING
X user_expire *= DAY;
X#endif
X }
X break;
X case 'f':
X fflg++;
X def_inactive = atoi (optarg);
X break;
X case 'g':
X gflg++;
X if (isdigit (optarg[0]))
X grp = getgrgid (atoi (optarg));
X else
X grp = getgrnam (optarg);
X
X if (! grp) {
X fprintf (stderr,
X "%s: unknown group %s\n",
X Prog, optarg);
X exit (1);
X }
X user_gid = grp->gr_gid;
X break;
X case 'G':
X Gflg++;
X if (get_groups (optarg))
X exit (1);
X
X break;
X case 'k':
X if (! mflg)
X usage ();
X
X strncpy (def_template, optarg, BUFSIZ);
X kflg++;
X break;
X case 'm':
X mflg++;
X break;
X case 'o':
X if (! uflg)
X usage ();
X
X oflg++;
X break;
X case 's':
X if (! VALID (optarg)) {
X fprintf (stderr,
X "%s: invalid field `%s'\n",
X Prog, optarg);
X exit (3);
X }
X sflg++;
X strncpy (user_shell, optarg, BUFSIZ);
X break;
X case 'u':
X uflg++;
X user_id = atoi (optarg);
X break;
X default:
X usage ();
X }
X anyflag++;
X }
X if (! Dflg && optind == argc - 1)
X strcpy (user_name, argv[argc - 1]);
X
X if (! dflg)
X sprintf (user_home, "%s/%s", def_home, user_name);
X
X if (! gflg)
X user_gid = def_group;
X
X if (Dflg) {
X if (optind != argc)
X usage ();
X
X if (uflg || oflg || Gflg || dflg ||
X sflg || cflg || mflg || kflg)
X usage ();
X } else {
X if (optind != argc - 1)
X usage ();
X }
X}
X
X/*
X * close_files - close all of the files that were opened
X *
X * close_files() closes all of the files that were opened for this
X * new user. This causes any modified entries to be written out.
X */
X
Xclose_files ()
X{
X if (! pw_close ()) {
X fprintf (stderr, "%s: cannot rewrite password file\n", Prog);
X exit (1);
X }
X if (! spw_close ()) {
X fprintf (stderr, "%s: cannot rewrite shadow password file\n",
X Prog);
X exit (1);
X }
X if (user_ngroups > 0) {
X if (! gr_close ()) {
X fprintf (stderr, "%s: cannot rewrite group file\n",
X Prog);
X exit (1);
X }
X (void) gr_unlock ();
X#ifdef SHADOWGRP
X if (! sgr_close ()) {
X fprintf (stderr, "%s: cannot rewrite shadow group file\n",
X Prog);
X exit (1);
X }
X (void) sgr_unlock ();
X#endif
X }
X (void) spw_unlock ();
X (void) pw_unlock ();
X}
X
X/*
X * open_files - lock and open the password files
X *
X * open_files() opens the two password files.
X */
X
Xopen_files ()
X{
X if (! pw_lock ()) {
X fprintf (stderr, "%s: unable to lock password file\n", Prog);
X exit (1);
X }
X if (! pw_open (O_RDWR)) {
X fprintf (stderr, "%s: unable to open password file\n", Prog);
X exit (1);
X }
X if (! spw_lock ()) {
X fprintf (stderr, "%s: cannot lock shadow password file\n", Prog);
X exit (1);
X }
X if (! spw_open (O_RDWR)) {
X fprintf (stderr, "%s: cannot open shadow password file\n", Prog);
X exit (1);
X }
X}
X
X/*
X * usr_update - create the user entries
X *
X * usr_update() creates the password file entries for this user
X * and will update the group entries if required.
X */
X
Xusr_update ()
X{
X struct passwd pwent;
X struct spwd spent;
X
X if (! oflg)
X find_new_uid ();
X
X new_pwent (&pwent);
X if (! pw_update (&pwent)) {
X fprintf (stderr, "%s: error adding new password entry\n", Prog);
X exit (1);
X }
X new_spent (&spent);
X if (! spw_update (&spent)) {
X fprintf (stderr, "%s: error adding new shadow password entry\n",
X Prog);
X exit (1);
X }
X#if defined(DBM) || defined(NDBM)
X if (access ("/etc/passwd.pag", 0) == 0 && ! pw_dbm_update (&pwent)) {
X fprintf (stderr, "%s: error updating password dbm entry\n",
X Prog);
X exit (1);
X }
X endpwent ();
X#endif
X#ifdef NDBM
X if (access ("/etc/shadow.pag", 0) == 0 && ! sp_dbm_update (&spent)) {
X fprintf (stderr, "%s: error updating shadow passwd dbm entry\n",
X Prog);
X exit (1);
X }
X endspent ();
X#endif
X#ifdef USE_SYSLOG
X syslog (LOG_INFO,
X "new user: name=%s, uid=%d, gid=%d, home=%s, shell=%s\n",
X user_name, user_id, user_gid, user_home, user_shell);
X#endif
X if (user_ngroups > 0)
X grp_update ();
X}
X
X/*
X * create_home - create the user's home directory
X *
X * create_home() creates the user's home directory if it does not
X * already exist. It will be created mode 755 owned by the user
X * with the user's default group.
X */
X
Xcreate_home ()
X{
X if (access (user_home, 0)) {
X if (mkdir (user_home, 0755)) {
X fprintf (stderr, "%s: cannot create directory %s\n",
X Prog, user_home);
X exit (1);
X }
X chown (user_home, user_id, user_gid);
X chmod (user_home, 0755);
X }
X}
X
X/*
X * main - useradd command
X */
X
Xmain (argc, argv)
Xint argc;
Xchar **argv;
X{
X /*
X * Get my name so that I can use it to report errors.
X */
X
X if (Prog = strrchr (argv[0], '/'))
X Prog++;
X else
X Prog = argv[0];
X
X#ifdef USE_SYSLOG
X openlog (Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
X#endif
X
X /*
X * The open routines for the DBM files don't use read-write
X * as the mode, so we have to clue them in.
X */
X
X#if defined(DBM) || defined(NDBM)
X pw_dbm_mode = O_RDWR;
X#endif
X#ifdef NDBM
X sp_dbm_mode = O_RDWR;
X gr_dbm_mode = O_RDWR;
X#ifdef SHADOWGRP
X sg_dbm_mode = O_RDWR;
X#endif
X#endif
X get_defaults ();
X
X process_flags (argc, argv);
X
X /*
X * See if we are messing with the defaults file, or creating
X * a new user.
X */
X
X if (Dflg) {
X if (gflg || bflg || fflg || eflg)
X exit (set_defaults () ? 1:0);
X
X show_defaults ();
X exit (0);
X }
X
X /*
X * Start with a quick check to see if the user exists.
X */
X
X if (getpwnam (user_name)) {
X fprintf (stderr, "%s: user %s exists\n", Prog, user_name);
X exit (1);
X }
X
X /*
X * Do the hard stuff - open the files, create the user entries,
X * create the home directory, then close and update the files.
X */
X
X open_files ();
X
X usr_update ();
X
X if (mflg) {
X create_home ();
X copy_tree (def_template, user_home, user_id, user_gid);
X }
X close_files ();
X exit (0);
X /*NOTREACHED*/
X}
END_OF_FILE
if test 25685 -ne `wc -c <'useradd.c'`; then
echo shar: \"'useradd.c'\" unpacked with wrong size!
fi
# end of 'useradd.c'
fi
echo shar: End of archive 1 \(of 11\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 11 archives.
rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
echo You still must unpack the following archives:
echo " " ${MISSING}
fi
exit 0
exit 0 # Just in case...