Shadow Login Suite, patch 4
John F Haugh II
jfh at rpp386.cactus.org
Tue Jun 11 21:27:15 AEST 1991
[ Either this didn't make it off this site or I'm in really bad shape. ]
This is patch #4 for the current beta test release of the shadow login
suite. It adds three new commands, useradd, userdel and usermod. The
documentation that I promised in patch #3 hasn't been written. I've been
busy planning a wedding (mine) and I've not had the time. You may refer
either to the source code or your nearest SVR4 documentation for the
behavior of these commands.
It is known that there are bugs in these three new commands. Since they
are the newest code in the entire suite, that's to be expected. Have
patience and I'll get it fixed. Some of the bugs have been fixed already,
and other fixes are due out in patch #6 (patch #5 is currently being
verified by Chip).
Also added was a "patchlevel.h" file. So many of you asked for it, so
I've provided it ...
--
*** /dev/null Fri Jun 7 11:02:59 1991
--- patchlevel.h Fri Jun 7 11:13:28 1991
***************
*** 0 ****
--- 1,13 ----
+ /*
+ * Copyright 1991, 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.
+ */
+
+ #define RELEASE 3
+ #define PATCHLEVEL 4
*** rel3/config.h Thu Jun 6 09:35:02 1991
--- config.h Fri Jun 7 11:11:26 1991
***************
*** 12,18 ****
/*
* Configuration file for login.
*
! * @(#)config.h 3.7 08:57:09 5/30/91
*/
/*
--- 12,18 ----
/*
* Configuration file for login.
*
! * @(#)config.h 3.8 11:11:17 6/7/91
*/
/*
***************
*** 31,40 ****
--- 31,44 ----
* for getpwuid() and getpwnam(). This provides compatibility for
* privileged applications which are shadow-ignorant. YOU ARE
* ENCOURAGED TO NOT USE THIS OPTION UNLESS ABSOLUTELY NECESSARY.
+ *
+ * Define SHADOWGRP to user shadowed group files. This feature adds
+ * the concept of a group administrator.
*/
#define SHADOWPWD
#undef AUTOSHADOW
+ #define SHADOWGRP
/*
* Define DOUBLESIZE to use 16 character passwords
***************
*** 270,275 ****
--- 274,282 ----
#define GETPWENT /* Define if you want my GETPWENT(3) routines */
#define GETGRENT /* Define if you want my GETGRENT(3) routines */
#define NEED_AL64 /* Define if library does not include a64l() */
+ #define NEED_MKDIR /* Define if system does not have mkdir() */
+ #define NEED_RMDIR /* Define if system does not have rmdir() */
+ #define NEED_RENAME /* Define if system does not have rename() */
#undef NO_STRSTR /* Define if library does not include strstr() */
/*
*** rel3/Makefile Thu Jun 6 09:35:02 1991
--- Makefile Fri Jun 7 11:12:38 1991
***************
*** 8,16 ****
# and conspicuously displayed on all copies of object code or
# distribution media.
#
! # @(#)Makefile 3.10 09:05:50 - Shadow password system
#
! # @(#)Makefile 3.10 09:05:50 5/30/91
#
SHELL = /bin/sh
--- 8,16 ----
# and conspicuously displayed on all copies of object code or
# distribution media.
#
! # @(#)Makefile 3.11 11:11:42 - Shadow password system
#
! # @(#)Makefile 3.11 11:11:42 6/7/91
#
SHELL = /bin/sh
***************
*** 127,135 ****
utmp.c valid.c port.c newgrp.c gpmain.c grent.c mkpasswd.c pwpack.c \
chfn.c chsh.c chage.c rad64.c encrypt.c chpasswd.c shadowio.c pwio.c \
newusers.c groupio.c fields.c pwdbm.c grpack.c grdbm.c sppack.c \
! spdbm.c dpmain.c gshadow.c gsdbm.c gspack.c sgroupio.c
! FILES1 = README newgrp.c Makefile config.h pwunconv.c obscure.c age.c id.c
FILES2 = passwd.c port.c lmain.c mkpasswd.c sulogin.c pwpack.c dialup.c \
sulog.c getpass.c
--- 127,137 ----
utmp.c valid.c port.c newgrp.c gpmain.c grent.c mkpasswd.c pwpack.c \
chfn.c chsh.c chage.c rad64.c encrypt.c chpasswd.c shadowio.c pwio.c \
newusers.c groupio.c fields.c pwdbm.c grpack.c grdbm.c sppack.c \
! spdbm.c dpmain.c gshadow.c gsdbm.c gspack.c sgroupio.c useradd.c \
! userdel.c patchlevel.h usermod.c
! FILES1 = README newgrp.c Makefile config.h pwunconv.c obscure.c age.c id.c \
! patchlevel.h
FILES2 = passwd.c port.c lmain.c mkpasswd.c sulogin.c pwpack.c dialup.c \
sulog.c getpass.c
***************
*** 147,152 ****
--- 149,156 ----
FILES7 = groupio.c shadowio.c sgroupio.c
+ FILES8 = useradd.c userdel.c usermod.c
+
MAN_1 = chage.1 chfn.1 chsh.1 login.1 passwd.1 su.1
MAN_3 = shadow.3
MAN_4 = faillog.4 passwd.4 porttime.4 shadow.4
***************
*** 155,161 ****
DOCS = $(MAN_1) $(MAN_3) $(MAN_4) $(MAN_8)
BINS = su login pwconv pwunconv passwd sulogin faillog newgrp gpasswd \
! mkpasswd chfn chsh chage chpasswd newusers dpasswd id
all: $(BINS) $(DOCS)
--- 159,166 ----
DOCS = $(MAN_1) $(MAN_3) $(MAN_4) $(MAN_8)
BINS = su login pwconv pwunconv passwd sulogin faillog newgrp gpasswd \
! mkpasswd chfn chsh chage chpasswd newusers dpasswd id useradd \
! userdel usermod
all: $(BINS) $(DOCS)
***************
*** 214,220 ****
lint: su.lint login.lint pwconv.lint pwunconv.lint passwd.lint sulogin.lint \
faillog.lint newgrp.lint gpasswd.lint mkpasswd.lint chfn.lint \
! chsh.lint chage.lint dpasswd.lint $(ALLSRCS:.c=.L)
tags: $(ALLSRCS)
$(TAGS) $(ALLSRCS)
--- 219,226 ----
lint: su.lint login.lint pwconv.lint pwunconv.lint passwd.lint sulogin.lint \
faillog.lint newgrp.lint gpasswd.lint mkpasswd.lint chfn.lint \
! chsh.lint chage.lint dpasswd.lint id.lint useradd.lint userdel.lint \
! usermod.lint $(ALLSRCS:.c=.L)
tags: $(ALLSRCS)
$(TAGS) $(ALLSRCS)
***************
*** 327,332 ****
--- 333,356 ----
id.lint: id.c
$(LINT) $(LINTFLAGS) id.c > id.lint
+ useradd: useradd.o libshadow.a
+ $(CC) -o useradd $(LDFLAGS) useradd.o libshadow.a $(LIBS)
+
+ useradd.lint: useradd.c
+ $(LINT) $(LINTFLAGS) useradd.c > useradd.lint
+
+ userdel: userdel.o libshadow.a
+ $(CC) -o userdel $(LDFLAGS) userdel.o libshadow.a $(LIBS)
+
+ userdel.lint: userdel.c
+ $(LINT) $(LINTFLAGS) userdel.c > userdel.lint
+
+ usermod: usermod.o libshadow.a
+ $(CC) -o usermod $(LDFLAGS) usermod.o libshadow.a $(LIBS)
+
+ usermod.lint: usermod.c
+ $(LINT) $(LINTFLAGS) usermod.c > usermod.lint
+
sulog.o: config.h
susetup.c: setup.c
***************
*** 395,400 ****
--- 419,426 ----
id.o: pwd.h
newusers.o: config.h shadow.h pwd.h
dpmain.o: dialup.h
+ useradd.o: config.h shadow.h pwd.h
+ userdel.o: config.h shadow.h pwd.h
clean:
-rm -f *.o a.out core npasswd nshadow *.pag *.dir
***************
*** 410,416 ****
done
shar: login.sh.1 login.sh.2 login.sh.3 login.sh.4 login.sh.5 login.sh.6 \
! login.sh.7 login.sh.8
login.sh.1: $(FILES1) Makefile
shar -a $(FILES1) > login.sh.1
--- 436,442 ----
done
shar: login.sh.1 login.sh.2 login.sh.3 login.sh.4 login.sh.5 login.sh.6 \
! login.sh.7 login.sh.8 login.sh.9
login.sh.1: $(FILES1) Makefile
shar -a $(FILES1) > login.sh.1
***************
*** 433,437 ****
login.sh.7: $(FILES7) Makefile
shar -a $(FILES7) > login.sh.7
! login.sh.8: $(DOCS) Makefile
! shar -a $(DOCS) > login.sh.8
--- 459,466 ----
login.sh.7: $(FILES7) Makefile
shar -a $(FILES7) > login.sh.7
! login.sh.8: $(FILES8) Makefile
! shar -a $(FILES8) > login.sh.8
!
! login.sh.9: $(DOCS) Makefile
! shar -a $(DOCS) > login.sh.9
*** /dev/null Fri Jun 7 11:02:59 1991
--- useradd.c Fri Jun 7 11:10:07 1991
***************
*** 0 ****
--- 1,1245 ----
+ /*
+ * Copyright 1991, 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.
+ */
+
+ #ifndef lint
+ static char sccsid[] = "@(#)useradd.c 3.1 11:08:18 6/7/91";
+ #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"
+ #include "shadow.h"
+
+ #ifdef USE_SYSLOG
+ #include <syslog.h>
+
+ #ifndef LOG_WARN
+ #define LOG_WARN LOG_WARNING
+ #endif
+ #endif
+
+ gid_t def_group;
+ char def_home[BUFSIZ];
+ char def_shell[BUFSIZ];
+ long def_inactive;
+ long def_expire;
+ char def_file[] = "/etc/default/useradd";
+
+ #ifndef NGROUPS_MAX
+ #define NGROUPS_MAX 64
+ #endif
+
+ char user_name[BUFSIZ];
+ uid_t user_id;
+ gid_t user_gid;
+ char user_comment[BUFSIZ];
+ char user_home[BUFSIZ];
+ char user_shell[BUFSIZ];
+ long user_expire;
+ int user_ngroups;
+ gid_t user_groups[NGROUPS_MAX];
+
+ char *Prog;
+
+ 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 bflg; /* new default root of home directory */
+ 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 kflg; /* specify a directory to fill new user directory */
+ int fflg; /* days until account with expired password is locked */
+ int eflg; /* days after password changed before it becomes expired */
+ int Dflg; /* set/show new user default values */
+
+ #if defined(DBM) || defined(NDBM)
+ extern int pw_dbm_mode;
+ #endif
+ #ifdef NDBM
+ extern int sp_dbm_mode;
+ extern int gr_dbm_mode;
+ extern int sg_dbm_mode;
+ #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 int pw_lock();
+ extern int pw_unlock();
+ extern int pw_rewind();
+ extern int pw_open();
+
+ extern int spw_lock();
+ extern int spw_unlock();
+ extern int spw_open();
+
+ #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_MKDIR
+ /*
+ * mkdir - create a directory
+ *
+ * mkdir is provided for systems which do not include the mkdir()
+ * system call.
+ */
+
+ int
+ mkdir (dir, mode)
+ char *dir;
+ int mode;
+ {
+ int status;
+
+ if (fork ()) {
+ while (wait (&status) != -1)
+ ;
+
+ return status >> 8;
+ }
+ #ifdef USE_SYSLOG
+ closelog ();
+ #endif
+ close (2);
+ open ("/dev/null", O_WRONLY);
+ umask (0777 & ~ mode);
+ execl ("/bin/mkdir", "mkdir", dir, 0);
+ _exit (128);
+ }
+ #endif
+ #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. this is
+ * a chauvanistic program - it only takes date input in
+ * the standard USA format.
+ */
+
+ if (sscanf (str, "%d/%d/%d%c", &month, &day, &year, slop) != 3)
+ return -1;
+
+ /*
+ * 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;
+ }
+
+ /*
+ * get_defaults - read the defaults file
+ *
+ * get_defaults() reads the defaults file for this command. It sets
+ * the various values from the file, or uses built-in default values
+ * if the file does not exist.
+ */
+
+ void
+ get_defaults ()
+ {
+ FILE *fp;
+ char buf[BUFSIZ];
+ char *cp;
+ struct group *grp;
+
+ /*
+ * Open the defaults file for reading.
+ */
+
+ if (! (fp = fopen (def_file, "r"))) {
+
+ /*
+ * No defaults file - set up the defaults that are given
+ * in the documentation.
+ */
+
+ def_group = 1;
+ strcpy (def_home, "/home");
+ def_inactive = 0;
+ def_expire = 0;
+ return;
+ }
+
+ /*
+ * Read the file a line at a time. Only the lines that have
+ * relevant values are used, everything else can be ignored.
+ */
+
+ while (fgets (buf, BUFSIZ, fp)) {
+ if (cp = strrchr (buf, '\n'))
+ *cp = '\0';
+
+ /*
+ * Primary GROUP identifier
+ */
+
+ if (strncmp ("GROUP=", buf, 6) == 0) {
+ cp = buf + 6;
+ if (isdigit (*cp))
+ def_group = atoi (cp);
+ else if (grp = getgrnam (cp))
+ def_group = grp->gr_gid;
+ else
+ fprintf (stderr, "%s: unknown group %s\n", cp);
+ }
+
+ /*
+ * Default HOME filesystem
+ */
+
+ else if (strncmp ("HOME=", buf, 5) == 0) {
+ strncpy (def_home, buf + 5, BUFSIZ);
+ }
+
+ /*
+ * Default Login Shell command
+ */
+
+ else if (strncmp ("SHELL=", buf, 6) == 0) {
+ strncpy (def_shell, buf + 6, BUFSIZ);
+ }
+
+ /*
+ * Default Password Inactive value
+ */
+
+ else if (strncmp ("INACTIVE=", buf, 9) == 0) {
+ def_inactive = atoi (buf + 9);
+ }
+
+ /*
+ * Default Password Expiration value
+ */
+
+ else if (strncmp ("EXPIRE=", buf, 7) == 0) {
+ def_expire = atoi (buf + 7);
+ }
+ }
+ }
+
+ /*
+ * show_defaults - show the contents of the defaults file
+ *
+ * show_defaults() displays the values that are used from the default
+ * file and the built-in values.
+ */
+
+ void
+ show_defaults ()
+ {
+ printf ("GROUP=%d\n", def_group);
+ printf ("HOME=%s\n", def_home);
+ printf ("INACTIVE=%d\n", def_inactive);
+ printf ("EXPIRE=%d\n", def_expire);
+ }
+
+ /*
+ * set_defaults - write new defaults file
+ *
+ * set_defaults() re-writes the defaults file using the values that
+ * are currently set. Duplicated lines are pruned, missing lines are
+ * added, and unrecognized lines are copied as is.
+ */
+
+ int
+ set_defaults ()
+ {
+ FILE *ifp;
+ FILE *ofp;
+ char buf[BUFSIZ];
+ static char new_file[] = "/etc/default/nuaddXXXXXX";
+ char *cp;
+ int out_group = 0;
+ int out_home = 0;
+ int out_inactive = 0;
+ int out_expire = 0;
+
+ /*
+ * Create a temporary file to copy the new output to.
+ */
+
+ mktemp (new_file);
+ if (! (ofp = fopen (new_file, "w"))) {
+ fprintf (stderr, "%s: cannot create new defaults file\n", Prog);
+ return -1;
+ }
+
+ /*
+ * Open the existing defaults file and copy the lines to the
+ * temporary files, using any new values. Each line is checked
+ * to insure that it is not output more than once.
+ */
+
+ if (ifp = fopen (def_file, "r")) {
+ while (fgets (buf, BUFSIZ, ifp)) {
+ if (cp = strrchr (buf, '\n'))
+ *cp = '\0';
+
+ if (strncmp ("GROUP=", buf, 6) == 0) {
+ if (! out_group)
+ fprintf (ofp, "GROUP=%d\n", def_group);
+
+ out_group++;
+ } else if (strncmp ("HOME=", buf, 5) == 0) {
+ if (! out_home)
+ fprintf (ofp, "HOME=%s\n", def_home);
+
+ out_home++;
+ } else if (strncmp ("INACTIVE=", buf, 9) == 0) {
+ if (! out_inactive)
+ fprintf (ofp, "INACTIVE=%d\n",
+ def_inactive);
+
+ out_inactive++;
+ } else if (strncmp ("EXPIRE=", buf, 7) == 0) {
+ if (! out_expire)
+ fprintf (ofp, "EXPIRE=%d\n",
+ def_expire);
+
+ out_expire++;
+ } else
+ fprintf (ofp, "%s\n", buf);
+ }
+ fclose ((FILE *) ifp);
+ }
+
+ /*
+ * Check each line to insure that every line was output. This
+ * causes new values to be added to a file which did not previously
+ * have an entry for that value.
+ */
+
+ if (! out_group)
+ fprintf (ofp, "GROUP=%d\n", def_group);
+
+ if (! out_home)
+ fprintf (ofp, "HOME=%s\n", def_home);
+
+ if (! out_inactive)
+ fprintf (ofp, "INACTIVE=%d\n", def_inactive);
+
+ if (! out_expire)
+ fprintf (ofp, "EXPIRE=%d\n", def_expire);
+
+ /*
+ * Flush and close the file. Check for errors to make certain
+ * the new file is intact.
+ */
+
+ (void) fflush (ofp);
+ if (ferror (ofp) || fclose ((FILE *) ofp)) {
+ unlink (new_file);
+ return -1;
+ }
+
+ /*
+ * Rename the current default file to its backup name.
+ */
+
+ sprintf (buf, "%s-", def_file);
+ if (rename (def_file, buf) && errno != ENOENT) {
+ sprintf (buf, "%s: rename: %s", Prog, def_file);
+ perror (buf);
+ unlink (new_file);
+ return -1;
+ }
+
+ /*
+ * Rename the new default file to its correct name.
+ */
+
+ if (rename (new_file, def_file)) {
+ sprintf (buf, "%s: rename: %s", Prog, new_file);
+ perror (buf);
+ return -1;
+ }
+ #ifdef USE_SYSLOG
+ syslog (LOG_INFO, "defaults: group=%d, home=%s, inactive=%d, expire=%d",
+ def_group, def_home, def_inactive, def_expire);
+ #endif
+ return 0;
+ }
+
+ /*
+ * 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:\tuseradd [-u uid [-o]] [-g group] [-G group,...] \n");
+ fprintf (stderr,
+ "\t\t[-d home] [-s shell] [-c comment] [-m [-k template]]\n");
+ fprintf (stderr,
+ "\t\t[-f inactive] [-e expire] name\n");
+
+ fprintf (stderr,
+ "\tuseradd -D [-g group] [-b base] [-f inactive] [-e expire]\n");
+
+ exit (1);
+ }
+
+ /*
+ * 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;
+ {
+ memset (pwent, 0, sizeof *pwent);
+ pwent->pw_name = user_name;
+ pwent->pw_passwd = "*";
+ pwent->pw_age = "";
+ pwent->pw_uid = user_id;
+ pwent->pw_gid = user_gid;
+ pwent->pw_gecos = user_comment;
+ pwent->pw_comment = "";
+ pwent->pw_dir = user_home;
+ pwent->pw_shell = user_shell;
+ }
+
+ /*
+ * 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;
+ {
+ memset (spent, 0, sizeof *spent);
+ spent->sp_namp = user_name;
+ spent->sp_pwdp = "!";
+ spent->sp_lstchg = 0;
+ spent->sp_min = 0;
+ spent->sp_max = def_expire;
+ spent->sp_warn = 0;
+ spent->sp_inact = def_inactive;
+ spent->sp_expire = user_expire;
+ }
+
+ /*
+ * 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;
+ struct group *grp;
+ struct sgrp *sgrp;
+
+ /*
+ * 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);
+ exit (1);
+ }
+ if (! gr_open (O_RDWR)) {
+ fprintf (stderr, "%s: error opening group file\n", Prog);
+ exit (1);
+ }
+ #ifdef SHADOWGRP
+ if (! sgr_lock ()) {
+ fprintf (stderr, "%s: error locking shadow group file\n", Prog);
+ exit (1);
+ }
+ if (! sgr_open (O_RDWR)) {
+ fprintf (stderr, "%s: error opening shadow group file\n", Prog);
+ exit (1);
+ }
+ #endif
+
+ /*
+ * 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;
+
+ if (i == user_ngroups)
+ continue;
+
+ /*
+ * Add the username to the list of group members and
+ * update the group entry to reflect the change.
+ */
+
+ grp->gr_mem = add_list (grp->gr_mem, user_name);
+ if (! gr_update (grp)) {
+ fprintf (stderr, "%s: error adding new group entry\n",
+ Prog);
+ 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);
+ exit (1);
+ }
+ #endif
+ #ifdef USE_SYSLOG
+ syslog (LOG_INFO, "add `%s' to group `%s'\n",
+ user_name, grp->gr_name);
+ #endif
+ }
+
+ #ifdef SHADOWGRP
+ /*
+ * Scan through the entire shadow group file looking for the groups
+ * that the user is a member of. The administrative list isn't
+ * modified.
+ */
+
+ for (sgr_rewind (), sgrp = sgr_next ();sgrp;sgrp = sgr_next ()) {
+
+ /*
+ * 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;
+ }
+ if (i == user_ngroups)
+ continue;
+
+ /*
+ * Add the username to the list of group members and
+ * update the group entry to reflect the change.
+ */
+
+ sgrp->sg_mem = add_list (sgrp->sg_mem, user_name);
+ if (! sgr_update (sgrp)) {
+ fprintf (stderr, "%s: error adding new group entry\n",
+ Prog);
+ exit (1);
+ }
+ #ifdef NDBM
+ /*
+ * Update the DBM group file with the new entry as well.
+ */
+
+ if (! sgr_dbm_update (sgrp)) {
+ fprintf (stderr, "%s: cannot add new dbm group entry\n",
+ Prog);
+ exit (1);
+ }
+ #endif
+ #ifdef USE_SYSLOG
+ syslog (LOG_INFO, "add `%s' to shadow group `%s'\n",
+ user_name, sgrp->sg_name);
+ #endif
+ }
+ #endif
+ }
+
+ /*
+ * find_new_uid - find the next available UID
+ *
+ * find_new_uid() locates the next highest unused UID in the password
+ * file, or checks the given user ID against the existing ones for
+ * uniqueness.
+ */
+
+ int
+ find_new_uid ()
+ {
+ struct passwd *pwd;
+
+ /*
+ * Start with some UID value if the user didn't provide us with
+ * one already.
+ */
+
+ if (! uflg)
+ user_id = 100;
+
+ /*
+ * Search the entire password file, either looking for this
+ * UID (if the user specified one with -u) or looking for the
+ * largest unused value.
+ */
+
+ for (pw_rewind (), pwd = pw_next ();pwd;pwd = pw_next ()) {
+ if (strcmp (user_name, pwd->pw_name) == 0) {
+ fprintf (stderr, "%s: name %s is not unique\n",
+ Prog, user_name);
+ exit (1);
+ }
+ if (uflg && user_id == pwd->pw_uid) {
+ fprintf (stderr, "%s: uid %d is not unique\n",
+ Prog, user_id);
+ exit (1);
+ }
+ if (! uflg && pwd->pw_uid >= user_id)
+ user_id = pwd->pw_uid + 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;
+ int anyflag = 0;
+ int arg;
+
+ while ((arg = getopt (argc, argv, "Du:og:G:d:s:c:mk:f:e:b:")) != EOF) {
+ switch (arg) {
+ case 'b':
+ bflg++;
+ if (! Dflg)
+ usage ();
+
+ strncpy (def_home, optarg, BUFSIZ);
+ break;
+ case 'c':
+ cflg++;
+ strncpy (user_comment, optarg, BUFSIZ);
+ break;
+ case 'd':
+ dflg++;
+ strncpy (user_home, optarg, BUFSIZ);
+ break;
+ case 'D':
+ if (anyflag)
+ usage ();
+
+ Dflg++;
+ break;
+ case 'e':
+ eflg++;
+ if (Dflg)
+ def_expire = atoi (optarg);
+ else {
+ user_expire = strtoday (optarg);
+ #ifdef ITI_AGING
+ user_expire *= DAY;
+ #endif
+ }
+ break;
+ case 'f':
+ fflg++;
+ def_inactive = atoi (optarg);
+ break;
+ case 'g':
+ gflg++;
+ if (isdigit (optarg[0]))
+ grp = getgrgid (atoi (optarg));
+ else
+ grp = getgrnam (optarg);
+
+ if (! grp) {
+ fprintf (stderr,
+ "%s: unknown group %s\n",
+ optarg);
+ exit (1);
+ }
+ def_group = grp->gr_gid;
+ break;
+ case 'G':
+ Gflg++;
+ if (get_groups (optarg))
+ exit (1);
+
+ break;
+ case 'k':
+ if (! mflg)
+ usage ();
+
+ kflg++;
+ break;
+ case 'm':
+ mflg++;
+ break;
+ case 'o':
+ if (! uflg)
+ usage ();
+
+ oflg++;
+ break;
+ case 's':
+ sflg++;
+ strncpy (user_shell, optarg, BUFSIZ);
+ break;
+ case 'u':
+ uflg++;
+ user_id = atoi (optarg);
+ break;
+ default:
+ usage ();
+ }
+ anyflag++;
+ }
+ if (! Dflg && optind == argc - 1)
+ strcpy (user_name, argv[argc - 1]);
+
+ if (! dflg)
+ sprintf (user_home, "%s/%s", def_home, user_name);
+
+ if (! gflg)
+ user_gid = def_group;
+
+ if (Dflg) {
+ if (optind != argc)
+ usage ();
+
+ if (uflg || oflg || Gflg || dflg ||
+ sflg || cflg || mflg || kflg)
+ usage ();
+ } else {
+ if (optind != argc - 1)
+ usage ();
+ }
+ }
+
+ /*
+ * 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);
+ exit (1);
+ }
+ if (! spw_close ()) {
+ fprintf (stderr, "%s: cannot rewrite shadow password file\n",
+ Prog);
+ exit (1);
+ }
+ if (user_ngroups > 0) {
+ if (! gr_close ()) {
+ fprintf (stderr, "%s: cannot rewrite group file\n",
+ Prog);
+ exit (1);
+ }
+ (void) gr_unlock ();
+ #ifdef SHADOWGRP
+ if (! sgr_close ()) {
+ fprintf (stderr, "%s: cannot rewrite shadow group file\n",
+ Prog);
+ exit (1);
+ }
+ #endif
+ }
+ (void) pw_unlock ();
+ }
+
+ /*
+ * 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);
+ exit (1);
+ }
+ if (! spw_lock ()) {
+ fprintf ("%s: cannot lock shadow password file\n", Prog);
+ exit (1);
+ }
+ if (! spw_open (O_RDWR)) {
+ fprintf ("%s: cannot open shadow password file\n", Prog);
+ exit (1);
+ }
+ }
+
+ /*
+ * 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 spwd spent;
+
+ if (! oflg)
+ find_new_uid ();
+
+ new_pwent (&pwent);
+ if (! pw_update (&pwent)) {
+ fprintf (stderr, "%s: error adding new password entry\n", Prog);
+ exit (1);
+ }
+ new_spent (&spent);
+ if (! spw_update (&spent)) {
+ fprintf (stderr, "%s: error adding new shadow password entry\n",
+ Prog);
+ exit (1);
+ }
+ #if defined(DBM) || defined(NDBM)
+ if (access ("/etc/passwd.pag", 0) == 0 && ! pw_dbm_update (&pwent)) {
+ fprintf (stderr, "%s: error updating password dbm entry\n",
+ Prog);
+ exit (1);
+ }
+ #endif
+ #ifdef NDBM
+ if (access ("/etc/shadow.pag", 0) == 0 && ! sp_dbm_update (&spent)) {
+ fprintf (stderr, "%s: error updating shadow passwd dbm entry\n",
+ Prog);
+ exit (1);
+ }
+ #endif
+ #ifdef USE_SYSLOG
+ syslog (LOG_INFO,
+ "new user: name=%s, uid=%d, gid=%d, home=%s, shell=%s\n",
+ user_name, user_id, user_gid, user_home, user_shell);
+ #endif
+ if (user_ngroups > 0)
+ grp_update ();
+ }
+
+ /*
+ * create_home - create the user's home directory
+ *
+ * create_home() creates the user's home directory if it does not
+ * already exist. It will be created mode 755 owned by the user
+ * with the user's default group.
+ */
+
+ create_home ()
+ {
+ if (access (user_home, 0)) {
+ if (mkdir (user_home, 0755)) {
+ fprintf (stderr, "%s: cannot create directory %s\n",
+ Prog, user_home);
+ exit (1);
+ }
+ chown (user_home, user_id, user_gid);
+ chmod (user_home, 0755);
+ }
+ }
+
+ /*
+ * main - useradd 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 DBM files don't use read-write
+ * as the mode, so we have to clue them in.
+ */
+
+ #if defined(DBM) || defined(NDBM)
+ pw_dbm_mode = O_RDWR;
+ #endif
+ #ifdef NDBM
+ sp_dbm_mode = O_RDWR;
+ gr_dbm_mode = O_RDWR;
+ #ifdef SHADOWGRP
+ sg_dbm_mode = O_RDWR;
+ #endif
+ #endif
+ get_defaults ();
+
+ process_flags (argc, argv);
+
+ /*
+ * See if we are messing with the defaults file, or creating
+ * a new user.
+ */
+
+ if (Dflg) {
+ if (gflg || bflg || fflg || eflg)
+ exit (set_defaults () ? 1:0);
+
+ show_defaults ();
+ exit (0);
+ }
+
+ /*
+ * Start with a quick check to see if the user exists.
+ */
+
+ if (getpwnam (user_name)) {
+ fprintf (stderr, "%s: user %s exists\n", Prog, user_name);
+ exit (1);
+ }
+
+ /*
+ * Do the hard stuff - open the files, create the user entries,
+ * create the home directory, then close and update the files.
+ */
+
+ open_files ();
+
+ usr_update ();
+
+ if (mflg)
+ create_home ();
+
+ close_files ();
+ exit (0);
+ /*NOTREACHED*/
+ }
*** /dev/null Fri Jun 7 11:02:59 1991
--- userdel.c Fri Jun 7 11:10:08 1991
***************
*** 0 ****
--- 1,499 ----
+ /*
+ * Copyright 1991, 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.
+ */
+
+ #ifndef lint
+ static char sccsid[] = "@(#)userdel.c 3.1 11:08:47 6/7/91";
+ #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"
+ #include "shadow.h"
+
+ #ifdef USE_SYSLOG
+ #include <syslog.h>
+
+ #ifndef LOG_WARN
+ #define LOG_WARN LOG_WARNING
+ #endif
+ #endif
+
+ gid_t default_group;
+ char default_home[BUFSIZ];
+ char default_shell[BUFSIZ];
+ long default_inactive;
+ long default_expire;
+ char default_file[] = "/etc/default/useradd";
+
+ #ifndef NGROUPS_MAX
+ #define NGROUPS_MAX 64
+ #endif
+
+ char user_name[BUFSIZ];
+ uid_t user_id;
+ gid_t user_group;
+ char user_comment[BUFSIZ];
+ char user_home[BUFSIZ];
+ char user_shell[BUFSIZ];
+ int user_ngroups;
+ int user_expire;
+ gid_t user_groups[NGROUPS_MAX];
+
+ char *Prog;
+ int rflg;
+
+ #if defined(DBM) || defined(NDBM)
+ extern int pw_dbm_mode;
+ #endif
+ #ifdef NDBM
+ extern int sp_dbm_mode;
+ extern int gr_dbm_mode;
+ #ifdef SHADOWGRP
+ extern int sg_dbm_mode;
+ #endif
+ #endif
+ extern struct group *getgrnam();
+ extern struct group *getgrgid();
+ extern struct group *gr_next();
+ extern struct passwd *getpwnam();
+ extern struct passwd *pw_next();
+
+ #ifdef SHADOWGRP
+ extern int sgr_lock();
+ extern int sgr_unlock();
+ extern int sgr_open();
+ extern int sgr_close();
+ extern struct sgrp *sgr_next();
+ #endif
+
+ extern char *malloc();
+
+ #ifdef NEED_RMDIR
+ /*
+ * rmdir - remove a directory
+ *
+ * rmdir is provided for systems which do not include the rmdir()
+ * system call.
+ */
+
+ int
+ rmdir (dir)
+ char *dir;
+ {
+ int status;
+
+ if (fork ()) {
+ while (wait (&status) != -1)
+ ;
+
+ return status >> 8;
+ }
+ close (2);
+ open ("/dev/null", O_WRONLY);
+ execl ("/bin/rmdir", "rmdir", dir, 0);
+ _exit (128);
+ }
+ #endif
+
+ /*
+ * 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;
+ }
+
+ /*
+ * usage - display usage message and exit
+ */
+
+ usage ()
+ {
+ fprintf (stderr, "usage: userdel [-r] name\n");
+ exit (1);
+ }
+
+ /*
+ * update_groups - delete user from secondary group set
+ *
+ * update_groups() takes the user name that was given and searches
+ * the group files for membership in any group.
+ */
+
+ void
+ update_groups ()
+ {
+ int i;
+ struct group *grp;
+ #ifdef SHADOWGRP
+ struct sgrp *sgrp;
+ #endif
+
+ /*
+ * 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;grp->gr_mem[i];i++)
+ if (strcmp (grp->gr_mem[i], user_name) == 0)
+ break;
+
+ if (grp->gr_mem[i] == (char *) 0)
+ continue;
+
+ /*
+ * Delete the username from the list of group members and
+ * update the group entry to reflect the change.
+ */
+
+ grp->gr_mem = del_list (grp->gr_mem, user_name);
+ if (! gr_update (grp)) {
+ fprintf (stderr, "%s: error updating group entry\n",
+ Prog);
+ exit (1);
+ }
+ #ifdef NDBM
+ /*
+ * Update the DBM group file with the new entry as well.
+ */
+
+ if (! gr_dbm_update (grp)) {
+ fprintf (stderr, "%s: cannot update dbm group entry\n",
+ Prog);
+ exit (1);
+ }
+ #endif
+ }
+
+ #ifdef SHADOWGRP
+ /*
+ * Scan through the entire shadow group file looking for the groups
+ * that the user is a member of. Both the administrative list and
+ * the ordinary membership list is checked.
+ */
+
+ for (sgr_rewind (), sgrp = sgr_next ();sgrp;sgrp = sgr_next ()) {
+ int group_changed = 0;
+
+ /*
+ * See if the user specified this group as one of their
+ * concurrent groups.
+ */
+
+ for (i = 0;sgrp->sg_mem[i];i++)
+ if (strcmp (sgrp->sg_mem[i], user_name) == 0)
+ break;
+
+ if (sgrp->sg_mem[i]) {
+ sgrp->sg_mem = del_list (sgrp->sg_mem, user_name);
+ group_changed = 1;
+ }
+ for (i = 0;sgrp->sg_adm[i];i++)
+ if (strcmp (sgrp->sg_adm[i], user_name) == 0)
+ break;
+
+ if (sgrp->sg_adm[i]) {
+ sgrp->sg_adm = del_list (sgrp->sg_adm, user_name);
+ group_changed = 1;
+ }
+ if (! group_changed)
+ continue;
+
+ if (! sgr_update (sgrp)) {
+ fprintf (stderr, "%s: error updating group entry\n",
+ Prog);
+ exit (1);
+ }
+ #ifdef NDBM
+ /*
+ * Update the DBM group file with the new entry as well.
+ */
+
+ if (! sgr_dbm_update (sgrp)) {
+ fprintf (stderr, "%s: cannot update dbm group entry\n",
+ Prog);
+ exit (1);
+ }
+ #endif
+ }
+ #endif
+ }
+
+ /*
+ * 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);
+ exit (1);
+ }
+ if (! spw_close ()) {
+ fprintf (stderr, "%s: cannot rewrite shadow password file\n",
+ Prog);
+ exit (1);
+ }
+ if (! gr_close ()) {
+ fprintf (stderr, "%s: cannot rewrite group file\n",
+ Prog);
+ exit (1);
+ }
+ (void) gr_unlock ();
+ #ifdef SHADOWGRP
+ if (! sgr_close ()) {
+ fprintf (stderr, "%s: cannot rewrite shadow group file\n",
+ Prog);
+ exit (1);
+ }
+ (void) sgr_unlock ();
+ #endif
+ (void) pw_unlock ();
+ }
+
+ /*
+ * 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);
+ exit (1);
+ }
+ if (! spw_lock ()) {
+ fprintf ("%s: cannot lock shadow password file\n", Prog);
+ exit (1);
+ }
+ if (! spw_open (O_RDWR)) {
+ fprintf ("%s: cannot open shadow password file\n", Prog);
+ exit (1);
+ }
+ if (! gr_lock ()) {
+ fprintf (stderr, "%s: unable to lock group file\n", Prog);
+ exit (1);
+ }
+ if (! gr_open (O_RDWR)) {
+ fprintf (stderr, "%s: cannot open group file\n", Prog);
+ exit (1);
+ }
+ #ifdef SHADOWGRP
+ if (! sgr_lock ()) {
+ fprintf (stderr, "%s: unable to lock shadow group file\n", Prog);
+ exit (1);
+ }
+ if (! sgr_open (O_RDWR)) {
+ fprintf (stderr, "%s: cannot open shadow group file\n", Prog);
+ exit (1);
+ }
+ #endif
+ }
+
+ /*
+ * update_user - delete the user entries
+ *
+ * update_user() deletes the password file entries for this user
+ * and will update the group entries as required.
+ */
+
+ update_user ()
+ {
+ struct passwd *pwd;
+
+ if (! pw_remove (user_name))
+ fprintf (stderr, "%s: error deleting password entry\n", Prog);
+
+ if (! spw_remove (user_name))
+ fprintf (stderr, "%s: error deleting shadow password entry\n",
+ Prog);
+
+ #if defined(DBM) || defined(NDBM)
+ if (access ("/etc/passwd.pag", 0) == 0) {
+ if ((pwd = getpwnam (user_name)) && ! pw_dbm_remove (pwd))
+ fprintf (stderr,
+ "%s: error deleting password dbm entry\n",
+ Prog);
+ }
+
+ /*
+ * If the user's UID is a duplicate the duplicated entry needs
+ * to be updated so that a UID match can be found in the DBM
+ * files.
+ */
+
+ for (pw_rewind (), pwd = pw_next ();pwd;pwd = pw_next ()) {
+ if (pwd->pw_uid == user_id) {
+ pw_dbm_update (pwd);
+ break;
+ }
+ }
+ #endif
+ #ifdef NDBM
+ if (access ("/etc/shadow.pag", 0) == 0 && ! sp_dbm_remove (user_name))
+ fprintf (stderr, "%s: error deleting shadow passwd dbm entry\n",
+ Prog);
+ #endif
+ }
+
+ /*
+ * main - useradd command
+ */
+
+ main (argc, argv)
+ int argc;
+ char **argv;
+ {
+ struct passwd *pwd;
+ int arg;
+ extern int optind;
+ extern char *optarg;
+
+ /*
+ * 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 DBM files don't use read-write
+ * as the mode, so we have to clue them in.
+ */
+
+ #if defined(DBM) || defined(NDBM)
+ pw_dbm_mode = O_RDWR;
+ #endif
+ #ifdef NDBM
+ sp_dbm_mode = O_RDWR;
+ gr_dbm_mode = O_RDWR;
+ #ifdef SHADOWGRP
+ sg_dbm_mode = O_RDWR;
+ #endif
+ #endif
+ while ((arg = getopt (argc, argv, "r")) != EOF)
+ if (arg != 'r')
+ usage ();
+ else
+ rflg++;
+
+ if (optind == argc)
+ usage ();
+
+ /*
+ * Start with a quick check to see if the user exists.
+ */
+
+ strncpy (user_name, argv[argc - 1], BUFSIZ);
+
+ if (! (pwd = getpwnam (user_name))) {
+ fprintf (stderr, "%s: user %s does not exist\n",
+ Prog, user_name);
+ exit (1);
+ }
+ user_id = pwd->pw_uid;
+ strcpy (user_home, pwd->pw_dir);
+
+ /*
+ * Do the hard stuff - open the files, create the user entries,
+ * create the home directory, then close and update the files.
+ */
+
+ open_files ();
+
+ update_user ();
+ update_groups ();
+
+ if (rflg)
+ rmdir (user_home);
+
+ close_files ();
+ exit (0);
+ /*NOTREACHED*/
+ }
*** /dev/null Fri Jun 7 11:02:59 1991
--- usermod.c Fri Jun 7 11:10:08 1991
***************
*** 0 ****
--- 1,1149 ----
+ /*
+ * Copyright 1991, 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.
+ */
+
+ #ifndef lint
+ static char sccsid[] = "@(#)usermod.c 3.1 11:08:35 6/7/91";
+ #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"
+ #include "shadow.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
+
+ char user_name[BUFSIZ];
+ char user_newname[BUFSIZ];
+ uid_t user_id;
+ uid_t user_newid;
+ gid_t user_gid;
+ char user_comment[BUFSIZ];
+ char user_home[BUFSIZ];
+ char user_newhome[BUFSIZ];
+ char user_shell[BUFSIZ];
+ long user_expire;
+ long user_inactive;
+ int user_ngroups;
+ gid_t user_groups[NGROUPS_MAX];
+ struct passwd user_pwd;
+ struct spwd user_spwd;
+
+ char *Prog;
+
+ 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 */
+
+ #if defined(DBM) || defined(NDBM)
+ extern int pw_dbm_mode;
+ #endif
+ #ifdef NDBM
+ extern int sp_dbm_mode;
+ extern int gr_dbm_mode;
+ extern int sg_dbm_mode;
+ #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();
+
+ extern int spw_lock();
+ extern int spw_unlock();
+ extern int spw_open();
+ extern int spw_locate();
+
+ #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_MKDIR
+ /*
+ * mkdir - create a directory
+ *
+ * mkdir is provided for systems which do not include the mkdir()
+ * system call.
+ */
+
+ int
+ mkdir (dir, mode)
+ char *dir;
+ int mode;
+ {
+ int status;
+
+ if (fork ()) {
+ while (wait (&status) != -1)
+ ;
+
+ return status >> 8;
+ }
+ #ifdef USE_SYSLOG
+ closelog ();
+ #endif
+ close (2);
+ open ("/dev/null", O_WRONLY);
+ umask (0777 & ~ mode);
+ execl ("/bin/mkdir", "mkdir", dir, 0);
+ _exit (128);
+ }
+ #endif
+ #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. this is
+ * a chauvanistic program - it only takes date input in
+ * the standard USA format.
+ */
+
+ if (sscanf (str, "%d/%d/%d%c", &month, &day, &year, slop) != 3)
+ return -1;
+
+ /*
+ * 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);
+ fprintf (stderr,
+ "\t\t[-d home [-m]] [-s shell] [-c comment] [-l new_name]\n");
+ fprintf (stderr,
+ "\t\t[-f inactive] [-e expire] name\n");
+
+ 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)
+ pwent->pw_name = strdup (user_newname);
+
+ if (uflg)
+ pwent->pw_uid = user_newid;
+
+ if (gflg)
+ pwent->pw_gid = user_gid;
+
+ if (cflg)
+ pwent->pw_gecos = strdup (user_comment);
+
+ if (dflg)
+ pwent->pw_dir = strdup (user_newhome);
+
+ if (sflg)
+ pwent->pw_shell = strdup (user_shell);
+ }
+
+ /*
+ * 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);
+
+ spent->sp_inact = user_inactive;
+ spent->sp_expire = user_expire;
+ }
+
+ /*
+ * 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;
+ int was_admin;
+ struct group *grp;
+ struct sgrp *sgrp;
+
+ /*
+ * 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);
+ exit (1);
+ }
+ if (! gr_open (O_RDWR)) {
+ fprintf (stderr, "%s: error opening group file\n", Prog);
+ exit (1);
+ }
+ #ifdef SHADOWGRP
+ if (! sgr_lock ()) {
+ fprintf (stderr, "%s: error locking shadow group file\n", Prog);
+ exit (1);
+ }
+ if (! sgr_open (O_RDWR)) {
+ fprintf (stderr, "%s: error opening shadow group file\n", Prog);
+ exit (1);
+ }
+ #endif
+
+ /*
+ * 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);
+ 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);
+ exit (1);
+ }
+ #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);
+ exit (1);
+ }
+ #ifdef NDBM
+ /*
+ * Update the DBM group file with the new entry as well.
+ */
+
+ if (! sgr_dbm_update (sgrp)) {
+ fprintf (stderr, "%s: cannot add new dbm group entry\n",
+ Prog);
+ exit (1);
+ }
+ #endif
+ }
+ #endif
+ }
+
+ /*
+ * 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;
+ }
+
+ /*
+ * 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);
+
+ if (spwd = getspnam (user_name)) {
+ user_expire = spwd->sp_expire;
+ user_inactive = spwd->sp_inact;
+ }
+ while ((arg = getopt (argc, argv, "u:og:G:d:s:c:mf:e:l:")) != EOF) {
+ switch (arg) {
+ case 'c':
+ if (strcmp (optarg, user_comment)) {
+ cflg++;
+ strncpy (user_comment, optarg, BUFSIZ);
+ }
+ break;
+ case 'd':
+ dflg++;
+ strncpy (user_newhome, optarg, BUFSIZ);
+ break;
+ case 'e':
+ l = strtoday (optarg);
+ #ifdef ITI_AGING
+ l *= DAY;
+ #endif
+ if (l != user_expire) {
+ eflg++;
+ user_expire = l;
+ }
+ break;
+ case 'f':
+ if (user_inactive != atoi (optarg)) {
+ fflg++;
+ user_inactive = atoi (optarg);
+ }
+ break;
+ case 'g':
+ if (isdigit (optarg[0]))
+ grp = getgrgid (atoi (optarg));
+ else
+ grp = getgrnam (optarg);
+
+ if (! grp) {
+ fprintf (stderr,
+ "%s: unknown group %s\n",
+ optarg);
+ exit (1);
+ }
+ if (grp->gr_gid != user_gid) {
+ gflg++;
+ user_gid = grp->gr_gid;
+ }
+ break;
+ case 'G':
+ Gflg++;
+ if (get_groups (optarg))
+ exit (1);
+
+ break;
+ case 'l':
+ if (strcmp (user_name, optarg)) {
+ lflg++;
+ strcpy (user_newname, optarg);
+ }
+ break;
+ case 'm':
+ if (! dflg)
+ usage ();
+
+ mflg++;
+ break;
+ case 'o':
+ if (! uflg)
+ usage ();
+
+ oflg++;
+ break;
+ case 's':
+ if (strcmp (user_shell, optarg)) {
+ strncpy (user_shell, optarg, BUFSIZ);
+ sflg++;
+ }
+ break;
+ case 'u':
+ uflg++;
+ user_newid = atoi (optarg);
+ break;
+ default:
+ usage ();
+ }
+ anyflag++;
+ }
+ 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", 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);
+ exit (1);
+ }
+ if (! spw_close ()) {
+ fprintf (stderr, "%s: cannot rewrite shadow password file\n",
+ Prog);
+ exit (1);
+ }
+ if (user_ngroups > 0) {
+ if (! gr_close ()) {
+ fprintf (stderr, "%s: cannot rewrite group file\n",
+ Prog);
+ exit (1);
+ }
+ (void) gr_unlock ();
+ #ifdef SHADOWGRP
+ if (! sgr_close ()) {
+ fprintf (stderr, "%s: cannot rewrite shadow group file\n",
+ Prog);
+ exit (1);
+ }
+ #endif
+ }
+ (void) pw_unlock ();
+ }
+
+ /*
+ * 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);
+ exit (1);
+ }
+ if (! spw_lock ()) {
+ fprintf ("%s: cannot lock shadow password file\n", Prog);
+ exit (1);
+ }
+ if (! spw_open (O_RDWR)) {
+ fprintf ("%s: cannot open shadow password file\n", Prog);
+ exit (1);
+ }
+ }
+
+ /*
+ * 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 spwd spent;
+ struct passwd *pwd;
+ struct spwd *spwd;
+
+ pwd = pw_locate (user_name);
+ pwent = *pwd;
+
+ spwd = spw_locate (user_name);
+ spent = *spwd;
+
+ new_pwent (&pwent);
+ new_spent (&spent);
+
+ if (lflg || uflg || gflg || cflg || dflg || sflg) {
+ if (! pw_update (&pwent)) {
+ fprintf (stderr, "%s: error changing password entry\n");
+ exit (1);
+ }
+ if (lflg && ! pw_remove (user_name)) {
+ fprintf (stderr, "%s: error removing password entry\n");
+ 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);
+ exit (1);
+ }
+ if (lflg && (pwd = getpwnam (user_name)) && ! pw_dbm_remove (pwd)) {
+ fprintf (stderr, "%s: error removing passwd dbm entry\n",
+ Prog);
+ exit (1);
+ }
+ }
+ #endif
+ }
+ if (lflg || eflg || fflg) {
+ if (! spw_update (&spent)) {
+ fprintf (stderr, "%s: error adding new shadow password entry\n",
+ Prog);
+ exit (1);
+ }
+ if (lflg && ! spw_remove (user_name)) {
+ fprintf (stderr, "%s: error removing shadow password entry\n",
+ Prog);
+ exit (1);
+ }
+ }
+ #ifdef NDBM
+ if (access ("/etc/shadow.pag", 0) == 0) {
+ if (! sp_dbm_update (&spent)) {
+ fprintf (stderr, "%s: error updating shadow passwd dbm entry\n",
+ Prog);
+ exit (1);
+ }
+ if (lflg && ! sp_dbm_remove (user_name)) {
+ fprintf (stderr, "%s: error removing shadow passwd db entry\n",
+ Prog);
+ exit (1);
+ }
+ }
+ #endif
+ if (Gflg || lflg)
+ grp_update ();
+ }
+
+ /*
+ * move_home - move the user's home directory
+ *
+ * move_home() creates the user's home directory if it does not
+ * already exist. It will be created mode 755 owned by the user
+ * with the user's default group.
+ */
+
+ move_home ()
+ {
+ if (mflg && access (user_home, 0) == 0) {
+ if (access (user_newhome, 0) == 0) {
+ fprintf (stderr, "%s: directory %s exists\n",
+ Prog, user_newhome);
+ exit (12);
+ } else if (rename (user_home, user_newhome)) {
+ fprintf (stderr,
+ "%s: cannot rename directory %s to %s\n",
+ Prog, user_home, user_newhome);
+ exit (12);
+ }
+ }
+ if (uflg || gflg)
+ chown (dflg ? user_newhome:user_home, user_id, user_gid);
+ }
+
+ /*
+ * main - useradd 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 DBM files don't use read-write
+ * as the mode, so we have to clue them in.
+ */
+
+ #if defined(DBM) || defined(NDBM)
+ pw_dbm_mode = O_RDWR;
+ #endif
+ #ifdef NDBM
+ sp_dbm_mode = O_RDWR;
+ gr_dbm_mode = O_RDWR;
+ #ifdef SHADOWGRP
+ sg_dbm_mode = O_RDWR;
+ #endif
+ #endif
+ 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 ();
+
+ exit (0);
+ /*NOTREACHED*/
+ }
--
John F. Haugh II | Distribution to | UUCP: ...!cs.utexas.edu!rpp386!jfh
Ma Bell: (512) 255-8251 | GEnie PROHIBITED :-) | Domain: jfh at rpp386.cactus.org
"If liberals interpreted the 2nd Amendment the same way they interpret the
rest of the Constitution, gun ownership would be mandatory."
More information about the Alt.sources
mailing list