home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
misc
/
volume26
/
shadow
/
part02
< prev
next >
Wrap
Text File
|
1991-11-24
|
56KB
|
2,444 lines
Newsgroups: comp.sources.misc
From: jfh@rpp386.Cactus.ORG (John F Haugh II)
Subject: v26i055: shadow - Shadow Password Suite, Part02/11
Message-ID: <1991Nov24.184939.20009@sparky.imd.sterling.com>
X-Md4-Signature: 6188fc99729d72bc4b581316b96e90f5
Date: Sun, 24 Nov 1991 18:49:39 GMT
Approved: kent@sparky.imd.sterling.com
Submitted-by: jfh@rpp386.Cactus.ORG (John F Haugh II)
Posting-number: Volume 26, Issue 55
Archive-name: shadow/part02
Environment: UNIX
Supersedes: shadow-2: Volume 06, Issue 22-24
#! /bin/sh
# 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: groupadd.c passwd.c usermod.c
# Wrapped by kent@sparky on Sun Nov 24 11:03:41 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 2 (of 11)."'
if test -f 'groupadd.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'groupadd.c'\"
else
echo shar: Extracting \"'groupadd.c'\" \(7395 characters\)
sed "s/^X//" >'groupadd.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[] = "@(#)groupadd.c 3.3 08:43:44 9/12/91";
X#endif
X
X#include <sys/types.h>
X#include <stdio.h>
X#include <grp.h>
X#include <ctype.h>
X#include <fcntl.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#endif
X
Xchar group_name[BUFSIZ];
Xint group_id;
X
Xchar *Prog;
X
Xint oflg; /* permit non-unique group ID to be specified with -g */
Xint gflg; /* ID value for the new group */
X
X#ifdef NDBM
Xextern int gr_dbm_mode;
Xextern int sg_dbm_mode;
X#endif
Xextern char *malloc();
X
Xextern struct group *getgrnam();
Xextern struct group *gr_next();
Xextern int gr_lock();
Xextern int gr_unlock();
Xextern int gr_rewind();
Xextern int gr_open();
X
X#ifdef SHADOWGRP
Xextern int sgr_lock();
Xextern int sgr_unlock();
Xextern int sgr_open();
X#endif
X
X/*
X * usage - display usage message and exit
X */
X
Xusage ()
X{
X fprintf (stderr, "usage: groupadd [-g gid [-o]] group\n");
X exit (2);
X}
X
X/*
X * new_grent - initialize the values in a group file entry
X *
X * new_grent() takes all of the values that have been entered and
X * fills in a (struct group) with them.
X */
X
Xvoid
Xnew_grent (grent)
Xstruct group *grent;
X{
X static char *empty_list = 0;
X
X memset (grent, 0, sizeof *grent);
X grent->gr_name = group_name;
X grent->gr_passwd = "*";
X grent->gr_gid = group_id;
X grent->gr_mem = &empty_list;
X}
X
X#ifdef SHADOWGRP
X/*
X * new_sgent - initialize the values in a shadow group file entry
X *
X * new_sgent() takes all of the values that have been entered and
X * fills in a (struct sgrp) with them.
X */
X
Xvoid
Xnew_sgent (sgent)
Xstruct sgrp *sgent;
X{
X static char *empty_list = 0;
X
X memset (sgent, 0, sizeof *sgent);
X sgent->sg_name = group_name;
X sgent->sg_passwd = "!";
X sgent->sg_adm = &empty_list;
X sgent->sg_mem = &empty_list;
X}
X#endif /* SHADOWGRP */
X
X/*
X * grp_update - add new group file entries
X *
X * grp_update() writes the new records to the group files.
X */
X
Xvoid
Xgrp_update ()
X{
X struct group grp;
X#ifdef SHADOWGRP
X struct sgrp sgrp;
X#endif /* SHADOWGRP */
X
X /*
X * Create the initial entries for this new group.
X */
X
X new_grent (&grp);
X#ifdef SHADOWGRP
X new_sgent (&sgrp);
X#endif /* SHADOWGRP */
X
X /*
X * Write out the new group file entry.
X */
X
X if (! gr_update (&grp)) {
X fprintf (stderr, "%s: error adding new group entry\n", Prog);
X exit (1);
X }
X#ifdef NDBM
X
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", Prog);
X exit (1);
X }
X endgrent ();
X#endif /* NDBM */
X
X#ifdef SHADOWGRP
X
X /*
X * Write out the new shadow group entries as well.
X */
X
X if (! sgr_update (&sgrp)) {
X fprintf (stderr, "%s: error adding new group entry\n", Prog);
X exit (1);
X }
X#ifdef NDBM
X
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", Prog);
X exit (1);
X }
X endsgent ();
X#endif /* NDBM */
X#endif /* SHADOWGRP */
X#ifdef USE_SYSLOG
X syslog (LOG_INFO, "new group: name=%s, gid=%d\n",
X group_name, group_id);
X#endif /* USE_SYSLOG */
X}
X
X/*
X * find_new_gid - find the next available GID
X *
X * find_new_gid() locates the next highest unused GID in the group
X * file, or checks the given group ID against the existing ones for
X * uniqueness.
X */
X
Xvoid
Xfind_new_gid ()
X{
X struct group *grp;
X
X /*
X * Start with some GID value if the user didn't provide us with
X * one already.
X */
X
X if (! gflg)
X group_id = 100;
X
X /*
X * Search the entire group file, either looking for this
X * GID (if the user specified one with -g) or looking for the
X * largest unused value.
X */
X
X for (gr_rewind (), grp = gr_next ();grp;grp = gr_next ()) {
X if (strcmp (group_name, grp->gr_name) == 0) {
X fprintf (stderr, "%s: name %s is not unique\n",
X Prog, group_name);
X exit (1);
X }
X if (gflg && group_id == grp->gr_gid) {
X fprintf (stderr, "%s: gid %d is not unique\n",
X Prog, group_id);
X exit (1);
X }
X if (! gflg && grp->gr_gid >= group_id)
X group_id = grp->gr_gid + 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 char *end;
X int arg;
X
X while ((arg = getopt (argc, argv, "og:")) != EOF) {
X switch (arg) {
X case 'g':
X gflg++;
X if (! isdigit (optarg[0]))
X usage ();
X
X group_id = strtol (optarg, &end, 10);
X if (*end != '\0') {
X fprintf (stderr, "%s: invalid group %s\n",
X Prog, optarg);
X exit (3);
X }
X break;
X case 'o':
X if (! gflg)
X usage ();
X
X oflg++;
X break;
X default:
X usage ();
X }
X }
X if (! gflg)
X find_new_gid ();
X
X if (optind == argc - 1)
X strcpy (group_name, argv[argc - 1]);
X else
X usage ();
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 group. This causes any modified entries to be written out.
X */
X
Xclose_files ()
X{
X if (! gr_close ()) {
X fprintf (stderr, "%s: cannot rewrite group file\n", 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 /* SHADOWGRP */
X}
X
X/*
X * open_files - lock and open the group files
X *
X * open_files() opens the two group files.
X */
X
Xopen_files ()
X{
X if (! gr_lock ()) {
X fprintf (stderr, "%s: unable to lock group file\n", Prog);
X exit (1);
X }
X if (! gr_open (O_RDWR)) {
X fprintf (stderr, "%s: unable to open group file\n", Prog);
X exit (1);
X }
X#ifdef SHADOWGRP
X if (! sgr_lock ()) {
X fprintf (stderr, "%s: unable to lock shadow group file\n",
X Prog);
X exit (1);
X }
X if (! sgr_open (O_RDWR)) {
X fprintf (stderr, "%s: unable to open shadow group file\n",
X Prog);
X exit (1);
X }
X#endif /* SHADOWGRP */
X}
X
X/*
X * main - useradd command
X */
X
Xmain (argc, argv)
Xint argc;
Xchar **argv;
X{
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 /* USE_SYSLOG */
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#ifdef NDBM
X gr_dbm_mode = O_RDWR;
X#ifdef SHADOWGRP
X sg_dbm_mode = O_RDWR;
X#endif /* SHADOWGRP */
X#endif /* NDBM */
X process_flags (argc, argv);
X
X /*
X * Start with a quick check to see if the group exists.
X */
X
X if (getgrnam (group_name)) {
X fprintf (stderr, "%s: group %s exists\n", Prog, group_name);
X exit (9);
X }
X
X /*
X * Do the hard stuff - open the files, create the group entries,
X * then close and update the files.
X */
X
X open_files ();
X
X grp_update ();
X
X close_files ();
X exit (0);
X /*NOTREACHED*/
X}
END_OF_FILE
if test 7395 -ne `wc -c <'groupadd.c'`; then
echo shar: \"'groupadd.c'\" unpacked with wrong size!
fi
# end of 'groupadd.c'
fi
if test -f 'passwd.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'passwd.c'\"
else
echo shar: Extracting \"'passwd.c'\" \(18487 characters\)
sed "s/^X//" >'passwd.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 <time.h>
X#include <stdio.h>
X#include <fcntl.h>
X#include <signal.h>
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)passwd.c 3.6 08:43:55 9/12/91";
X#endif
X
X/*
X * Set up some BSD defines so that all the BSD ifdef's are
X * kept right here
X */
X
X#ifndef BSD
X#include <string.h>
X#include <memory.h>
X#define bzero(a,n) memset(a, 0, n)
X#else
X#include <strings.h>
X#define strchr index
X#define strrchr rindex
X#endif
X
X#include "config.h"
X#include "pwd.h"
X#include "lastlog.h"
X#include "shadow.h"
X
X#ifdef USE_SYSLOG
X#include <syslog.h>
X
X#ifndef LOG_WARN
X#define LOG_WARN LOG_WARNING
X#endif
X#endif
X
X/*
X * Password aging constants
X *
X * DAY - seconds in a day
X * WEEK - seconds in a week
X * SCALE - convert from clock to aging units
X */
X
X#define DAY (24L*3600L)
X#define WEEK (7L*DAY)
X
X#ifdef ITI_AGING
X#define SCALE (1)
X#else
X#define SCALE DAY
X#endif
X
X/*
X * Global variables
X */
X
Xchar name[32]; /* The user's name */
Xchar *Prog; /* Program name */
Xint amroot; /* The real UID was 0 */
X
X/*
X * External identifiers
X */
X
Xextern char *getpass();
Xextern char *pw_encrypt();
Xextern char *getlogin();
Xextern int optind; /* Index into argv[] for current option */
Xextern char *optarg; /* Pointer to current option value */
X#ifdef NDBM
Xextern int sp_dbm_mode;
Xextern int pw_dbm_mode;
X#endif
X
X/*
X * #defines for messages. This facilities foreign language conversion
X * since all messages are defined right here.
X */
X
X#define USAGE "usage: %s [ -f | -s ] [ name ]\n"
X#define ADMUSAGE \
X " %s [ -x max ] [ -n min ] [ -w warn ] [ -i inact ] name\n"
X#define ADMUSAGE2 \
X " %s { -l | -d | -S } name\n"
X#define OLDPASS "Old Password:"
X#define NEWPASSMSG \
X"Enter the new password (minimum of 5 characters)\n\
XPlease use a combination of upper and lower case letters and numbers.\n"
X#define CHANGING "Changing password for %s\n"
X#define NEWPASS "New Password:"
X#define NEWPASS2 "Re-enter new password:"
X#define WRONGPWD "Incorrect password for %s.\n"
X#define WRONGPWD2 "incorrect password for `%s'\n"
X#define NOMATCH "They don't match; try again.\n"
X#define CANTCHANGE "The password for %s cannot be changed.\n"
X#define CANTCHANGE2 "password locked for `%s'\n"
X#define TOOSOON "Sorry, the password for %s cannot be changed yet.\n"
X#define TOOSOON2 "now < sp_min for `%s'\n"
X#define EXECFAILED "%s: Cannot execute %s"
X#define EXECFAILED2 "cannot execute %s\n"
X#define WHOAREYOU "%s: Cannot determine you user name.\n"
X#define UNKUSER "%s: Unknown user %s\n"
X#define NOPERM "You may not change the password for %s.\n"
X#define NOPERM2 "can't change pwd for `%s'\n"
X#define UNCHANGED "The password for %s is unchanged.\n"
X#define SPWDBUSY "Cannot lock the password file; try again later.\n"
X#define SPWDBUSY2 "can't lock /etc/shadow\n"
X#define OPNERROR "Cannot open the password file.\n"
X#define OPNERROR2 "can't open /etc/shadow\n"
X#define UPDERROR "Error updating the password entry.\n"
X#define UPDERROR2 "error updating shadow entry\n"
X#define DBMERROR "Error updating the DBM password entry.\n"
X#define DBMERROR2 "error updating DBM shadow entry.\n"
X#define NOTROOT "Cannot change ID to root.\n"
X#define NOTROOT2 "can't setuid(0).\n"
X#define CLSERROR "Cannot commit shadow file changes.\n"
X#define CLSERROR2 "can't rewrite /etc/shadow.\n"
X#define UNLKERROR "Cannot unlock the shadow file.\n"
X#define UNLKERROR2 "can't unlock /etc/shadow.\n"
X#define TRYAGAIN "Try again.\n"
X#define CHGPASSWD "changed password for `%s'\n"
X
X/*
X * usage - print command usage and exit
X */
X
Xvoid
Xusage ()
X{
X fprintf (stderr, USAGE, Prog);
X if (amroot) {
X fprintf (stderr, ADMUSAGE, Prog);
X fprintf (stderr, ADMUSAGE2, Prog);
X }
X exit (1);
X}
X
X/*
X * new_password - validate old password and replace with new
X */
X
X/*ARGSUSED*/
Xint
Xnew_password (pw, sp)
Xstruct passwd *pw;
Xstruct spwd *sp;
X{
X char *clear; /* Pointer to clear text */
X char *cipher; /* Pointer to cipher text */
X char *cp; /* Pointer to getpass() response */
X char orig[BUFSIZ]; /* Original password */
X char pass[BUFSIZ]; /* New password */
X int i; /* Counter for retries */
X
X /*
X * Authenticate the user. The user will be prompted for their
X * own password.
X */
X
X if (! amroot && sp->sp_pwdp[0]) {
X bzero (orig, sizeof orig);
X
X if (! (clear = getpass (OLDPASS)))
X return -1;
X
X cipher = pw_encrypt (clear, sp->sp_pwdp);
X if (strcmp (cipher, sp->sp_pwdp) != 0) {
X sleep (1);
X fprintf (stderr, WRONGPWD, sp->sp_namp);
X#ifdef USE_SYSLOG
X syslog (LOG_WARN, WRONGPWD2, sp->sp_namp);
X#endif
X return -1;
X }
X strcpy (orig, clear);
X bzero (cipher, strlen (cipher));
X bzero (clear, strlen (clear));
X }
X
X /*
X * Get the new password. The user is prompted for the new password
X * and has three tries to get it right. The password will be tested
X * for strength, unless it is the root user. This provides an escape
X * for initial login passwords.
X */
X
X printf (NEWPASSMSG);
X for (i = 0;i < 3;i++) {
X if (! (cp = getpass (NEWPASS))) {
X bzero (orig, sizeof orig);
X return -1;
X } else
X strcpy (pass, cp);
X
X if (! amroot && ! obscure (orig, pass)) {
X printf (TRYAGAIN);
X continue;
X }
X if (! (cp = getpass (NEWPASS2))) {
X bzero (orig, sizeof orig);
X return -1;
X }
X if (strcmp (cp, pass))
X fprintf (stderr, NOMATCH);
X else {
X bzero (cp, strlen (cp));
X break;
X }
X }
X bzero (orig, sizeof orig);
X
X if (i == 3) {
X bzero (pass, sizeof pass);
X return -1;
X }
X
X /*
X * Encrypt the password. The new password is encrypted and
X * the shadow password structure updated to reflect the change.
X */
X
X sp->sp_pwdp = pw_encrypt (pass, (char *) 0);
X sp->sp_lstchg = time ((time_t *) 0) / SCALE;
X bzero (pass, sizeof pass);
X
X return 0;
X}
X
X/*
X * check_password - test a password to see if it can be changed
X *
X * check_password() sees if the invoker has permission to change the
X * password for the given user.
X */
X
X/*ARGSUSED*/
Xvoid
Xcheck_password (pw, sp)
Xstruct passwd *pw;
Xstruct spwd *sp;
X{
X time_t now = time ((time_t *) 0) / SCALE;
X
X /*
X * Root can change any password any time.
X */
X
X if (amroot)
X return;
X
X /*
X * Expired accounts cannot be changed ever. Passwords
X * which are locked may not be changed. Passwords where
X * min > max may not be changed. Passwords which have
X * been inactive too long cannot be changed.
X */
X
X if ((sp->sp_expire > 0 && now >= sp->sp_expire) ||
X (sp->sp_inact >= 0 && sp->sp_max >= 0 &&
X now >= (sp->sp_lstchg + sp->sp_inact + sp->sp_max)) ||
X strcmp (sp->sp_pwdp, "!") == 0 ||
X sp->sp_min > sp->sp_max) {
X fprintf (stderr, CANTCHANGE, sp->sp_namp);
X#ifdef USE_SYSLOG
X syslog (LOG_WARN, CANTCHANGE2, sp->sp_namp);
X closelog ();
X#endif
X exit (1);
X }
X
X /*
X * Passwords may only be changed after sp_min time is up.
X */
X
X if (sp->sp_min >= 0 && now < (sp->sp_lstchg + sp->sp_min)) {
X fprintf (stderr, TOOSOON, sp->sp_namp);
X#ifdef USE_SYSLOG
X syslog (LOG_WARN, TOOSOON2, sp->sp_namp);
X closelog ();
X#endif
X exit (1);
X }
X}
X
X/*
X * pwd_to_spwd - create entries for new spwd structure
X *
X * pwd_to_spwd() creates a new (struct spwd) containing the
X * information in the pointed-to (struct passwd).
X */
X
Xvoid
Xpwd_to_spwd (pw, sp)
Xstruct passwd *pw;
Xstruct spwd *sp;
X{
X time_t t;
X
X /*
X * Nice, easy parts first. The name and passwd map directly
X * from the old password structure to the new one.
X */
X
X sp->sp_namp = strdup (pw->pw_name);
X sp->sp_pwdp = strdup (pw->pw_passwd);
X#ifdef ATT_AGE
X
X /*
X * AT&T-style password aging maps the sp_min, sp_max, and
X * sp_lstchg information from the pw_age field, which appears
X * after the encrypted password.
X */
X
X if (pw->pw_age[0]) {
X t = (c64i (pw->pw_age[0]) * WEEK) / SCALE;
X sp->sp_max = t;
X
X if (pw->pw_age[1]) {
X t = (c64i (pw->pw_age[1]) * WEEK) / SCALE;
X sp->sp_min = t;
X } else
X sp->sp_min = (10000L * DAY) / SCALE;
X
X if (pw->pw_age[1] && pw->pw_age[2]) {
X t = (a64l (pw->pw_age + 2) * WEEK) / SCALE;
X sp->sp_lstchg = t;
X } else
X sp->sp_lstchg = time ((time_t *) 0) / SCALE;
X } else {
X sp->sp_min = 0;
X sp->sp_max = (10000L * DAY) / SCALE;
X sp->sp_lstchg = time ((time_t *) 0) / SCALE;
X }
X#else
X /*
X * BSD does not use the pw_age field and has no aging information
X * anywheres. The default values are used to initialize the
X * fields which are in the missing pw_age field;
X */
X
X sp->sp_min = 0;
X sp->sp_max = (10000L * DAY) / SCALE;
X sp->sp_lstchg = time ((time_t *) 0) / SCALE;
X#endif
X
X /*
X * These fields have no corresponding information in the password
X * file. They are set to uninitialized values.
X */
X
X sp->sp_warn = -1;
X sp->sp_inact = -1;
X sp->sp_expire = -1;
X sp->sp_flag = -1;
X}
X
X/*
X * print_status - print current password status
X */
X
X/*ARGSUSED*/
Xvoid
Xprint_status (pw, sp)
Xstruct passwd *pw;
Xstruct spwd *sp;
X{
X struct tm *tm;
X time_t last_time;
X
X last_time = sp->sp_lstchg * SCALE;
X tm = gmtime (&last_time);
X
X printf ("%s ", sp->sp_namp);
X printf ("%s ",
X sp->sp_pwdp[0] ? (sp->sp_pwdp[0] == '!' ? "L":"P"):"NP");
X printf ("%02.2d/%02.2d/%02.2d ",
X tm->tm_mon + 1, tm->tm_mday, tm->tm_year % 100);
X printf ("%d %d %d %d\n",
X (sp->sp_min * SCALE) / DAY, (sp->sp_max * SCALE) / DAY,
X (sp->sp_warn * SCALE) / DAY, (sp->sp_inact * SCALE) / DAY);
X}
X
X/*
X * passwd - change a user's password file information
X *
X * This command controls the password file and commands which are
X * used to modify it.
X *
X * The valid options are
X *
X * -l lock the named account (*)
X * -d delete the password for the named account (*)
X * -x # set sp_max to # days (*)
X * -n # set sp_min to # days (*)
X * -w # set sp_warn to # days (*)
X * -i # set sp_inact to # days (*)
X * -S show password status of named account (*)
X * -g execute gpasswd command to interpret flags
X * -f execute chfn command to interpret flags
X * -s execute chsh command to interpret flags
X *
X * (*) requires root permission to execute.
X *
X * All of the time fields are entered in days and converted to the
X * appropriate internal format. For finer resolute the chage
X * command must be used.
X */
X
Xint
Xmain (argc, argv)
Xint argc;
Xchar **argv;
X{
X char buf[BUFSIZ]; /* I/O buffer for messages, etc. */
X char *cp; /* Miscellaneous character pointing */
X time_t min; /* Minimum days before change */
X time_t max; /* Maximum days until change */
X time_t warn; /* Warning days before change */
X time_t inact; /* Days without change before locked */
X int i; /* Loop control variable */
X int flag; /* Current option to process */
X int lflg = 0; /* -l - lock account option */
X int dflg = 0; /* -d - delete password option */
X int xflg = 0; /* -x - set maximum days */
X int nflg = 0; /* -n - set minimum days */
X int wflg = 0; /* -w - set warning days */
X int iflg = 0; /* -i - set inactive days */
X int Sflg = 0; /* -S - show password status */
X struct passwd *pw; /* Password file entry for user */
X struct spwd *sp; /* Shadow file entry for user */
X struct spwd tspwd; /* New shadow file entry if none */
X
X /*
X * The program behaves differently when executed by root
X * than when executed by a normal user.
X */
X
X amroot = getuid () == 0;
X#ifdef NDBM
X sp_dbm_mode = O_RDWR;
X pw_dbm_mode = O_RDWR;
X#endif
X
X /*
X * Get the program name. The program name is used as a
X * prefix to most error messages. It is also used as input
X * to the openlog() function for error logging.
X */
X
X if (Prog = strrchr (argv[0], '/'))
X Prog++;
X else
X Prog = argv[0];
X
X#ifdef USE_SYSLOG
X openlog (Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
X#endif
X
X /*
X * Start with the flags which cause another command to be
X * executed. The effective UID will be set back to the
X * real UID and the new command executed with the flags
X */
X
X if (argc > 1 && argv[1][0] == '-' && strchr ("gfs", argv[1][1])) {
X setuid (getuid ());
X switch (argv[1][1]) {
X case 'g':
X argv[1] = "gpasswd";
X execv ("/bin/gpasswd", &argv[1]);
X break;
X case 'f':
X argv[1] = "chfn";
X execv ("/bin/chfn", &argv[1]);
X break;
X case 's':
X argv[1] = "chsh";
X execv ("/bin/chsh", &argv[1]);
X break;
X default:
X usage ();
X }
X sprintf (buf, EXECFAILED, Prog, argv[1]);
X perror (buf);
X#ifdef USE_SYSLOG
X syslog (LOG_CRIT, EXECFAILED2, argv[1]);
X closelog ();
X#endif
X exit (1);
X }
X
X /*
X * The remaining arguments will be processed one by one and
X * executed by this command. The name is the last argument
X * if it does not begin with a "-", otherwise the name is
X * determined from the environment and must agree with the
X * real UID. Also, the UID will be checked for any commands
X * which are restricted to root only.
X */
X
X while ((flag = getopt (argc, argv, "ldx:n:w:i:S")) != EOF) {
X switch (flag) {
X case 'x':
X max = strtol (optarg, &cp, 10);
X if (*cp || getuid ())
X usage ();
X
X xflg++;
X break;
X case 'n':
X min = strtol (optarg, &cp, 10);
X if (*cp || getuid ())
X usage ();
X
X nflg++;
X break;
X case 'w':
X warn = strtol (optarg, &cp, 10);
X if (*cp || getuid ())
X usage ();
X
X wflg++;
X break;
X case 'i':
X inact = strtol (optarg, &cp, 10);
X if (*cp || getuid ())
X usage ();
X
X iflg++;
X break;
X case 'S':
X if (getuid ())
X usage ();
X
X Sflg++;
X break;
X case 'd':
X dflg++;
X break;
X case 'l':
X lflg++;
X break;
X default:
X usage ();
X }
X }
X
X /*
X * If any of the flags were given, a user name must be supplied
X * on the command line. Only an unadorned command line doesn't
X * require the user's name be given. Also, on -x, -n, -m, and
X * -i may appear with each other. -d, -l and -S must appear alone.
X */
X
X if ((dflg || lflg || xflg || nflg ||
X wflg || iflg || Sflg) && optind >= argc)
X usage ();
X
X if ((dflg + lflg + (xflg || nflg || wflg || iflg) + Sflg) > 1)
X usage ();
X
X /*
X * Now I have to get the user name. The name will be gotten
X * from the command line if possible. Otherwise it is figured
X * out from the environment.
X */
X
X if (optind < argc) {
X strncpy (name, argv[optind], sizeof name);
X name[sizeof name - 1] = '\0';
X } else if (amroot) {
X strcpy (name, "root");
X } else if (cp = getlogin ()) {
X strncpy (name, cp, sizeof name);
X name[sizeof name - 1] = '\0';
X } else {
X fprintf (stderr, WHOAREYOU, Prog);
X#ifdef USE_SYSLOG
X closelog ();
X#endif
X exit (1);
X }
X
X /*
X * Now I have a name, let's see if the UID for the name
X * matches the current real UID.
X */
X
X if (! (pw = getpwnam (name))) {
X fprintf (stderr, UNKUSER, Prog, name);
X#ifdef USE_SYSLOG
X closelog ();
X#endif
X exit (1);
X }
X if (! amroot && pw->pw_uid != getuid ()) {
X fprintf (stderr, NOPERM, name);
X#ifdef USE_SYSLOG
X syslog (LOG_WARN, NOPERM2, name);
X closelog ();
X#endif
X exit (1);
X }
X
X /*
X * Let the user know whose password is being changed.
X */
X
X if (! Sflg)
X printf (CHANGING, name);
X
X /*
X * The user name is valid, so let's get the shadow file
X * entry.
X */
X
X if (! (sp = getspnam (name)))
X pwd_to_spwd (pw, sp = &tspwd);
X
X /*
X * Save the shadow entry off to the side so it doesn't
X * get changed by any of the following code.
X */
X
X if (sp != &tspwd) {
X tspwd = *sp;
X sp = &tspwd;
X }
X tspwd.sp_namp = strdup (sp->sp_namp);
X tspwd.sp_pwdp = strdup (sp->sp_pwdp);
X
X if (Sflg) {
X print_status (pw, sp);
X#ifdef USE_SYSLOG
X closelog ();
X#endif
X exit (0);
X }
X
X /*
X * If there are no other flags, just change the password.
X */
X
X if (! (dflg || lflg || xflg || nflg || wflg || iflg)) {
X
X /*
X * See if the user is permitted to change the password.
X * Otherwise, go ahead and set a new password.
X */
X
X check_password (pw, sp);
X
X if (new_password (pw, sp)) {
X fprintf (stderr, UNCHANGED, name);
X#ifdef USE_SYSLOG
X closelog ();
X#endif
X exit (1);
X }
X }
X
X /*
X * The other options are incredibly simple. Just modify the
X * field in the shadow file entry.
X */
X
X if (dflg) /* Set password to blank */
X sp->sp_pwdp = "";
X
X if (lflg) /* Set password to "locked" value */
X sp->sp_pwdp = "!";
X
X if (xflg)
X sp->sp_max = (max * DAY) / SCALE;
X
X if (nflg)
X sp->sp_min = (min * DAY) / SCALE;
X
X if (wflg)
X sp->sp_warn = (warn * DAY) / SCALE;
X
X if (iflg)
X sp->sp_inact = (inact * DAY) / SCALE;
X
X /*
X * Before going any further, raise the ulimit to prevent
X * colliding into a lowered ulimit, and set the real UID
X * to root to protect against unexpected signals. Any
X * keyboard signals are set to be ignored.
X */
X
X ulimit (2, 30000);
X if (setuid (0)) {
X fprintf (stderr, NOTROOT);
X#ifdef USE_SYSLOG
X syslog (LOG_ERR, NOTROOT2);
X closelog ();
X#endif
X exit (1);
X }
X signal (SIGHUP, SIG_IGN);
X signal (SIGINT, SIG_IGN);
X signal (SIGQUIT, SIG_IGN);
X#ifdef SIGTSTP
X signal (SIGTSTP, SIG_IGN);
X#endif
X
X /*
X * The shadow entry is now ready to be committed back to
X * the shadow file. Get a lock on the file and open it.
X */
X
X for (i = 0;i < 30;i++)
X if (spw_lock ())
X break;
X
X if (i == 30) {
X fprintf (stderr, SPWDBUSY);
X#ifdef USE_SYSLOG
X syslog (LOG_WARN, SPWDBUSY2);
X closelog ();
X#endif
X exit (1);
X }
X if (! spw_open (O_RDWR)) {
X fprintf (stderr, OPNERROR);
X#ifdef USE_SYSLOG
X syslog (LOG_ERR, OPNERROR2);
X closelog ();
X#endif
X (void) spw_unlock ();
X exit (1);
X }
X
X /*
X * Update the shadow file entry. If there is a DBM file,
X * update that entry as well.
X */
X
X if (! spw_update (sp)) {
X fprintf (stderr, UPDERROR);
X#ifdef USE_SYSLOG
X syslog (LOG_ERR, UPDERROR2);
X closelog ();
X#endif
X (void) spw_unlock ();
X exit (1);
X }
X#ifdef NDBM
X if (access ("/etc/shadow.pag", 0) == 0 && ! sp_dbm_update (sp)) {
X fprintf (stderr, DBMERROR);
X#ifdef USE_SYSLOG
X syslog (LOG_ERR, DBMERROR2);
X closelog ();
X#endif
X (void) spw_unlock ();
X exit (1);
X }
X endspent ();
X#endif
X
X /*
X * Changes have all been made, so commit them and unlock the
X * file.
X */
X
X if (! spw_close ()) {
X fprintf (stderr, CLSERROR);
X#ifdef USE_SYSLOG
X syslog (LOG_ERR, CLSERROR2);
X closelog ();
X#endif
X (void) spw_unlock ();
X exit (1);
X }
X if (! spw_unlock ()) {
X fprintf (stderr, UNLKERROR);
X#ifdef USE_SYSLOG
X syslog (LOG_ERR, UNLKERROR2);
X closelog ();
X#endif
X exit (1);
X }
X#ifdef USE_SYSLOG
X syslog (LOG_INFO, CHGPASSWD, name);
X closelog ();
X#endif
X exit (0);
X /*NOTREACHED*/
X}
END_OF_FILE
if test 18487 -ne `wc -c <'passwd.c'`; then
echo shar: \"'passwd.c'\" unpacked with wrong size!
fi
# end of 'passwd.c'
fi
if test -f 'usermod.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'usermod.c'\"
else
echo shar: Extracting \"'usermod.c'\" \(24732 characters\)
sed "s/^X//" >'usermod.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[] = "@(#)usermod.c 3.5 14:38:40 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
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];
Xchar user_newname[BUFSIZ];
Xuid_t user_id;
Xuid_t user_newid;
Xgid_t user_gid;
Xchar user_comment[BUFSIZ];
Xchar user_home[BUFSIZ];
Xchar user_newhome[BUFSIZ];
Xchar user_shell[BUFSIZ];
Xlong user_expire;
Xlong user_inactive;
Xint user_ngroups = -1;
Xgid_t user_groups[NGROUPS_MAX];
Xstruct passwd user_pwd;
Xstruct spwd user_spwd;
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 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 fflg; /* days until account with expired password is locked */
Xint eflg; /* days after password changed before it becomes expired */
Xint lflg; /* new user name for user */
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 struct passwd *pw_locate();
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();
Xextern struct spwd *spw_locate();
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 * del_list - delete a member from a list of group members
X *
X * the array of member names is searched for the old member
X * name, and if present it is deleted from a freshly allocated
X * list of users.
X */
X
Xchar **
Xdel_list (list, member)
Xchar **list;
Xchar *member;
X{
X int i, j;
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 = j = 0;list[i] != (char *) 0;i++)
X if (strcmp (list[i], member))
X j++;
X
X if (j == i)
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 ((j + 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 = j = 0;list[i] != (char *) 0;i++)
X if (strcmp (list[i], member))
X tmp[j++] = list[i];
X
X tmp[j] = (char *) 0;
X
X return tmp;
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: %s [-u uid [-o]] [-g group] [-G group,...] \n", Prog);
X fprintf (stderr,
X "\t\t[-d home [-m]] [-s shell] [-c comment] [-l new_name]\n");
X fprintf (stderr,
X "\t\t[-f inactive] [-e expire] name\n");
X
X exit (2);
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 if (lflg)
X pwent->pw_name = strdup (user_newname);
X
X if (uflg)
X pwent->pw_uid = user_newid;
X
X if (gflg)
X pwent->pw_gid = user_gid;
X
X if (cflg)
X pwent->pw_gecos = strdup (user_comment);
X
X if (dflg)
X pwent->pw_dir = strdup (user_newhome);
X
X if (sflg)
X pwent->pw_shell = strdup (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 if (lflg)
X spent->sp_namp = strdup (user_newname);
X
X spent->sp_inact = user_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 int is_member;
X int was_member;
X int was_admin;
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 is_member = i == user_ngroups ? -1:i;
X
X for (i = 0;grp->gr_mem[i];i++)
X if (strcmp (user_name, grp->gr_mem[i]) == 0)
X break;
X
X was_member = grp->gr_mem[i] ? i:-1;
X
X if (is_member == -1 && was_member == -1)
X continue;
X
X if (was_member >= 0 && (! Gflg || is_member >= 0)) {
X if (lflg) {
X grp->gr_mem = del_list (grp->gr_mem,
X user_name);
X grp->gr_mem = add_list (grp->gr_mem,
X user_newname);
X#ifdef USE_SYSLOG
X syslog (LOG_INFO,
X "change `%s' to `%s' in group `%s'\n",
X user_name, user_newname, grp->gr_name);
X#endif
X }
X } else if (was_member >= 0 && Gflg && is_member < 0) {
X grp->gr_mem = del_list (grp->gr_mem, user_name);
X#ifdef USE_SYSLOG
X syslog (LOG_INFO, "delete `%s' from group `%s'\n",
X user_name, grp->gr_name);
X#endif
X } else if (was_member < 0 && Gflg && is_member >= 0) {
X grp->gr_mem = add_list (grp->gr_mem,
X lflg ? user_newname:user_name);
X#ifdef USE_SYSLOG
X syslog (LOG_INFO, "add `%s' to group `%s'\n",
X lflg ? user_newname:user_name, grp->gr_name);
X#endif
X } else
X continue;
X
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 }
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.
X */
X
X for (sgr_rewind (), sgrp = sgr_next ();sgrp;sgrp = sgr_next ()) {
X
X /*
X * See if the user was a member of this group
X */
X
X for (i = 0;sgrp->sg_mem[i];i++)
X if (strcmp (sgrp->sg_mem[i], user_name) == 0)
X break;
X
X was_member = sgrp->sg_mem[i] ? i:-1;
X
X /*
X * See if the user was an administrator of this group
X */
X
X for (i = 0;sgrp->sg_adm[i];i++)
X if (strcmp (sgrp->sg_adm[i], user_name) == 0)
X break;
X
X was_admin = sgrp->sg_adm[i] ? i:-1;
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 is_member = i == user_ngroups ? -1:i;
X
X if (is_member == -1 && was_admin == -1 && was_member == -1)
X continue;
X
X if (was_admin >= 0 && lflg) {
X sgrp->sg_adm = del_list (sgrp->sg_adm, user_name);
X sgrp->sg_adm = add_list (sgrp->sg_adm, user_newname);
X#ifdef USE_SYSLOG
X syslog (LOG_INFO, "change admin `%s' to `%s' in shadow group `%s'\n",
X user_name, user_newname, sgrp->sg_name);
X#endif
X }
X if (was_member >= 0 && (! Gflg || is_member >= 0)) {
X if (lflg) {
X sgrp->sg_mem = del_list (sgrp->sg_mem,
X user_name);
X sgrp->sg_mem = add_list (sgrp->sg_mem,
X user_newname);
X#ifdef USE_SYSLOG
X syslog (LOG_INFO, "change `%s' to `%s' in shadow group `%s'\n",
X user_name, user_newname, sgrp->sg_name);
X#endif
X }
X } else if (was_member >= 0 && Gflg && is_member < 0) {
X sgrp->sg_mem = del_list (sgrp->sg_mem, user_name);
X#ifdef USE_SYSLOG
X syslog (LOG_INFO, "delete `%s' from shadow group `%s'\n",
X user_name, sgrp->sg_name);
X#endif
X } else if (was_member < 0 && Gflg && is_member >= 0) {
X sgrp->sg_mem = add_list (sgrp->sg_mem,
X lflg ? user_newname:user_name);
X#ifdef USE_SYSLOG
X syslog (LOG_INFO, "add `%s' to shadow group `%s'\n",
X lflg ? user_newname:user_name, grp->gr_name);
X#endif
X } else
X continue;
X
X /*
X * Update the group entry to reflect the changes.
X */
X
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 }
X#endif
X}
X
X/*
X * check_new_id - verify the new UID for uniqueness
X *
X * check_new_id() insures that the new UID does not exist already.
X * It does this by checking that the UID has changed and that there
X * is no current entry for this user ID.
X */
X
Xint
Xcheck_new_id ()
X{
X /*
X * First, the easy stuff. If the ID can be duplicated, or if
X * the ID didn't really change, just return. If the ID didn't
X * change, turn off those flags. No sense doing needless work.
X */
X
X if (oflg)
X return 0;
X
X if (user_id == user_newid) {
X uflg = lflg = 0;
X return 0;
X }
X if (getpwuid (user_newid))
X return -1;
X
X return 0;
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 struct passwd *pwd;
X struct spwd *spwd;
X long l;
X int anyflag = 0;
X int arg;
X
X if (argc == 1 || argv[argc - 1][0] == '-')
X usage ();
X
X if (! (pwd = getpwnam (argv[argc - 1]))) {
X fprintf (stderr, "%s: user %s does not exist\n",
X Prog, argv[argc - 1]);
X exit (6);
X }
X strcpy (user_name, pwd->pw_name);
X user_id = pwd->pw_uid;
X user_gid = pwd->pw_gid;
X strcpy (user_comment, pwd->pw_gecos);
X strcpy (user_home, pwd->pw_dir);
X strcpy (user_shell, pwd->pw_shell);
X
X if (spwd = getspnam (user_name)) {
X user_expire = spwd->sp_expire;
X user_inactive = spwd->sp_inact;
X }
X while ((arg = getopt (argc, argv, "u:og:G:d:s:c:mf:e:l:")) != EOF) {
X switch (arg) {
X case 'c':
X if (! VALID (optarg)) {
X fprintf (stderr,
X "%s: invalid field `%s'\n",
X Prog, optarg);
X exit (3);
X }
X if (strcmp (optarg, user_comment)) {
X cflg++;
X strncpy (user_comment, optarg, BUFSIZ);
X }
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_newhome, optarg, BUFSIZ);
X break;
X case 'e':
X l = strtoday (optarg);
X#ifdef ITI_AGING
X l *= DAY;
X#endif
X if (l != user_expire) {
X eflg++;
X user_expire = l;
X }
X break;
X case 'f':
X if (user_inactive != atoi (optarg)) {
X fflg++;
X user_inactive = atoi (optarg);
X }
X break;
X case 'g':
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 if (grp->gr_gid != user_gid) {
X gflg++;
X user_gid = grp->gr_gid;
X }
X break;
X case 'G':
X Gflg++;
X if (get_groups (optarg))
X exit (1);
X
X break;
X case 'l':
X if (! VALID (optarg)) {
X fprintf (stderr,
X "%s: invalid field `%s'\n",
X Prog, optarg);
X exit (3);
X }
X if (strcmp (user_name, optarg)) {
X lflg++;
X strcpy (user_newname, optarg);
X }
X break;
X case 'm':
X if (! dflg)
X usage ();
X
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 if (strcmp (user_shell, optarg)) {
X strncpy (user_shell, optarg, BUFSIZ);
X sflg++;
X }
X break;
X case 'u':
X uflg++;
X user_newid = atoi (optarg);
X break;
X default:
X usage ();
X }
X anyflag++;
X }
X if (anyflag == 0) {
X fprintf (stderr, "%s: no flags given\n", Prog);
X exit (1);
X }
X if (optind != argc - 1)
X usage ();
X
X if (dflg && strcmp (user_home, user_newhome) == 0)
X dflg = mflg = 0;
X
X if (uflg && user_id == user_newid)
X uflg = oflg = 0;
X
X if (lflg && getpwnam (user_newname)) {
X fprintf (stderr, "%s: user %s exists\n", Prog, user_newname);
X exit (9);
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 struct passwd *pwd;
X struct spwd *spwd;
X
X pwd = pw_locate (user_name);
X pwent = *pwd;
X
X spwd = spw_locate (user_name);
X spent = *spwd;
X
X new_pwent (&pwent);
X new_spent (&spent);
X
X if (lflg || uflg || gflg || cflg || dflg || sflg) {
X if (! pw_update (&pwent)) {
X fprintf (stderr, "%s: error changing password entry\n",
X Prog);
X exit (1);
X }
X if (lflg && ! pw_remove (user_name)) {
X fprintf (stderr, "%s: error removing password entry\n",
X Prog);
X exit (1);
X }
X#if defined(DBM) || defined(NDBM)
X if (access ("/etc/passwd.pag", 0) == 0) {
X if (! pw_dbm_update (&pwent)) {
X fprintf (stderr, "%s: error adding password dbm entry\n",
X Prog);
X exit (1);
X }
X if (lflg && (pwd = getpwnam (user_name)) && ! pw_dbm_remove (pwd)) {
X fprintf (stderr, "%s: error removing passwd dbm entry\n",
X Prog);
X exit (1);
X }
X endpwent ();
X }
X#endif
X }
X if (lflg || eflg || fflg) {
X if (! spw_update (&spent)) {
X fprintf (stderr, "%s: error adding new shadow password entry\n",
X Prog);
X exit (1);
X }
X if (lflg && ! spw_remove (user_name)) {
X fprintf (stderr, "%s: error removing shadow password entry\n",
X Prog);
X exit (1);
X }
X }
X#ifdef NDBM
X if (access ("/etc/shadow.pag", 0) == 0) {
X if (! sp_dbm_update (&spent)) {
X fprintf (stderr, "%s: error updating shadow passwd dbm entry\n",
X Prog);
X exit (1);
X }
X if (lflg && ! sp_dbm_remove (user_name)) {
X fprintf (stderr, "%s: error removing shadow passwd db entry\n",
X Prog);
X exit (1);
X }
X endspent ();
X }
X#endif
X if (Gflg || lflg)
X grp_update ();
X}
X
X/*
X * move_home - move the user's home directory
X *
X * move_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
Xmove_home ()
X{
X struct stat sb;
X
X if (mflg && stat (user_home, &sb) == 0) {
X if (access (user_newhome, 0) == 0) {
X fprintf (stderr, "%s: directory %s exists\n",
X Prog, user_newhome);
X exit (12);
X } else if (rename (user_home, user_newhome)) {
X if (errno == EXDEV) {
X if (mkdir (user_newhome, sb.st_mode & 0777)) {
X fprintf (stderr,
X "%s: can't create %s\n",
X Prog, user_newhome);
X }
X chown (user_newhome, sb.st_uid, sb.st_gid);
X if (copy_tree (user_home, user_newhome,
X -1, -1) == 0 &&
X remove_tree (user_home) == 0 &&
X rmdir (user_home) == 0)
X return;
X
X remove_tree (user_newhome);
X rmdir (user_newhome);
X }
X fprintf (stderr,
X "%s: cannot rename directory %s to %s\n",
X Prog, user_home, user_newhome);
X exit (12);
X }
X }
X if (uflg || gflg)
X chown (dflg ? user_newhome:user_home, user_id, user_gid);
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 process_flags (argc, argv);
X
X /*
X * Do the hard stuff - open the files, change the user entries,
X * change the home directory, then close and update the files.
X */
X
X open_files ();
X
X usr_update ();
X
X close_files ();
X
X if (mflg)
X move_home ();
X
X exit (0);
X /*NOTREACHED*/
X}
END_OF_FILE
if test 24732 -ne `wc -c <'usermod.c'`; then
echo shar: \"'usermod.c'\" unpacked with wrong size!
fi
# end of 'usermod.c'
fi
echo shar: End of archive 2 \(of 11\).
cp /dev/null ark2isdone
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...