home *** CD-ROM | disk | FTP | other *** search
Text File | 1991-11-24 | 54.4 KB | 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...
-