home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
misc
/
volume38
/
shadow
/
part02
/
usermod.c
< prev
Wrap
C/C++ Source or Header
|
1993-08-14
|
36KB
|
1,730 lines
/*
* Copyright 1991, 1992, 1993, John F. Haugh II
* All rights reserved.
*
* Permission is granted to copy and create derivative works for any
* non-commercial purpose, provided this copyright notice is preserved
* in all copies of source code, or included in human readable form
* and conspicuously displayed on all copies of object code or
* distribution media.
*
* This software is provided on an AS-IS basis and the author makes
* no warrantee of any kind.
*/
#ifndef lint
static char sccsid[] = "@(#)usermod.c 3.16 08:11:52 07 May 1993";
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include "pwd.h"
#include <grp.h>
#include <ctype.h>
#include <fcntl.h>
#include <time.h>
#ifdef BSD
#include <strings.h>
#else
#include <string.h>
#endif
#include "config.h"
#ifdef SHADOWPWD
#include "shadow.h"
#endif
#include "faillog.h"
#include "lastlog.h"
#include "pwauth.h"
#ifdef USE_SYSLOG
#include <syslog.h>
#ifndef LOG_WARN
#define LOG_WARN LOG_WARNING
#endif
#endif
#ifndef NGROUPS_MAX
#define NGROUPS_MAX 64
#endif
#if defined(DIR_XENIX) || defined(DIR_BSD) || defined(DIR_SYSV)
#define DIR_ANY
#endif
#if !defined(MDY_DATE) && !defined(DMY_DATE) && !defined(YMD_DATE)
#define MDY_DATE 1
#endif
#if (defined (MDY_DATE) && (defined (DMY_DATE) || defined (YMD_DATE))) || \
(defined (DMY_DATE) && (defined (MDY_DATE) || defined (YMD_DATE)))
Error: You must only define one of MDY_DATE, DMY_DATE, or YMD_DATE
#endif
#define VALID(s) (strcspn (s, ":\n") == strlen (s))
char user_name[BUFSIZ];
char user_newname[BUFSIZ];
char user_auth[BUFSIZ];
char user_newauth[BUFSIZ];
uid_t user_id;
uid_t user_newid;
gid_t user_gid;
gid_t user_newgid;
char user_comment[BUFSIZ];
char user_home[BUFSIZ];
char user_newhome[BUFSIZ];
char user_shell[BUFSIZ];
#ifdef SHADOWPWD
long user_expire;
long user_inactive;
#endif
int user_ngroups = -1;
gid_t user_groups[NGROUPS_MAX];
struct passwd user_pwd;
#ifdef SHADOWPWD
struct spwd user_spwd;
#endif
char *Prog;
char *auth_arg;
int Aflg; /* specify user defined authentication method */
int uflg; /* specify user ID for new account */
int oflg; /* permit non-unique user ID to be specified with -u */
int gflg; /* primary group ID for new account */
int Gflg; /* secondary group set for new account */
int dflg; /* home directory for new account */
int sflg; /* shell program for new account */
int cflg; /* comment (GECOS) field for new account */
int mflg; /* create user's home directory if it doesn't exist */
int fflg; /* days until account with expired password is locked */
int eflg; /* days after password changed before it becomes expired */
int lflg; /* new user name for user */
#ifdef NDBM
extern int pw_dbm_mode;
#ifdef SHADOWPWD
extern int sp_dbm_mode;
#endif
extern int gr_dbm_mode;
#ifdef SHADOWGRP
extern int sg_dbm_mode;
#endif
#endif
extern FILE *fopen();
extern int fclose();
extern char *malloc();
extern char *mktemp();
extern struct group *getgrnam();
extern struct group *getgrgid();
extern struct group *gr_next();
extern struct group *gr_locate();
extern int gr_lock();
extern int gr_unlock();
extern int gr_rewind();
extern int gr_open();
#ifdef SHADOWGRP
extern struct sgrp *sgr_next();
extern int sgr_lock();
extern int sgr_unlock();
extern int sgr_rewind();
extern int sgr_open();
#endif
extern struct passwd *getpwnam();
extern struct passwd *pw_next();
extern struct passwd *pw_locate();
extern int pw_lock();
extern int pw_unlock();
extern int pw_rewind();
extern int pw_open();
#ifdef SHADOWPWD
extern int spw_lock();
extern int spw_unlock();
extern int spw_open();
extern struct spwd *spw_locate();
#endif
#define DAY (24L*3600L)
#define WEEK (7*DAY)
#ifdef ITI_AGING
#define SCALE (1)
#else
#define SCALE (DAY)
#endif
/*
* days and juldays are used to compute the number of days in the
* current month, and the cummulative number of days in the preceding
* months. they are declared so that january is 1, not 0.
*/
static short days[13] = { 0,
31, 28, 31, 30, 31, 30, /* JAN - JUN */
31, 31, 30, 31, 30, 31 }; /* JUL - DEC */
static short juldays[13] = { 0,
0, 31, 59, 90, 120, 151, /* JAN - JUN */
181, 212, 243, 273, 304, 334 }; /* JUL - DEC */
#ifdef NEED_RENAME
/*
* rename - rename a file to another name
*
* rename is provided for systems which do not include the rename()
* system call.
*/
int
rename (begin, end)
char *begin;
char *end;
{
struct stat s1, s2;
extern int errno;
int orig_err = errno;
if (stat (begin, &s1))
return -1;
if (stat (end, &s2)) {
errno = orig_err;
} else {
/*
* See if this is a cross-device link. We do this to
* insure that the link below has a chance of working.
*/
if (s1.st_dev != s2.st_dev) {
errno = EXDEV;
return -1;
}
/*
* See if we can unlink the existing destination
* file. If the unlink works the directory is writable,
* so there is no need here to figure that out.
*/
if (unlink (end))
return -1;
}
/*
* Now just link the original name to the final name. If there
* was no file previously, this link will fail if the target
* directory isn't writable. The unlink will fail if the source
* directory isn't writable, but life stinks ...
*/
if (link (begin, end) || unlink (begin))
return -1;
return 0;
}
#endif
/*
* strtoday - compute the number of days since 1970.
*
* the total number of days prior to the current date is
* computed. january 1, 1970 is used as the origin with
* it having a day number of 0.
*/
long
strtoday (str)
char *str;
{
char slop[2];
int month;
int day;
int year;
long total;
/*
* start by separating the month, day and year. the order
* is compiled in ...
*/
#ifdef MDY_DATE
if (sscanf (str, "%d/%d/%d%c", &month, &day, &year, slop) != 3)
return -1;
#endif
#ifdef DMY_DATE
if (sscanf (str, "%d/%d/%d%c", &day, &month, &year, slop) != 3)
return -1;
#endif
#ifdef YMD_DATE
if (sscanf (str, "%d/%d/%d%c", &year, &month, &day, slop) != 3)
return -1;
#endif
/*
* the month, day of the month, and year are checked for
* correctness and the year adjusted so it falls between
* 1970 and 2069.
*/
if (month < 1 || month > 12)
return -1;
if (day < 1)
return -1;
if ((month != 2 || (year % 4) != 0) && day > days[month])
return -1;
else if ((month == 2 && (year % 4) == 0) && day > 29)
return -1;
if (year < 0)
return -1;
else if (year < 69)
year += 2000;
else if (year < 99)
year += 1900;
if (year < 1970 || year > 2069)
return -1;
/*
* the total number of days is the total number of days in all
* the whole years, plus the number of leap days, plus the
* number of days in the whole months preceding, plus the number
* of days so far in the month.
*/
total = (long) ((year - 1970) * 365L) + (((year + 1) - 1970) / 4);
total += (long) juldays[month] + (month > 2 && (year % 4) == 0 ? 1:0);
total += (long) day - 1;
return total;
}
/*
* add_list - add a member to a list of group members
*
* the array of member names is searched for the new member
* name, and if not present it is added to a freshly allocated
* list of users.
*/
char **
add_list (list, member)
char **list;
char *member;
{
int i;
char **tmp;
/*
* Scan the list for the new name. Return the original list
* pointer if it is present.
*/
for (i = 0;list[i] != (char *) 0;i++)
if (strcmp (list[i], member) == 0)
return list;
/*
* Allocate a new list pointer large enough to hold all the
* old entries, and the new entries as well.
*/
if (! (tmp = (char **) malloc ((i + 2) * sizeof member)))
return 0;
/*
* Copy the original list to the new list, then append the
* new member and NULL terminate the result. This new list
* is returned to the invoker.
*/
for (i = 0;list[i] != (char *) 0;i++)
tmp[i] = list[i];
tmp[i++] = strdup (member);
tmp[i] = (char *) 0;
return tmp;
}
/*
* del_list - delete a member from a list of group members
*
* the array of member names is searched for the old member
* name, and if present it is deleted from a freshly allocated
* list of users.
*/
char **
del_list (list, member)
char **list;
char *member;
{
int i, j;
char **tmp;
/*
* Scan the list for the new name. Return the original list
* pointer if it is present.
*/
for (i = j = 0;list[i] != (char *) 0;i++)
if (strcmp (list[i], member))
j++;
if (j == i)
return list;
/*
* Allocate a new list pointer large enough to hold all the
* old entries, and the new entries as well.
*/
if (! (tmp = (char **) malloc ((j + 2) * sizeof member)))
return 0;
/*
* Copy the original list to the new list, then append the
* new member and NULL terminate the result. This new list
* is returned to the invoker.
*/
for (i = j = 0;list[i] != (char *) 0;i++)
if (strcmp (list[i], member))
tmp[j++] = list[i];
tmp[j] = (char *) 0;
return tmp;
}
/*
* get_groups - convert a list of group names to an array of group IDs
*
* get_groups() takes a comma-separated list of group names and
* converts it to an array of group ID values. Any unknown group
* names are reported as errors.
*/
int
get_groups (list)
char *list;
{
char *cp;
struct group *grp;
int errors = 0;
/*
* Initialize the list to be empty
*/
user_ngroups = 0;
if (! *list)
return 0;
/*
* So long as there is some data to be converted, strip off
* each name and look it up. A mix of numerical and string
* values for group identifiers is permitted.
*/
do {
/*
* Strip off a single name from the list
*/
if (cp = strchr (list, ','))
*cp++ = '\0';
/*
* Names starting with digits are treated as numerical
* GID values, otherwise the string is looked up as is.
*/
if (isdigit (*list))
grp = getgrgid (atoi (list));
else
grp = getgrnam (list);
/*
* There must be a match, either by GID value or by
* string name.
*/
if (! grp) {
fprintf (stderr, "%s: unknown group %s\n", Prog, list);
errors++;
}
/*
* Add the GID value from the group file to the user's
* list of groups.
*/
user_groups[user_ngroups++] = grp->gr_gid;
list = cp;
} while (list);
/*
* Any errors in finding group names are fatal
*/
if (errors)
return -1;
return 0;
}
/*
* usage - display usage message and exit
*/
usage ()
{
fprintf (stderr,
"usage: %s [-u uid [-o]] [-g group] [-G group,...] \n", Prog);
#ifdef SHADOWPWD
fprintf (stderr,
"\t\t[-d home [-m]] [-s shell] [-c comment] [-l new_name]\n");
#ifdef MDY_DATE
fprintf (stderr,
"\t\t[-f inactive ] [-e expire mm/dd/yy ] name\n");
#endif
#ifdef DMY_DATE
fprintf (stderr,
"\t\t[-f inactive ] [-e expire dd/mm/yy ] name\n");
#endif
#ifdef YMD_DATE
fprintf (stderr,
"\t\t[-f inactive ] [-e expire yy/mm/dd ] name\n");
#endif
#else /* !SHADOWPWD */
fprintf (stderr,
"\t\t[-d home [-m]] [-s shell] [-c comment] [-l new_name]\n");
fprintf (stderr,
"\t\t[ -A {DEFAULT|program},... ] name\n");
#endif /* SHADOWPWD */
exit (2);
}
/*
* new_pwent - initialize the values in a password file entry
*
* new_pwent() takes all of the values that have been entered and
* fills in a (struct passwd) with them.
*/
void
new_pwent (pwent)
struct passwd *pwent;
{
if (lflg) {
#ifdef USE_SYSLOG
syslog (LOG_INFO, "change user name `%s' to `%s'\n",
pwent->pw_name, user_newname);
#endif
pwent->pw_name = strdup (user_newname);
}
if (uflg) {
#ifdef USE_SYSLOG
syslog (LOG_INFO, "change user `%s' UID from `%d' to `%d'\n",
pwent->pw_name, pwent->pw_uid, user_newid);
#endif
pwent->pw_uid = user_newid;
}
if (gflg) {
#ifdef USE_SYSLOG
syslog (LOG_INFO, "change user `%s' GID from `%d' to `%d'\n",
pwent->pw_name, pwent->pw_gid, user_newgid);
#endif
pwent->pw_gid = user_newgid;
}
if (cflg)
pwent->pw_gecos = strdup (user_comment);
if (dflg) {
#ifdef USE_SYSLOG
syslog (LOG_INFO, "change user `%s' home from `%s' to `%s'\n",
pwent->pw_name, pwent->pw_dir, user_newhome);
#endif
pwent->pw_dir = strdup (user_newhome);
}
if (sflg) {
#ifdef USE_SYSLOG
syslog (LOG_INFO, "change user `%s' shell from `%s' to `%s'\n",
pwent->pw_name, pwent->pw_shell, user_shell);
#endif
pwent->pw_shell = strdup (user_shell);
}
}
#ifdef SHADOWPWD
/*
* new_spent - initialize the values in a shadow password file entry
*
* new_spent() takes all of the values that have been entered and
* fills in a (struct spwd) with them.
*/
void
new_spent (spent)
struct spwd *spent;
{
if (lflg)
spent->sp_namp = strdup (user_newname);
if (fflg) {
#ifdef USE_SYSLOG
syslog (LOG_INFO, "change user `%s' inactive from `%d' to `%d'\n",
spent->sp_namp, spent->sp_inact, user_inactive);
#endif
spent->sp_inact = user_inactive;
}
if (eflg) {
#ifdef USE_SYSLOG
syslog (LOG_INFO, "change user `%s' expiration from `%d' to `%d'\n",
spent->sp_namp, spent->sp_expire, user_expire);
#endif
spent->sp_expire = user_expire;
}
}
#endif /* SHADOWPWD */
/*
* grp_update - add user to secondary group set
*
* grp_update() takes the secondary group set given in user_groups
* and adds the user to each group given by that set.
*/
void
grp_update ()
{
int i;
int is_member;
int was_member;
struct group *grp;
#ifdef SHADOWGRP
int was_admin;
struct sgrp *sgrp;
#endif
/*
* Lock and open the group file. This will load all of the group
* entries.
*/
if (! gr_lock ()) {
fprintf (stderr, "%s: error locking group file\n", Prog);
#ifdef USE_SYSLOG
syslog (LOG_ERR, "error locking group file");
#endif
exit (1);
}
if (! gr_open (O_RDWR)) {
fprintf (stderr, "%s: error opening group file\n", Prog);
#ifdef USE_SYSLOG
syslog (LOG_ERR, "error opening group file");
#endif
fail_exit (1);
}
#ifdef SHADOWGRP
if (! sgr_lock ()) {
fprintf (stderr, "%s: error locking shadow group file\n", Prog);
#ifdef USE_SYSLOG
syslog (LOG_ERR, "error locking shadow group file");
#endif
fail_exit (1);
}
if (! sgr_open (O_RDWR)) {
fprintf (stderr, "%s: error opening shadow group file\n", Prog);
#ifdef USE_SYSLOG
syslog (LOG_ERR, "error opening shadow group file");
#endif
fail_exit (1);
}
#endif /* SHADOWGRP */
/*
* Scan through the entire group file looking for the groups that
* the user is a member of.
*/
for (gr_rewind (), grp = gr_next ();grp;grp = gr_next ()) {
/*
* See if the user specified this group as one of their
* concurrent groups.
*/
for (i = 0;i < user_ngroups;i++)
if (grp->gr_gid == user_groups[i])
break;
is_member = i == user_ngroups ? -1:i;
for (i = 0;grp->gr_mem[i];i++)
if (strcmp (user_name, grp->gr_mem[i]) == 0)
break;
was_member = grp->gr_mem[i] ? i:-1;
if (is_member == -1 && was_member == -1)
continue;
if (was_member >= 0 && (! Gflg || is_member >= 0)) {
if (lflg) {
grp->gr_mem = del_list (grp->gr_mem,
user_name);
grp->gr_mem = add_list (grp->gr_mem,
user_newname);
#ifdef USE_SYSLOG
syslog (LOG_INFO,
"change `%s' to `%s' in group `%s'\n",
user_name, user_newname, grp->gr_name);
#endif
}
} else if (was_member >= 0 && Gflg && is_member < 0) {
grp->gr_mem = del_list (grp->gr_mem, user_name);
#ifdef USE_SYSLOG
syslog (LOG_INFO, "delete `%s' from group `%s'\n",
user_name, grp->gr_name);
#endif
} else if (was_member < 0 && Gflg && is_member >= 0) {
grp->gr_mem = add_list (grp->gr_mem,
lflg ? user_newname:user_name);
#ifdef USE_SYSLOG
syslog (LOG_INFO, "add `%s' to group `%s'\n",
lflg ? user_newname:user_name, grp->gr_name);
#endif
} else
continue;
if (! gr_update (grp)) {
fprintf (stderr, "%s: error adding new group entry\n",
Prog);
#ifdef USE_SYSLOG
syslog (LOG_ERR, "error adding group entry");
#endif
fail_exit (1);
}
#ifdef NDBM
/*
* Update the DBM group file with the new entry as well.
*/
if (! gr_dbm_update (grp)) {
fprintf (stderr, "%s: cannot add new dbm group entry\n",
Prog);
#ifdef USE_SYSLOG
syslog (LOG_ERR, "error adding dbm group entry");
#endif
fail_exit (1);
}
#endif /* NDBM */
}
#ifdef NDBM
endgrent ();
#endif
#ifdef SHADOWGRP
/*
* Scan through the entire shadow group file looking for the groups
* that the user is a member of.
*/
for (sgr_rewind (), sgrp = sgr_next ();sgrp;sgrp = sgr_next ()) {
/*
* See if the user was a member of this group
*/
for (i = 0;sgrp->sg_mem[i];i++)
if (strcmp (sgrp->sg_mem[i], user_name) == 0)
break;
was_member = sgrp->sg_mem[i] ? i:-1;
/*
* See if the user was an administrator of this group
*/
for (i = 0;sgrp->sg_adm[i];i++)
if (strcmp (sgrp->sg_adm[i], user_name) == 0)
break;
was_admin = sgrp->sg_adm[i] ? i:-1;
/*
* See if the user specified this group as one of their
* concurrent groups.
*/
for (i = 0;i < user_ngroups;i++) {
if (! (grp = gr_locate (sgrp->sg_name)))
continue;
if (grp->gr_gid == user_groups[i])
break;
}
is_member = i == user_ngroups ? -1:i;
if (is_member == -1 && was_admin == -1 && was_member == -1)
continue;
if (was_admin >= 0 && lflg) {
sgrp->sg_adm = del_list (sgrp->sg_adm, user_name);
sgrp->sg_adm = add_list (sgrp->sg_adm, user_newname);
#ifdef USE_SYSLOG
syslog (LOG_INFO, "change admin `%s' to `%s' in shadow group `%s'\n",
user_name, user_newname, sgrp->sg_name);
#endif
}
if (was_member >= 0 && (! Gflg || is_member >= 0)) {
if (lflg) {
sgrp->sg_mem = del_list (sgrp->sg_mem,
user_name);
sgrp->sg_mem = add_list (sgrp->sg_mem,
user_newname);
#ifdef USE_SYSLOG
syslog (LOG_INFO, "change `%s' to `%s' in shadow group `%s'\n",
user_name, user_newname, sgrp->sg_name);
#endif
}
} else if (was_member >= 0 && Gflg && is_member < 0) {
sgrp->sg_mem = del_list (sgrp->sg_mem, user_name);
#ifdef USE_SYSLOG
syslog (LOG_INFO,
"delete `%s' from shadow group `%s'\n",
user_name, sgrp->sg_name);
#endif
} else if (was_member < 0 && Gflg && is_member >= 0) {
sgrp->sg_mem = add_list (sgrp->sg_mem,
lflg ? user_newname:user_name);
#ifdef USE_SYSLOG
syslog (LOG_INFO, "add `%s' to shadow group `%s'\n",
lflg ? user_newname:user_name, grp->gr_name);
#endif
} else
continue;
/*
* Update the group entry to reflect the changes.
*/
if (! sgr_update (sgrp)) {
fprintf (stderr, "%s: error adding new group entry\n",
Prog);
#ifdef USE_SYSLOG
syslog (LOG_ERR, "error adding shadow group entry\n");
#endif
fail_exit (1);
}
#ifdef NDBM
/*
* Update the DBM group file with the new entry as well.
*/
if (! sg_dbm_update (sgrp)) {
fprintf (stderr, "%s: cannot add new dbm group entry\n",
Prog);
#ifdef USE_SYSLOG
syslog (LOG_ERR,
"error adding dbm shadow group entry\n");
#endif
fail_exit (1);
}
#endif /* NDBM */
}
#ifdef NDBM
endsgent ();
#endif /* NDBM */
#endif /* SHADOWGRP */
}
/*
* check_new_id - verify the new UID for uniqueness
*
* check_new_id() insures that the new UID does not exist already.
* It does this by checking that the UID has changed and that there
* is no current entry for this user ID.
*/
int
check_new_id ()
{
/*
* First, the easy stuff. If the ID can be duplicated, or if
* the ID didn't really change, just return. If the ID didn't
* change, turn off those flags. No sense doing needless work.
*/
if (oflg)
return 0;
if (user_id == user_newid) {
uflg = lflg = 0;
return 0;
}
if (getpwuid (user_newid))
return -1;
return 0;
}
/*
* get_password - locate encrypted password in authentication list
*/
char *
get_password (list)
char *list;
{
char *cp, *end;
static char buf[257];
strcpy (buf, list);
for (cp = buf;cp;cp = end) {
if (end = strchr (cp, ';'))
*end++ = 0;
if (cp[0] == '@')
continue;
return cp;
}
return (char *) 0;
}
/*
* split_auths - break up comma list into (char *) array
*/
split_auths (list, array)
char *list;
char **array;
{
char *cp, *end;
int i = 0;
for (cp = list;cp;cp = end) {
if (end = strchr (cp, ';'))
*end++ = '\0';
array[i++] = cp;
}
array[i] = 0;
}
/*
* update_auths - find list of methods to update
*/
update_auths (old, new, update)
char *old;
char *new;
char *update;
{
char oldbuf[257], newbuf[257];
char *oldv[32], *newv[32], *updatev[32];
int i, j, k;
strcpy (oldbuf, old);
split_auths (oldbuf, oldv);
strcpy (newbuf, new);
split_auths (newbuf, newv);
for (i = j = k = 0;oldv[i];i++) {
for (j = 0;newv[j];j++)
if (strcmp (oldv[i], newv[j]) != 0)
break;
if (newv[j] != (char *) 0)
updatev[k++] = oldv[i];
}
updatev[k] = 0;
update[0] = '\0';
for (i = 0;updatev[i];i++) {
if (i)
strcat (update, ";");
strcat (update, updatev[i]);
}
}
/*
* add_auths - find list of methods to add
*/
add_auths (old, new, add)
char *old;
char *new;
char *add;
{
char oldbuf[257], newbuf[257];
char *oldv[32], *newv[32], *addv[32];
int i, j, k;
strcpy (oldbuf, old);
split_auths (oldbuf, oldv);
strcpy (newbuf, new);
split_auths (newbuf, newv);
for (i = j = k = 0;newv[i];i++) {
for (j = 0;oldv[j];j++)
if (strcmp (oldv[i], newv[j]) == 0)
break;
if (oldv[j] == (char *) 0)
addv[k++] = newv[i];
}
addv[k] = 0;
add[0] = '\0';
for (i = 0;addv[i];i++) {
if (i)
strcat (add, ";");
strcat (add, addv[i]);
}
}
/*
* delete_auths - find list of methods to delete
*/
delete_auths (old, new, remove)
char *old;
char *new;
char *remove;
{
char oldbuf[257], newbuf[257];
char *oldv[32], *newv[32], *removev[32];
int i, j, k;
strcpy (oldbuf, old);
split_auths (oldbuf, oldv);
strcpy (newbuf, new);
split_auths (newbuf, newv);
for (i = j = k = 0;oldv[i];i++) {
for (j = 0;newv[j];j++)
if (strcmp (oldv[i], newv[j]) == 0)
break;
if (newv[j] == (char *) 0)
removev[k++] = oldv[i];
}
removev[k] = 0;
remove[0] = '\0';
for (i = 0;removev[i];i++) {
if (i)
strcat (remove, ";");
strcat (remove, removev[i]);
}
}
/*
* convert_auth - convert the argument list to a authentication list
*/
convert_auth (auths, oldauths, list)
char *auths;
char *oldauths;
char *list;
{
char *cp, *end;
char *old;
char buf[257];
/*
* Copy each method. DEFAULT is replaced by an encrypted string
* if one can be found in the current authentication list.
*/
strcpy (buf, list);
auths[0] = '\0';
for (cp = buf;cp;cp = end) {
if (auths[0])
strcat (auths, ";");
if (end = strchr (cp, ','))
*end++ = '\0';
if (strcmp (cp, "DEFAULT") == 0) {
if (old = get_password (oldauths))
strcat (auths, old);
else
strcat (auths, "!");
} else {
strcat (auths, "@");
strcat (auths, cp);
}
}
}
/*
* valid_auth - check authentication list for validity
*/
valid_auth (methods)
char *methods;
{
char *cp, *end;
char buf[257];
int default_cnt = 0;
/*
* Cursory checks, length and illegal characters
*/
if (strlen (methods) > 256)
return 0;
if (! VALID (methods))
return 0;
/*
* Pick each method apart and check it.
*/
strcpy (buf, methods);
for (cp = buf;cp;cp = end) {
if (end = strchr (cp, ','))
*end++ = '\0';
if (strcmp (cp, "DEFAULT") == 0) {
if (default_cnt++ > 0)
return 0;
} else if (cp[0] != '/')
return 0;
}
return 1;
}
/*
* process_flags - perform command line argument setting
*
* process_flags() interprets the command line arguments and sets
* the values that the user will be created with accordingly. The
* values are checked for sanity.
*/
void
process_flags (argc, argv)
int argc;
char **argv;
{
extern int optind;
extern char *optarg;
struct group *grp;
struct passwd *pwd;
struct spwd *spwd;
long l;
int anyflag = 0;
int arg;
if (argc == 1 || argv[argc - 1][0] == '-')
usage ();
if (! (pwd = getpwnam (argv[argc - 1]))) {
fprintf (stderr, "%s: user %s does not exist\n",
Prog, argv[argc - 1]);
exit (6);
}
strcpy (user_name, pwd->pw_name);
user_id = pwd->pw_uid;
user_gid = pwd->pw_gid;
strcpy (user_comment, pwd->pw_gecos);
strcpy (user_home, pwd->pw_dir);
strcpy (user_shell, pwd->pw_shell);
#ifdef SHADOWPWD
if (spwd = getspnam (user_name)) {
user_expire = spwd->sp_expire;
user_inactive = spwd->sp_inact;
}
#endif
#ifdef SHADOWPWD
while ((arg = getopt (argc, argv, "A:u:og:G:d:s:c:mf:e:l:")) != EOF)
#else
while ((arg = getopt (argc, argv, "A:u:og:G:d:s:c:ml:")) != EOF)
#endif
{
switch (arg) {
case 'A':
if (! valid_auth (optarg)) {
fprintf (stderr,
"%s: invalid field `%s'\n",
Prog, optarg);
exit (3);
}
auth_arg = optarg;
Aflg++;
break;
case 'c':
if (! VALID (optarg)) {
fprintf (stderr,
"%s: invalid field `%s'\n",
Prog, optarg);
exit (3);
}
strncpy (user_comment, optarg, BUFSIZ);
cflg++;
break;
case 'd':
if (! VALID (optarg)) {
fprintf (stderr,
"%s: invalid field `%s'\n",
Prog, optarg);
exit (3);
}
dflg++;
strncpy (user_newhome, optarg, BUFSIZ);
break;
#ifdef SHADOWPWD
case 'e':
l = strtoday (optarg);
#ifdef ITI_AGING
l *= DAY;
#endif
user_expire = l;
eflg++;
break;
case 'f':
user_inactive = atoi (optarg);
fflg++;
break;
#endif /* SHADOWPWD */
case 'g':
if (isdigit (optarg[0]))
grp = getgrgid (atoi (optarg));
else
grp = getgrnam (optarg);
if (! grp) {
fprintf (stderr,
"%s: unknown group %s\n",
Prog, optarg);
exit (1);
}
user_newgid = grp->gr_gid;
gflg++;
break;
case 'G':
Gflg++;
if (get_groups (optarg))
exit (1);
break;
case 'l':
if (! VALID (optarg)) {
fprintf (stderr,
"%s: invalid field `%s'\n",
Prog, optarg);
exit (3);
}
/*
* If the name does not really change, we
* mustn't set the flag as this will cause
* rather serious problems later!
*/
if (strcmp (user_newname, optarg)) {
strcpy (user_newname, optarg);
lflg++;
}
break;
case 'm':
if (! dflg)
usage ();
mflg++;
break;
case 'o':
if (! uflg)
usage ();
oflg++;
break;
case 's':
if (! VALID (optarg)) {
fprintf (stderr,
"%s: invalid field `%s'\n",
Prog, optarg);
exit (3);
}
strncpy (user_shell, optarg, BUFSIZ);
sflg++;
break;
case 'u':
uflg++;
user_newid = atoi (optarg);
break;
default:
usage ();
}
anyflag++;
}
if (anyflag == 0) {
fprintf (stderr, "%s: no flags given\n", Prog);
exit (1);
}
if (optind != argc - 1)
usage ();
if (dflg && strcmp (user_home, user_newhome) == 0)
dflg = mflg = 0;
if (uflg && user_id == user_newid)
uflg = oflg = 0;
if (lflg && getpwnam (user_newname)) {
fprintf (stderr, "%s: user %s exists\n", Prog, user_newname);
exit (9);
}
}
/*
* close_files - close all of the files that were opened
*
* close_files() closes all of the files that were opened for this
* new user. This causes any modified entries to be written out.
*/
close_files ()
{
if (! pw_close ()) {
fprintf (stderr, "%s: cannot rewrite password file\n", Prog);
fail_exit (1);
}
#ifdef SHADOWPWD
if (! spw_close ()) {
fprintf (stderr, "%s: cannot rewrite shadow password file\n",
Prog);
fail_exit (1);
}
#endif
if (user_ngroups >= 0) {
if (! gr_close ()) {
fprintf (stderr, "%s: cannot rewrite group file\n",
Prog);
fail_exit (1);
}
}
(void) gr_unlock ();
#ifdef SHADOWGRP
if (user_ngroups >= 0) {
if (! sgr_close ()) {
fprintf (stderr, "%s: cannot rewrite shadow group file\n",
Prog);
fail_exit (1);
}
}
(void) sgr_unlock ();
#endif
(void) spw_unlock ();
(void) pw_unlock ();
/*
* Close the DBM and/or flat files
*/
endpwent ();
#ifdef SHADOWPWD
endspent ();
#endif
endgrent ();
#ifdef SHADOWGRP
endsgent ();
#endif
}
/*
* open_files - lock and open the password files
*
* open_files() opens the two password files.
*/
open_files ()
{
if (! pw_lock ()) {
fprintf (stderr, "%s: unable to lock password file\n", Prog);
exit (1);
}
if (! pw_open (O_RDWR)) {
fprintf (stderr, "%s: unable to open password file\n", Prog);
fail_exit (1);
}
#ifdef SHADOWPWD
if (! spw_lock ()) {
fprintf (stderr, "%s: cannot lock shadow password file\n",
Prog);
fail_exit (1);
}
if (! spw_open (O_RDWR)) {
fprintf (stderr, "%s: cannot open shadow password file\n",
Prog);
fail_exit (1);
}
#endif
}
/*
* usr_update - create the user entries
*
* usr_update() creates the password file entries for this user
* and will update the group entries if required.
*/
usr_update ()
{
struct passwd pwent;
struct passwd *pwd;
#ifdef SHADOWPWD
struct spwd spent;
struct spwd *spwd;
#endif
char old_auth[BUFSIZ];
char auth_buf[BUFSIZ];
old_auth[0] = '\0';
/*
* Locate the entry in /etc/passwd, which MUST exist.
*/
pwd = pw_locate (user_name);
pwent = *pwd;
new_pwent (&pwent);
#ifdef SHADOWPWD
/*
* Locate the entry in /etc/shadow. It doesn't have to
* exist, and won't be created if it doesn't.
*/
if (spwd = spw_locate (user_name)) {
spent = *spwd;
new_spent (&spent);
strcpy (old_auth, spent.sp_pwdp);
} else {
strcpy (old_auth, pwent.pw_passwd);
}
#else
strcpy (old_auth, pwent.pw_passwd);
#endif
if (lflg || (Aflg && strcmp (old_auth, user_auth) != 0)) {
convert_auth (user_auth, old_auth, auth_arg);
delete_auths (old_auth, user_auth, auth_buf);
if (auth_buf[0] && pw_auth (auth_buf, user_name,
PW_DELETE, 0)) {
fprintf (stderr,
"%s: error deleting authentication method\n",
Prog);
#ifdef USE_SYSLOG
syslog (LOG_ERR, "error deleting auth for `%s'\n",
user_name);
#endif
fail_exit (1);
}
add_auths (old_auth, user_auth, auth_buf);
if (auth_buf[0] == '@' && pw_auth (auth_buf,
lflg ? user_newname:user_name, PW_ADD, 0)) {
fprintf (stderr,
"%s: error adding authentication method\n",
Prog);
#ifdef USE_SYSLOG
syslog (LOG_ERR, "error adding auth for `%s'\n",
lflg ? user_newname:user_name);
#endif
fail_exit (1);
}
update_auths (old_auth, user_auth, auth_buf);
if (lflg && auth_buf[0] == '@' && pw_auth (auth_buf,
user_newname, PW_CHANGE, user_name)) {
fprintf (stderr,
"%s: error changing authentication method\n",
Prog);
#ifdef USE_SYSLOG
syslog (LOG_ERR, "error changing auth for `%s'\n",
lflg ? user_newname:user_name);
#endif
fail_exit (1);
}
#ifdef SHADOWPWD
spent.sp_pwdp = user_auth;
#else
pwent.pw_passwd = user_auth;
#endif
}
if (lflg || uflg || gflg || cflg || dflg || sflg || Aflg) {
if (! pw_update (&pwent)) {
fprintf (stderr, "%s: error changing password entry\n",
Prog);
fail_exit (1);
}
if (lflg && ! pw_remove (user_name)) {
fprintf (stderr, "%s: error removing password entry\n",
Prog);
fail_exit (1);
}
#if defined(DBM) || defined(NDBM)
if (access ("/etc/passwd.pag", 0) == 0) {
if (! pw_dbm_update (&pwent)) {
fprintf (stderr,
"%s: error adding password dbm entry\n",
Prog);
fail_exit (1);
}
if (lflg && (pwd = getpwnam (user_name)) &&
! pw_dbm_remove (pwd)) {
fprintf (stderr,
"%s: error removing passwd dbm entry\n",
Prog);
fail_exit (1);
}
}
#endif
}
#ifdef SHADOWPWD
if (spwd && (lflg || eflg || fflg || Aflg)) {
if (! spw_update (&spent)) {
fprintf (stderr,
"%s: error adding new shadow password entry\n",
Prog);
fail_exit (1);
}
if (lflg && ! spw_remove (user_name)) {
fprintf (stderr,
"%s: error removing shadow password entry\n",
Prog);
fail_exit (1);
}
}
#ifdef NDBM
if (spwd && access ("/etc/shadow.pag", 0) == 0) {
if (! sp_dbm_update (&spent)) {
fprintf (stderr,
"%s: error updating shadow passwd dbm entry\n",
Prog);
fail_exit (1);
}
if (lflg && ! sp_dbm_remove (user_name)) {
fprintf (stderr,
"%s: error removing shadow passwd db entry\n",
Prog);
fail_exit (1);
}
}
#endif /* NDBM */
#endif /* SHADOWPWD */
if (Gflg || lflg)
grp_update ();
}
/*
* move_home - move the user's home directory
*
* move_home() moves the user's home directory to a new location.
* The files will be copied if the directory cannot simply be
* renamed.
*/
move_home ()
{
struct stat sb;
if (mflg && stat (user_home, &sb) == 0) {
if (access (user_newhome, 0) == 0) {
fprintf (stderr, "%s: directory %s exists\n",
Prog, user_newhome);
fail_exit (12);
} else if (rename (user_home, user_newhome)) {
if (errno == EXDEV) {
if (mkdir (user_newhome, sb.st_mode & 0777)) {
fprintf (stderr,
"%s: can't create %s\n",
Prog, user_newhome);
}
if (chown (user_newhome,
sb.st_uid, sb.st_gid)) {
fprintf (stderr, "%s: can't chown %s\n",
Prog, user_newhome);
rmdir (user_newhome);
fail_exit (12);
}
#ifdef DIR_ANY
if (copy_tree (user_home, user_newhome,
uflg ? user_newid:-1,
gflg ? user_newgid:-1,
user_id, user_gid) == 0 &&
remove_tree (user_home) == 0 &&
rmdir (user_home) == 0)
return;
(void) remove_tree (user_newhome);
(void) rmdir (user_newhome);
#else
return;
#endif
}
fprintf (stderr,
"%s: cannot rename directory %s to %s\n",
Prog, user_home, user_newhome);
fail_exit (12);
}
}
if (uflg || gflg)
chown (dflg ? user_newhome:user_home,
uflg ? user_newid:user_id,
gflg ? user_newgid:user_gid);
}
/*
* update_files - update the lastlog and faillog files
*/
void
update_files ()
{
struct lastlog ll;
struct faillog fl;
int fd;
/*
* Relocate the "lastlog" entries for the user. The old entry
* is left alone in case the UID was shared. It doesn't hurt
* anything to just leave it be.
*/
if ((fd = open ("/usr/adm/lastlog", O_RDWR)) != -1) {
lseek (fd, (long) user_id * sizeof ll, 0);
if (read (fd, &ll, sizeof ll) == sizeof ll) {
lseek (fd, (long) user_newid * sizeof ll, 0);
write (fd, &ll, sizeof ll);
}
close (fd);
}
/*
* Relocate the "faillog" entries in the same manner.
*/
if ((fd = open (FAILFILE, O_RDWR)) != -1) {
lseek (fd, (long) user_id * sizeof fl, 0);
if (read (fd, &fl, sizeof fl) == sizeof fl) {
lseek (fd, (long) user_newid * sizeof ll, 0);
write (fd, &fl, sizeof fl);
}
close (fd);
}
}
/*
* fail_exit - exit with an error code after unlocking files
*/
fail_exit (code)
int code;
{
(void) gr_unlock ();
#ifdef SHADOWGRP
(void) sgr_unlock ();
#endif
#ifdef SHADOWPWD
(void) spw_unlock ();
#endif
(void) pw_unlock ();
exit (code);
}
/*
* main - usermod command
*/
main (argc, argv)
int argc;
char **argv;
{
/*
* Get my name so that I can use it to report errors.
*/
if (Prog = strrchr (argv[0], '/'))
Prog++;
else
Prog = argv[0];
#ifdef USE_SYSLOG
openlog (Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
#endif
/*
* The open routines for the NDBM files don't use read-write
* as the mode, so we have to clue them in.
*/
#ifdef NDBM
pw_dbm_mode = O_RDWR;
#ifdef SHADOWPWD
sp_dbm_mode = O_RDWR;
#endif
gr_dbm_mode = O_RDWR;
#ifdef SHADOWGRP
sg_dbm_mode = O_RDWR;
#endif
#endif /* NDBM */
process_flags (argc, argv);
/*
* Do the hard stuff - open the files, change the user entries,
* change the home directory, then close and update the files.
*/
open_files ();
usr_update ();
close_files ();
if (mflg)
move_home ();
if (uflg) {
update_files ();
/*
* Change the UID on all of the files owned by `user_id'
* to `user_newid' in the user's home directory.
*/
chown_tree (dflg ? user_newhome:user_home,
user_id, user_newid,
user_gid, gflg ? user_newgid:user_gid);
}
exit (0);
/*NOTREACHED*/
}