[svn-upgrade] Integrating new upstream version, shadow (19990709)

This commit is contained in:
nekral-guest
2007-10-07 11:44:02 +00:00
parent 9c72ed9062
commit 45c6603cc8
350 changed files with 89554 additions and 0 deletions

90
src/Makefile.am Normal file
View File

@ -0,0 +1,90 @@
AUTOMAKE_OPTIONS = 1.0 foreign
# Watch out; note the difference between prefix & exec_prefix.
# Normally configure sets exec_prefix to root when prefix is /usr.
bindir = ${exec_prefix}/bin
sbindir = ${exec_prefix}/sbin
ubindir = ${prefix}/bin
usbindir = ${prefix}/sbin
localedir = $(datadir)/locale
noinst_HEADERS = patchlevel.h
DEFS = -DLOCALEDIR=\"$(localedir)\" -I. -I$(srcdir) -I.. @DEFS@
# XXX why are login and su in /bin anyway (other than for
# historical reasons)?
#
# if the system is screwed so badly that it can't mount /usr,
# you can (hopefully) boot single user, and then you're root
# so you don't need these programs for recovery.
#
# also /lib/libshadow.so.x.xx (if any) could be moved to /usr/lib
# and installation would be much simpler (just two directories,
# $prefix/bin and $prefix/sbin, no install-data hacks...)
bin_PROGRAMS = login \
su
ubin_PROGRAMS = faillog lastlog \
chage chfn chsh expiry gpasswd newgrp passwd
usbin_PROGRAMS = chpasswd dpasswd groupadd groupdel groupmod \
logoutd mkpasswd newusers \
useradd userdel usermod grpck pwck vipw \
grpconv grpunconv pwconv pwunconv
EXTRA_DIST = shadowconfig.sh
# id and groups are from gnu, sulogin from sysvinit,
# also suid programs are installed by hand.
# XXX installation by hand breaks libtool shared lib support
# (the wrapper scripts get installed instead of binaries),
# so we now chmod the programs by hand after normal installation.
suidbins = su
suidubins = chage chfn chsh expiry gpasswd newgrp passwd
install-exec-hook:
for i in $(suidbins); do \
chmod 4755 $(bindir)/$$i; \
done
install-data-hook:
for i in $(suidubins); do \
chmod 4755 $(ubindir)/$$i; \
done
rm -f $(ubindir)/sg
ln -s newgrp $(ubindir)/sg
noinst_PROGRAMS = groups id sulogin
#install-exec-local:
# $(mkinstalldirs) $(bindir)
# for i in $(suidbins); do \
# $(INSTALL) -m 4755 $$i $(bindir); \
# done
# $(mkinstalldirs) $(ubindir)
# for i in $(suidubins); do \
# $(INSTALL) -m 4755 $$i $(ubindir); \
# done
# rm -f $(bindir)/sg
# ln -s $(ubindir)/newgrp $(bindir)/sg
#
#noinst_PROGRAMS = id groups \
# su \
# chage chfn chsh expiry gpasswd newgrp passwd \
# sulogin
shlibs = ../lib/libshadow.la
# With glibc2, almost all programs need libcrypt for some reason,
# even those that don't actually use crypt().
LDADD = ${shlibs} ../libmisc/libmisc.a ../lib/libshadow.a @INTLLIBS@ @LIBCRYPT@ @LIBTCFS@ @LIBSKEY@
INCLUDES = -I${top_srcdir}/lib -I$(top_srcdir)/libmisc
chfn_LDADD = ${LDADD} @LIBPAM@
chsh_LDADD = ${LDADD} @LIBPAM@
login_LDADD = ${LDADD} @LIBPAM@
passwd_LDADD = ${LDADD} @LIBCRACK@ @LIBPAM@
su_LDADD = ${LDADD} @LIBPAM@

873
src/Makefile.in Normal file
View File

@ -0,0 +1,873 @@
# Makefile.in generated automatically by automake 1.3 from Makefile.am
# Copyright (C) 1994, 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
SHELL = /bin/sh
srcdir = @srcdir@
top_srcdir = @top_srcdir@
VPATH = @srcdir@
prefix = @prefix@
exec_prefix = @exec_prefix@
libexecdir = @libexecdir@
datadir = @datadir@
sysconfdir = @sysconfdir@
sharedstatedir = @sharedstatedir@
localstatedir = @localstatedir@
libdir = @libdir@
infodir = @infodir@
mandir = @mandir@
includedir = @includedir@
oldincludedir = /usr/include
DISTDIR =
pkgdatadir = $(datadir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
top_builddir = ..
ACLOCAL = @ACLOCAL@
AUTOCONF = @AUTOCONF@
AUTOMAKE = @AUTOMAKE@
AUTOHEADER = @AUTOHEADER@
INSTALL = @INSTALL@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
transform = @program_transform_name@
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
host_alias = @host_alias@
host_triplet = @host@
CATALOGS = @CATALOGS@
CATOBJEXT = @CATOBJEXT@
CC = @CC@
CPP = @CPP@
DATADIRNAME = @DATADIRNAME@
GENCAT = @GENCAT@
GMOFILES = @GMOFILES@
GMSGFMT = @GMSGFMT@
GT_NO = @GT_NO@
GT_YES = @GT_YES@
INCLUDE_LOCALE_H = @INCLUDE_LOCALE_H@
INSTOBJEXT = @INSTOBJEXT@
INTLDEPS = @INTLDEPS@
INTLLIBS = @INTLLIBS@
INTLOBJS = @INTLOBJS@
LD = @LD@
LIBCRACK = @LIBCRACK@
LIBCRYPT = @LIBCRYPT@
LIBPAM = @LIBPAM@
LIBSKEY = @LIBSKEY@
LIBTCFS = @LIBTCFS@
LIBTOOL = @LIBTOOL@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
MAKEINFO = @MAKEINFO@
MKINSTALLDIRS = @MKINSTALLDIRS@
MSGFMT = @MSGFMT@
NM = @NM@
PACKAGE = @PACKAGE@
POFILES = @POFILES@
POSUB = @POSUB@
RANLIB = @RANLIB@
U = @U@
USE_INCLUDED_LIBINTL = @USE_INCLUDED_LIBINTL@
USE_NLS = @USE_NLS@
VERSION = @VERSION@
YACC = @YACC@
l = @l@
AUTOMAKE_OPTIONS = 1.0 foreign
# Watch out; note the difference between prefix & exec_prefix.
# Normally configure sets exec_prefix to root when prefix is /usr.
bindir = ${exec_prefix}/bin
sbindir = ${exec_prefix}/sbin
ubindir = ${prefix}/bin
usbindir = ${prefix}/sbin
localedir = $(datadir)/locale
noinst_HEADERS = patchlevel.h
DEFS = -DLOCALEDIR=\"$(localedir)\" -I. -I$(srcdir) -I.. @DEFS@
# XXX why are login and su in /bin anyway (other than for
# historical reasons)?
#
# if the system is screwed so badly that it can't mount /usr,
# you can (hopefully) boot single user, and then you're root
# so you don't need these programs for recovery.
#
# also /lib/libshadow.so.x.xx (if any) could be moved to /usr/lib
# and installation would be much simpler (just two directories,
# $prefix/bin and $prefix/sbin, no install-data hacks...)
bin_PROGRAMS = login \
su
ubin_PROGRAMS = faillog lastlog \
chage chfn chsh expiry gpasswd newgrp passwd
usbin_PROGRAMS = chpasswd dpasswd groupadd groupdel groupmod \
logoutd mkpasswd newusers \
useradd userdel usermod grpck pwck vipw \
grpconv grpunconv pwconv pwunconv
EXTRA_DIST = shadowconfig.sh
# id and groups are from gnu, sulogin from sysvinit,
# also suid programs are installed by hand.
# XXX installation by hand breaks libtool shared lib support
# (the wrapper scripts get installed instead of binaries),
# so we now chmod the programs by hand after normal installation.
suidbins = su
suidubins = chage chfn chsh expiry gpasswd newgrp passwd
noinst_PROGRAMS = groups id sulogin
#install-exec-local:
# $(mkinstalldirs) $(bindir)
# for i in $(suidbins); do \
# $(INSTALL) -m 4755 $$i $(bindir); \
# done
# $(mkinstalldirs) $(ubindir)
# for i in $(suidubins); do \
# $(INSTALL) -m 4755 $$i $(ubindir); \
# done
# rm -f $(bindir)/sg
# ln -s $(ubindir)/newgrp $(bindir)/sg
#
#noinst_PROGRAMS = id groups \
# su \
# chage chfn chsh expiry gpasswd newgrp passwd \
# sulogin
shlibs = ../lib/libshadow.la
# With glibc2, almost all programs need libcrypt for some reason,
# even those that don't actually use crypt().
LDADD = ${shlibs} ../libmisc/libmisc.a ../lib/libshadow.a @INTLLIBS@ @LIBCRYPT@ @LIBTCFS@ @LIBSKEY@
INCLUDES = -I${top_srcdir}/lib -I$(top_srcdir)/libmisc
chfn_LDADD = ${LDADD} @LIBPAM@
chsh_LDADD = ${LDADD} @LIBPAM@
login_LDADD = ${LDADD} @LIBPAM@
passwd_LDADD = ${LDADD} @LIBCRACK@ @LIBPAM@
su_LDADD = ${LDADD} @LIBPAM@
mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
CONFIG_HEADER = ../config.h
CONFIG_CLEAN_FILES =
PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS) $(ubin_PROGRAMS) \
$(usbin_PROGRAMS)
CPPFLAGS = @CPPFLAGS@
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@
login_SOURCES = login.c
login_OBJECTS = login.o
login_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
login_LDFLAGS =
su_SOURCES = su.c
su_OBJECTS = su.o
su_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
su_LDFLAGS =
groups_SOURCES = groups.c
groups_OBJECTS = groups.o
groups_LDADD = $(LDADD)
groups_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
groups_LDFLAGS =
id_SOURCES = id.c
id_OBJECTS = id.o
id_LDADD = $(LDADD)
id_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
id_LDFLAGS =
sulogin_SOURCES = sulogin.c
sulogin_OBJECTS = sulogin.o
sulogin_LDADD = $(LDADD)
sulogin_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
sulogin_LDFLAGS =
faillog_SOURCES = faillog.c
faillog_OBJECTS = faillog.o
faillog_LDADD = $(LDADD)
faillog_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
faillog_LDFLAGS =
lastlog_SOURCES = lastlog.c
lastlog_OBJECTS = lastlog.o
lastlog_LDADD = $(LDADD)
lastlog_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
lastlog_LDFLAGS =
chage_SOURCES = chage.c
chage_OBJECTS = chage.o
chage_LDADD = $(LDADD)
chage_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
chage_LDFLAGS =
chfn_SOURCES = chfn.c
chfn_OBJECTS = chfn.o
chfn_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
chfn_LDFLAGS =
chsh_SOURCES = chsh.c
chsh_OBJECTS = chsh.o
chsh_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
chsh_LDFLAGS =
expiry_SOURCES = expiry.c
expiry_OBJECTS = expiry.o
expiry_LDADD = $(LDADD)
expiry_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
expiry_LDFLAGS =
gpasswd_SOURCES = gpasswd.c
gpasswd_OBJECTS = gpasswd.o
gpasswd_LDADD = $(LDADD)
gpasswd_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
gpasswd_LDFLAGS =
newgrp_SOURCES = newgrp.c
newgrp_OBJECTS = newgrp.o
newgrp_LDADD = $(LDADD)
newgrp_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
newgrp_LDFLAGS =
passwd_SOURCES = passwd.c
passwd_OBJECTS = passwd.o
passwd_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
passwd_LDFLAGS =
chpasswd_SOURCES = chpasswd.c
chpasswd_OBJECTS = chpasswd.o
chpasswd_LDADD = $(LDADD)
chpasswd_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
chpasswd_LDFLAGS =
dpasswd_SOURCES = dpasswd.c
dpasswd_OBJECTS = dpasswd.o
dpasswd_LDADD = $(LDADD)
dpasswd_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
dpasswd_LDFLAGS =
groupadd_SOURCES = groupadd.c
groupadd_OBJECTS = groupadd.o
groupadd_LDADD = $(LDADD)
groupadd_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
groupadd_LDFLAGS =
groupdel_SOURCES = groupdel.c
groupdel_OBJECTS = groupdel.o
groupdel_LDADD = $(LDADD)
groupdel_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
groupdel_LDFLAGS =
groupmod_SOURCES = groupmod.c
groupmod_OBJECTS = groupmod.o
groupmod_LDADD = $(LDADD)
groupmod_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
groupmod_LDFLAGS =
logoutd_SOURCES = logoutd.c
logoutd_OBJECTS = logoutd.o
logoutd_LDADD = $(LDADD)
logoutd_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
logoutd_LDFLAGS =
mkpasswd_SOURCES = mkpasswd.c
mkpasswd_OBJECTS = mkpasswd.o
mkpasswd_LDADD = $(LDADD)
mkpasswd_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
mkpasswd_LDFLAGS =
newusers_SOURCES = newusers.c
newusers_OBJECTS = newusers.o
newusers_LDADD = $(LDADD)
newusers_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
newusers_LDFLAGS =
useradd_SOURCES = useradd.c
useradd_OBJECTS = useradd.o
useradd_LDADD = $(LDADD)
useradd_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
useradd_LDFLAGS =
userdel_SOURCES = userdel.c
userdel_OBJECTS = userdel.o
userdel_LDADD = $(LDADD)
userdel_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
userdel_LDFLAGS =
usermod_SOURCES = usermod.c
usermod_OBJECTS = usermod.o
usermod_LDADD = $(LDADD)
usermod_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
usermod_LDFLAGS =
grpck_SOURCES = grpck.c
grpck_OBJECTS = grpck.o
grpck_LDADD = $(LDADD)
grpck_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
grpck_LDFLAGS =
pwck_SOURCES = pwck.c
pwck_OBJECTS = pwck.o
pwck_LDADD = $(LDADD)
pwck_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
pwck_LDFLAGS =
vipw_SOURCES = vipw.c
vipw_OBJECTS = vipw.o
vipw_LDADD = $(LDADD)
vipw_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
vipw_LDFLAGS =
grpconv_SOURCES = grpconv.c
grpconv_OBJECTS = grpconv.o
grpconv_LDADD = $(LDADD)
grpconv_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
grpconv_LDFLAGS =
grpunconv_SOURCES = grpunconv.c
grpunconv_OBJECTS = grpunconv.o
grpunconv_LDADD = $(LDADD)
grpunconv_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
grpunconv_LDFLAGS =
pwconv_SOURCES = pwconv.c
pwconv_OBJECTS = pwconv.o
pwconv_LDADD = $(LDADD)
pwconv_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
pwconv_LDFLAGS =
pwunconv_SOURCES = pwunconv.c
pwunconv_OBJECTS = pwunconv.o
pwunconv_LDADD = $(LDADD)
pwunconv_DEPENDENCIES = ../lib/libshadow.la ../libmisc/libmisc.a \
../lib/libshadow.a
pwunconv_LDFLAGS =
CFLAGS = @CFLAGS@
COMPILE = $(CC) $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS)
LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS)
LINK = $(LIBTOOL) --mode=link $(CC) $(CFLAGS) $(LDFLAGS) -o $@
HEADERS = $(noinst_HEADERS)
DIST_COMMON = Makefile.am Makefile.in
DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
TAR = tar
GZIP = --best
SOURCES = login.c su.c groups.c id.c sulogin.c faillog.c lastlog.c chage.c chfn.c chsh.c expiry.c gpasswd.c newgrp.c passwd.c chpasswd.c dpasswd.c groupadd.c groupdel.c groupmod.c logoutd.c mkpasswd.c newusers.c useradd.c userdel.c usermod.c grpck.c pwck.c vipw.c grpconv.c grpunconv.c pwconv.c pwunconv.c
OBJECTS = login.o su.o groups.o id.o sulogin.o faillog.o lastlog.o chage.o chfn.o chsh.o expiry.o gpasswd.o newgrp.o passwd.o chpasswd.o dpasswd.o groupadd.o groupdel.o groupmod.o logoutd.o mkpasswd.o newusers.o useradd.o userdel.o usermod.o grpck.o pwck.o vipw.o grpconv.o grpunconv.o pwconv.o pwunconv.o
all: Makefile $(PROGRAMS) $(HEADERS)
.SUFFIXES:
.SUFFIXES: .S .c .lo .o .s
$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4)
cd $(top_srcdir) && $(AUTOMAKE) --foreign --include-deps src/Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
cd $(top_builddir) \
&& CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
mostlyclean-binPROGRAMS:
clean-binPROGRAMS:
-test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
distclean-binPROGRAMS:
maintainer-clean-binPROGRAMS:
install-binPROGRAMS: $(bin_PROGRAMS)
@$(NORMAL_INSTALL)
$(mkinstalldirs) $(DESTDIR)$(bindir)
@list='$(bin_PROGRAMS)'; for p in $$list; do \
if test -f $$p; then \
echo " $(LIBTOOL) --mode=install $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed '$(transform)'`"; \
$(LIBTOOL) --mode=install $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed '$(transform)'`; \
else :; fi; \
done
uninstall-binPROGRAMS:
@$(NORMAL_UNINSTALL)
list='$(bin_PROGRAMS)'; for p in $$list; do \
rm -f $(DESTDIR)$(bindir)/`echo $$p|sed '$(transform)'`; \
done
mostlyclean-noinstPROGRAMS:
clean-noinstPROGRAMS:
-test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS)
distclean-noinstPROGRAMS:
maintainer-clean-noinstPROGRAMS:
mostlyclean-ubinPROGRAMS:
clean-ubinPROGRAMS:
-test -z "$(ubin_PROGRAMS)" || rm -f $(ubin_PROGRAMS)
distclean-ubinPROGRAMS:
maintainer-clean-ubinPROGRAMS:
install-ubinPROGRAMS: $(ubin_PROGRAMS)
@$(NORMAL_INSTALL)
$(mkinstalldirs) $(DESTDIR)$(ubindir)
@list='$(ubin_PROGRAMS)'; for p in $$list; do \
if test -f $$p; then \
echo " $(LIBTOOL) --mode=install $(INSTALL_PROGRAM) $$p $(DESTDIR)$(ubindir)/`echo $$p|sed '$(transform)'`"; \
$(LIBTOOL) --mode=install $(INSTALL_PROGRAM) $$p $(DESTDIR)$(ubindir)/`echo $$p|sed '$(transform)'`; \
else :; fi; \
done
uninstall-ubinPROGRAMS:
@$(NORMAL_UNINSTALL)
list='$(ubin_PROGRAMS)'; for p in $$list; do \
rm -f $(DESTDIR)$(ubindir)/`echo $$p|sed '$(transform)'`; \
done
mostlyclean-usbinPROGRAMS:
clean-usbinPROGRAMS:
-test -z "$(usbin_PROGRAMS)" || rm -f $(usbin_PROGRAMS)
distclean-usbinPROGRAMS:
maintainer-clean-usbinPROGRAMS:
install-usbinPROGRAMS: $(usbin_PROGRAMS)
@$(NORMAL_INSTALL)
$(mkinstalldirs) $(DESTDIR)$(usbindir)
@list='$(usbin_PROGRAMS)'; for p in $$list; do \
if test -f $$p; then \
echo " $(LIBTOOL) --mode=install $(INSTALL_PROGRAM) $$p $(DESTDIR)$(usbindir)/`echo $$p|sed '$(transform)'`"; \
$(LIBTOOL) --mode=install $(INSTALL_PROGRAM) $$p $(DESTDIR)$(usbindir)/`echo $$p|sed '$(transform)'`; \
else :; fi; \
done
uninstall-usbinPROGRAMS:
@$(NORMAL_UNINSTALL)
list='$(usbin_PROGRAMS)'; for p in $$list; do \
rm -f $(DESTDIR)$(usbindir)/`echo $$p|sed '$(transform)'`; \
done
.c.o:
$(COMPILE) -c $<
.s.o:
$(COMPILE) -c $<
.S.o:
$(COMPILE) -c $<
mostlyclean-compile:
-rm -f *.o core *.core
clean-compile:
distclean-compile:
-rm -f *.tab.c
maintainer-clean-compile:
.c.lo:
$(LIBTOOL) --mode=compile $(COMPILE) -c $<
.s.lo:
$(LIBTOOL) --mode=compile $(COMPILE) -c $<
.S.lo:
$(LIBTOOL) --mode=compile $(COMPILE) -c $<
mostlyclean-libtool:
-rm -f *.lo
clean-libtool:
-rm -rf .libs _libs
distclean-libtool:
maintainer-clean-libtool:
login: $(login_OBJECTS) $(login_DEPENDENCIES)
@rm -f login
$(LINK) $(login_LDFLAGS) $(login_OBJECTS) $(login_LDADD) $(LIBS)
su: $(su_OBJECTS) $(su_DEPENDENCIES)
@rm -f su
$(LINK) $(su_LDFLAGS) $(su_OBJECTS) $(su_LDADD) $(LIBS)
groups: $(groups_OBJECTS) $(groups_DEPENDENCIES)
@rm -f groups
$(LINK) $(groups_LDFLAGS) $(groups_OBJECTS) $(groups_LDADD) $(LIBS)
id: $(id_OBJECTS) $(id_DEPENDENCIES)
@rm -f id
$(LINK) $(id_LDFLAGS) $(id_OBJECTS) $(id_LDADD) $(LIBS)
sulogin: $(sulogin_OBJECTS) $(sulogin_DEPENDENCIES)
@rm -f sulogin
$(LINK) $(sulogin_LDFLAGS) $(sulogin_OBJECTS) $(sulogin_LDADD) $(LIBS)
faillog: $(faillog_OBJECTS) $(faillog_DEPENDENCIES)
@rm -f faillog
$(LINK) $(faillog_LDFLAGS) $(faillog_OBJECTS) $(faillog_LDADD) $(LIBS)
lastlog: $(lastlog_OBJECTS) $(lastlog_DEPENDENCIES)
@rm -f lastlog
$(LINK) $(lastlog_LDFLAGS) $(lastlog_OBJECTS) $(lastlog_LDADD) $(LIBS)
chage: $(chage_OBJECTS) $(chage_DEPENDENCIES)
@rm -f chage
$(LINK) $(chage_LDFLAGS) $(chage_OBJECTS) $(chage_LDADD) $(LIBS)
chfn: $(chfn_OBJECTS) $(chfn_DEPENDENCIES)
@rm -f chfn
$(LINK) $(chfn_LDFLAGS) $(chfn_OBJECTS) $(chfn_LDADD) $(LIBS)
chsh: $(chsh_OBJECTS) $(chsh_DEPENDENCIES)
@rm -f chsh
$(LINK) $(chsh_LDFLAGS) $(chsh_OBJECTS) $(chsh_LDADD) $(LIBS)
expiry: $(expiry_OBJECTS) $(expiry_DEPENDENCIES)
@rm -f expiry
$(LINK) $(expiry_LDFLAGS) $(expiry_OBJECTS) $(expiry_LDADD) $(LIBS)
gpasswd: $(gpasswd_OBJECTS) $(gpasswd_DEPENDENCIES)
@rm -f gpasswd
$(LINK) $(gpasswd_LDFLAGS) $(gpasswd_OBJECTS) $(gpasswd_LDADD) $(LIBS)
newgrp: $(newgrp_OBJECTS) $(newgrp_DEPENDENCIES)
@rm -f newgrp
$(LINK) $(newgrp_LDFLAGS) $(newgrp_OBJECTS) $(newgrp_LDADD) $(LIBS)
passwd: $(passwd_OBJECTS) $(passwd_DEPENDENCIES)
@rm -f passwd
$(LINK) $(passwd_LDFLAGS) $(passwd_OBJECTS) $(passwd_LDADD) $(LIBS)
chpasswd: $(chpasswd_OBJECTS) $(chpasswd_DEPENDENCIES)
@rm -f chpasswd
$(LINK) $(chpasswd_LDFLAGS) $(chpasswd_OBJECTS) $(chpasswd_LDADD) $(LIBS)
dpasswd: $(dpasswd_OBJECTS) $(dpasswd_DEPENDENCIES)
@rm -f dpasswd
$(LINK) $(dpasswd_LDFLAGS) $(dpasswd_OBJECTS) $(dpasswd_LDADD) $(LIBS)
groupadd: $(groupadd_OBJECTS) $(groupadd_DEPENDENCIES)
@rm -f groupadd
$(LINK) $(groupadd_LDFLAGS) $(groupadd_OBJECTS) $(groupadd_LDADD) $(LIBS)
groupdel: $(groupdel_OBJECTS) $(groupdel_DEPENDENCIES)
@rm -f groupdel
$(LINK) $(groupdel_LDFLAGS) $(groupdel_OBJECTS) $(groupdel_LDADD) $(LIBS)
groupmod: $(groupmod_OBJECTS) $(groupmod_DEPENDENCIES)
@rm -f groupmod
$(LINK) $(groupmod_LDFLAGS) $(groupmod_OBJECTS) $(groupmod_LDADD) $(LIBS)
logoutd: $(logoutd_OBJECTS) $(logoutd_DEPENDENCIES)
@rm -f logoutd
$(LINK) $(logoutd_LDFLAGS) $(logoutd_OBJECTS) $(logoutd_LDADD) $(LIBS)
mkpasswd: $(mkpasswd_OBJECTS) $(mkpasswd_DEPENDENCIES)
@rm -f mkpasswd
$(LINK) $(mkpasswd_LDFLAGS) $(mkpasswd_OBJECTS) $(mkpasswd_LDADD) $(LIBS)
newusers: $(newusers_OBJECTS) $(newusers_DEPENDENCIES)
@rm -f newusers
$(LINK) $(newusers_LDFLAGS) $(newusers_OBJECTS) $(newusers_LDADD) $(LIBS)
useradd: $(useradd_OBJECTS) $(useradd_DEPENDENCIES)
@rm -f useradd
$(LINK) $(useradd_LDFLAGS) $(useradd_OBJECTS) $(useradd_LDADD) $(LIBS)
userdel: $(userdel_OBJECTS) $(userdel_DEPENDENCIES)
@rm -f userdel
$(LINK) $(userdel_LDFLAGS) $(userdel_OBJECTS) $(userdel_LDADD) $(LIBS)
usermod: $(usermod_OBJECTS) $(usermod_DEPENDENCIES)
@rm -f usermod
$(LINK) $(usermod_LDFLAGS) $(usermod_OBJECTS) $(usermod_LDADD) $(LIBS)
grpck: $(grpck_OBJECTS) $(grpck_DEPENDENCIES)
@rm -f grpck
$(LINK) $(grpck_LDFLAGS) $(grpck_OBJECTS) $(grpck_LDADD) $(LIBS)
pwck: $(pwck_OBJECTS) $(pwck_DEPENDENCIES)
@rm -f pwck
$(LINK) $(pwck_LDFLAGS) $(pwck_OBJECTS) $(pwck_LDADD) $(LIBS)
vipw: $(vipw_OBJECTS) $(vipw_DEPENDENCIES)
@rm -f vipw
$(LINK) $(vipw_LDFLAGS) $(vipw_OBJECTS) $(vipw_LDADD) $(LIBS)
grpconv: $(grpconv_OBJECTS) $(grpconv_DEPENDENCIES)
@rm -f grpconv
$(LINK) $(grpconv_LDFLAGS) $(grpconv_OBJECTS) $(grpconv_LDADD) $(LIBS)
grpunconv: $(grpunconv_OBJECTS) $(grpunconv_DEPENDENCIES)
@rm -f grpunconv
$(LINK) $(grpunconv_LDFLAGS) $(grpunconv_OBJECTS) $(grpunconv_LDADD) $(LIBS)
pwconv: $(pwconv_OBJECTS) $(pwconv_DEPENDENCIES)
@rm -f pwconv
$(LINK) $(pwconv_LDFLAGS) $(pwconv_OBJECTS) $(pwconv_LDADD) $(LIBS)
pwunconv: $(pwunconv_OBJECTS) $(pwunconv_DEPENDENCIES)
@rm -f pwunconv
$(LINK) $(pwunconv_LDFLAGS) $(pwunconv_OBJECTS) $(pwunconv_LDADD) $(LIBS)
tags: TAGS
ID: $(HEADERS) $(SOURCES) $(LISP)
here=`pwd` && cd $(srcdir) \
&& mkid -f$$here/ID $(SOURCES) $(HEADERS) $(LISP)
TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP)
tags=; \
here=`pwd`; \
list='$(SOURCES) $(HEADERS)'; \
unique=`for i in $$list; do echo $$i; done | \
awk ' { files[$$0] = 1; } \
END { for (i in files) print i; }'`; \
test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \
|| (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS)
mostlyclean-tags:
clean-tags:
distclean-tags:
-rm -f TAGS ID
maintainer-clean-tags:
distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
subdir = src
distdir: $(DISTFILES)
@for file in $(DISTFILES); do \
d=$(srcdir); \
test -f $(distdir)/$$file \
|| ln $$d/$$file $(distdir)/$$file 2> /dev/null \
|| cp -p $$d/$$file $(distdir)/$$file; \
done
chage.o: chage.c ../config.h ../lib/rcsid.h ../lib/prototypes.h \
../lib/defines.h ../lib/gshadow_.h ../lib/pwio.h \
../lib/shadowio.h
chfn.o: chfn.c ../config.h ../lib/rcsid.h ../lib/prototypes.h \
../lib/defines.h ../lib/gshadow_.h ../lib/pwio.h \
../lib/getdef.h ../lib/pwauth.h
chpasswd.o: chpasswd.c ../config.h ../lib/rcsid.h ../lib/prototypes.h \
../lib/defines.h ../lib/gshadow_.h ../lib/pwio.h \
../lib/shadowio.h
chsh.o: chsh.c ../config.h ../lib/rcsid.h ../lib/prototypes.h \
../lib/defines.h ../lib/gshadow_.h ../lib/pwio.h \
../lib/getdef.h ../lib/pwauth.h
dpasswd.o: dpasswd.c ../config.h ../lib/rcsid.h ../lib/prototypes.h \
../lib/defines.h ../lib/gshadow_.h ../lib/dialup.h
expiry.o: expiry.c ../config.h ../lib/rcsid.h ../lib/prototypes.h \
../lib/defines.h ../lib/gshadow_.h
faillog.o: faillog.c ../config.h ../lib/rcsid.h ../lib/prototypes.h \
../lib/defines.h ../lib/gshadow_.h ../lib/faillog.h
gpasswd.o: gpasswd.c ../config.h ../lib/rcsid.h ../lib/prototypes.h \
../lib/defines.h ../lib/gshadow_.h ../lib/groupio.h \
../lib/sgroupio.h
groupadd.o: groupadd.c ../config.h ../lib/rcsid.h ../lib/defines.h \
../lib/gshadow_.h ../lib/prototypes.h ../libmisc/chkname.h \
../lib/getdef.h ../lib/groupio.h ../lib/sgroupio.h
groupdel.o: groupdel.c ../config.h ../lib/rcsid.h ../lib/prototypes.h \
../lib/defines.h ../lib/gshadow_.h ../lib/groupio.h \
../lib/sgroupio.h
groupmod.o: groupmod.c ../config.h ../lib/rcsid.h ../lib/prototypes.h \
../lib/defines.h ../lib/gshadow_.h ../libmisc/chkname.h \
../lib/groupio.h ../lib/sgroupio.h
groups.o: groups.c ../config.h ../lib/rcsid.h ../lib/prototypes.h \
../lib/defines.h ../lib/gshadow_.h
grpck.o: grpck.c ../config.h ../lib/rcsid.h ../lib/prototypes.h \
../lib/defines.h ../lib/gshadow_.h ../libmisc/chkname.h \
../lib/commonio.h ../lib/groupio.h ../lib/sgroupio.h
grpconv.o: grpconv.c ../config.h ../lib/prototypes.h ../lib/defines.h \
../lib/gshadow_.h ../lib/groupio.h ../lib/sgroupio.h \
../lib/rcsid.h
grpunconv.o: grpunconv.c ../config.h ../lib/rcsid.h ../lib/prototypes.h \
../lib/defines.h ../lib/gshadow_.h ../lib/groupio.h \
../lib/sgroupio.h
id.o: id.c ../config.h ../lib/rcsid.h ../lib/defines.h ../lib/gshadow_.h
lastlog.o: lastlog.c ../config.h ../lib/rcsid.h ../lib/prototypes.h \
../lib/defines.h ../lib/gshadow_.h
login.o: login.c ../config.h ../lib/rcsid.h ../lib/prototypes.h \
../lib/defines.h ../lib/gshadow_.h ../lib/faillog.h \
../libmisc/failure.h ../lib/pwauth.h ../lib/getdef.h \
../lib/dialchk.h
logoutd.o: logoutd.c ../config.h ../lib/rcsid.h ../lib/prototypes.h \
../lib/defines.h ../lib/gshadow_.h
mkpasswd.o: mkpasswd.c ../config.h ../lib/rcsid.h ../lib/prototypes.h \
../lib/defines.h ../lib/gshadow_.h
newgrp.o: newgrp.c ../config.h ../lib/rcsid.h ../lib/prototypes.h \
../lib/defines.h ../lib/gshadow_.h ../lib/getdef.h
newusers.o: newusers.c ../config.h ../lib/rcsid.h ../lib/prototypes.h \
../lib/defines.h ../lib/gshadow_.h ../lib/getdef.h \
../lib/pwio.h ../lib/groupio.h ../lib/shadowio.h
passwd.o: passwd.c ../config.h ../lib/rcsid.h ../lib/prototypes.h \
../lib/defines.h ../lib/gshadow_.h ../lib/pwauth.h \
../lib/shadowio.h ../lib/pwio.h ../lib/getdef.h
pwck.o: pwck.c ../config.h ../lib/rcsid.h ../lib/prototypes.h \
../lib/defines.h ../lib/gshadow_.h ../libmisc/chkname.h \
../lib/commonio.h ../lib/pwio.h ../lib/shadowio.h
pwconv.o: pwconv.c ../config.h ../lib/rcsid.h ../lib/prototypes.h \
../lib/defines.h ../lib/gshadow_.h ../lib/pwio.h \
../lib/shadowio.h ../lib/getdef.h
pwunconv.o: pwunconv.c ../config.h ../lib/rcsid.h ../lib/defines.h \
../lib/gshadow_.h ../lib/prototypes.h ../lib/pwio.h \
../lib/shadowio.h
sulogin.o: sulogin.c ../config.h ../lib/rcsid.h ../lib/prototypes.h \
../lib/defines.h ../lib/gshadow_.h ../lib/getdef.h \
../lib/pwauth.h
su.o: su.c ../config.h ../lib/rcsid.h ../lib/prototypes.h \
../lib/defines.h ../lib/gshadow_.h ../lib/pwauth.h \
../lib/getdef.h
useradd.o: useradd.c ../config.h ../lib/rcsid.h ../lib/prototypes.h \
../lib/defines.h ../lib/gshadow_.h ../libmisc/chkname.h \
../lib/pwauth.h ../lib/faillog.h ../lib/groupio.h \
../lib/sgroupio.h ../lib/pwio.h ../lib/shadowio.h \
../lib/getdef.h
userdel.o: userdel.c ../config.h ../lib/rcsid.h ../lib/prototypes.h \
../lib/defines.h ../lib/gshadow_.h ../lib/getdef.h \
../lib/pwauth.h ../lib/groupio.h ../lib/pwio.h \
../lib/shadowio.h ../lib/sgroupio.h
usermod.o: usermod.c ../config.h ../lib/rcsid.h ../lib/prototypes.h \
../lib/defines.h ../lib/gshadow_.h ../libmisc/chkname.h \
../lib/faillog.h ../lib/pwauth.h ../lib/getdef.h \
../lib/groupio.h ../lib/sgroupio.h ../lib/pwio.h \
../lib/shadowio.h
vipw.o: vipw.c ../config.h ../lib/rcsid.h ../lib/defines.h \
../lib/gshadow_.h ../lib/prototypes.h ../lib/pwio.h \
../lib/shadowio.h ../lib/groupio.h ../lib/sgroupio.h
info:
dvi:
check: all
$(MAKE)
installcheck:
install-exec: install-binPROGRAMS
@$(NORMAL_INSTALL)
$(MAKE) install-exec-hook
install-data: install-ubinPROGRAMS install-usbinPROGRAMS
@$(NORMAL_INSTALL)
$(MAKE) install-data-hook
install: install-exec install-data all
@:
uninstall: uninstall-binPROGRAMS uninstall-ubinPROGRAMS uninstall-usbinPROGRAMS
install-strip:
$(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' INSTALL_SCRIPT='$(INSTALL_PROGRAM)' install
installdirs:
$(mkinstalldirs) $(DATADIR)$(bindir) $(DATADIR)$(ubindir) \
$(DATADIR)$(usbindir)
mostlyclean-generic:
-test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES)
clean-generic:
-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
distclean-generic:
-rm -f Makefile $(DISTCLEANFILES)
-rm -f config.cache config.log stamp-h stamp-h[0-9]*
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
maintainer-clean-generic:
-test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
-test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
mostlyclean: mostlyclean-binPROGRAMS mostlyclean-noinstPROGRAMS \
mostlyclean-ubinPROGRAMS mostlyclean-usbinPROGRAMS \
mostlyclean-compile mostlyclean-libtool \
mostlyclean-tags mostlyclean-generic
clean: clean-binPROGRAMS clean-noinstPROGRAMS clean-ubinPROGRAMS \
clean-usbinPROGRAMS clean-compile clean-libtool \
clean-tags clean-generic mostlyclean
distclean: distclean-binPROGRAMS distclean-noinstPROGRAMS \
distclean-ubinPROGRAMS distclean-usbinPROGRAMS \
distclean-compile distclean-libtool distclean-tags \
distclean-generic clean
-rm -f config.status
-rm -f libtool
maintainer-clean: maintainer-clean-binPROGRAMS \
maintainer-clean-noinstPROGRAMS \
maintainer-clean-ubinPROGRAMS \
maintainer-clean-usbinPROGRAMS maintainer-clean-compile \
maintainer-clean-libtool maintainer-clean-tags \
maintainer-clean-generic distclean
@echo "This command is intended for maintainers to use;"
@echo "it deletes files that may require special tools to rebuild."
.PHONY: mostlyclean-binPROGRAMS distclean-binPROGRAMS clean-binPROGRAMS \
maintainer-clean-binPROGRAMS uninstall-binPROGRAMS install-binPROGRAMS \
mostlyclean-noinstPROGRAMS distclean-noinstPROGRAMS \
clean-noinstPROGRAMS maintainer-clean-noinstPROGRAMS \
mostlyclean-ubinPROGRAMS distclean-ubinPROGRAMS clean-ubinPROGRAMS \
maintainer-clean-ubinPROGRAMS uninstall-ubinPROGRAMS \
install-ubinPROGRAMS mostlyclean-usbinPROGRAMS distclean-usbinPROGRAMS \
clean-usbinPROGRAMS maintainer-clean-usbinPROGRAMS \
uninstall-usbinPROGRAMS install-usbinPROGRAMS mostlyclean-compile \
distclean-compile clean-compile maintainer-clean-compile \
mostlyclean-libtool distclean-libtool clean-libtool \
maintainer-clean-libtool tags mostlyclean-tags distclean-tags \
clean-tags maintainer-clean-tags distdir info dvi installcheck \
install-exec install-data install uninstall all installdirs \
mostlyclean-generic distclean-generic clean-generic \
maintainer-clean-generic clean mostlyclean distclean maintainer-clean
install-exec-hook:
for i in $(suidbins); do \
chmod 4755 $(bindir)/$$i; \
done
install-data-hook:
for i in $(suidubins); do \
chmod 4755 $(ubindir)/$$i; \
done
rm -f $(ubindir)/sg
ln -s newgrp $(ubindir)/sg
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:

821
src/chage.c Normal file
View File

@ -0,0 +1,821 @@
/*
* Copyright 1989 - 1994, Julianne Frances Haugh
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Julianne F. Haugh nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <config.h>
#include "rcsid.h"
RCSID(PKG_VER "$Id: chage.c,v 1.15 1999/06/07 16:40:45 marekm Exp $")
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <ctype.h>
#include <time.h>
#include "prototypes.h"
#include "defines.h"
#include <pwd.h>
/*
* chage depends on some form of aging being present. It makes no sense
* to have a program that has no input.
*/
#ifdef SHADOWPWD
#ifndef AGING
#define AGING
#endif /* AGING */
#else /* !SHADOWPWD */
#if !defined(ATT_AGE) && defined(AGING)
#undef AGING
#endif /* !ATT_AGE && AGING */
#endif /* SHADOWPWD */
static char *Prog;
#ifdef AGING /*{*/
/*
* Global variables
*/
static long mindays;
static long maxdays;
static long lastday;
#ifdef SHADOWPWD
static long warndays;
static long inactdays;
static long expdays;
#endif
/*
* External identifiers
*/
extern long a64l();
extern char *l64a();
#include "pwio.h"
#ifdef SHADOWPWD
#include "shadowio.h"
#endif
extern int optind;
extern char *optarg;
#ifdef NDBM
extern int pw_dbm_mode;
#ifdef SHADOWPWD
extern int sp_dbm_mode;
#endif
#endif
/*
* #defines for messages. This facilitates foreign language conversion
* since all messages are defined right here.
*/
/*
* xgettext doesn't like #defines, so now we only leave untranslated
* messages here. -MM
*/
#define AGE_CHANGED "changed password expiry for %s\n"
#define LOCK_FAIL "failed locking %s\n"
#define OPEN_FAIL "failed opening %s\n"
#define WRITE_FAIL "failed updating %s\n"
#define CLOSE_FAIL "failed rewriting %s\n"
#define EPOCH "1969-12-31"
#ifdef SHADOWPWD
#define DBMERROR2 "error updating DBM shadow entry.\n"
#else
#define DBMERROR2 "error updating DBM passwd entry.\n"
#endif
/* local function prototypes */
static void usage P_((void));
static void date_to_str P_((char *, size_t, time_t));
static int new_fields P_((void));
static void print_date P_((time_t));
static void list_fields P_((void));
int main P_((int, char **));
static void cleanup P_((int));
/*
* usage - print command line syntax and exit
*/
static void
usage(void)
{
#ifdef SHADOWPWD
fprintf(stderr, _("Usage: %s [ -l ] [ -m min_days ] [ -M max_days ] [ -W warn ]\n [ -I inactive ] [ -E expire ] [ -d last_day ] user\n"), Prog);
#else
fprintf(stderr, _("Usage: %s [ -l ] [ -m min_days ] [ -M max_days ] [ -d last_day ] user\n"), Prog);
#endif
exit(1);
}
static void
date_to_str(char *buf, size_t maxsize, time_t date)
{
struct tm *tp;
tp = gmtime(&date);
#ifdef HAVE_STRFTIME
strftime(buf, maxsize, "%Y-%m-%d", tp);
#else
snprintf(buf, maxsize, "%04d-%02d-%02d",
tp->tm_year + 1900, tp->tm_mon + 1, tp->tm_mday);
#endif /* HAVE_STRFTIME */
}
/*
* new_fields - change the user's password aging information interactively.
*
* prompt the user for all of the password age values. set the fields
* from the user's response, or leave alone if nothing was entered. the
* value (-1) is used to indicate the field should be removed if possible.
* any other negative value is an error. very large positive values will
* be handled elsewhere.
*/
static int
new_fields(void)
{
char buf[200];
char *cp;
printf(_("Enter the new value, or press return for the default\n\n"));
snprintf(buf, sizeof buf, "%ld", mindays);
change_field(buf, sizeof buf, _("Minimum Password Age"));
if (((mindays = strtol (buf, &cp, 10)) == 0 && *cp) || mindays < -1)
return 0;
snprintf(buf, sizeof buf, "%ld", maxdays);
change_field(buf, sizeof buf, _("Maximum Password Age"));
if (((maxdays = strtol (buf, &cp, 10)) == 0 && *cp) || maxdays < -1)
return 0;
date_to_str(buf, sizeof buf, lastday * SCALE);
change_field(buf, sizeof buf, _("Last Password Change (YYYY-MM-DD)"));
if (strcmp (buf, EPOCH) == 0)
lastday = -1;
else if ((lastday = strtoday (buf)) == -1)
return 0;
#ifdef SHADOWPWD
snprintf(buf, sizeof buf, "%ld", warndays);
change_field (buf, sizeof buf, _("Password Expiration Warning"));
if (((warndays = strtol (buf, &cp, 10)) == 0 && *cp) || warndays < -1)
return 0;
snprintf(buf, sizeof buf, "%ld", inactdays);
change_field(buf, sizeof buf, _("Password Inactive"));
if (((inactdays = strtol (buf, &cp, 10)) == 0 && *cp) || inactdays < -1)
return 0;
date_to_str(buf, sizeof buf, expdays * SCALE);
change_field(buf, sizeof buf, _("Account Expiration Date (YYYY-MM-DD)"));
if (strcmp (buf, EPOCH) == 0)
expdays = -1;
else if ((expdays = strtoday (buf)) == -1)
return 0;
#endif /* SHADOWPWD */
return 1;
}
static void
print_date(time_t date)
{
#ifdef HAVE_STRFTIME
struct tm *tp;
char buf[80];
tp = gmtime(&date);
strftime(buf, sizeof buf, "%b %d, %Y", tp);
puts(buf);
#else
struct tm *tp;
char *cp;
tp = gmtime(&date);
cp = asctime(tp);
printf("%6.6s, %4.4s\n", cp + 4, cp + 20);
#endif
}
/*
* list_fields - display the current values of the expiration fields
*
* display the password age information from the password fields. date
* values will be displayed as a calendar date, or the word "Never" if
* the date is 1/1/70, which is day number 0.
*/
static void
list_fields(void)
{
long changed = 0;
long expires;
/*
* Start with the easy numbers - the number of days before the
* password can be changed, the number of days after which the
* password must be chaged, the number of days before the
* password expires that the user is told, and the number of
* days after the password expires that the account becomes
* unusable.
*/
printf(_("Minimum:\t%ld\n"), mindays);
printf(_("Maximum:\t%ld\n"), maxdays);
#ifdef SHADOWPWD
printf(_("Warning:\t%ld\n"), warndays);
printf(_("Inactive:\t%ld\n"), inactdays);
#endif
/*
* The "last change" date is either "Never" or the date the
* password was last modified. The date is the number of
* days since 1/1/1970.
*/
printf(_("Last Change:\t\t"));
if (lastday <= 0) {
printf(_("Never\n"));
} else {
changed = lastday * SCALE;
print_date(changed);
}
/*
* The password expiration date is determined from the last
* change date plus the number of days the password is valid
* for.
*/
printf(_("Password Expires:\t"));
if (lastday <= 0 || maxdays >= 10000*(DAY/SCALE) || maxdays <= 0) {
printf (_("Never\n"));
} else {
expires = changed + maxdays * SCALE;
print_date(expires);
}
#ifdef SHADOWPWD
/*
* The account becomes inactive if the password is expired
* for more than "inactdays". The expiration date is calculated
* and the number of inactive days is added. The resulting date
* is when the active will be disabled.
*/
printf ("Password Inactive:\t");
if (lastday <= 0 || inactdays <= 0 ||
maxdays >= 10000*(DAY/SCALE) || maxdays <= 0) {
printf ("Never\n");
} else {
expires = changed + (maxdays + inactdays) * SCALE;
print_date(expires);
}
/*
* The account will expire on the given date regardless of the
* password expiring or not.
*/
printf ("Account Expires:\t");
if (expdays <= 0) {
printf ("Never\n");
} else {
expires = expdays * SCALE;
print_date(expires);
}
#endif
}
/*
* chage - change a user's password aging information
*
* This command controls the password aging information.
*
* The valid options are
*
* -m minimum number of days before password change (*)
* -M maximim number of days before password change (*)
* -d last password change date (*)
* -l password aging information
* -W expiration warning days (*)
* -I password inactive after expiration (*)
* -E account expiration date (*)
*
* (*) requires root permission to execute.
*
* All of the time fields are entered in the internal format
* which is either seconds or days.
*
* The options -W, -I and -E all depend on the SHADOWPWD
* macro being defined.
*/
int
main(int argc, char **argv)
{
int flag;
int lflg = 0;
int mflg = 0;
int Mflg = 0;
int dflg = 0;
#ifdef SHADOWPWD
int Wflg = 0;
int Iflg = 0;
int Eflg = 0;
const struct spwd *sp;
struct spwd spwd;
#else
char new_age[5];
#endif
uid_t ruid = getuid ();
const struct passwd *pw;
struct passwd pwent;
char name[BUFSIZ];
sanitize_env();
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
/*
* Get the program name so that error messages can use it.
*/
Prog = Basename(argv[0]);
openlog("chage", LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
#ifdef NDBM
#ifdef SHADOWPWD
sp_dbm_mode = O_RDWR;
#endif
pw_dbm_mode = O_RDWR;
#endif
/*
* Parse the flags. The difference between password file
* formats includes the number of fields, and whether the
* dates are entered as days or weeks. Shadow password
* file info =must= be entered in days, while regular
* password file info =must= be entered in weeks.
*/
#ifdef SHADOWPWD
#define FLAGS "lm:M:W:I:E:d:"
#else
#define FLAGS "lm:M:d:"
#endif
while ((flag = getopt(argc, argv, FLAGS)) != EOF) {
#undef FLAGS
switch (flag) {
case 'l':
lflg++;
break;
case 'm':
mflg++;
mindays = strtol (optarg, 0, 10);
break;
case 'M':
Mflg++;
maxdays = strtol (optarg, 0, 10);
break;
case 'd':
dflg++;
if (strchr (optarg, '/'))
lastday = strtoday (optarg);
else
lastday = strtol (optarg, 0, 10);
break;
#ifdef SHADOWPWD
case 'W':
Wflg++;
warndays = strtol (optarg, 0, 10);
break;
case 'I':
Iflg++;
inactdays = strtol (optarg, 0, 10);
break;
case 'E':
Eflg++;
if (strchr (optarg, '/'))
expdays = strtoday (optarg);
else
expdays = strtol (optarg, 0, 10);
break;
#endif
default:
usage ();
}
}
/*
* Make certain the flags do not conflict and that there is
* a user name on the command line.
*/
if (argc != optind + 1)
usage ();
#ifdef SHADOWPWD
if (lflg && (mflg || Mflg || dflg || Wflg || Iflg || Eflg))
#else
if (lflg && (mflg || Mflg || dflg))
#endif
{
fprintf (stderr, _("%s: do not include \"l\" with other flags\n"), Prog);
closelog();
usage ();
}
/*
* An unprivileged user can ask for their own aging information,
* but only root can change it, or list another user's aging
* information.
*/
if (ruid != 0 && ! lflg) {
fprintf (stderr, _("%s: permission denied\n"), Prog);
closelog();
exit (1);
}
/*
* Lock and open the password file. This loads all of the
* password file entries into memory. Then we get a pointer
* to the password file entry for the requested user.
*/
if (!pw_lock()) {
fprintf(stderr, _("%s: can't lock password file\n"), Prog);
SYSLOG((LOG_ERR, LOCK_FAIL, PASSWD_FILE));
closelog();
exit(1);
}
if (!pw_open((ruid != 0 || lflg) ? O_RDONLY:O_RDWR)) {
fprintf(stderr, _("%s: can't open password file\n"), Prog);
cleanup(1);
SYSLOG((LOG_ERR, OPEN_FAIL, PASSWD_FILE));
closelog();
exit(1);
}
if (!(pw = pw_locate(argv[optind]))) {
fprintf(stderr, _("%s: unknown user: %s\n"), Prog, argv[optind]);
cleanup(1);
closelog();
exit(1);
}
pwent = *pw;
STRFCPY(name, pwent.pw_name);
#ifdef SHADOWPWD
/*
* For shadow password files we have to lock the file and
* read in the entries as was done for the password file.
* The user entries does not have to exist in this case;
* a new entry will be created for this user if one does
* not exist already.
*/
if (!spw_lock()) {
fprintf(stderr, _("%s: can't lock shadow password file\n"), Prog);
cleanup(1);
SYSLOG((LOG_ERR, LOCK_FAIL, SHADOW_FILE));
closelog();
exit(1);
}
if (!spw_open((ruid != 0 || lflg) ? O_RDONLY:O_RDWR)) {
fprintf(stderr, _("%s: can't open shadow password file\n"), Prog);
cleanup(2);
SYSLOG((LOG_ERR, OPEN_FAIL, SHADOW_FILE));
closelog();
exit(1);
}
sp = spw_locate(argv[optind]);
/*
* Set the fields that aren't being set from the command line
* from the password file.
*/
if (sp) {
spwd = *sp;
if (! Mflg)
maxdays = spwd.sp_max;
if (! mflg)
mindays = spwd.sp_min;
if (! dflg)
lastday = spwd.sp_lstchg;
if (! Wflg)
warndays = spwd.sp_warn;
if (! Iflg)
inactdays = spwd.sp_inact;
if (! Eflg)
expdays = spwd.sp_expire;
}
#ifdef ATT_AGE
else
#endif /* ATT_AGE */
#endif /* SHADOWPWD */
#ifdef ATT_AGE
{
if (pwent.pw_age && strlen (pwent.pw_age) >= 2) {
if (! Mflg)
maxdays = c64i (pwent.pw_age[0]) * (WEEK/SCALE);
if (! mflg)
mindays = c64i (pwent.pw_age[1]) * (WEEK/SCALE);
if (! dflg && strlen (pwent.pw_age) == 4)
lastday = a64l (pwent.pw_age+2) * (WEEK/SCALE);
} else {
mindays = 0;
maxdays = 10000L * (DAY/SCALE);
lastday = -1;
}
#ifdef SHADOWPWD
warndays = inactdays = expdays = -1;
#endif /* SHADOWPWD */
}
#endif /* ATT_AGE */
/*
* Print out the expiration fields if the user has
* requested the list option.
*/
if (lflg) {
if (ruid != 0 && ruid != pwent.pw_uid) {
fprintf(stderr, _("%s: permission denied\n"), Prog);
closelog();
exit(1);
}
list_fields();
cleanup(2);
closelog();
exit(0);
}
/*
* If none of the fields were changed from the command line,
* let the user interactively change them.
*/
#ifdef SHADOWPWD
if (! mflg && ! Mflg && ! dflg && ! Wflg && ! Iflg && ! Eflg)
#else
if (! mflg && ! Mflg && ! dflg)
#endif
{
printf(_("Changing the aging information for %s\n"), name);
if (!new_fields()) {
fprintf(stderr, _("%s: error changing fields\n"), Prog);
cleanup(2);
closelog();
exit(1);
}
}
#ifdef SHADOWPWD
/*
* There was no shadow entry. The new entry will have the
* encrypted password transferred from the normal password
* file along with the aging information.
*/
if (sp == 0) {
sp = &spwd;
memzero(&spwd, sizeof spwd);
spwd.sp_namp = xstrdup (pwent.pw_name);
spwd.sp_pwdp = xstrdup (pwent.pw_passwd);
spwd.sp_flag = -1;
pwent.pw_passwd = SHADOW_PASSWD_STRING; /* XXX warning: const */
#ifdef ATT_AGE
pwent.pw_age = "";
#endif
if (!pw_update(&pwent)) {
fprintf(stderr, _("%s: can't update password file\n"), Prog);
cleanup(2);
SYSLOG((LOG_ERR, WRITE_FAIL, PASSWD_FILE));
closelog();
exit(1);
}
#ifdef NDBM
(void) pw_dbm_update (&pwent);
endpwent ();
#endif
}
#endif /* SHADOWPWD */
#ifdef SHADOWPWD
/*
* Copy the fields back to the shadow file entry and
* write the modified entry back to the shadow file.
* Closing the shadow and password files will commit
* any changes that have been made.
*/
spwd.sp_max = maxdays;
spwd.sp_min = mindays;
spwd.sp_lstchg = lastday;
spwd.sp_warn = warndays;
spwd.sp_inact = inactdays;
spwd.sp_expire = expdays;
if (!spw_update(&spwd)) {
fprintf(stderr, _("%s: can't update shadow password file\n"), Prog);
cleanup(2);
SYSLOG((LOG_ERR, WRITE_FAIL, SHADOW_FILE));
closelog();
exit(1);
}
#else /* !SHADOWPWD */
/*
* fill in the new_age string with the new values
*/
if (maxdays > (63 * 7) && mindays == 0) {
new_age[0] = '\0';
} else {
if (maxdays > (63 * 7))
maxdays = 63 * 7;
if (mindays > (63 * 7))
mindays = 63 * 7;
new_age[0] = i64c (maxdays / 7);
new_age[1] = i64c ((mindays + 6) / 7);
if (lastday == 0)
new_age[2] = '\0';
else
strcpy (new_age + 2, l64a (lastday / 7));
}
pwent.pw_age = new_age;
if (!pw_update(&pwent)) {
fprintf(stderr, _("%s: can't update password file\n"), Prog);
cleanup(2);
SYSLOG((LOG_ERR, WRITE_FAIL, PASSWD_FILE));
closelog();
exit(1);
}
#endif /* SHADOWPWD */
#ifdef NDBM
#ifdef SHADOWPWD
/*
* See if the shadow DBM file exists and try to update it.
*/
if (sp_dbm_present() && !sp_dbm_update(&spwd)) {
fprintf(stderr, _("Error updating the DBM password entry.\n"));
cleanup(2);
SYSLOG((LOG_ERR, DBMERROR2));
closelog();
exit(1);
}
endspent();
#else /* !SHADOWPWD */
/*
* See if the password DBM file exists and try to update it.
*/
if (pw_dbm_present() && !pw_dbm_update(&pwent)) {
fprintf(stderr, _("Error updating the DBM password entry.\n"));
cleanup(2);
SYSLOG((LOG_ERR, DBMERROR2));
closelog();
exit(1);
}
endpwent ();
#endif /* SHADOWPWD */
#endif /* NDBM */
#ifdef SHADOWPWD
/*
* Now close the shadow password file, which will cause all
* of the entries to be re-written.
*/
if (!spw_close()) {
fprintf(stderr, _("%s: can't rewrite shadow password file\n"), Prog);
cleanup(2);
SYSLOG((LOG_ERR, CLOSE_FAIL, SHADOW_FILE));
closelog();
exit(1);
}
#endif /* SHADOWPWD */
/*
* Close the password file. If any entries were modified, the
* file will be re-written.
*/
if (!pw_close()) {
fprintf(stderr, _("%s: can't rewrite password file\n"), Prog);
cleanup(2);
SYSLOG((LOG_ERR, CLOSE_FAIL, PASSWD_FILE));
closelog();
exit(1);
}
cleanup(2);
SYSLOG((LOG_INFO, AGE_CHANGED, name));
closelog();
exit(0);
/*NOTREACHED*/
}
/*
* cleanup - unlock any locked password files
*/
static void
cleanup(int state)
{
switch (state) {
case 2:
#ifdef SHADOWPWD
spw_unlock ();
#endif
case 1:
pw_unlock ();
case 0:
break;
}
}
#else /*} !AGING {*/
/*
* chage - but there is no age info!
*/
int
main(int argc, char **argv)
{
char *Prog;
Prog = Basename(argv[0]);
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
fprintf (stderr, _("%s: no aging information present\n"), Prog);
exit(1);
}
#endif /*} AGING */

602
src/chfn.c Normal file
View File

@ -0,0 +1,602 @@
/*
* Copyright 1989 - 1994, Julianne Frances Haugh
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Julianne F. Haugh nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <config.h>
#include "rcsid.h"
RCSID(PKG_VER "$Id: chfn.c,v 1.15 1999/07/09 18:02:43 marekm Exp $")
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include "prototypes.h"
#include "defines.h"
#include <pwd.h>
#include "pwio.h"
#include "getdef.h"
#include "pwauth.h"
#ifdef HAVE_SHADOW_H
#include <shadow.h>
#endif
#ifdef USE_PAM
#include "pam_defs.h"
#endif
/*
* Global variables.
*/
static char *Prog;
static char fullnm[BUFSIZ];
static char roomno[BUFSIZ];
static char workph[BUFSIZ];
static char homeph[BUFSIZ];
static char slop[BUFSIZ];
static int amroot;
/*
* External identifiers
*/
extern int optind;
extern char *optarg;
#ifdef NDBM
extern int pw_dbm_mode;
#endif
/*
* #defines for messages. This facilitates foreign language conversion
* since all messages are defined right here.
*/
#define WRONGPWD2 "incorrect password for `%s'"
#define PWDBUSY2 "can't lock /etc/passwd\n"
#define OPNERROR2 "can't open /etc/passwd\n"
#define UPDERROR2 "error updating passwd entry\n"
#define DBMERROR2 "error updating DBM passwd entry.\n"
#define NOTROOT2 "can't setuid(0).\n"
#define CLSERROR2 "can't rewrite /etc/passwd.\n"
#define UNLKERROR2 "can't unlock /etc/passwd.\n"
#define CHGGECOS "changed user `%s' information.\n"
/* local function prototypes */
static void usage P_((void));
static int may_change_field P_((int));
static void new_fields P_((void));
static char *copy_field P_((char *, char *, char *));
int main P_((int, char **));
/*
* usage - print command line syntax and exit
*/
static void
usage(void)
{
if (amroot)
fprintf(stderr,
_("Usage: %s [ -f full_name ] [ -r room_no ] [ -w work_ph ]\n\t[ -h home_ph ] [ -o other ] [ user ]\n"),
Prog);
else
fprintf(stderr,
_("Usage: %s [ -f full_name ] [ -r room_no ] [ -w work_ph ] [ -h home_ph ]\n"),
Prog);
exit(1);
}
static int
may_change_field(int field)
{
const char *cp;
/*
* CHFN_RESTRICT can now specify exactly which fields may be
* changed by regular users, by using any combination of the
* following letters:
* f - full name
* r - room number
* w - work phone
* h - home phone
*
* This makes it possible to disallow changing the room number
* information, for example - this feature was suggested by
* Maciej 'Tycoon' Majchrowski.
*
* For backward compatibility, "yes" is equivalent to "rwh",
* "no" is equivalent to "frwh". Only root can change anything
* if the string is empty or not defined at all.
*/
if (amroot)
return 1;
cp = getdef_str("CHFN_RESTRICT");
if (!cp)
cp = "";
else if (strcmp(cp, "yes") == 0)
cp = "rwh";
else if (strcmp(cp, "no") == 0)
cp = "frwh";
if (strchr(cp, field))
return 1;
return 0;
}
/*
* new_fields - change the user's GECOS information interactively
*
* prompt the user for each of the four fields and fill in the fields
* from the user's response, or leave alone if nothing was entered.
*/
static void
new_fields(void)
{
printf(_("Enter the new value, or press return for the default\n"));
if (may_change_field('f'))
change_field(fullnm, sizeof fullnm, _("Full Name"));
else
printf(_("\tFull Name: %s\n"), fullnm);
if (may_change_field('r'))
change_field(roomno, sizeof roomno, _("Room Number"));
else
printf(_("\tRoom Number: %s\n"), roomno);
if (may_change_field('w'))
change_field(workph, sizeof workph, _("Work Phone"));
else
printf(_("\tWork Phone: %s\n"), workph);
if (may_change_field('h'))
change_field(homeph, sizeof homeph, _("Home Phone"));
else
printf(_("\tHome Phone: %s\n"), homeph);
if (amroot)
change_field(slop, sizeof slop, _("Other"));
}
/*
* copy_field - get the next field from the gecos field
*
* copy_field copies the next field from the gecos field, returning a
* pointer to the field which follows, or NULL if there are no more
* fields.
*
* in - the current GECOS field
* out - where to copy the field to
* extra - fields with '=' get copied here
*/
static char *
copy_field(char *in, char *out, char *extra)
{
char *cp = NULL;
while (in) {
if ((cp = strchr (in, ',')))
*cp++ = '\0';
if (! strchr (in, '='))
break;
if (extra) {
if (extra[0])
strcat (extra, ",");
strcat (extra, in);
}
in = cp;
}
if (in && out)
strcpy (out, in);
return cp;
}
/*
* chfn - change a user's password file information
*
* This command controls the GECOS field information in the
* password file entry.
*
* The valid options are
*
* -f full name
* -r room number
* -w work phone number
* -h home phone number
* -o other information (*)
*
* (*) requires root permission to execute.
*/
int
main(int argc, char **argv)
{
char *cp; /* temporary character pointer */
const struct passwd *pw; /* password file entry */
struct passwd pwent; /* modified password file entry */
char old_gecos[BUFSIZ]; /* buffer for old GECOS fields */
char new_gecos[BUFSIZ]; /* buffer for new GECOS fields */
int flag; /* flag currently being processed */
int fflg = 0; /* -f - set full name */
int rflg = 0; /* -r - set room number */
int wflg = 0; /* -w - set work phone number */
int hflg = 0; /* -h - set home phone number */
int oflg = 0; /* -o - set other information */
char *user;
sanitize_env();
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
/*
* This command behaves different for root and non-root
* users.
*/
amroot = (getuid () == 0);
#ifdef NDBM
pw_dbm_mode = O_RDWR;
#endif
/*
* Get the program name. The program name is used as a
* prefix to most error messages. It is also used as input
* to the openlog() function for error logging.
*/
Prog = Basename(argv[0]);
openlog("chfn", LOG_PID, LOG_AUTH);
/*
* The remaining arguments will be processed one by one and
* executed by this command. The name is the last argument
* if it does not begin with a "-", otherwise the name is
* determined from the environment and must agree with the
* real UID. Also, the UID will be checked for any commands
* which are restricted to root only.
*/
while ((flag = getopt (argc, argv, "f:r:w:h:o:")) != EOF) {
switch (flag) {
case 'f':
if (!may_change_field('f')) {
fprintf(stderr, _("%s: Permission denied.\n"), Prog);
exit(1);
}
fflg++;
STRFCPY(fullnm, optarg);
break;
case 'r':
if (!may_change_field('r')) {
fprintf(stderr, _("%s: Permission denied.\n"), Prog);
exit(1);
}
rflg++;
STRFCPY(roomno, optarg);
break;
case 'w':
if (!may_change_field('w')) {
fprintf(stderr, _("%s: Permission denied.\n"), Prog);
exit(1);
}
wflg++;
STRFCPY(workph, optarg);
break;
case 'h':
if (!may_change_field('h')) {
fprintf(stderr, _("%s: Permission denied.\n"), Prog);
exit(1);
}
hflg++;
STRFCPY(homeph, optarg);
break;
case 'o':
if (!amroot) {
fprintf(stderr, _("%s: Permission denied.\n"), Prog);
exit(1);
}
oflg++;
STRFCPY(slop, optarg);
break;
default:
usage();
}
}
/*
* Get the name of the user to check. It is either
* the command line name, or the name getlogin()
* returns.
*/
if (optind < argc) {
user = argv[optind];
pw = getpwnam(user);
if (!pw) {
fprintf(stderr, _("%s: Unknown user %s\n"), Prog, user);
exit(1);
}
} else {
pw = get_my_pwent();
if (!pw) {
fprintf(stderr, _("%s: Cannot determine your user name.\n"), Prog);
exit(1);
}
user = xstrdup(pw->pw_name);
}
#ifdef USE_NIS
/*
* Now we make sure this is a LOCAL password entry for
* this user ...
*/
if (__ispwNIS ()) {
char *nis_domain;
char *nis_master;
fprintf (stderr, _("%s: cannot change user `%s' on NIS client.\n"), Prog, user);
if (! yp_get_default_domain (&nis_domain) &&
! yp_master (nis_domain, "passwd.byname",
&nis_master)) {
fprintf (stderr, _("%s: `%s' is the NIS master for this client.\n"), Prog, nis_master);
}
exit (1);
}
#endif
/*
* Non-privileged users are only allowed to change the
* gecos field if the UID of the user matches the current
* real UID.
*/
if (!amroot && pw->pw_uid != getuid()) {
fprintf (stderr, _("%s: Permission denied.\n"), Prog);
closelog();
exit(1);
}
/*
* Non-privileged users are optionally authenticated
* (must enter the password of the user whose information
* is being changed) before any changes can be made.
* Idea from util-linux chfn/chsh. --marekm
*/
if (!amroot && getdef_bool("CHFN_AUTH"))
passwd_check(pw->pw_name, pw->pw_passwd, "chfn");
/*
* Now get the full name. It is the first comma separated field
* in the GECOS field.
*/
STRFCPY(old_gecos, pw->pw_gecos);
cp = copy_field (old_gecos, fflg ? (char *) 0:fullnm, slop);
/*
* Now get the room number. It is the next comma separated field,
* if there is indeed one.
*/
if (cp)
cp = copy_field (cp, rflg ? (char *) 0:roomno, slop);
/*
* Now get the work phone number. It is the third field.
*/
if (cp)
cp = copy_field (cp, wflg ? (char *) 0:workph, slop);
/*
* Now get the home phone number. It is the fourth field.
*/
if (cp)
cp = copy_field (cp, hflg ? (char *) 0:homeph, slop);
/*
* Anything left over is "slop".
*/
if (cp && !oflg) {
if (slop[0])
strcat (slop, ",");
strcat (slop, cp);
}
/*
* If none of the fields were changed from the command line,
* let the user interactively change them.
*/
if (!fflg && !rflg && !wflg && !hflg && !oflg) {
printf(_("Changing the user information for %s\n"), user);
new_fields();
}
/*
* Check all of the fields for valid information
*/
if (valid_field(fullnm, ":,=")) {
fprintf(stderr, _("%s: invalid name: \"%s\"\n"), Prog, fullnm);
closelog();
exit(1);
}
if (valid_field(roomno, ":,=")) {
fprintf(stderr, _("%s: invalid room number: \"%s\"\n"), Prog, roomno);
closelog();
exit(1);
}
if (valid_field(workph, ":,=")) {
fprintf(stderr, _("%s: invalid work phone: \"%s\"\n"), Prog, workph);
closelog();
exit(1);
}
if (valid_field (homeph, ":,=")) {
fprintf(stderr, _("%s: invalid home phone: \"%s\"\n"), Prog, homeph);
closelog();
exit(1);
}
if (valid_field(slop, ":")) {
fprintf(stderr, _("%s: \"%s\" contains illegal characters\n"), Prog, slop);
closelog();
exit(1);
}
/*
* Build the new GECOS field by plastering all the pieces together,
* if they will fit ...
*/
if (strlen(fullnm) + strlen(roomno) + strlen(workph) +
strlen(homeph) + strlen(slop) > (unsigned int) 80) {
fprintf(stderr, _("%s: fields too long\n"), Prog);
closelog();
exit(1);
}
snprintf(new_gecos, sizeof new_gecos, "%s,%s,%s,%s%s%s",
fullnm, roomno, workph, homeph, slop[0] ? "," : "", slop);
/*
* Before going any further, raise the ulimit to prevent
* colliding into a lowered ulimit, and set the real UID
* to root to protect against unexpected signals. Any
* keyboard signals are set to be ignored.
*/
if (setuid(0)) {
fprintf(stderr, _("Cannot change ID to root.\n"));
SYSLOG((LOG_ERR, NOTROOT2));
closelog();
exit(1);
}
pwd_init();
/*
* The passwd entry is now ready to be committed back to
* the password file. Get a lock on the file and open it.
*/
if (!pw_lock()) {
fprintf(stderr, _("Cannot lock the password file; try again later.\n"));
SYSLOG((LOG_WARN, PWDBUSY2));
closelog();
exit(1);
}
if (!pw_open(O_RDWR)) {
fprintf(stderr, _("Cannot open the password file.\n"));
pw_unlock();
SYSLOG((LOG_ERR, OPNERROR2));
closelog();
exit(1);
}
/*
* Get the entry to update using pw_locate() - we want the real
* one from /etc/passwd, not the one from getpwnam() which could
* contain the shadow password if (despite the warnings) someone
* enables AUTOSHADOW (or SHADOW_COMPAT in libc). --marekm
*/
pw = pw_locate(user);
if (!pw) {
pw_unlock();
fprintf(stderr,
_("%s: %s not found in /etc/passwd\n"), Prog, user);
exit(1);
}
/*
* Make a copy of the entry, then change the gecos field. The other
* fields remain unchanged.
*/
pwent = *pw;
pwent.pw_gecos = new_gecos;
/*
* Update the passwd file entry. If there is a DBM file,
* update that entry as well.
*/
if (!pw_update(&pwent)) {
fprintf(stderr, _("Error updating the password entry.\n"));
pw_unlock();
SYSLOG((LOG_ERR, UPDERROR2));
closelog();
exit(1);
}
#ifdef NDBM
if (pw_dbm_present() && !pw_dbm_update(&pwent)) {
fprintf(stderr, _("Error updating the DBM password entry.\n"));
pw_unlock ();
SYSLOG((LOG_ERR, DBMERROR2));
closelog();
exit(1);
}
endpwent();
#endif
/*
* Changes have all been made, so commit them and unlock the
* file.
*/
if (!pw_close()) {
fprintf(stderr, _("Cannot commit password file changes.\n"));
pw_unlock();
SYSLOG((LOG_ERR, CLSERROR2));
closelog();
exit(1);
}
if (!pw_unlock()) {
fprintf(stderr, _("Cannot unlock the password file.\n"));
SYSLOG((LOG_ERR, UNLKERROR2));
closelog();
exit(1);
}
SYSLOG((LOG_INFO, CHGGECOS, user));
closelog();
exit (0);
}

290
src/chpasswd.c Normal file
View File

@ -0,0 +1,290 @@
/*
* Copyright 1990 - 1994, Julianne Frances Haugh
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Julianne F. Haugh nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* chpasswd - update passwords in batch
*
* chpasswd reads standard input for a list of colon separated
* user names and new passwords. the appropriate password
* files are updated to reflect the changes. because the
* changes are made in a batch fashion, the user must run
* the mkpasswd command after this command terminates since
* no password updates occur until the very end.
*
* 1997/07/29: Modified to take "-e" argument which specifies that
* the passwords have already been encrypted.
* -- Jay Soffian <jay@lw.net>
*/
#include <config.h>
#include "rcsid.h"
RCSID(PKG_VER "$Id: chpasswd.c,v 1.9 1999/06/07 16:40:45 marekm Exp $")
#include <stdio.h>
#include "prototypes.h"
#include "defines.h"
#include <pwd.h>
#include <fcntl.h>
#include "pwio.h"
#ifdef SHADOWPWD
#include "shadowio.h"
#endif
static char *Prog;
static int eflg = 0;
#ifdef SHADOWPWD
static int is_shadow_pwd;
#endif
extern char *crypt_make_salt P_((void));
extern char *l64a();
/* local function prototypes */
static void usage P_((void));
int main P_((int, char **));
/*
* usage - display usage message and exit
*/
static void
usage(void)
{
fprintf(stderr, _("usage: %s [-e]\n"), Prog);
exit(1);
}
int
main(int argc, char **argv)
{
char buf[BUFSIZ];
char *name;
char *newpwd;
char *cp;
#ifdef SHADOWPWD
const struct spwd *sp;
struct spwd newsp;
#endif
const struct passwd *pw;
struct passwd newpw;
#ifdef ATT_AGE
char newage[5];
#endif
int errors = 0;
int line = 0;
long now = time ((long *) 0) / (24L*3600L);
int ok;
Prog = Basename(argv[0]);
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
/* XXX - use getopt() */
if (!(argc == 1 || (argc == 2 && !strcmp(argv[1], "-e"))))
usage();
if (argc == 2)
eflg = 1;
/*
* Lock the password file and open it for reading. This will
* bring all of the entries into memory where they may be
* updated.
*/
if (!pw_lock()) {
fprintf(stderr, _("%s: can't lock password file\n"), Prog);
exit(1);
}
if (! pw_open (O_RDWR)) {
fprintf(stderr, _("%s: can't open password file\n"), Prog);
pw_unlock();
exit(1);
}
#ifdef SHADOWPWD
is_shadow_pwd = spw_file_present();
if (is_shadow_pwd) {
if (!spw_lock()) {
fprintf(stderr, _("%s: can't lock shadow file\n"), Prog);
pw_unlock();
exit(1);
}
if (!spw_open(O_RDWR)) {
fprintf(stderr, _("%s: can't open shadow file\n"), Prog);
pw_unlock();
spw_unlock();
exit(1);
}
}
#endif
/*
* Read each line, separating the user name from the password.
* The password entry for each user will be looked up in the
* appropriate file (shadow or passwd) and the password changed.
* For shadow files the last change date is set directly, for
* passwd files the last change date is set in the age only if
* aging information is present.
*/
while (fgets (buf, sizeof buf, stdin) != (char *) 0) {
line++;
if ((cp = strrchr (buf, '\n'))) {
*cp = '\0';
} else {
fprintf(stderr, _("%s: line %d: line too long\n"),
Prog, line);
errors++;
continue;
}
/*
* The username is the first field. It is separated
* from the password with a ":" character which is
* replaced with a NUL to give the new password. The
* new password will then be encrypted in the normal
* fashion with a new salt generated, unless the '-e'
* is given, in which case it is assumed to already be
* encrypted.
*/
name = buf;
if ((cp = strchr (name, ':'))) {
*cp++ = '\0';
} else {
fprintf(stderr, _("%s: line %d: missing new password\n"),
Prog, line);
errors++;
continue;
}
newpwd = cp;
if (!eflg)
cp = pw_encrypt(newpwd, crypt_make_salt());
/*
* Get the password file entry for this user. The user
* must already exist.
*/
pw = pw_locate(name);
if (!pw) {
fprintf (stderr, _("%s: line %d: unknown user %s\n"),
Prog, line, name);
errors++;
continue;
}
#ifdef SHADOWPWD
if (is_shadow_pwd)
sp = spw_locate(name);
else
sp = NULL;
#endif
/*
* The freshly encrypted new password is merged into
* the user's password file entry and the last password
* change date is set to the current date.
*/
#ifdef SHADOWPWD
if (sp) {
newsp = *sp;
newsp.sp_pwdp = cp;
newsp.sp_lstchg = now;
} else
#endif
{
newpw = *pw;
newpw.pw_passwd = cp;
#ifdef ATT_AGE
if (newpw.pw_age[0]) {
strcpy(newage, newpw.pw_age);
strcpy(newage + 2, l64a(now / 7));
newpw.pw_age = newage;
}
#endif
}
/*
* The updated password file entry is then put back
* and will be written to the password file later, after
* all the other entries have been updated as well.
*/
#ifdef SHADOWPWD
if (sp)
ok = spw_update(&newsp);
else
#endif
ok = pw_update(&newpw);
if (!ok) {
fprintf(stderr, _("%s: line %d: cannot update password entry\n"),
Prog, line);
errors++;
continue;
}
}
/*
* Any detected errors will cause the entire set of changes
* to be aborted. Unlocking the password file will cause
* all of the changes to be ignored. Otherwise the file is
* closed, causing the changes to be written out all at
* once, and then unlocked afterwards.
*/
if (errors) {
fprintf(stderr, _("%s: error detected, changes ignored\n"), Prog);
#ifdef SHADOWPWD
if (is_shadow_pwd)
spw_unlock();
#endif
pw_unlock();
exit(1);
}
#ifdef SHADOWPWD
if (is_shadow_pwd) {
if (!spw_close()) {
fprintf(stderr, _("%s: error updating shadow file\n"), Prog);
pw_unlock();
exit(1);
}
spw_unlock();
}
#endif
if (!pw_close()) {
fprintf(stderr, _("%s: error updating password file\n"), Prog);
exit(1);
}
pw_unlock();
return (0);
}

439
src/chsh.c Normal file
View File

@ -0,0 +1,439 @@
/*
* Copyright 1989 - 1994, Julianne Frances Haugh
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Julianne F. Haugh nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <config.h>
#include "rcsid.h"
RCSID(PKG_VER "$Id: chsh.c,v 1.15 1999/07/09 18:02:43 marekm Exp $")
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include "prototypes.h"
#include "defines.h"
#include <pwd.h>
#include "pwio.h"
#include "getdef.h"
#include "pwauth.h"
#ifdef HAVE_SHADOW_H
#include <shadow.h>
#endif
#ifdef USE_PAM
#include "pam_defs.h"
#endif
#ifndef SHELLS_FILE
#define SHELLS_FILE "/etc/shells"
#endif
/*
* Global variables.
*/
static char *Prog; /* Program name */
static int amroot; /* Real UID is root */
static char loginsh[BUFSIZ]; /* Name of new login shell */
/*
* External identifiers
*/
extern int optind;
extern char *optarg;
#ifdef NDBM
extern int pw_dbm_mode;
#endif
/*
* #defines for messages. This facilitates foreign language conversion
* since all messages are defined right here.
*/
#define WRONGPWD2 "incorrect password for `%s'"
#define NOPERM2 "can't change shell for `%s'\n"
#define PWDBUSY2 "can't lock /etc/passwd\n"
#define OPNERROR2 "can't open /etc/passwd\n"
#define UPDERROR2 "error updating passwd entry\n"
#define DBMERROR2 "error updating DBM passwd entry.\n"
#define NOTROOT2 "can't setuid(0).\n"
#define CLSERROR2 "can't rewrite /etc/passwd.\n"
#define UNLKERROR2 "can't unlock /etc/passwd.\n"
#define CHGSHELL "changed user `%s' shell to `%s'\n"
/* local function prototypes */
static void usage P_((void));
static void new_fields P_((void));
static int restricted_shell P_((const char *));
int main P_((int, char **));
/*
* usage - print command line syntax and exit
*/
static void
usage(void)
{
fprintf(stderr, _("Usage: %s [ -s shell ] [ name ]\n"), Prog);
exit(1);
}
/*
* new_fields - change the user's login shell information interactively
*
* prompt the user for the login shell and change it according to the
* response, or leave it alone if nothing was entered.
*/
static void
new_fields(void)
{
printf(_("Enter the new value, or press return for the default\n"));
change_field(loginsh, sizeof loginsh, _("Login Shell"));
}
/*
* restricted_shell - return true if the named shell begins with 'r' or 'R'
*
* If the first letter of the filename is 'r' or 'R', the shell is
* considered to be restricted.
*/
static int
restricted_shell(const char *sh)
{
#if 0
char *cp = Basename((char *) sh);
return *cp == 'r' || *cp == 'R';
#else
/*
* Shells not listed in /etc/shells are considered to be
* restricted. Changed this to avoid confusion with "rc"
* (the plan9 shell - not restricted despite the name
* starting with 'r'). --marekm
*/
return !check_shell(sh);
#endif
}
/*
* chsh - this command controls changes to the user's shell
*
* The only supported option is -s which permits the
* the login shell to be set from the command line.
*/
int
main(int argc, char **argv)
{
char *user; /* User name */
int flag; /* Current command line flag */
int sflg = 0; /* -s - set shell from command line */
const struct passwd *pw; /* Password entry from /etc/passwd */
struct passwd pwent; /* New password entry */
sanitize_env();
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
/*
* This command behaves different for root and non-root
* users.
*/
amroot = getuid () == 0;
#ifdef NDBM
pw_dbm_mode = O_RDWR;
#endif
/*
* Get the program name. The program name is used as a
* prefix to most error messages. It is also used as input
* to the openlog() function for error logging.
*/
Prog = Basename(argv[0]);
openlog("chsh", LOG_PID, LOG_AUTH);
/*
* There is only one option, but use getopt() anyway to
* keep things consistent.
*/
while ((flag = getopt (argc, argv, "s:")) != EOF) {
switch (flag) {
case 's':
sflg++;
STRFCPY(loginsh, optarg);
break;
default:
usage ();
}
}
/*
* There should be only one remaining argument at most
* and it should be the user's name.
*/
if (argc > optind + 1)
usage ();
/*
* Get the name of the user to check. It is either
* the command line name, or the name getlogin()
* returns.
*/
if (optind < argc) {
user = argv[optind];
pw = getpwnam(user);
if (!pw) {
fprintf(stderr,
_("%s: Unknown user %s\n"),
Prog, user);
exit(1);
}
} else {
pw = get_my_pwent();
if (!pw) {
fprintf(stderr,
_("%s: Cannot determine your user name.\n"),
Prog);
exit(1);
}
user = xstrdup(pw->pw_name);
}
#ifdef USE_NIS
/*
* Now we make sure this is a LOCAL password entry for
* this user ...
*/
if (__ispwNIS ()) {
char *nis_domain;
char *nis_master;
fprintf(stderr,
_("%s: cannot change user `%s' on NIS client.\n"),
Prog, user);
if (! yp_get_default_domain (&nis_domain) &&
! yp_master (nis_domain, "passwd.byname",
&nis_master)) {
fprintf(stderr,
_("%s: `%s' is the NIS master for this client.\n"),
Prog, nis_master);
}
exit (1);
}
#endif
/*
* Non-privileged users are only allowed to change the
* shell if the UID of the user matches the current
* real UID.
*/
if (! amroot && pw->pw_uid != getuid()) {
SYSLOG((LOG_WARN, NOPERM2, user));
closelog();
fprintf(stderr, _("You may not change the shell for %s.\n"),
user);
exit(1);
}
/*
* Non-privileged users are only allowed to change the
* shell if it is not a restricted one.
*/
if (! amroot && restricted_shell(pw->pw_shell)) {
SYSLOG((LOG_WARN, NOPERM2, user));
closelog();
fprintf(stderr, _("You may not change the shell for %s.\n"),
user);
exit(1);
}
/*
* Non-privileged users are optionally authenticated
* (must enter the password of the user whose information
* is being changed) before any changes can be made.
* Idea from util-linux chfn/chsh. --marekm
*/
if (!amroot && getdef_bool("CHFN_AUTH"))
passwd_check(pw->pw_name, pw->pw_passwd, "chsh");
/*
* Now get the login shell. Either get it from the password
* file, or use the value from the command line.
*/
if (! sflg)
STRFCPY(loginsh, pw->pw_shell);
/*
* If the login shell was not set on the command line,
* let the user interactively change it.
*/
if (! sflg) {
printf(_("Changing the login shell for %s\n"), user);
new_fields();
}
/*
* Check all of the fields for valid information. The shell
* field may not contain any illegal characters. Non-privileged
* users are restricted to using the shells in /etc/shells.
* The shell must be executable by the user.
*/
if (valid_field (loginsh, ":,=")) {
fprintf(stderr, _("%s: Invalid entry: %s\n"), Prog, loginsh);
closelog();
exit(1);
}
if (!amroot && (!check_shell(loginsh) || access(loginsh, X_OK) != 0)) {
fprintf(stderr, _("%s is an invalid shell.\n"), loginsh);
closelog();
exit(1);
}
/*
* Before going any further, raise the ulimit to prevent
* colliding into a lowered ulimit, and set the real UID
* to root to protect against unexpected signals. Any
* keyboard signals are set to be ignored.
*/
if (setuid(0)) {
SYSLOG((LOG_ERR, NOTROOT2));
closelog();
fprintf (stderr, _("Cannot change ID to root.\n"));
exit(1);
}
pwd_init();
/*
* The passwd entry is now ready to be committed back to
* the password file. Get a lock on the file and open it.
*/
if (!pw_lock()) {
SYSLOG((LOG_WARN, PWDBUSY2));
closelog();
fprintf(stderr,
_("Cannot lock the password file; try again later.\n"));
exit(1);
}
if (! pw_open (O_RDWR)) {
SYSLOG((LOG_ERR, OPNERROR2));
closelog();
fprintf(stderr, _("Cannot open the password file.\n"));
pw_unlock();
exit(1);
}
/*
* Get the entry to update using pw_locate() - we want the real
* one from /etc/passwd, not the one from getpwnam() which could
* contain the shadow password if (despite the warnings) someone
* enables AUTOSHADOW (or SHADOW_COMPAT in libc). --marekm
*/
pw = pw_locate(user);
if (!pw) {
pw_unlock();
fprintf(stderr,
_("%s: %s not found in /etc/passwd\n"), Prog, user);
exit(1);
}
/*
* Make a copy of the entry, then change the shell field. The other
* fields remain unchanged.
*/
pwent = *pw;
pwent.pw_shell = loginsh;
/*
* Update the passwd file entry. If there is a DBM file,
* update that entry as well.
*/
if (!pw_update(&pwent)) {
SYSLOG((LOG_ERR, UPDERROR2));
closelog();
fprintf(stderr, _("Error updating the password entry.\n"));
pw_unlock();
exit(1);
}
#ifdef NDBM
if (pw_dbm_present() && ! pw_dbm_update (&pwent)) {
SYSLOG((LOG_ERR, DBMERROR2));
closelog();
fprintf (stderr, _("Error updating the DBM password entry.\n"));
pw_unlock();
exit(1);
}
endpwent();
#endif
/*
* Changes have all been made, so commit them and unlock the
* file.
*/
if (!pw_close()) {
SYSLOG((LOG_ERR, CLSERROR2));
closelog();
fprintf(stderr, _("Cannot commit password file changes.\n"));
pw_unlock();
exit(1);
}
if (!pw_unlock()) {
SYSLOG((LOG_ERR, UNLKERROR2));
closelog();
fprintf(stderr, _("Cannot unlock the password file.\n"));
exit(1);
}
SYSLOG((LOG_INFO, CHGSHELL, user, loginsh));
closelog();
exit (0);
}

261
src/dpasswd.c Normal file
View File

@ -0,0 +1,261 @@
/*
* Copyright 1990 - 1993, Julianne Frances Haugh
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Julianne F. Haugh nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <config.h>
#include "rcsid.h"
RCSID(PKG_VER "$Id: dpasswd.c,v 1.9 1999/06/07 16:40:45 marekm Exp $")
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include "prototypes.h"
#include "defines.h"
#include "dialup.h"
#define DTMP "/etc/d_passwd.tmp"
/*
* Prompts and messages go here.
*/
#define DIALCHG "changed password for %s\n"
#define DIALADD "added password for %s\n"
#define DIALREM "removed password for %s\n"
static int aflg = 0;
static int dflg = 0;
static char *Prog;
extern int optind;
extern char *optarg;
extern char *crypt_make_salt P_((void));
extern char *getpass();
/* local function prototypes */
static void usage P_((void));
int main P_((int, char **));
static void
usage(void)
{
fprintf(stderr, _("Usage: %s [ -(a|d) ] shell\n"), Prog);
exit(1);
}
int
main(int argc, char **argv)
{
struct dialup *dial;
struct dialup dent;
struct stat sb;
FILE *fp;
char *sh = 0;
char *cp;
char pass[BUFSIZ];
int fd;
int found = 0;
int opt;
Prog = Basename(argv[0]);
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
openlog(Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
while ((opt = getopt (argc, argv, "a:d:")) != EOF) {
switch (opt) {
case 'a':
aflg++;
sh = optarg;
break;
case 'd':
dflg++;
sh = optarg;
break;
default:
usage ();
}
}
if (! aflg && ! dflg)
aflg++;
if (! sh) {
if (optind >= argc)
usage ();
else
sh = argv[optind];
}
if (aflg + dflg != 1)
usage ();
/*
* Add a new shell to the password file, or update an existing
* entry. Begin by getting an encrypted password for this
* shell.
*/
if (aflg) {
int tries = 3;
dent.du_shell = sh;
dent.du_passwd = ""; /* XXX warning: const */
again:
if (! (cp = getpass(_("Shell password:"))))
exit (1);
STRFCPY(pass, cp);
strzero(cp);
if (! (cp = getpass(_("re-enter Shell password:"))))
exit (1);
if (strcmp (pass, cp)) {
strzero(pass);
strzero(cp);
fprintf(stderr,
_("%s: Passwords do not match, try again.\n"),
Prog);
if (--tries)
goto again;
exit(1);
}
strzero(cp);
dent.du_passwd = pw_encrypt(pass, crypt_make_salt());
strzero(pass);
}
/*
* Create the temporary file for the updated dialup password
* information to be placed into. Turn it into a (FILE *)
* for use by putduent().
*/
if ((fd = open (DTMP, O_CREAT|O_EXCL|O_RDWR, 0600)) < 0) {
snprintf(pass, sizeof pass, _("%s: can't create %s"), Prog, DTMP);
perror (pass);
exit (1);
}
if (! (fp = fdopen (fd, "r+"))) {
snprintf(pass, sizeof pass, _("%s: can't open %s"), Prog, DTMP);
perror (pass);
unlink (DTMP);
exit (1);
}
/*
* Scan the dialup password file for the named entry,
* copying out other entries along the way. Copying
* stops when a match is found or the file runs out.
*/
while ((dial = getduent ())) {
if (strcmp (dial->du_shell, sh) == 0) {
found = 1;
break;
}
if (putduent (dial, fp))
goto failure;
}
/*
* To delete the entry, just don't copy it. To update
* the entry, output the modified version - works with
* new entries as well.
*/
if (dflg && ! found) {
fprintf(stderr, _("%s: Shell %s not found.\n"), Prog, sh);
goto failure;
}
if (aflg)
if (putduent (&dent, fp))
goto failure;
/*
* Now copy out the remaining entries. Flush and close the
* new file before doing anything nasty to the existing
* file.
*/
while ((dial = getduent ()))
if (putduent (dial, fp))
goto failure;
if (fflush (fp))
goto failure;
fclose (fp);
/*
* If the original file did not exist, we must create a new
* file with owner "root" and mode 400. Otherwise we copy
* the modes from the existing file to the new file.
*
* After this is done the new file will replace the old file.
*/
pwd_init();
if (! stat (DIALPWD, &sb)) {
chown (DTMP, sb.st_uid, sb.st_gid);
chmod (DTMP, sb.st_mode);
unlink (DIALPWD);
} else {
chown (DTMP, 0, 0);
chmod (DTMP, 0400);
}
if (! link (DTMP, DIALPWD))
unlink (DTMP);
if (aflg && ! found)
SYSLOG((LOG_INFO, DIALADD, sh));
else if (aflg && found)
SYSLOG((LOG_INFO, DIALCHG, sh));
else if (dflg)
SYSLOG((LOG_INFO, DIALREM, sh));
closelog();
sync ();
exit (0);
failure:
unlink (DTMP);
closelog();
exit (1);
}

211
src/expiry.c Normal file
View File

@ -0,0 +1,211 @@
/*
* Copyright 1994, Julianne Frances Haugh
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Julianne F. Haugh nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <config.h>
#include "rcsid.h"
RCSID(PKG_VER "$Id: expiry.c,v 1.8 1999/06/07 16:40:45 marekm Exp $")
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include "prototypes.h"
#include "defines.h"
#include <pwd.h>
#ifndef AGING
#if defined(HAVE_USERSEC_H) || defined(SHADOWPWD)
#define AGING 1
#endif
#endif
int main P_((int, char **));
#if !defined(SHADOWPWD) && !defined(AGING) /*{*/
/*
* Not much to do here ...
*/
int
main(int argc, char **argv)
{
exit (0);
}
#else /*} AGING || SHADOWPWD {*/
/* local function prototypes */
static RETSIGTYPE catch P_((int));
static void usage P_((void));
/*
* catch - signal catcher
*/
static RETSIGTYPE
catch(int sig)
{
exit (10);
}
/*
* usage - print syntax message and exit
*/
static void
usage(void)
{
fprintf(stderr, _("Usage: expiry { -f | -c }\n"));
exit(10);
}
/*
* expiry - check and enforce password expiration policy
*
* expiry checks (-c) the current password expiraction and
* forces (-f) changes when required. It is callable as a
* normal user command.
*/
int
main(int argc, char **argv)
{
struct passwd *pwd;
#ifdef SHADOWPWD
struct spwd *spwd;
#endif
char *Prog = argv[0];
sanitize_env();
/*
* Start by disabling all of the keyboard signals.
*/
signal (SIGHUP, catch);
signal (SIGINT, catch);
signal (SIGQUIT, catch);
#ifdef SIGTSTP
signal (SIGTSTP, catch);
#endif
/*
* expiry takes one of two arguments. The default action
* is to give the usage message.
*/
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
if (argc != 2 || (strcmp (argv[1], "-f") && strcmp (argv[1], "-c")))
usage ();
#if 0 /* could be setgid shadow with /etc/shadow mode 0640 */
/*
* Make sure I am root. Can't open /etc/shadow without root
* authority.
*/
if (geteuid () != 0) {
fprintf(stderr, _("%s: WARNING! Must be set-UID root!\n"),
argv[0]);
exit(10);
}
#endif
/*
* Get user entries for /etc/passwd and /etc/shadow
*/
if (!(pwd = get_my_pwent())) {
fprintf(stderr, _("%s: unknown user\n"), Prog);
exit(10);
}
#ifdef SHADOWPWD
spwd = getspnam(pwd->pw_name);
#endif
/*
* If checking accounts, use agecheck() function.
*/
if (strcmp (argv[1], "-c") == 0) {
/*
* Print out number of days until expiration.
*/
#ifdef SHADOWPWD
agecheck (pwd, spwd);
#else
agecheck (pwd);
#endif
/*
* Exit with status indicating state of account --
*/
#ifdef SHADOWPWD
exit (isexpired (pwd, spwd));
#else
exit (isexpired (pwd));
#endif
}
/*
* If forcing password change, use expire() function.
*/
if (strcmp (argv[1], "-f") == 0) {
/*
* Just call expire(). It will force the change
* or give a message indicating what to do. And
* it doesn't return at all unless the account
* is unexpired.
*/
#ifdef SHADOWPWD
expire (pwd, spwd);
#else
expire (pwd);
#endif
exit (0);
}
/*
* Can't get here ...
*/
usage ();
exit (1);
}
#endif /*}*/

380
src/faillog.c Normal file
View File

@ -0,0 +1,380 @@
/*
* Copyright 1989 - 1993, Julianne Frances Haugh
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Julianne F. Haugh nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <config.h>
#include "rcsid.h"
RCSID(PKG_VER "$Id: faillog.c,v 1.9 1999/07/09 18:02:43 marekm Exp $")
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <pwd.h>
#include <time.h>
#include "prototypes.h"
#include "defines.h"
#include "faillog.h"
static char *Prog; /* program name */
static FILE *fail; /* failure file stream */
static uid_t user; /* one single user, specified on command line */
static int days; /* number of days to consider for print command */
static time_t seconds; /* that number of days in seconds */
static int
aflg = 0, /* set if all users are to be printed always */
uflg = 0, /* set if user is a valid user id */
tflg = 0; /* print is restricted to most recent days */
static struct stat statbuf; /* fstat buffer for file size */
#if !defined(UNISTD_H) && !defined(STDLIB_H)
extern char *optarg;
#endif
#define NOW (time((time_t *) 0))
/* local function prototypes */
static void usage P_((void));
int main P_((int, char **));
static void print P_((void));
static void print_one P_((const struct faillog *, uid_t));
static void reset P_((void));
static int reset_one P_((uid_t));
static void setmax P_((int));
static void setmax_one P_((uid_t, int));
static void set_locktime P_((long));
static void set_locktime_one P_((uid_t, long));
static void
usage(void)
{
fprintf(stderr,
_("usage: %s [-a|-u user] [-m max] [-r] [-t days] [-l locksecs]\n"),
Prog);
exit(1);
}
int
main(int argc, char **argv)
{
int c, anyflag = 0;
struct passwd *pwent;
Prog = Basename(argv[0]);
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
/* try to open for read/write, if that fails - read only */
fail = fopen(FAILLOG_FILE, "r+");
if (!fail)
fail = fopen(FAILLOG_FILE, "r");
if (!fail) {
perror(FAILLOG_FILE);
exit(1);
}
while ((c = getopt(argc, argv, "al:m:pru:t:")) != EOF) {
switch (c) {
case 'a':
aflg++;
if (uflg)
usage();
break;
case 'l':
set_locktime((long) atoi(optarg));
anyflag++;
break;
case 'm':
setmax(atoi(optarg));
anyflag++;
break;
case 'p':
print();
anyflag++;
break;
case 'r':
reset();
anyflag++;
break;
case 'u':
if (aflg)
usage();
pwent = getpwnam(optarg);
if (!pwent) {
fprintf(stderr, _("Unknown User: %s\n"), optarg);
exit(1);
}
uflg++;
user = pwent->pw_uid;
break;
case 't':
days = atoi(optarg);
seconds = days * DAY;
tflg++;
break;
default:
usage();
}
}
/* no flags implies -a -p (= print information for all users) */
if (!(anyflag || aflg || tflg || uflg))
aflg++;
/* (-a or -t days or -u user) and no other flags implies -p
(= print information for selected users) */
if (!anyflag && (aflg || tflg || uflg))
print();
fclose(fail);
return 0;
/*NOTREACHED*/
}
static void
print(void)
{
uid_t uid;
off_t offset;
struct faillog faillog;
if (uflg) {
offset = user * sizeof faillog;
if (fstat(fileno(fail), &statbuf)) {
perror(FAILLOG_FILE);
return;
}
if (offset >= statbuf.st_size)
return;
fseek(fail, (off_t) user * sizeof faillog, SEEK_SET);
if (fread((char *) &faillog, sizeof faillog, 1, fail) == 1)
print_one(&faillog, user);
else
perror(FAILLOG_FILE);
} else {
for (uid = 0;
fread((char *) &faillog, sizeof faillog, 1, fail) == 1;
uid++) {
if (aflg == 0 && faillog.fail_cnt == 0)
continue;
if (aflg == 0 && tflg &&
NOW - faillog.fail_time > seconds)
continue;
if (aflg && faillog.fail_time == 0)
continue;
print_one(&faillog, uid);
}
}
}
static void
print_one(const struct faillog *fl, uid_t uid)
{
static int once;
char *cp;
struct tm *tm;
time_t now;
struct passwd *pwent;
#ifdef HAVE_STRFTIME
char ptime[80];
#endif
if (!once) {
printf(_("Username Failures Maximum Latest\n"));
once++;
}
pwent = getpwuid(uid);
time(&now);
tm = localtime(&fl->fail_time);
#ifdef HAVE_STRFTIME
strftime(ptime, sizeof(ptime), "%a %b %e %H:%M:%S %z %Y",tm);
cp = ptime;
#else
cp = asctime(tm);
cp[24] = '\0';
#endif
if (pwent) {
printf("%-12s %4d %4d",
pwent->pw_name, fl->fail_cnt, fl->fail_max);
if (fl->fail_time) {
printf(_(" %s on %s"), cp, fl->fail_line);
if (fl->fail_locktime) {
if (fl->fail_time + fl->fail_locktime > now
&& fl->fail_cnt)
printf(_(" [%lds left]"),
fl->fail_time + fl->fail_locktime - now);
else
printf(_(" [%lds lock]"), fl->fail_locktime);
}
}
putchar('\n');
}
}
static void
reset(void)
{
uid_t uid;
if (uflg)
reset_one(user);
else
for (uid = 0; reset_one(uid); uid++)
;
}
static int
reset_one(uid_t uid)
{
off_t offset;
struct faillog faillog;
offset = uid * sizeof faillog;
if (fstat(fileno(fail), &statbuf)) {
perror(FAILLOG_FILE);
return 0;
}
if (offset >= statbuf.st_size)
return 0;
if (fseek(fail, offset, SEEK_SET) != 0) {
perror(FAILLOG_FILE);
return 0;
}
if (fread((char *) &faillog, sizeof faillog, 1, fail) != 1) {
if (!feof(fail))
perror(FAILLOG_FILE);
return 0;
}
if (faillog.fail_cnt == 0)
return 1; /* don't fill in no holes ... */
faillog.fail_cnt = 0;
if (fseek(fail, offset, SEEK_SET) == 0
&& fwrite((char *) &faillog, sizeof faillog, 1, fail) == 1) {
fflush(fail);
return 1;
} else {
perror(FAILLOG_FILE);
}
return 0;
}
static void
setmax(int max)
{
struct passwd *pwent;
if (uflg) {
setmax_one(user, max);
} else {
setpwent();
while ((pwent = getpwent()))
setmax_one(pwent->pw_uid, max);
}
}
static void
setmax_one(uid_t uid, int max)
{
off_t offset;
struct faillog faillog;
offset = uid * sizeof faillog;
if (fseek(fail, offset, SEEK_SET) != 0) {
perror(FAILLOG_FILE);
return;
}
if (fread((char *) &faillog, sizeof faillog, 1, fail) != 1) {
if (!feof(fail))
perror(FAILLOG_FILE);
memzero(&faillog, sizeof faillog);
}
faillog.fail_max = max;
if (fseek(fail, offset, SEEK_SET) == 0
&& fwrite((char *) &faillog, sizeof faillog, 1, fail) == 1)
fflush(fail);
else
perror(FAILLOG_FILE);
}
/*
* XXX - this needs to be written properly some day, right now it is
* a quick cut-and-paste hack from the above two functions. --marekm
*/
static void
set_locktime(long locktime)
{
struct passwd *pwent;
if (uflg) {
set_locktime_one(user, locktime);
} else {
setpwent();
while ((pwent = getpwent()))
set_locktime_one(pwent->pw_uid, locktime);
}
}
static void
set_locktime_one(uid_t uid, long locktime)
{
off_t offset;
struct faillog faillog;
offset = uid * sizeof faillog;
if (fseek(fail, offset, SEEK_SET) != 0) {
perror(FAILLOG_FILE);
return;
}
if (fread((char *) &faillog, sizeof faillog, 1, fail) != 1) {
if (!feof(fail))
perror(FAILLOG_FILE);
memzero(&faillog, sizeof faillog);
}
faillog.fail_locktime = locktime;
if (fseek(fail, offset, SEEK_SET) == 0
&& fwrite((char *) &faillog, sizeof faillog, 1, fail) == 1)
fflush(fail);
else
perror(FAILLOG_FILE);
}

662
src/gpasswd.c Normal file
View File

@ -0,0 +1,662 @@
/*
* Copyright 1990 - 1994, Julianne Frances Haugh
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Julianne F. Haugh nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <config.h>
#include "rcsid.h"
RCSID(PKG_VER "$Id: gpasswd.c,v 1.14 1999/06/07 16:40:45 marekm Exp $")
#include <sys/types.h>
#include <stdio.h>
#include <pwd.h>
#include <grp.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include "prototypes.h"
#include "defines.h"
#include "groupio.h"
#ifdef SHADOWGRP
#include "sgroupio.h"
#endif
static char *Prog;
#ifdef SHADOWGRP
static int is_shadowgrp;
#endif
static int
aflg = 0,
Aflg = 0,
dflg = 0,
Mflg = 0,
rflg = 0,
Rflg = 0;
#ifndef RETRIES
#define RETRIES 3
#endif
extern char *crypt_make_salt P_((void));
extern int optind;
extern char *optarg;
#ifdef NDBM
#ifdef SHADOWGRP
extern int sg_dbm_mode;
#endif
extern int gr_dbm_mode;
#endif
/* local function prototypes */
static void usage P_((void));
static RETSIGTYPE die P_((int));
static int check_list P_((const char *));
int main P_((int, char **));
/*
* usage - display usage message
*/
static void
usage(void)
{
fprintf(stderr, _("usage: %s [-r|-R] group\n"), Prog);
fprintf(stderr, _(" %s [-a user] group\n"), Prog);
fprintf(stderr, _(" %s [-d user] group\n"), Prog);
#ifdef SHADOWGRP
fprintf(stderr, _(" %s [-A user,...] [-M user,...] group\n"),
Prog);
#else
fprintf(stderr, _(" %s [-M user,...] group\n"), Prog);
#endif
exit (1);
}
/*
* die - set or reset termio modes.
*
* die() is called before processing begins. signal() is then
* called with die() as the signal handler. If signal later
* calls die() with a signal number, the terminal modes are
* then reset.
*/
static RETSIGTYPE
die(int killed)
{
static TERMIO sgtty;
if (killed)
STTY(0, &sgtty);
else
GTTY(0, &sgtty);
if (killed) {
putchar ('\n');
fflush (stdout);
exit (killed);
}
}
/*
* check_list - check a comma-separated list of user names for validity
*
* check_list scans a comma-separated list of user names and checks
* that each listed name exists.
*/
static int
check_list(const char *users)
{
const char *start, *end;
char username[32];
int errors = 0;
int len;
for (start = users; start && *start; start = end) {
if ((end = strchr (start, ','))) {
len = end - start;
end++;
} else {
len = strlen(start);
}
if (len > sizeof(username) - 1)
len = sizeof(username) - 1;
strncpy(username, start, len);
username[len] = '\0';
/*
* This user must exist.
*/
if (!getpwnam(username)) {
fprintf(stderr, _("%s: unknown user %s\n"),
Prog, username);
errors++;
}
}
return errors;
}
static void
failure(void)
{
fprintf(stderr, _("Permission denied.\n"));
exit(1);
/*NOTREACHED*/
}
/*
* gpasswd - administer the /etc/group file
*
* -a user add user to the named group
* -d user remove user from the named group
* -r remove password from the named group
* -R restrict access to the named group
* -A user,... make list of users the administrative users
* -M user,... make list of users the group members
*/
int
main(int argc, char **argv)
{
int flag;
char *cp;
int amroot;
int retries;
struct group *gr = NULL;
struct group grent;
static char pass[BUFSIZ];
#ifdef SHADOWGRP
struct sgrp *sg = NULL;
struct sgrp sgent;
char *admins = NULL;
#endif
struct passwd *pw = NULL;
char *myname;
char *user = NULL;
char *group = NULL;
char *members = NULL;
sanitize_env();
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
/*
* Make a note of whether or not this command was invoked
* by root. This will be used to bypass certain checks
* later on. Also, set the real user ID to match the
* effective user ID. This will prevent the invoker from
* issuing signals which would interfer with this command.
*/
amroot = getuid () == 0;
#ifdef NDBM
#ifdef SHADOWGRP
sg_dbm_mode = O_RDWR;
#endif
gr_dbm_mode = O_RDWR;
#endif
Prog = Basename(argv[0]);
openlog("gpasswd", LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
setbuf (stdout, (char *) 0);
setbuf (stderr, (char *) 0);
#ifdef SHADOWGRP
is_shadowgrp = sgr_file_present();
#endif
while ((flag = getopt (argc, argv, "a:d:grRA:M:")) != EOF) {
switch (flag) {
case 'a': /* add a user */
user = optarg;
if (!getpwnam(user)) {
fprintf(stderr, _("%s: unknown user %s\n"),
Prog, user);
exit(1);
}
aflg++;
break;
#ifdef SHADOWGRP
case 'A':
if (!amroot)
failure();
if (!is_shadowgrp) {
fprintf(stderr,
_("%s: shadow group passwords required for -A\n"),
Prog);
exit(2);
}
admins = optarg;
if (check_list(admins))
exit(1);
Aflg++;
break;
#endif
case 'd': /* delete a user */
dflg++;
user = optarg;
break;
case 'g': /* no-op from normal password */
break;
case 'M':
if (!amroot)
failure();
members = optarg;
if (check_list(members))
exit(1);
Mflg++;
break;
case 'r': /* remove group password */
rflg++;
break;
case 'R': /* restrict group password */
Rflg++;
break;
default:
usage();
}
}
/*
* Make sure exclusive flags are exclusive
*/
if (aflg + dflg + rflg + Rflg + (Aflg || Mflg) > 1)
usage ();
/*
* Determine the name of the user that invoked this command.
* This is really hit or miss because there are so many ways
* that command can be executed and so many ways to trip up
* the routines that report the user name.
*/
pw = get_my_pwent();
if (!pw) {
fprintf(stderr, _("Who are you?\n"));
exit(1);
}
myname = xstrdup(pw->pw_name);
/*
* Get the name of the group that is being affected. The group
* entry will be completely replicated so it may be modified
* later on.
*/
/*
* XXX - should get the entry using gr_locate() and modify
* that, getgrnam() could give us a NIS group. --marekm
*/
if (! (group = argv[optind]))
usage ();
if (! (gr = getgrnam (group))) {
fprintf (stderr, _("unknown group: %s\n"), group);
exit (1);
}
grent = *gr;
grent.gr_name = xstrdup(gr->gr_name);
grent.gr_passwd = xstrdup(gr->gr_passwd);
grent.gr_mem = dup_list(gr->gr_mem);
#ifdef SHADOWGRP
if ((sg = getsgnam (group))) {
sgent = *sg;
sgent.sg_name = xstrdup(sg->sg_name);
sgent.sg_passwd = xstrdup(sg->sg_passwd);
sgent.sg_mem = dup_list(sg->sg_mem);
sgent.sg_adm = dup_list(sg->sg_adm);
} else {
sgent.sg_name = xstrdup(group);
sgent.sg_passwd = grent.gr_passwd;
grent.gr_passwd = "!"; /* XXX warning: const */
sgent.sg_mem = dup_list(grent.gr_mem);
sgent.sg_adm = (char **) xmalloc(sizeof(char *) * 2);
#ifdef FIRST_MEMBER_IS_ADMIN
if (sgent.sg_mem[0]) {
sgent.sg_adm[0] = xstrdup(sgent.sg_mem[0]);
sgent.sg_adm[1] = 0;
} else
#endif
sgent.sg_adm[0] = 0;
sg = &sgent;
}
/*
* The policy here for changing a group is that 1) you must be
* root or 2). you must be listed as an administrative member.
* Administrative members can do anything to a group that the
* root user can.
*/
if (!amroot && !is_on_list(sgent.sg_adm, myname))
failure();
#else /* ! SHADOWGRP */
#ifdef FIRST_MEMBER_IS_ADMIN
/*
* The policy here for changing a group is that 1) you must bes
* root or 2) you must be the first listed member of the group.
* The first listed member of a group can do anything to that
* group that the root user can. The rationale for this hack is
* that the FIRST user is probably the most important user in
* this entire group.
*/
if (! amroot) {
if (grent.gr_mem[0] == (char *) 0)
failure();
if (strcmp(grent.gr_mem[0], myname) != 0)
failure();
}
#else
/*
* This feature enabled by default could be a security problem
* when installed on existing systems where the first group
* member might be just a normal user... --marekm
*/
if (!amroot)
failure();
#endif
#endif /* SHADOWGRP */
/*
* Removing a password is straight forward. Just set the
* password field to a "".
*/
if (rflg) {
grent.gr_passwd = ""; /* XXX warning: const */
#ifdef SHADOWGRP
sgent.sg_passwd = ""; /* XXX warning: const */
#endif
SYSLOG((LOG_INFO, "remove password from group %s by %s\n", group, myname));
goto output;
} else if (Rflg) {
/*
* Same thing for restricting the group. Set the password
* field to "!".
*/
grent.gr_passwd = "!"; /* XXX warning: const */
#ifdef SHADOWGRP
sgent.sg_passwd = "!"; /* XXX warning: const */
#endif
SYSLOG((LOG_INFO, "restrict access to group %s by %s\n", group, myname));
goto output;
}
/*
* Adding a member to a member list is pretty straightforward
* as well. Call the appropriate routine and split.
*/
if (aflg) {
printf(_("Adding user %s to group %s\n"), user, group);
grent.gr_mem = add_list (grent.gr_mem, user);
#ifdef SHADOWGRP
sgent.sg_mem = add_list (sgent.sg_mem, user);
#endif
SYSLOG((LOG_INFO, "add member %s to group %s by %s\n", user, group, myname));
goto output;
}
/*
* Removing a member from the member list is the same deal
* as adding one, except the routine is different.
*/
if (dflg) {
int removed = 0;
printf(_("Removing user %s from group %s\n"), user, group);
if (is_on_list(grent.gr_mem, user)) {
removed = 1;
grent.gr_mem = del_list (grent.gr_mem, user);
}
#ifdef SHADOWGRP
if (is_on_list(sgent.sg_mem, user)) {
removed = 1;
sgent.sg_mem = del_list (sgent.sg_mem, user);
}
#endif
if (! removed) {
fprintf(stderr, _("%s: unknown member %s\n"),
Prog, user);
exit (1);
}
SYSLOG((LOG_INFO, "remove member %s from group %s by %s\n",
user, group, myname));
goto output;
}
#ifdef SHADOWGRP
/*
* Replacing the entire list of administators is simple. Check the
* list to make sure everyone is a real user. Then slap the new
* list in place.
*/
if (Aflg) {
SYSLOG((LOG_INFO, "set administrators of %s to %s\n",
group, admins));
sgent.sg_adm = comma_to_list(admins);
if (!Mflg)
goto output;
}
#endif
/*
* Replacing the entire list of members is simple. Check the list
* to make sure everyone is a real user. Then slap the new list
* in place.
*/
if (Mflg) {
SYSLOG((LOG_INFO,"set members of %s to %s\n",group,members));
#ifdef SHADOWGRP
sgent.sg_mem = comma_to_list(members);
#endif
grent.gr_mem = comma_to_list(members);
goto output;
}
/*
* If the password is being changed, the input and output must
* both be a tty. The typical keyboard signals are caught
* so the termio modes can be restored.
*/
if (! isatty (0) || ! isatty (1)) {
fprintf(stderr, _("%s: Not a tty\n"), Prog);
exit (1);
}
die (0); /* save tty modes */
signal (SIGHUP, die);
signal (SIGINT, die);
signal (SIGQUIT, die);
signal (SIGTERM, die);
#ifdef SIGTSTP
signal (SIGTSTP, die);
#endif
/*
* A new password is to be entered and it must be encrypted,
* etc. The password will be prompted for twice, and both
* entries must be identical. There is no need to validate
* the old password since the invoker is either the group
* owner, or root.
*/
printf(_("Changing the password for group %s\n"), group);
for (retries = 0; retries < RETRIES; retries++) {
if (! (cp = getpass(_("New Password:"))))
exit (1);
STRFCPY(pass, cp);
strzero(cp);
if (! (cp = getpass (_("Re-enter new password:"))))
exit (1);
if (strcmp(pass, cp) == 0) {
strzero(cp);
break;
}
strzero(cp);
memzero(pass, sizeof pass);
if (retries + 1 < RETRIES)
puts(_("They don't match; try again"));
}
if (retries == RETRIES) {
fprintf(stderr, _("%s: Try again later\n"), Prog);
exit(1);
}
cp = pw_encrypt(pass, crypt_make_salt());
memzero(pass, sizeof pass);
#ifdef SHADOWGRP
sgent.sg_passwd = cp;
#else
grent.gr_passwd = cp;
#endif
SYSLOG((LOG_INFO, "change the password for group %s by %s\n", group, myname));
/*
* This is the common arrival point to output the new group
* file. The freshly crafted entry is in allocated space.
* The group file will be locked and opened for writing. The
* new entry will be output, etc.
*/
output:
if (setuid(0)) {
fprintf(stderr, _("Cannot change ID to root.\n"));
SYSLOG((LOG_ERR, "can't setuid(0)"));
closelog();
exit(1);
}
pwd_init();
if (! gr_lock ()) {
fprintf(stderr, _("%s: can't get lock\n"), Prog);
SYSLOG((LOG_WARN, "failed to get lock for /etc/group\n"));
exit (1);
}
#ifdef SHADOWGRP
if (is_shadowgrp && ! sgr_lock ()) {
fprintf(stderr, _("%s: can't get shadow lock\n"), Prog);
SYSLOG((LOG_WARN, "failed to get lock for /etc/gshadow\n"));
exit (1);
}
#endif
if (! gr_open (O_RDWR)) {
fprintf(stderr, _("%s: can't open file\n"), Prog);
SYSLOG((LOG_WARN, "cannot open /etc/group\n"));
exit (1);
}
#ifdef SHADOWGRP
if (is_shadowgrp && ! sgr_open (O_RDWR)) {
fprintf(stderr, _("%s: can't open shadow file\n"), Prog);
SYSLOG((LOG_WARN, "cannot open /etc/gshadow\n"));
exit (1);
}
#endif
if (! gr_update (&grent)) {
fprintf(stderr, _("%s: can't update entry\n"), Prog);
SYSLOG((LOG_WARN, "cannot update /etc/group\n"));
exit (1);
}
#ifdef SHADOWGRP
if (is_shadowgrp && ! sgr_update (&sgent)) {
fprintf(stderr, _("%s: can't update shadow entry\n"), Prog);
SYSLOG((LOG_WARN, "cannot update /etc/gshadow\n"));
exit (1);
}
#endif
if (! gr_close ()) {
fprintf(stderr, _("%s: can't re-write file\n"), Prog);
SYSLOG((LOG_WARN, "cannot re-write /etc/group\n"));
exit (1);
}
#ifdef SHADOWGRP
if (is_shadowgrp && ! sgr_close ()) {
fprintf(stderr, _("%s: can't re-write shadow file\n"), Prog);
SYSLOG((LOG_WARN, "cannot re-write /etc/gshadow\n"));
exit (1);
}
if (is_shadowgrp)
sgr_unlock ();
#endif
if (! gr_unlock ()) {
fprintf(stderr, _("%s: can't unlock file\n"), Prog);
exit (1);
}
#ifdef NDBM
if (gr_dbm_present() && ! gr_dbm_update (&grent)) {
fprintf(stderr, _("%s: can't update DBM files\n"), Prog);
SYSLOG((LOG_WARN, "cannot update /etc/group DBM files\n"));
exit (1);
}
endgrent ();
#ifdef SHADOWGRP
if (is_shadowgrp && sg_dbm_present() && ! sg_dbm_update (&sgent)) {
fprintf(stderr, _("%s: can't update DBM shadow files\n"), Prog);
SYSLOG((LOG_WARN, "cannot update /etc/gshadow DBM files\n"));
exit (1);
}
endsgent ();
#endif
#endif
exit (0);
/*NOTREACHED*/
}

538
src/groupadd.c Normal file
View File

@ -0,0 +1,538 @@
/*
* Copyright 1991 - 1993, Julianne Frances Haugh
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Julianne F. Haugh nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <config.h>
#include "rcsid.h"
RCSID(PKG_VER "$Id: groupadd.c,v 1.14 1999/06/07 16:40:45 marekm Exp $")
#include <sys/types.h>
#include <stdio.h>
#include <grp.h>
#include <ctype.h>
#include <fcntl.h>
#include "defines.h"
#include "prototypes.h"
#include "chkname.h"
#include "getdef.h"
#include "groupio.h"
#ifdef SHADOWGRP
#include "sgroupio.h"
static int is_shadow_grp;
#endif
/*
* exit status values
*/
#define E_SUCCESS 0 /* success */
#define E_USAGE 2 /* invalid command syntax */
#define E_BAD_ARG 3 /* invalid argument to option */
#define E_GID_IN_USE 4 /* gid not unique (when -o not used) */
#define E_NAME_IN_USE 9 /* group name nut unique */
#define E_GRP_UPDATE 10 /* can't update group file */
static char *group_name;
static gid_t group_id;
static char *empty_list = NULL;
static char *Prog;
static int oflg = 0; /* permit non-unique group ID to be specified with -g */
static int gflg = 0; /* ID value for the new group */
static int fflg = 0; /* if group already exists, do nothing and exit(0) */
#ifdef NDBM
extern int gr_dbm_mode;
extern int sg_dbm_mode;
#endif
extern int optind;
extern char *optarg;
/* local function prototypes */
static void usage P_((void));
static void new_grent P_((struct group *));
#ifdef SHADOWGRP
static void new_sgent P_((struct sgrp *));
#endif
static void grp_update P_((void));
static void find_new_gid P_((void));
static void check_new_name P_((void));
static void process_flags P_((int, char **));
static void close_files P_((void));
static void open_files P_((void));
static void fail_exit P_((int));
int main P_((int, char **));
/*
* usage - display usage message and exit
*/
static void
usage(void)
{
fprintf(stderr, _("usage: groupadd [-g gid [-o]] group\n"));
exit(E_USAGE);
}
/*
* new_grent - initialize the values in a group file entry
*
* new_grent() takes all of the values that have been entered and
* fills in a (struct group) with them.
*/
static void
new_grent(struct group *grent)
{
memzero(grent, sizeof *grent);
grent->gr_name = group_name;
grent->gr_passwd = SHADOW_PASSWD_STRING; /* XXX warning: const */
grent->gr_gid = group_id;
grent->gr_mem = &empty_list;
}
#ifdef SHADOWGRP
/*
* new_sgent - initialize the values in a shadow group file entry
*
* new_sgent() takes all of the values that have been entered and
* fills in a (struct sgrp) with them.
*/
static void
new_sgent(struct sgrp *sgent)
{
memzero(sgent, sizeof *sgent);
sgent->sg_name = group_name;
sgent->sg_passwd = "!"; /* XXX warning: const */
sgent->sg_adm = &empty_list;
sgent->sg_mem = &empty_list;
}
#endif /* SHADOWGRP */
/*
* grp_update - add new group file entries
*
* grp_update() writes the new records to the group files.
*/
static void
grp_update(void)
{
struct group grp;
#ifdef SHADOWGRP
struct sgrp sgrp;
#endif /* SHADOWGRP */
/*
* Create the initial entries for this new group.
*/
new_grent (&grp);
#ifdef SHADOWGRP
new_sgent (&sgrp);
#endif /* SHADOWGRP */
/*
* Write out the new group file entry.
*/
if (! gr_update (&grp)) {
fprintf(stderr, _("%s: error adding new group entry\n"), Prog);
fail_exit(E_GRP_UPDATE);
}
#ifdef NDBM
/*
* Update the DBM group file with the new entry as well.
*/
if (gr_dbm_present() && ! gr_dbm_update (&grp)) {
fprintf(stderr, _("%s: cannot add new dbm group entry\n"), Prog);
fail_exit(E_GRP_UPDATE);
}
endgrent ();
#endif /* NDBM */
#ifdef SHADOWGRP
/*
* Write out the new shadow group entries as well.
*/
if (is_shadow_grp && ! sgr_update (&sgrp)) {
fprintf(stderr, _("%s: error adding new group entry\n"), Prog);
fail_exit(E_GRP_UPDATE);
}
#ifdef NDBM
/*
* Update the DBM group file with the new entry as well.
*/
if (is_shadow_grp && sg_dbm_present() && ! sg_dbm_update (&sgrp)) {
fprintf(stderr, _("%s: cannot add new dbm group entry\n"), Prog);
fail_exit(E_GRP_UPDATE);
}
endsgent ();
#endif /* NDBM */
#endif /* SHADOWGRP */
SYSLOG((LOG_INFO, "new group: name=%s, gid=%d\n",
group_name, group_id));
}
/*
* find_new_gid - find the next available GID
*
* find_new_gid() locates the next highest unused GID in the group
* file, or checks the given group ID against the existing ones for
* uniqueness.
*/
static void
find_new_gid(void)
{
const struct group *grp;
gid_t gid_min, gid_max;
gid_min = getdef_num("GID_MIN", 100);
gid_max = getdef_num("GID_MAX", 60000);
/*
* Start with some GID value if the user didn't provide us with
* one already.
*/
if (! gflg)
group_id = gid_min;
/*
* Search the entire group file, either looking for this
* GID (if the user specified one with -g) or looking for the
* largest unused value.
*/
#ifdef NO_GETGRENT
gr_rewind();
while ((grp = gr_next())) {
#else
setgrent();
while ((grp = getgrent())) {
#endif
if (strcmp(group_name, grp->gr_name) == 0) {
if (fflg) {
fail_exit(E_SUCCESS);
}
fprintf(stderr, _("%s: name %s is not unique\n"),
Prog, group_name);
fail_exit(E_NAME_IN_USE);
}
if (gflg && group_id == grp->gr_gid) {
if (fflg) {
/* turn off -g and search again */
gflg = 0;
#ifdef NO_GETGRENT
gr_rewind();
#else
setgrent();
#endif
continue;
}
fprintf(stderr, _("%s: gid %ld is not unique\n"),
Prog, (long) group_id);
fail_exit(E_GID_IN_USE);
}
if (! gflg && grp->gr_gid >= group_id) {
if (grp->gr_gid > gid_max)
continue;
group_id = grp->gr_gid + 1;
}
}
if (!gflg && group_id == gid_max + 1) {
for (group_id = gid_min; group_id < gid_max; group_id++) {
#ifdef NO_GETGRENT
gr_rewind();
while ((grp = gr_next()) && grp->gr_gid != group_id)
;
if (!grp)
break;
#else
if (!getgrgid(group_id))
break;
#endif
}
if (group_id == gid_max) {
fprintf(stderr, _("%s: can't get unique gid\n"),
Prog);
fail_exit(E_GID_IN_USE);
}
}
}
/*
* check_new_name - check the new name for validity
*
* check_new_name() insures that the new name doesn't contain
* any illegal characters.
*/
static void
check_new_name(void)
{
if (check_group_name(group_name))
return;
/*
* All invalid group names land here.
*/
fprintf(stderr, _("%s: %s is a not a valid group name\n"),
Prog, group_name);
exit(E_BAD_ARG);
}
/*
* 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.
*/
static void
process_flags(int argc, char **argv)
{
char *cp;
int arg;
while ((arg = getopt(argc, argv, "og:O:f")) != EOF) {
switch (arg) {
case 'g':
gflg++;
if (! isdigit (optarg[0]))
usage ();
group_id = strtol(optarg, &cp, 10);
if (*cp != '\0') {
fprintf(stderr, _("%s: invalid group %s\n"),
Prog, optarg);
fail_exit(E_BAD_ARG);
}
break;
case 'o':
oflg++;
break;
case 'O':
/*
* override login.defs defaults (-O name=value)
* example: -O GID_MIN=100 -O GID_MAX=499
* note: -O GID_MIN=10,GID_MAX=499 doesn't work yet
*/
cp = strchr(optarg, '=');
if (!cp) {
fprintf(stderr,
_("%s: -O requires NAME=VALUE\n"),
Prog);
exit(E_BAD_ARG);
}
/* terminate name, point to value */
*cp++ = '\0';
if (putdef_str(optarg, cp) < 0)
exit(E_BAD_ARG);
break;
case 'f':
/*
* "force" - do nothing, just exit(0), if the
* specified group already exists. With -g, if
* specified gid already exists, choose another
* (unique) gid (turn off -g). Based on the
* RedHat's patch from shadow-utils-970616-9.
*/
fflg++;
break;
default:
usage();
}
}
if (oflg && !gflg)
usage();
if (optind != argc - 1)
usage();
group_name = argv[argc - 1];
check_new_name();
}
/*
* close_files - close all of the files that were opened
*
* close_files() closes all of the files that were opened for this
* new group. This causes any modified entries to be written out.
*/
static void
close_files(void)
{
if (!gr_close()) {
fprintf(stderr, _("%s: cannot rewrite group file\n"), Prog);
fail_exit(E_GRP_UPDATE);
}
gr_unlock();
#ifdef SHADOWGRP
if (is_shadow_grp && !sgr_close()) {
fprintf(stderr, _("%s: cannot rewrite shadow group file\n"),
Prog);
fail_exit(E_GRP_UPDATE);
}
if (is_shadow_grp)
sgr_unlock ();
#endif /* SHADOWGRP */
}
/*
* open_files - lock and open the group files
*
* open_files() opens the two group files.
*/
static void
open_files(void)
{
if (! gr_lock ()) {
fprintf(stderr, _("%s: unable to lock group file\n"), Prog);
exit(E_GRP_UPDATE);
}
if (! gr_open (O_RDWR)) {
fprintf(stderr, _("%s: unable to open group file\n"), Prog);
fail_exit(E_GRP_UPDATE);
}
#ifdef SHADOWGRP
if (is_shadow_grp && ! sgr_lock ()) {
fprintf(stderr, _("%s: unable to lock shadow group file\n"),
Prog);
fail_exit(E_GRP_UPDATE);
}
if (is_shadow_grp && ! sgr_open (O_RDWR)) {
fprintf(stderr, _("%s: unable to open shadow group file\n"),
Prog);
fail_exit(E_GRP_UPDATE);
}
#endif /* SHADOWGRP */
}
/*
* fail_exit - exit with an error code after unlocking files
*/
static void
fail_exit(int code)
{
(void) gr_unlock ();
#ifdef SHADOWGRP
if (is_shadow_grp)
sgr_unlock ();
#endif
exit (code);
}
/*
* main - groupadd command
*/
int
main(int argc, char **argv)
{
/*
* Get my name so that I can use it to report errors.
*/
Prog = Basename(argv[0]);
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
openlog(Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
#ifdef SHADOWGRP
is_shadow_grp = sgr_file_present();
#endif
/*
* The open routines for the DBM files don't use read-write
* as the mode, so we have to clue them in.
*/
#ifdef NDBM
gr_dbm_mode = O_RDWR;
#ifdef SHADOWGRP
sg_dbm_mode = O_RDWR;
#endif /* SHADOWGRP */
#endif /* NDBM */
process_flags(argc, argv);
/*
* Start with a quick check to see if the group exists.
*/
if (getgrnam(group_name)) {
if (fflg) {
exit(E_SUCCESS);
}
fprintf(stderr, _("%s: group %s exists\n"), Prog, group_name);
exit(E_NAME_IN_USE);
}
/*
* Do the hard stuff - open the files, create the group entries,
* then close and update the files.
*/
open_files();
if (!gflg || !oflg)
find_new_gid();
grp_update();
close_files();
exit(E_SUCCESS);
/*NOTREACHED*/
}

352
src/groupdel.c Normal file
View File

@ -0,0 +1,352 @@
/*
* Copyright 1991 - 1994, Julianne Frances Haugh
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Julianne F. Haugh nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <config.h>
#include "rcsid.h"
RCSID(PKG_VER "$Id: groupdel.c,v 1.10 1999/06/07 16:40:45 marekm Exp $")
#include <sys/types.h>
#include <stdio.h>
#include <grp.h>
#include <ctype.h>
#include <fcntl.h>
#include <pwd.h>
#include "prototypes.h"
#include "defines.h"
static char *group_name;
static char *Prog;
static int errors;
#ifdef NDBM
extern int gr_dbm_mode;
extern int sg_dbm_mode;
#endif
#include "groupio.h"
#ifdef SHADOWGRP
#include "sgroupio.h"
static int is_shadow_grp;
#endif
/*
* exit status values
*/
#define E_SUCCESS 0 /* success */
#define E_USAGE 2 /* invalid command syntax */
#define E_NOTFOUND 6 /* specified group doesn't exist */
#define E_GROUP_BUSY 8 /* can't remove user's primary group */
#define E_GRP_UPDATE 10 /* can't update group file */
/* local function prototypes */
static void usage P_((void));
static void grp_update P_((void));
static void close_files P_((void));
static void open_files P_((void));
static void group_busy P_((gid_t));
int main P_((int, char **));
/*
* usage - display usage message and exit
*/
static void
usage(void)
{
fprintf(stderr, _("usage: groupdel group\n"));
exit(E_USAGE);
}
/*
* grp_update - update group file entries
*
* grp_update() writes the new records to the group files.
*/
static void
grp_update(void)
{
#ifdef NDBM
struct group *ogrp;
#endif
if (!gr_remove(group_name)) {
fprintf(stderr, _("%s: error removing group entry\n"), Prog);
errors++;
}
#ifdef NDBM
/*
* Update the DBM group file
*/
if (gr_dbm_present()) {
if ((ogrp = getgrnam (group_name)) &&
! gr_dbm_remove (ogrp)) {
fprintf(stderr, _("%s: error removing group dbm entry\n"),
Prog);
errors++;
}
}
endgrent ();
#endif /* NDBM */
#ifdef SHADOWGRP
/*
* Delete the shadow group entries as well.
*/
if (is_shadow_grp && ! sgr_remove (group_name)) {
fprintf(stderr, _("%s: error removing shadow group entry\n"),
Prog);
errors++;
}
#ifdef NDBM
/*
* Update the DBM shadow group file
*/
if (is_shadow_grp && sg_dbm_present()) {
if (! sg_dbm_remove (group_name)) {
fprintf(stderr,
_("%s: error removing shadow group dbm entry\n"),
Prog);
errors++;
}
}
endsgent ();
#endif /* NDBM */
#endif /* SHADOWGRP */
SYSLOG((LOG_INFO, "remove group `%s'\n", group_name));
return;
}
/*
* close_files - close all of the files that were opened
*
* close_files() closes all of the files that were opened for this
* new group. This causes any modified entries to be written out.
*/
static void
close_files(void)
{
if (!gr_close()) {
fprintf(stderr, _("%s: cannot rewrite group file\n"), Prog);
errors++;
}
gr_unlock();
#ifdef SHADOWGRP
if (is_shadow_grp && !sgr_close()) {
fprintf(stderr, _("%s: cannot rewrite shadow group file\n"),
Prog);
errors++;
}
if (is_shadow_grp)
sgr_unlock();
#endif /* SHADOWGRP */
}
/*
* open_files - lock and open the group files
*
* open_files() opens the two group files.
*/
static void
open_files(void)
{
if (!gr_lock()) {
fprintf(stderr, _("%s: unable to lock group file\n"), Prog);
exit(E_GRP_UPDATE);
}
if (!gr_open(O_RDWR)) {
fprintf(stderr, _("%s: unable to open group file\n"), Prog);
exit(E_GRP_UPDATE);
}
#ifdef SHADOWGRP
if (is_shadow_grp && !sgr_lock()) {
fprintf(stderr, _("%s: unable to lock shadow group file\n"),
Prog);
exit(E_GRP_UPDATE);
}
if (is_shadow_grp && !sgr_open(O_RDWR)) {
fprintf(stderr, _("%s: unable to open shadow group file\n"),
Prog);
exit(E_GRP_UPDATE);
}
#endif /* SHADOWGRP */
}
/*
* group_busy - check if this is any user's primary group
*
* group_busy verifies that this group is not the primary group
* for any user. You must remove all users before you remove
* the group.
*/
static void
group_busy(gid_t gid)
{
struct passwd *pwd;
/*
* Nice slow linear search.
*/
setpwent ();
while ((pwd = getpwent ()) && pwd->pw_gid != gid)
;
endpwent ();
/*
* If pwd isn't NULL, it stopped becaues the gid's matched.
*/
if (pwd == (struct passwd *) 0)
return;
/*
* Can't remove the group.
*/
fprintf(stderr, _("%s: cannot remove user's primary group.\n"), Prog);
exit(E_GROUP_BUSY);
}
/*
* main - groupdel command
*
* The syntax of the groupdel command is
*
* groupdel group
*
* The named group will be deleted.
*/
int
main(int argc, char **argv)
{
struct group *grp;
/*
* Get my name so that I can use it to report errors.
*/
Prog = Basename(argv[0]);
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
if (argc != 2)
usage ();
group_name = argv[1];
openlog(Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
#ifdef SHADOWGRP
is_shadow_grp = sgr_file_present();
#endif
/*
* The open routines for the DBM files don't use read-write
* as the mode, so we have to clue them in.
*/
#ifdef NDBM
gr_dbm_mode = O_RDWR;
#ifdef SHADOWGRP
sg_dbm_mode = O_RDWR;
#endif /* SHADOWGRP */
#endif /* NDBM */
/*
* Start with a quick check to see if the group exists.
*/
if (! (grp = getgrnam(group_name))) {
fprintf(stderr, _("%s: group %s does not exist\n"),
Prog, group_name);
exit(E_NOTFOUND);
}
#ifdef USE_NIS
/*
* Make sure this isn't a NIS group
*/
if (__isgrNIS ()) {
char *nis_domain;
char *nis_master;
fprintf(stderr, _("%s: group %s is a NIS group\n"),
Prog, group_name);
if (! yp_get_default_domain (&nis_domain) &&
! yp_master (nis_domain, "group.byname",
&nis_master)) {
fprintf (stderr, _("%s: %s is the NIS master\n"),
Prog, nis_master);
}
exit(E_NOTFOUND);
}
#endif
/*
* Now check to insure that this isn't the primary group of
* anyone.
*/
group_busy (grp->gr_gid);
/*
* Do the hard stuff - open the files, delete the group entries,
* then close and update the files.
*/
open_files ();
grp_update ();
close_files ();
exit(errors == 0 ? E_SUCCESS : E_GRP_UPDATE);
/*NOTREACHED*/
}

549
src/groupmod.c Normal file
View File

@ -0,0 +1,549 @@
/*
* Copyright 1991 - 1994, Julianne Frances Haugh
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Julianne F. Haugh nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <config.h>
#include "rcsid.h"
RCSID(PKG_VER "$Id: groupmod.c,v 1.12 1999/07/09 18:02:43 marekm Exp $")
#include <sys/types.h>
#include <stdio.h>
#include <grp.h>
#include <ctype.h>
#include <fcntl.h>
#include "prototypes.h"
#include "chkname.h"
#include "defines.h"
#include "groupio.h"
#ifdef SHADOWGRP
#include "sgroupio.h"
static int is_shadow_grp;
#endif
/*
* exit status values
*/
#define E_SUCCESS 0 /* success */
#define E_USAGE 2 /* invalid command syntax */
#define E_BAD_ARG 3 /* invalid argument to option */
#define E_GID_IN_USE 4 /* gid already in use (and no -o) */
#define E_NOTFOUND 6 /* specified group doesn't exist */
#define E_NAME_IN_USE 9 /* group name already in use */
#define E_GRP_UPDATE 10 /* can't update group file */
static char *group_name;
static char *group_newname;
static gid_t group_id;
static gid_t group_newid;
static char *Prog;
static int
oflg = 0, /* permit non-unique group ID to be specified with -g */
gflg = 0, /* new ID value for the group */
nflg = 0; /* a new name has been specified for the group */
#ifdef NDBM
extern int gr_dbm_mode;
extern int sg_dbm_mode;
#endif
extern int optind;
extern char *optarg;
/* local function prototypes */
static void usage P_((void));
static void new_grent P_((struct group *));
#ifdef SHADOWGRP
static void new_sgent P_((struct sgrp *));
#endif
static void grp_update P_((void));
static void check_new_gid P_((void));
static void check_new_name P_((void));
static void process_flags P_((int, char **));
static void close_files P_((void));
static void open_files P_((void));
int main P_((int, char **));
/*
* usage - display usage message and exit
*/
static void
usage(void)
{
fprintf(stderr, _("usage: groupmod [-g gid [-o]] [-n name] group\n"));
exit(E_USAGE);
}
/*
* new_grent - updates the values in a group file entry
*
* new_grent() takes all of the values that have been entered and
* fills in a (struct group) with them.
*/
static void
new_grent(struct group *grent)
{
if (nflg)
grent->gr_name = xstrdup (group_newname);
if (gflg)
grent->gr_gid = group_newid;
}
#ifdef SHADOWGRP
/*
* new_sgent - updates the values in a shadow group file entry
*
* new_sgent() takes all of the values that have been entered and
* fills in a (struct sgrp) with them.
*/
static void
new_sgent(struct sgrp *sgent)
{
if (nflg)
sgent->sg_name = xstrdup (group_newname);
}
#endif /* SHADOWGRP */
/*
* grp_update - update group file entries
*
* grp_update() writes the new records to the group files.
*/
static void
grp_update(void)
{
struct group grp;
const struct group *ogrp;
#ifdef SHADOWGRP
struct sgrp sgrp;
const struct sgrp *osgrp = NULL;
#endif /* SHADOWGRP */
/*
* Get the current settings for this group.
*/
ogrp = gr_locate(group_name);
if (!ogrp) {
fprintf(stderr,
_("%s: %s not found in /etc/group\n"),
Prog, group_name);
exit(E_GRP_UPDATE);
}
grp = *ogrp;
new_grent (&grp);
#ifdef SHADOWGRP
if (is_shadow_grp && (osgrp = sgr_locate(group_name))) {
sgrp = *osgrp;
new_sgent (&sgrp);
}
#endif /* SHADOWGRP */
/*
* Write out the new group file entry.
*/
if (!gr_update(&grp)) {
fprintf(stderr, _("%s: error adding new group entry\n"), Prog);
exit(E_GRP_UPDATE);
}
if (nflg && !gr_remove(group_name)) {
fprintf(stderr, _("%s: error removing group entry\n"), Prog);
exit(E_GRP_UPDATE);
}
#ifdef NDBM
/*
* Update the DBM group file with the new entry as well.
*/
if (gr_dbm_present()) {
if (!gr_dbm_update(&grp)) {
fprintf(stderr,
_("%s: cannot add new dbm group entry\n"),
Prog);
exit(E_GRP_UPDATE);
}
if (nflg && (ogrp = getgrnam(group_name)) &&
!gr_dbm_remove(ogrp)) {
fprintf(stderr,
_("%s: error removing group dbm entry\n"),
Prog);
exit(E_GRP_UPDATE);
}
endgrent ();
}
#endif /* NDBM */
#ifdef SHADOWGRP
/*
* Make sure there was a shadow entry to begin with. Skip
* down to "out" if there wasn't. Can't just return because
* there might be some syslogging to do.
*/
if (! osgrp)
goto out;
/*
* Write out the new shadow group entries as well.
*/
if (!sgr_update(&sgrp)) {
fprintf(stderr, _("%s: error adding new group entry\n"), Prog);
exit(E_GRP_UPDATE);
}
if (nflg && !sgr_remove(group_name)) {
fprintf(stderr, _("%s: error removing group entry\n"), Prog);
exit(E_GRP_UPDATE);
}
#ifdef NDBM
/*
* Update the DBM shadow group file with the new entry as well.
*/
if (sg_dbm_present()) {
if (!sg_dbm_update(&sgrp)) {
fprintf(stderr,
_("%s: cannot add new dbm shadow group entry\n"),
Prog);
exit(E_GRP_UPDATE);
}
if (nflg && ! sg_dbm_remove (group_name)) {
fprintf (stderr,
_("%s: error removing shadow group dbm entry\n"),
Prog);
exit(E_GRP_UPDATE);
}
endsgent ();
}
#endif /* NDBM */
out:
#endif /* SHADOWGRP */
if (nflg)
SYSLOG((LOG_INFO, "change group `%s' to `%s'\n",
group_name, group_newname));
if (gflg)
SYSLOG((LOG_INFO, "change gid for `%s' to %d\n",
nflg ? group_newname:group_name, group_newid));
}
/*
* check_new_gid - check the new GID value for uniqueness
*
* check_new_gid() insures that the new GID value is unique.
*/
static void
check_new_gid(void)
{
/*
* 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 (group_id == group_newid) {
gflg = 0;
return;
}
if (oflg || ! getgrgid (group_newid))
return;
/*
* Tell the user what they did wrong.
*/
fprintf(stderr,
_("%s: %ld is not a unique gid\n"),
Prog, (long) group_newid);
exit(E_GID_IN_USE);
}
/*
* check_new_name - check the new name for uniqueness
*
* check_new_name() insures that the new name does not exist
* already. You can't have the same name twice, period.
*/
static void
check_new_name(void)
{
/*
* Make sure they are actually changing the name.
*/
if (strcmp(group_name, group_newname) == 0) {
nflg = 0;
return;
}
if (check_group_name(group_newname)) {
/*
* If the entry is found, too bad.
*/
if (getgrnam(group_newname)) {
fprintf(stderr, _("%s: %s is not a unique name\n"),
Prog, group_newname);
exit(E_NAME_IN_USE);
}
return;
}
/*
* All invalid group names land here.
*/
fprintf(stderr, _("%s: %s is a not a valid group name\n"),
Prog, group_newname);
exit(E_BAD_ARG);
}
/*
* 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.
*/
static void
process_flags(int argc, char **argv)
{
char *end;
int arg;
while ((arg = getopt (argc, argv, "og:n:")) != EOF) {
switch (arg) {
case 'g':
gflg++;
group_newid = strtol(optarg, &end, 10);
if (*end != '\0') {
fprintf(stderr,
_("%s: invalid group %s\n"),
Prog, optarg);
exit(E_BAD_ARG);
}
break;
case 'n':
nflg++;
group_newname = optarg;
break;
case 'o':
oflg++;
break;
default:
usage ();
}
}
if (oflg && !gflg)
usage();
if (optind != argc - 1)
usage();
group_name = argv[argc - 1];
}
/*
* close_files - close all of the files that were opened
*
* close_files() closes all of the files that were opened for this
* new group. This causes any modified entries to be written out.
*/
static void
close_files(void)
{
if (!gr_close()) {
fprintf(stderr, _("%s: cannot rewrite group file\n"), Prog);
exit(E_GRP_UPDATE);
}
gr_unlock();
#ifdef SHADOWGRP
if (is_shadow_grp && !sgr_close()) {
fprintf(stderr, _("%s: cannot rewrite shadow group file\n"),
Prog);
exit(E_GRP_UPDATE);
}
if (is_shadow_grp)
sgr_unlock ();
#endif /* SHADOWGRP */
}
/*
* open_files - lock and open the group files
*
* open_files() opens the two group files.
*/
static void
open_files(void)
{
if (!gr_lock()) {
fprintf(stderr, _("%s: unable to lock group file\n"), Prog);
exit(E_GRP_UPDATE);
}
if (!gr_open(O_RDWR)) {
fprintf(stderr, _("%s: unable to open group file\n"), Prog);
exit(E_GRP_UPDATE);
}
#ifdef SHADOWGRP
if (is_shadow_grp && !sgr_lock()) {
fprintf(stderr, _("%s: unable to lock shadow group file\n"),
Prog);
exit(E_GRP_UPDATE);
}
if (is_shadow_grp && !sgr_open(O_RDWR)) {
fprintf(stderr, _("%s: unable to open shadow group file\n"),
Prog);
exit(E_GRP_UPDATE);
}
#endif /* SHADOWGRP */
}
/*
* main - groupmod command
*
* The syntax of the groupmod command is
*
* groupmod [ -g gid [ -o ]] [ -n name ] group
*
* The flags are
* -g - specify a new group ID value
* -o - permit the group ID value to be non-unique
* -n - specify a new group name
*/
int
main(int argc, char **argv)
{
struct group *grp;
/*
* Get my name so that I can use it to report errors.
*/
Prog = Basename(argv[0]);
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
openlog(Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
#ifdef SHADOWGRP
is_shadow_grp = sgr_file_present();
#endif
/*
* The open routines for the DBM files don't use read-write
* as the mode, so we have to clue them in.
*/
#ifdef NDBM
gr_dbm_mode = O_RDWR;
#ifdef SHADOWGRP
sg_dbm_mode = O_RDWR;
#endif /* SHADOWGRP */
#endif /* NDBM */
process_flags (argc, argv);
/*
* Start with a quick check to see if the group exists.
*/
if (!(grp = getgrnam(group_name))) {
fprintf(stderr, _("%s: group %s does not exist\n"),
Prog, group_name);
exit(E_NOTFOUND);
} else
group_id = grp->gr_gid;
#ifdef USE_NIS
/*
* Now make sure it isn't an NIS group.
*/
if (__isgrNIS ()) {
char *nis_domain;
char *nis_master;
fprintf(stderr, _("%s: group %s is a NIS group\n"),
Prog, group_name);
if (! yp_get_default_domain (&nis_domain) &&
! yp_master (nis_domain, "group.byname",
&nis_master)) {
fprintf(stderr, _("%s: %s is the NIS master\n"),
Prog, nis_master);
}
exit(E_NOTFOUND);
}
#endif
if (gflg)
check_new_gid ();
if (nflg)
check_new_name ();
/*
* Do the hard stuff - open the files, create the group entries,
* then close and update the files.
*/
open_files ();
grp_update ();
close_files ();
exit(E_SUCCESS);
/*NOTREACHED*/
}

184
src/groups.c Normal file
View File

@ -0,0 +1,184 @@
/*
* Copyright 1991 - 1993, Julianne Frances Haugh
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Julianne F. Haugh nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <config.h>
#include "rcsid.h"
RCSID(PKG_VER "$Id: groups.c,v 1.5 1999/06/07 16:40:45 marekm Exp $")
#include <stdio.h>
#include <pwd.h>
#include <grp.h>
#include "prototypes.h"
#include "defines.h"
/* local function prototypes */
static void print_groups P_((const char *));
int main P_((int, char **));
/*
* print_groups - print the groups which the named user is a member of
*
* print_groups() scans the groups file for the list of groups
* which the user is listed as being a member of.
*/
static void
print_groups(const char *member)
{
int groups = 0;
struct group *grp;
struct passwd *pwd;
int flag = 0;
setgrent ();
if ((pwd = getpwnam(member)) == 0) {
fprintf(stderr, _("unknown user %s\n"), member);
exit(1);
}
while ((grp = getgrent ())) {
if (is_on_list(grp->gr_mem, member)) {
if (groups++)
putchar (' ');
printf ("%s", grp->gr_name);
if (grp->gr_gid == pwd->pw_gid)
flag = 1;
}
}
if (! flag && (grp = getgrgid (pwd->pw_gid))) {
if (groups++)
putchar (' ');
printf ("%s", grp->gr_name);
}
if (groups)
putchar ('\n');
}
/*
* groups - print out the groups a process is a member of
*/
int
main(int argc, char **argv)
{
#ifdef HAVE_GETGROUPS
int ngroups;
GETGROUPS_T groups[NGROUPS_MAX];
int pri_grp;
int i;
struct group *gr;
#else
char *logname;
char *getlogin();
#endif
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
if (argc == 1) {
/*
* Called with no arguments - give the group set
* for the current user.
*/
#ifdef HAVE_GETGROUPS
/*
* This system supports concurrent group sets, so
* I can ask the system to tell me which groups are
* currently set for this process.
*/
ngroups = getgroups(NGROUPS_MAX, groups);
if (ngroups < 0) {
perror("getgroups");
exit(1);
}
/*
* The groupset includes the primary group as well.
*/
pri_grp = getegid ();
for (i = 0;i < ngroups;i++)
if (pri_grp == (int) groups[i])
break;
if (i != ngroups)
pri_grp = -1;
/*
* Print out the name of every group in the current
* group set. Unknown groups are printed as their
* decimal group ID values.
*/
if (pri_grp != -1) {
if ((gr = getgrgid (pri_grp)))
printf ("%s", gr->gr_name);
else
printf ("%d", pri_grp);
}
for (i = 0;i < ngroups;i++) {
if (i || pri_grp != -1)
putchar (' ');
if ((gr = getgrgid (groups[i])))
printf ("%s", gr->gr_name);
else
printf ("%ld", (long) groups[i]);
}
putchar ('\n');
#else
/*
* This system does not have the getgroups() system
* call, so I must check the groups file directly.
*/
if ((logname = getlogin ()))
print_groups (logname);
else
exit (1);
#endif
} else {
/*
* The invoker wanted to know about some other
* user. Use that name to look up the groups instead.
*/
print_groups (argv[1]);
}
exit (0);
}

650
src/grpck.c Normal file
View File

@ -0,0 +1,650 @@
/*
* Copyright 1992 - 1994, Julianne Frances Haugh
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Julianne F. Haugh nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <config.h>
#include "rcsid.h"
RCSID(PKG_VER "$Id: grpck.c,v 1.12 1999/06/07 16:40:45 marekm Exp $")
#include <stdio.h>
#include <fcntl.h>
#include <grp.h>
#include "prototypes.h"
#include "defines.h"
#include "chkname.h"
#include <pwd.h>
#include "commonio.h"
#include "groupio.h"
extern void __gr_del_entry P_((const struct commonio_entry *));
extern struct commonio_entry *__gr_get_head P_((void));
#ifdef SHADOWGRP
#include "sgroupio.h"
extern void __sgr_del_entry P_((const struct commonio_entry *));
extern struct commonio_entry *__sgr_get_head P_((void));
#endif
/*
* Exit codes
*/
#define E_OKAY 0
#define E_USAGE 1
#define E_BAD_ENTRY 2
#define E_CANT_OPEN 3
#define E_CANT_LOCK 4
#define E_CANT_UPDATE 5
/*
* Global variables
*/
extern int optind;
extern char *optarg;
/*
* Local variables
*/
static char *Prog;
static const char *grp_file = GROUP_FILE;
#ifdef SHADOWGRP
static const char *sgr_file = SGROUP_FILE;
#endif
static int read_only = 0;
/* local function prototypes */
static void usage P_((void));
static int yes_or_no P_((void));
static void delete_member P_((char **, const char *));
int main P_((int, char **));
/*
* usage - print syntax message and exit
*/
static void
usage(void)
{
#ifdef SHADOWGRP
fprintf(stderr, _("Usage: %s [ -r ] [ group [ gshadow ] ]\n"), Prog);
#else
fprintf(stderr, _("Usage: %s [ -r ] [ group ]\n"), Prog);
#endif
exit(E_USAGE);
}
/*
* yes_or_no - get answer to question from the user
*/
static int
yes_or_no(void)
{
char buf[80];
/*
* In read-only mode all questions are answered "no".
*/
if (read_only) {
puts(_("No"));
return 0;
}
/*
* Get a line and see what the first character is.
*/
if (fgets(buf, sizeof buf, stdin))
return buf[0] == 'y' || buf[0] == 'Y';
return 0;
}
/*
* delete_member - delete an entry in a list of members
*/
static void
delete_member(char **list, const char *member)
{
int i;
for (i = 0; list[i]; i++)
if (list[i] == member)
break;
if (list[i])
for (; list[i]; i++)
list[i] = list[i + 1];
}
/*
* grpck - verify group file integrity
*/
int
main(int argc, char **argv)
{
int arg;
int errors = 0;
int deleted = 0;
int i;
struct commonio_entry *gre, *tgre;
struct group *grp;
#ifdef SHADOWGRP
struct commonio_entry *sge, *tsge;
struct sgrp *sgr;
int is_shadow = 0;
#endif
/*
* Get my name so that I can use it to report errors.
*/
Prog = Basename(argv[0]);
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
openlog(Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
/*
* Parse the command line arguments
*/
while ((arg = getopt(argc, argv, "qr")) != EOF) {
switch (arg) {
case 'q':
/* quiet - ignored for now */
break;
case 'r':
read_only = 1;
break;
default:
usage();
}
}
/*
* Make certain we have the right number of arguments
*/
#ifdef SHADOWGRP
if (optind != argc && optind + 1 != argc && optind + 2 != argc)
#else
if (optind != argc && optind + 1 != argc)
#endif
usage();
/*
* If there are two left over filenames, use those as the
* group and group password filenames.
*/
if (optind != argc) {
grp_file = argv[optind];
gr_name(grp_file);
}
#ifdef SHADOWGRP
if (optind + 2 == argc) {
sgr_file = argv[optind + 1];
sgr_name(sgr_file);
is_shadow = 1;
} else if (optind == argc)
is_shadow = sgr_file_present();
#endif
/*
* Lock the files if we aren't in "read-only" mode
*/
if (!read_only) {
if (!gr_lock()) {
fprintf(stderr, _("%s: cannot lock file %s\n"), Prog, grp_file);
if (optind == argc)
SYSLOG((LOG_WARN,"cannot lock %s\n",grp_file));
closelog();
exit(E_CANT_LOCK);
}
#ifdef SHADOWGRP
if (is_shadow && !sgr_lock()) {
fprintf(stderr, _("%s: cannot lock file %s\n"), Prog, sgr_file);
if (optind == argc)
SYSLOG((LOG_WARN,"cannot lock %s\n",sgr_file));
closelog();
exit(E_CANT_LOCK);
}
#endif
}
/*
* Open the files. Use O_RDONLY if we are in read_only mode,
* O_RDWR otherwise.
*/
if (!gr_open(read_only ? O_RDONLY : O_RDWR)) {
fprintf(stderr, _("%s: cannot open file %s\n"), Prog, grp_file);
if (optind == argc)
SYSLOG((LOG_WARN, "cannot open %s\n", grp_file));
closelog();
exit(E_CANT_OPEN);
}
#ifdef SHADOWGRP
if (is_shadow && !sgr_open(read_only ? O_RDONLY : O_RDWR)) {
fprintf(stderr, _("%s: cannot open file %s\n"), Prog, sgr_file);
if (optind == argc)
SYSLOG((LOG_WARN, "cannot open %s\n", sgr_file));
closelog();
exit(E_CANT_OPEN);
}
#endif
/*
* Loop through the entire group file.
*/
for (gre = __gr_get_head(); gre; gre = gre->next) {
/*
* Skip all NIS entries.
*/
if (gre->line[0] == '+' || gre->line[0] == '-')
continue;
/*
* Start with the entries that are completely corrupt.
* They have no (struct group) entry because they couldn't
* be parsed properly.
*/
if (!gre->entry) {
/*
* Tell the user this entire line is bogus and
* ask them to delete it.
*/
printf(_("invalid group file entry\n"));
printf(_("delete line `%s'? "), gre->line);
errors++;
/*
* prompt the user to delete the entry or not
*/
if (!yes_or_no())
continue;
/*
* All group file deletions wind up here. This
* code removes the current entry from the linked
* list. When done, it skips back to the top of
* the loop to try out the next list element.
*/
delete_gr:
SYSLOG((LOG_INFO, "delete group line `%s'\n",
gre->line));
deleted++;
__gr_del_entry(gre);
continue;
}
/*
* Group structure is good, start using it.
*/
grp = gre->entry;
/*
* Make sure this entry has a unique name.
*/
for (tgre = __gr_get_head(); tgre; tgre = tgre->next) {
const struct group *ent = tgre->entry;
/*
* Don't check this entry
*/
if (tgre == gre)
continue;
/*
* Don't check invalid entries.
*/
if (!ent)
continue;
if (strcmp(grp->gr_name, ent->gr_name) != 0)
continue;
/*
* Tell the user this entry is a duplicate of
* another and ask them to delete it.
*/
puts(_("duplicate group entry\n"));
printf(_("delete line `%s'? "), gre->line);
errors++;
/*
* prompt the user to delete the entry or not
*/
if (yes_or_no())
goto delete_gr;
}
/*
* Check for invalid group names. --marekm
*/
if (!check_group_name(grp->gr_name)) {
errors++;
printf(_("invalid group name `%s'\n"), grp->gr_name);
}
/*
* Check for a Slackware bug. Make sure GID is not -1
* (it has special meaning for some syscalls). --marekm
*/
if (grp->gr_gid == (gid_t) -1) {
errors++;
printf(_("group %s: bad GID (%d)\n"),
grp->gr_name, (int) grp->gr_gid);
}
/*
* Workaround for a NYS libc 5.3.12 bug on RedHat 4.2 -
* groups with no members are returned as groups with
* one member "", causing grpck to fail. --marekm
*/
if (grp->gr_mem[0] && !grp->gr_mem[1] && *(grp->gr_mem[0]) == '\0')
grp->gr_mem[0] = (char *) 0;
/*
* Make sure each member exists
*/
for (i = 0; grp->gr_mem[i]; i++) {
if (getpwnam(grp->gr_mem[i]))
continue;
/*
* Can't find this user. Remove them
* from the list.
*/
errors++;
printf(_("group %s: no user %s\n"),
grp->gr_name, grp->gr_mem[i]);
printf(_("delete member `%s'? "), grp->gr_mem[i]);
if (!yes_or_no())
continue;
SYSLOG((LOG_INFO, "delete member `%s' group `%s'\n",
grp->gr_mem[i], grp->gr_name));
deleted++;
delete_member(grp->gr_mem, grp->gr_mem[i]);
gre->changed = 1;
__gr_set_changed();
}
}
#ifdef SHADOWGRP
if (!is_shadow)
goto shadow_done;
/*
* Loop through the entire shadow group file.
*/
for (sge = __sgr_get_head(); sge; sge = sge->next) {
/*
* Start with the entries that are completely corrupt.
* They have no (struct sgrp) entry because they couldn't
* be parsed properly.
*/
if (!sge->entry) {
/*
* Tell the user this entire line is bogus and
* ask them to delete it.
*/
printf(_("invalid shadow group file entry\n"));
printf(_("delete line `%s'? "), sge->line);
errors++;
/*
* prompt the user to delete the entry or not
*/
if (!yes_or_no())
continue;
/*
* All shadow group file deletions wind up here.
* This code removes the current entry from the
* linked list. When done, it skips back to the
* top of the loop to try out the next list element.
*/
delete_sg:
SYSLOG((LOG_INFO, "delete shadow line `%s'\n",
sge->line));
deleted++;
__sgr_del_entry(sge);
continue;
}
/*
* Shadow group structure is good, start using it.
*/
sgr = sge->entry;
/*
* Make sure this entry has a unique name.
*/
for (tsge = __sgr_get_head(); tsge; tsge = tsge->next) {
const struct sgrp *ent = tsge->entry;
/*
* Don't check this entry
*/
if (tsge == sge)
continue;
/*
* Don't check invalid entries.
*/
if (!ent)
continue;
if (strcmp(sgr->sg_name, ent->sg_name) != 0)
continue;
/*
* Tell the user this entry is a duplicate of
* another and ask them to delete it.
*/
puts(_("duplicate shadow group entry\n"));
printf(_("delete line `%s'? "), sge->line);
errors++;
/*
* prompt the user to delete the entry or not
*/
if (yes_or_no())
goto delete_sg;
}
/*
* Make sure this entry exists in the /etc/group file.
*/
if (!gr_locate(sgr->sg_name)) {
puts(_("no matching group file entry\n"));
printf(_("delete line `%s'? "), sge->line);
errors++;
if (yes_or_no())
goto delete_sg;
}
/*
* Make sure each administrator exists
*/
for (i = 0; sgr->sg_adm[i]; i++) {
if (getpwnam(sgr->sg_adm[i]))
continue;
/*
* Can't find this user. Remove them
* from the list.
*/
errors++;
printf(_("shadow group %s: no administrative user %s\n"),
sgr->sg_name, sgr->sg_adm[i]);
printf(_("delete administrative member `%s'? "), sgr->sg_adm[i]);
if (!yes_or_no())
continue;
SYSLOG((LOG_INFO,
"delete admin `%s' from shadow group `%s'\n",
sgr->sg_adm[i], sgr->sg_name));
deleted++;
delete_member(sgr->sg_adm, sgr->sg_adm[i]);
sge->changed = 1;
__sgr_set_changed();
}
/*
* Make sure each member exists
*/
for (i = 0; sgr->sg_mem[i]; i++) {
if (getpwnam(sgr->sg_mem[i]))
continue;
/*
* Can't find this user. Remove them
* from the list.
*/
errors++;
printf(_("shadow group %s: no user %s\n"),
sgr->sg_name, sgr->sg_mem[i]);
printf(_("delete member `%s'? "), sgr->sg_mem[i]);
if (!yes_or_no())
continue;
SYSLOG((LOG_INFO,
"delete member `%s' from shadow group `%s'\n",
sgr->sg_mem[i], sgr->sg_name));
deleted++;
delete_member(sgr->sg_mem, sgr->sg_mem[i]);
sge->changed = 1;
__sgr_set_changed();
}
}
shadow_done:
#endif /* SHADOWGRP */
/*
* All done. If there were no deletions we can just abandon any
* changes to the files.
*/
if (deleted) {
if (!gr_close()) {
fprintf(stderr, _("%s: cannot update file %s\n"),
Prog, grp_file);
exit(E_CANT_UPDATE);
}
#ifdef SHADOWGRP
if (is_shadow && !sgr_close()) {
fprintf(stderr, _("%s: cannot update file %s\n"),
Prog, sgr_file);
exit(E_CANT_UPDATE);
}
#endif
}
/*
* Don't be anti-social - unlock the files when you're done.
*/
#ifdef SHADOWGRP
if (is_shadow)
sgr_unlock();
#endif
(void) gr_unlock();
/*
* Tell the user what we did and exit.
*/
if (errors)
#ifdef NDBM
printf(deleted ?
_("%s: the files have been updated; run mkpasswd\n") :
_("%s: no changes\n"), Prog);
#else
printf(deleted ?
_("%s: the files have been updated\n") :
_("%s: no changes\n"), Prog);
#endif
exit(errors ? E_BAD_ENTRY : E_OKAY);
}

174
src/grpconv.c Normal file
View File

@ -0,0 +1,174 @@
/*
* grpconv - create or update /etc/gshadow with information from
* /etc/group.
*
* Copyright (C) 1996, Marek Michalkiewicz
* <marekm@i17linuxb.ists.pwr.wroc.pl>
* This program may be freely used and distributed. If you improve
* it, please send me your changes. Thanks!
*/
#include <config.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <grp.h>
#include "prototypes.h"
#ifdef SHADOWGRP
#include "groupio.h"
#include "sgroupio.h"
#include "rcsid.h"
RCSID(PKG_VER "$Id: grpconv.c,v 1.10 1999/07/09 18:02:43 marekm Exp $")
static int group_locked = 0;
static int gshadow_locked = 0;
/* local function prototypes */
static void fail_exit P_((int));
int main P_((int, char **));
static void
fail_exit(int status)
{
if (group_locked)
gr_unlock();
if (gshadow_locked)
sgr_unlock();
exit(status);
}
int
main(int argc, char **argv)
{
const struct group *gr;
struct group grent;
const struct sgrp *sg;
struct sgrp sgent;
char *Prog = argv[0];
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
if (!gr_lock()) {
fprintf(stderr, _("%s: can't lock group file\n"), Prog);
fail_exit(5);
}
group_locked++;
if (!gr_open(O_RDWR)) {
fprintf(stderr, _("%s: can't open group file\n"), Prog);
fail_exit(1);
}
if (!sgr_lock()) {
fprintf(stderr, _("%s: can't lock shadow group file\n"), Prog);
fail_exit(5);
}
gshadow_locked++;
if (!sgr_open(O_CREAT | O_RDWR)) {
fprintf(stderr, _("%s: can't open shadow group file\n"), Prog);
fail_exit(1);
}
/*
* Remove /etc/gshadow entries for groups not in /etc/group.
*/
sgr_rewind();
while ((sg = sgr_next())) {
if (gr_locate(sg->sg_name))
continue;
if (!sgr_remove(sg->sg_name)) {
/*
* This shouldn't happen (the entry exists) but...
*/
fprintf(stderr, _("%s: can't remove shadow group %s\n"),
Prog, sg->sg_name);
fail_exit(3);
}
}
/*
* Update shadow group passwords if non-shadow password is not "x".
* Add any missing shadow group entries.
*/
gr_rewind();
while ((gr = gr_next())) {
sg = sgr_locate(gr->gr_name);
if (sg) {
#if 0 /* because of sg_mem, but see below */
if (strcmp(gr->gr_passwd, SHADOW_PASSWD_STRING) == 0)
continue;
#endif
/* update existing shadow group entry */
sgent = *sg;
if (strcmp(gr->gr_passwd, SHADOW_PASSWD_STRING) != 0)
sgent.sg_passwd = gr->gr_passwd;
} else {
static char *empty = 0;
/* add new shadow group entry */
memset(&sgent, 0, sizeof sgent);
sgent.sg_name = gr->gr_name;
sgent.sg_passwd = gr->gr_passwd;
sgent.sg_adm = &empty;
}
/*
* XXX - sg_mem is redundant, it is currently always a copy
* of gr_mem. Very few programs actually use sg_mem, and
* all of them are in the shadow suite... Maybe this field
* could be used for something else? Any suggestions?
*/
sgent.sg_mem = gr->gr_mem;
if (!sgr_update(&sgent)) {
fprintf(stderr,
_("%s: can't update shadow entry for %s\n"),
Prog, sgent.sg_name);
fail_exit(3);
}
/* remove password from /etc/group */
grent = *gr;
grent.gr_passwd = SHADOW_PASSWD_STRING; /* XXX warning: const */
if (!gr_update(&grent)) {
fprintf(stderr,
_("%s: can't update entry for group %s\n"),
Prog, grent.gr_name);
fail_exit(3);
}
}
if (!sgr_close()) {
fprintf(stderr, _("%s: can't update shadow group file\n"), Prog);
fail_exit(3);
}
if (!gr_close()) {
fprintf(stderr, _("%s: can't update group file\n"), Prog);
fail_exit(3);
}
sgr_unlock();
gr_unlock();
return 0;
}
#else /* !SHADOWGRP */
int
main(int argc, char **argv)
{
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
fprintf(stderr, _("%s: not configured for shadow group support.\n"),
argv[0]);
exit(1);
}
#endif /* !SHADOWGRP */

132
src/grpunconv.c Normal file
View File

@ -0,0 +1,132 @@
/*
* grpunconv - update /etc/group with information from /etc/gshadow.
*
* Copyright (C) 1996, Michael Meskes <meskes@debian.org>
* using sources from Marek Michalkiewicz
* <marekm@i17linuxb.ists.pwr.wroc.pl>
* This program may be freely used and distributed. If you improve
* it, please send me your changes. Thanks!
*/
#include <config.h>
#include "rcsid.h"
RCSID(PKG_VER "$Id: grpunconv.c,v 1.9 1999/07/09 18:02:43 marekm Exp $")
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <grp.h>
#include "prototypes.h"
#ifdef SHADOWGRP
#include "groupio.h"
#include "sgroupio.h"
static int group_locked = 0;
static int gshadow_locked = 0;
/* local function prototypes */
static void fail_exit P_((int));
int main P_((int, char **));
static void
fail_exit(int status)
{
if (group_locked)
gr_unlock();
if (gshadow_locked)
sgr_unlock();
exit(status);
}
int
main(int argc, char **argv)
{
const struct group *gr;
struct group grent;
const struct sgrp *sg;
char *Prog = argv[0];
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
if (!sgr_file_present())
exit(0); /* no /etc/gshadow, nothing to do */
if (!gr_lock()) {
fprintf(stderr, _("%s: can't lock group file\n"), Prog);
fail_exit(5);
}
group_locked++;
if (!gr_open(O_RDWR)) {
fprintf(stderr, _("%s: can't open group file\n"), Prog);
fail_exit(1);
}
if (!sgr_lock()) {
fprintf(stderr, _("%s: can't lock shadow group file\n"), Prog);
fail_exit(5);
}
gshadow_locked++;
if (!sgr_open(O_RDWR)) {
fprintf(stderr, _("%s: can't open shadow group file\n"), Prog);
fail_exit(1);
}
/*
* Update group passwords if non-shadow password is "x".
*/
gr_rewind();
while ((gr = gr_next())) {
sg = sgr_locate(gr->gr_name);
if (sg && strcmp(gr->gr_passwd, SHADOW_PASSWD_STRING) == 0) {
/* add password to /etc/group */
grent = *gr;
grent.gr_passwd = sg->sg_passwd;
if (!gr_update(&grent)) {
fprintf(stderr,
_("%s: can't update entry for group %s\n"),
Prog, grent.gr_name);
fail_exit(3);
}
}
}
if (!sgr_close()) {
fprintf(stderr, _("%s: can't update shadow group file\n"), Prog);
fail_exit(3);
}
if (!gr_close()) {
fprintf(stderr, _("%s: can't update group file\n"), Prog);
fail_exit(3);
}
if (unlink(SGROUP_FILE) != 0) {
fprintf(stderr, _("%s: can't delete shadow group file\n"), Prog);
fail_exit(3);
}
sgr_unlock();
gr_unlock();
return 0;
}
#else /* !SHADOWGRP */
int
main(int argc, char **argv)
{
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
fprintf(stderr, _("%s: not configured for shadow group support.\n"), argv[0]);
exit(1);
}
#endif /* !SHADOWGRP */

188
src/id.c Normal file
View File

@ -0,0 +1,188 @@
/*
* Copyright 1991 - 1994, Julianne Frances Haugh
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Julianne F. Haugh nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* id - print current process user identification information
*
* Print the current process identifiers. This includes the
* UID, GID, effective-UID and effective-GID. Optionally print
* the concurrent group set if the current system supports it.
*/
#include <config.h>
#include "rcsid.h"
RCSID(PKG_VER "$Id: id.c,v 1.5 1999/06/07 16:40:45 marekm Exp $")
#include <sys/types.h>
#include <stdio.h>
#include <grp.h>
#include <pwd.h>
#include "defines.h"
/* local function prototypes */
static void usage P_((void));
int main P_((int, char **));
static void
usage(void)
{
#ifdef HAVE_GETGROUPS
fprintf(stderr, _("usage: id [ -a ]\n"));
#else
fprintf(stderr, _("usage: id\n"));
#endif
exit(1);
}
/*ARGSUSED*/
int
main(int argc, char **argv)
{
uid_t ruid, euid;
gid_t rgid, egid;
int i;
/*
* This block of declarations is particularly strained because of several
* different ways of doing concurrent groups. Old BSD systems used int
* for gid's, but short for the type passed to getgroups(). Newer systems
* use gid_t for everything. Some systems have a small and fixed NGROUPS,
* usually about 16 or 32. Others use bigger values.
*/
#ifdef HAVE_GETGROUPS
GETGROUPS_T groups[NGROUPS_MAX];
int ngroups;
int aflg = 0;
#endif
struct passwd *pw;
struct group *gr;
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
#ifdef HAVE_GETGROUPS
/*
* See if the -a flag has been given to print out the
* concurrent group set.
*/
if (argc > 1) {
if (argc > 2 || strcmp (argv[1], "-a"))
usage();
else
aflg = 1;
}
#else
if (argc > 1)
usage();
#endif
ruid = getuid();
euid = geteuid();
rgid = getgid();
egid = getegid();
/*
* Print out the real user ID and group ID. If the user or
* group does not exist, just give the numerical value.
*/
pw = getpwuid(ruid);
if (pw)
printf(_("uid=%d(%s)"), (int) ruid, pw->pw_name);
else
printf(_("uid=%d"), (int) ruid);
gr = getgrgid(rgid);
if (gr)
printf(_(" gid=%d(%s)"), (int) rgid, gr->gr_name);
else
printf(_(" gid=%d"), (int) rgid);
/*
* Print out the effective user ID and group ID if they are
* different from the real values.
*/
if (ruid != euid) {
pw = getpwuid(euid);
if (pw)
printf(_(" euid=%d(%s)"), (int) euid, pw->pw_name);
else
printf(_(" euid=%d"), (int) euid);
}
if (rgid != egid) {
gr = getgrgid(egid);
if (gr)
printf(_(" egid=%d(%s)"), (int) egid, gr->gr_name);
else
printf(_(" egid=%d"), (int) egid);
}
#ifdef HAVE_GETGROUPS
/*
* Print out the concurrent group set if the user has requested
* it. The group numbers will be printed followed by their
* names.
*/
if (aflg && (ngroups = getgroups (NGROUPS_MAX, groups)) != -1) {
/*
* Start off the group message. It will be of the format
*
* groups=###(aaa),###(aaa),###(aaa)
*
* where "###" is a numerical value and "aaa" is the
* corresponding name for each respective numerical value.
*/
printf(_(" groups="));
for (i = 0; i < ngroups; i++) {
if (i)
putchar(',');
gr = getgrgid(groups[i]);
if (gr)
printf("%d(%s)", (int) groups[i], gr->gr_name);
else
printf("%d", (int) groups[i]);
}
}
#endif
/*
* Finish off the line.
*/
putchar('\n');
exit(0);
/*NOTREACHED*/
}

193
src/lastlog.c Normal file
View File

@ -0,0 +1,193 @@
/*
* Copyright 1989 - 1994, Julianne Frances Haugh
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Julianne F. Haugh nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <config.h>
#include "rcsid.h"
RCSID(PKG_VER "$Id: lastlog.c,v 1.5 1999/06/07 16:40:45 marekm Exp $")
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <pwd.h>
#include <time.h>
#include "prototypes.h"
#include "defines.h"
#if HAVE_LASTLOG_H
#include <lastlog.h>
#else
#include "lastlog_.h"
#endif
/*
* Needed for MkLinux DR1/2/2.1 - J.
*/
#ifndef LASTLOG_FILE
#define LASTLOG_FILE "/var/log/lastlog"
#endif
static FILE *lastlogfile; /* lastlog file stream */
static off_t user; /* one single user, specified on command line */
static int days; /* number of days to consider for print command */
static time_t seconds; /* that number of days in seconds */
static int uflg = 0; /* set if user is a valid user id */
static int tflg = 0; /* print is restricted to most recent days */
static struct lastlog lastlog; /* scratch structure to play with ... */
static struct stat statbuf; /* fstat buffer for file size */
static struct passwd *pwent;
extern char *optarg;
#define NOW (time ((time_t *) 0))
/* local function prototypes */
int main P_((int, char **));
static void print P_((void));
static void print_one P_((const struct passwd *));
int
main(int argc, char **argv)
{
int c;
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
if ((lastlogfile = fopen (LASTLOG_FILE,"r")) == (FILE *) 0) {
perror (LASTLOG_FILE);
exit (1);
}
while ((c = getopt (argc, argv, "u:t:")) != EOF) {
switch (c) {
case 'u':
pwent = getpwnam (optarg);
if (!pwent) {
fprintf(stderr,
_("Unknown User: %s\n"),
optarg);
exit (1);
}
uflg++;
user = pwent->pw_uid;
break;
case 't':
days = atoi (optarg);
seconds = days * DAY;
tflg++;
break;
}
}
print ();
fclose (lastlogfile);
exit (0);
/*NOTREACHED*/
}
static void
print(void)
{
off_t offset;
if (uflg) {
offset = (unsigned long) user * sizeof lastlog;
if (fstat (fileno (lastlogfile), &statbuf)) {
perror(LASTLOG_FILE);
return;
}
if (offset >= statbuf.st_size)
return;
fseek (lastlogfile, offset, SEEK_SET);
if (fread ((char *) &lastlog, sizeof lastlog, 1,
lastlogfile) == 1)
print_one (pwent);
else
perror (LASTLOG_FILE);
} else {
setpwent ();
while ((pwent = getpwent ())) {
user = pwent->pw_uid;
offset = (unsigned long) user * sizeof lastlog;
fseek (lastlogfile, offset, SEEK_SET);
if (fread ((char *) &lastlog, sizeof lastlog, 1,
lastlogfile) != 1)
continue;
if (tflg && NOW - lastlog.ll_time > seconds)
continue;
print_one (pwent);
}
}
}
static void
print_one(const struct passwd *pw)
{
static int once;
char *cp;
struct tm *tm;
#ifdef HAVE_STRFTIME
char ptime[80];
#endif
if (! pw)
return;
if (! once) {
#ifdef HAVE_LL_HOST
printf(_("Username Port From Latest\n"));
#else
printf(_("Username Port Latest\n"));
#endif
once++;
}
tm = localtime (&lastlog.ll_time);
#ifdef HAVE_STRFTIME
strftime(ptime, sizeof(ptime), "%a %b %e %H:%M:%S %z %Y", tm);
cp = ptime;
#else
cp = asctime (tm);
cp[24] = '\0';
#endif
if(lastlog.ll_time == (time_t) 0)
cp = _("**Never logged in**\0");
#ifdef HAVE_LL_HOST
printf ("%-16s %-8.8s %-16.16s %s\n", pw->pw_name,
lastlog.ll_line, lastlog.ll_host, cp);
#else
printf ("%-16s\t%-8.8s %s\n", pw->pw_name,
lastlog.ll_line, cp);
#endif
}

1260
src/login.c Normal file

File diff suppressed because it is too large Load Diff

316
src/logoutd.c Normal file
View File

@ -0,0 +1,316 @@
/*
* Copyright 1991 - 1993, Julianne Frances Haugh
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Julianne F. Haugh nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <config.h>
#include "rcsid.h"
RCSID(PKG_VER "$Id: logoutd.c,v 1.13 1999/06/07 16:40:45 marekm Exp $")
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <signal.h>
#include <utmp.h>
#include <fcntl.h>
#include "prototypes.h"
#include "defines.h"
#ifdef SVR4
#include <libgen.h>
#endif
#ifdef SVR4
#define signal sigset
#endif
static char *Prog;
static char *mesg_buf = "login time exceeded\r\n"; /* XXX warning: const */
static int mesg_len = 21;
static int mesg_size;
#ifndef HUP_MESG_FILE
#define HUP_MESG_FILE "/etc/logoutd.mesg"
#endif
/* local function prototypes */
static RETSIGTYPE reload_mesg P_((int));
static int check_login P_((const struct utmp *));
int main P_((int, char **));
/*
* reload_mesg - reload the message that is output when killing a process
*/
static RETSIGTYPE
reload_mesg(int sig)
{
int fd;
struct stat sb;
signal (sig, reload_mesg);
if (stat (HUP_MESG_FILE, &sb))
return;
if ((sb.st_mode & S_IFMT) != S_IFREG)
return;
if ((fd = open (HUP_MESG_FILE, O_RDONLY)) != -1) {
if (sb.st_size + 1 > mesg_size) {
if (mesg_buf && mesg_size)
free (mesg_buf);
mesg_len = sb.st_size;
mesg_size = mesg_len + 1;
if (! (mesg_buf = (char *) malloc (mesg_len + 1)))
goto end;
} else
mesg_len = sb.st_size;
if (read (fd, mesg_buf, mesg_len) != mesg_len) {
mesg_len = 0;
goto end;
}
} else
return;
end:
close (fd);
}
/*
* check_login - check if user (struct utmp) allowed to stay logged in
*/
static int
check_login(const struct utmp *ut)
{
char user[sizeof(ut->ut_user) + 1];
time_t now;
/*
* ut_user may not have the terminating NUL.
*/
strncpy(user, ut->ut_user, sizeof(ut->ut_user));
user[sizeof(ut->ut_user)] = '\0';
time(&now);
/*
* Check if they are allowed to be logged in right now.
*/
if (!isttytime(user, ut->ut_line, now))
return 0;
#if 0
/*
* Check for how long they are allowed to stay logged in.
* XXX - not implemented yet. Need to add a new field to
* /etc/porttime (login time limit in minutes, or no limit,
* based on username, tty, and time of login).
*/
if (now - ut->ut_time > get_time_limit(user, ut->ut_line, ut->ut_time))
return 0;
#endif
return 1;
}
/*
* logoutd - logout daemon to enforce /etc/porttime file policy
*
* logoutd is started at system boot time and enforces the login
* time and port restrictions specified in /etc/porttime. The
* utmp file is periodically scanned and offending users are logged
* off from the system.
*/
int
main(int argc, char **argv)
{
int i;
int status;
struct utmp *ut;
char user[sizeof(ut->ut_user) + 1]; /* terminating NUL */
char tty_name[sizeof(ut->ut_line) + 6]; /* /dev/ + NUL */
int tty_fd;
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
#ifndef DEBUG
for (i = 0;close (i) == 0;i++)
;
#ifdef HAVE_SETPGRP
#ifdef SETPGRP_VOID
setpgrp(); /* USG */
#else
setpgrp(getpid(), getpid());
#endif
#else /* !HAVE_SETPGRP */
setpgid(getpid(), getpid()); /* BSD || SUN || SUN4 */
#endif /* !HAVE_SETPGRP */
reload_mesg (SIGHUP);
/*
* Put this process in the background.
*/
if ((i = fork ()))
exit (i < 0 ? 1:0);
#endif /* !DEBUG */
/*
* Start syslogging everything
*/
Prog = Basename(argv[0]);
openlog(Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
/*
* Scan the UTMP file once per minute looking for users that
* are not supposed to still be logged in.
*/
while (1) {
#ifndef DEBUG
sleep(60);
#endif
/*
* Attempt to re-open the utmp file. The file is only
* open while it is being used.
*/
setutent();
/*
* Read all of the entries in the utmp file. The entries
* for login sessions will be checked to see if the user
* is permitted to be signed on at this time.
*/
while ((ut = getutent())) {
#ifdef USER_PROCESS
if (ut->ut_type != USER_PROCESS)
continue;
#endif
if (ut->ut_user[0] == '\0')
continue;
if (check_login(ut))
continue;
/*
* Put the rest of this in a child process. This
* keeps the scan from waiting on other ports to die.
*/
if (fork() != 0)
continue;
if (strncmp(ut->ut_line, "/dev/", 5) != 0)
strcpy(tty_name, "/dev/");
else
tty_name[0] = '\0';
strcat(tty_name, ut->ut_line);
#ifndef O_NOCTTY
#define O_NOCTTY 0
#endif
if ((tty_fd = open (tty_name,
O_WRONLY|O_NDELAY|O_NOCTTY)) != -1) {
/* Suggested by Ivan Nejgebauar <ian@unsux.ns.ac.yu>: set OPOST
before writing the message. --marekm */
TERMIO oldt, newt;
GTTY(tty_fd, &oldt);
newt = oldt;
#ifdef OPOST
newt.c_oflag |= OPOST;
#else /* XXX - I'm too young to know bsd sgtty, sorry :). --marekm */
#endif
STTY(tty_fd, &newt);
write (tty_fd, mesg_buf, mesg_len);
STTY(tty_fd, &oldt);
close (tty_fd);
sleep(10);
}
#ifdef USER_PROCESS /* USG_UTMP */
if (ut->ut_pid > 1) {
kill(- ut->ut_pid, SIGHUP);
sleep(10);
kill(- ut->ut_pid, SIGKILL);
}
#else /* BSD || SUN || SUN4 */
/*
* vhangup() the line to kill try and kill
* whatever is out there using it.
*/
if ((tty_fd = open (tty_name, O_RDONLY|O_NDELAY)) == -1)
continue;
vhangup (tty_fd);
close (tty_fd);
#endif /* BSD || SUN || SUN4 */
#if 0
SYSLOG((LOG_NOTICE,
"logged off user `%.*s' on `%.*s'\n",
(int) sizeof(ut->ut_user), ut->ut_user,
(int) sizeof(ut->ut_line), ut->ut_line));
#else
/* avoid gcc warnings about %.*s in syslog() */
strncpy(user, ut->ut_line, sizeof(user) - 1);
user[sizeof(user) - 1] = '\0';
SYSLOG((LOG_NOTICE, "logged off user `%s' on `%s'\n",
user, tty_name));
#endif
/*
* This child has done all it can, drop dead.
*/
exit (0);
}
endutent();
/*
* Reap any dead babies ...
*/
while (wait (&status) != -1)
;
}
return 1; /* not reached */
}

395
src/mkpasswd.c Normal file
View File

@ -0,0 +1,395 @@
/*
* Copyright 1990 - 1994, Julianne Frances Haugh
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Julianne F. Haugh nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <config.h>
#include "rcsid.h"
RCSID(PKG_VER "$Id: mkpasswd.c,v 1.6 1999/06/07 16:40:45 marekm Exp $")
#include <sys/stat.h>
#include "prototypes.h"
#include "defines.h"
#include <stdio.h>
#if !defined(NDBM) /*{*/
int
main(int argc, char **argv)
{
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
fprintf(stderr,
_("%s: no DBM database on system - no action performed\n"),
argv[0]);
return 0;
}
#else /*} defined(NDBM) {*/
#include <fcntl.h>
#include <pwd.h>
#include <ndbm.h>
#include <grp.h>
extern DBM *pw_dbm;
extern DBM *gr_dbm;
#ifdef SHADOWPWD
extern DBM *sp_dbm;
#endif
#ifdef SHADOWGRP
extern DBM *sg_dbm;
#endif
char *fgetsx();
#ifdef SHADOWPWD
#ifdef SHADOWGRP
#define USAGE _("Usage: %s [ -vf ] [ -p|g|sp|sg ] file\n")
#else /* !SHADOWGRP */
#define USAGE _("Usage: %s [ -vf ] [ -p|g|sp ] file\n")
#endif /* SHADOWGRP */
#else /* !SHADOWPWD */
#define USAGE _("Usage: %s [ -vf ] [ -p|g ] file\n")
#endif /* SHADOWPWD */
char *Progname;
int vflg = 0;
int fflg = 0;
int gflg = 0;
int sflg = 0; /* -s flag -- leave in, makes code nicer */
int pflg = 0;
extern struct passwd *sgetpwent();
extern int pw_dbm_update();
extern struct group *sgetgrent();
extern int gr_dbm_update();
#ifdef SHADOWPWD
extern struct spwd *sgetspent();
extern int sp_dbm_update();
#endif
#ifdef SHADOWGRP
extern struct sgrp *sgetsgent();
extern int sg_dbm_update();
#endif
/* local function prototypes */
int main P_((int, char **));
static void usage P_((void));
/*
* mkpasswd - create DBM files for /etc/passwd-like input file
*
* mkpasswd takes an an argument the name of a file in /etc/passwd format
* and creates a DBM file keyed by user ID and name. The output files have
* the same name as the input file, with .dir and .pag appended.
*
* this command will also create look-aside files for
* /etc/group, /etc/shadow, and /etc/gshadow.
*/
int
main(int argc, char **argv)
{
extern int optind;
extern char *optarg;
FILE *fp; /* File pointer for input file */
char *file; /* Name of input file */
char *dir; /* Name of .dir file */
char *pag; /* Name of .pag file */
char *cp; /* Temporary character pointer */
int flag; /* Flag for command line option */
int cnt = 0; /* Number of entries in database */
int longest = 0; /* Longest entry in database */
int len; /* Length of input line */
int errors = 0; /* Count of errors processing file */
char buf[BUFSIZ*8]; /* Input line from file */
struct passwd *passwd=NULL; /* Pointer to password file entry */
struct group *group=NULL; /* Pointer to group file entry */
#ifdef SHADOWPWD
struct spwd *shadow=NULL; /* Pointer to shadow passwd entry */
#endif
#ifdef SHADOWGRP
struct sgrp *gshadow=NULL; /* Pointer to shadow group entry */
#endif
DBM *dbm; /* Pointer to new NDBM files */
DBM *dbm_open(); /* Function to open NDBM files */
/*
* Figure out what my name is. I will use this later ...
*/
Progname = Basename(argv[0]);
/*
* Figure out what the flags might be ...
*/
while ((flag = getopt (argc, argv, "fvpgs")) != EOF) {
switch (flag) {
case 'v':
vflg++;
break;
case 'f':
fflg++;
break;
case 'g':
gflg++;
#ifndef SHADOWGRP
if (sflg)
usage ();
#endif
if (pflg)
usage ();
break;
#if defined(SHADOWPWD) || defined(SHADOWGRP)
case 's':
sflg++;
#ifndef SHADOWGRP
if (gflg)
usage ();
#endif
break;
#endif
case 'p':
pflg++;
if (gflg)
usage ();
break;
default:
usage();
}
}
/*
* Backwards compatibility fix for -p flag ...
*/
#ifdef SHADOWPWD
if (! sflg && ! gflg)
#else
if (! gflg)
#endif
pflg++;
/*
* The last and only remaining argument must be the file name
*/
if (argc - 1 != optind)
usage ();
file = argv[optind];
if (! (fp = fopen (file, "r"))) {
fprintf (stderr, _("%s: cannot open file %s\n"), Progname, file);
exit (1);
}
/*
* Make the filenames for the two DBM files.
*/
dir = xmalloc (strlen (file) + 5); /* space for .dir file */
strcat (strcpy (dir, file), ".dir");
pag = xmalloc (strlen (file) + 5); /* space for .pag file */
strcat (strcpy (pag, file), ".pag");
/*
* Remove existing files if requested.
*/
if (fflg) {
(void) unlink (dir);
(void) unlink (pag);
}
/*
* Create the two DBM files - it is an error for these files
* to have existed already.
*/
if (access(dir, F_OK) == 0) {
fprintf (stderr, _("%s: cannot overwrite file %s\n"), Progname, dir);
exit (1);
}
if (access(pag, F_OK) == 0) {
fprintf (stderr, _("%s: cannot overwrite file %s\n"), Progname, pag);
exit (1);
}
if (sflg)
umask(077);
else
umask(022);
/*
* Now the DBM database gets initialized
*/
if (! (dbm = dbm_open (file, O_RDWR|O_CREAT, 0644))) {
fprintf (stderr, _("%s: cannot open DBM files for %s\n"), Progname, file);
exit (1);
}
if (gflg) {
#ifdef SHADOWGRP
if (sflg)
sg_dbm = dbm;
else
#endif
gr_dbm = dbm;
} else {
#ifdef SHADOWPWD
if (sflg)
sp_dbm = dbm;
else
#endif
pw_dbm = dbm;
}
/*
* Read every line in the password file and convert it into a
* data structure to be put in the DBM database files.
*/
while (fgetsx (buf, BUFSIZ, fp) != NULL) {
/*
* Get the next line and strip off the trailing newline
* character.
*/
buf[sizeof buf - 1] = '\0';
if (! (cp = strchr (buf, '\n'))) {
fprintf (stderr, _("%s: the beginning with "%.16s ..." is too long\n"), Progname, buf);
exit (1);
}
*cp = '\0';
len = strlen (buf);
#ifdef USE_NIS
/*
* Parse the password file line into a (struct passwd).
* Erroneous lines cause error messages, but that's
* all. YP lines are ignored completely.
*/
if (buf[0] == '-' || buf[0] == '+')
continue;
#endif
if (! (((! sflg && pflg) && (passwd = sgetpwent (buf)))
#ifdef SHADOWPWD
|| ((sflg && pflg) && (shadow = sgetspent (buf)))
#endif
|| ((! sflg && gflg) && (group = sgetgrent (buf)))
#ifdef SHADOWGRP
|| ((sflg && gflg) && (gshadow = sgetsgent (buf)))
#endif
)) {
fprintf (stderr, _("%s: error parsing line \"%s\"\n"), Progname, buf);
errors++;
continue;
}
if (vflg) {
if (!sflg && pflg) printf (_("adding record for name "%s"\n"), passwd->pw_name);
#ifdef SHADOWPWD
if (sflg && pflg) printf (_("adding record for name "%s"\n"), shadow->sp_namp);
#endif
if (!sflg && gflg) printf (_("adding record for name "%s"\n"), group->gr_name);
#ifdef SHADOWGRP
if (sflg && gflg) printf (_("adding record for name "%s"\n"), gshadow->sg_name);
#endif
}
if (! sflg && pflg && ! pw_dbm_update (passwd))
fprintf (stderr, _("%s: error adding record for "%s"\n"),
Progname, passwd->pw_name);
#ifdef SHADOWPWD
if (sflg && pflg && ! sp_dbm_update (shadow))
fprintf (stderr, _("%s: error adding record for "%s"\n"),
Progname, shadow->sp_namp);
#endif
if (! sflg && gflg && ! gr_dbm_update (group))
fprintf (stderr, _("%s: error adding record for "%s"\n"),
Progname, group->gr_name);
#ifdef SHADOWGRP
if (sflg && gflg && ! sg_dbm_update (gshadow))
fprintf (stderr, _("%s: error adding record for "%s"\n"),
Progname, gshadow->sg_name);
#endif /* SHADOWGRP */
/*
* Update the longest record and record count
*/
if (len > longest)
longest = len;
cnt++;
}
/*
* Tell the user how things went ...
*/
if (vflg)
printf (_("added %d entries, longest was %d\n"), cnt, longest);
exit (errors);
/*NOTREACHED*/
}
/*
* usage - print error message and exit
*/
static void
usage(void)
{
#ifdef SHADOWPWD
#ifdef SHADOWGRP
fprintf (stderr, _("Usage: %s [ -vf ] [ -p|g|sp|sg ] file\n"), Progname);
#else /* !SHADOWGRP */
fprintf (stderr, _("Usage: %s [ -vf ] [ -p|g|sp ] file\n"), Progname);
#endif /* SHADOWGRP */
#else /* !SHADOWPWD */
fprintf (stderr, _("Usage: %s [ -vf ] [ -p|g ] file\n"), Progname);
#endif /* SHADOWPWD */
exit (1);
/*NOTREACHED*/
}
#endif /*} defined(NDBM) */

479
src/newgrp.c Normal file
View File

@ -0,0 +1,479 @@
/*
* Copyright 1990 - 1994, Julianne Frances Haugh
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Julianne F. Haugh nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <config.h>
#include "rcsid.h"
RCSID(PKG_VER "$Id: newgrp.c,v 1.13 1999/07/09 18:02:43 marekm Exp $")
#include <stdio.h>
#include <errno.h>
#include <grp.h>
#include <pwd.h>
#include "prototypes.h"
#include "defines.h"
#include "getdef.h"
extern char **environ;
#ifdef HAVE_SETGROUPS
static int ngroups;
static GETGROUPS_T *grouplist;
#endif
static char *Prog;
static int is_newgrp;
/* local function prototypes */
static void usage P_((void));
int main P_((int, char **));
/*
* usage - print command usage message
*/
static void
usage(void)
{
if (is_newgrp)
fprintf (stderr, _("usage: newgrp [ - ] [ group ]\n"));
else
fprintf (stderr, _("usage: sg group [ command ]\n"));
}
/*
* newgrp - change the invokers current real and effective group id
*/
int
main(int argc, char **argv)
{
int initflag = 0;
int needspasswd = 0;
int i;
int cflag = 0;
gid_t gid;
char *cp;
const char *cpasswd, *name, *prog;
char *group = NULL;
char *command=NULL;
char **envp = environ;
struct passwd *pwd;
struct group *grp;
#ifdef SHADOWPWD
struct spwd *spwd;
#endif
#ifdef SHADOWGRP
struct sgrp *sgrp;
#endif
sanitize_env();
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
/*
* save my name for error messages and save my real gid incase
* of errors. if there is an error i have to exec a new login
* shell for the user since her old shell won't have fork'd to
* create the process. skip over the program name to the next
* command line argument.
*/
Prog = Basename(argv[0]);
is_newgrp = (strcmp(Prog, "newgrp") == 0);
openlog(is_newgrp ? "newgrp" : "sg", LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
gid = getgid();
argc--; argv++;
initenv();
pwd = get_my_pwent();
if (!pwd) {
fprintf (stderr, _("unknown uid: %d\n"), (int) getuid());
SYSLOG((LOG_WARN, "unknown uid %d\n", (int) getuid()));
closelog();
exit(1);
}
name = pwd->pw_name;
/*
* Parse the command line. There are two accepted flags. The
* first is "-", which for newgrp means to re-create the entire
* environment as though a login had been performed, and "-c",
* which for sg causes a command string to be executed.
*
* The next argument, if present, must be the new group name.
* Any remaining remaining arguments will be used to execute a
* command as the named group. If the group name isn't present,
* I just use the login group ID of the current user.
*
* The valid syntax are
* newgrp [ - ] [ groupid ]
* newgrp [ -l ] [ groupid ]
* sg [ - ]
* sg [ - ] groupid [ command ]
*/
if (argc > 0 && (!strcmp(argv[0], "-") || !strcmp(argv[0], "-l"))) {
argc--; argv++;
initflag = 1;
}
if (!is_newgrp) {
/*
* Do the command line for everything that is
* not "newgrp".
*/
if (argc > 0 && argv[0][0] != '-') {
group = argv[0];
argc--; argv++;
} else {
usage ();
closelog();
exit (1);
}
if (argc > 0) {
command = argv[1];
cflag++;
}
} else {
/*
* Do the command line for "newgrp". It's just
* making sure there aren't any flags and getting
* the new group name.
*/
if (argc > 0 && argv[0][0] == '-') {
usage ();
goto failure;
} else if (argv[0] != (char *) 0) {
group = argv[0];
} else {
/*
* get the group file entry for her login group id.
* the entry must exist, simply to be annoying.
*/
if (! (grp = getgrgid (pwd->pw_gid))) {
fprintf(stderr, _("unknown gid: %ld\n"),
(long) pwd->pw_gid);
SYSLOG((LOG_CRIT, "unknown gid: %ld\n",
(long) pwd->pw_gid));
goto failure;
}
}
}
#ifdef HAVE_SETGROUPS
/*
* get the current users groupset. the new group will be
* added to the concurrent groupset if there is room, otherwise
* you get a nasty message but at least your real and effective
* group id's are set.
*/
/* don't use getgroups(0, 0) - it doesn't work on some systems */
i = 16;
for (;;) {
grouplist = (GETGROUPS_T *) xmalloc(i * sizeof(GETGROUPS_T));
ngroups = getgroups(i, grouplist);
/* XXX Bug#38672
login: newgrp/sg fails with user in 17 groups */
if (i > ngroups && !(ngroups == -1 && errno == EINVAL))
break;
/* not enough room, so try allocating a larger buffer */
free(grouplist);
i *= 2;
}
if (ngroups < 0) {
perror("getgroups");
exit(1);
}
#endif /* HAVE_SETGROUPS */
/*
* now we put her in the new group. the password file entry for
* her current user id has been gotten. if there was no optional
* group argument she will have her real and effective group id
* set to the value from her password file entry. otherwise
* we validate her access to the specified group.
*/
if (group == (char *) 0) {
if (! (grp = getgrgid (pwd->pw_gid))) {
fprintf (stderr, _("unknown gid: %d\n"), pwd->pw_gid);
goto failure;
}
group = grp->gr_name;
} else if (! (grp = getgrnam (group))) {
fprintf (stderr, _("unknown group: %s\n"), group);
goto failure;
}
#ifdef SHADOWGRP
if ((sgrp = getsgnam (group))) {
grp->gr_passwd = sgrp->sg_passwd;
grp->gr_mem = sgrp->sg_mem;
}
#endif
/*
* see if she is a member of this group.
* if she isn't a member, she needs to provide the
* group password. if there is no group password, she
* will be denied access anyway.
*/
if (!is_on_list(grp->gr_mem, name))
needspasswd = 1;
/*
* if she does not have either a shadowed password,
* or a regular password, and the group has a password,
* she needs to give the group password.
*/
#ifdef SHADOWPWD
if ((spwd = getspnam (name)))
pwd->pw_passwd = spwd->sp_pwdp;
#endif
if (pwd->pw_passwd[0] == '\0' && grp->gr_passwd[0])
needspasswd = 1;
/*
* now i see about letting her into the group she requested.
* if she is the root user, i'll let her in without having to
* prompt for the password. otherwise i ask for a password
* if she flunked one of the tests above. note that she
* won't have to provide the password to her login group even
* if she isn't listed as a member.
*/
if (getuid () != 0 && needspasswd) {
/*
* get the password from her, and set the salt for
* the decryption from the group file.
*/
if (! (cp = getpass (_("Password:"))))
goto failure;
/*
* encrypt the key she gave us using the salt from
* the password in the group file. the result of
* this encryption must match the previously
* encrypted value in the file.
*/
cpasswd = pw_encrypt (cp, grp->gr_passwd);
strzero(cp);
if (grp->gr_passwd[0] == '\0') {
/*
* there is no password, print out "Sorry" and give up
*/
sleep(1);
fputs (_("Sorry.\n"), stderr);
goto failure;
}
if (strcmp (cpasswd, grp->gr_passwd) != 0) {
SYSLOG((LOG_INFO,
"Invalid password for group `%s' from `%s'\n",
group, name));
sleep(1);
fputs (_("Sorry.\n"), stderr);
goto failure;
}
}
/*
* all successful validations pass through this point. the
* group id will be set, and the group added to the concurrent
* groupset.
*/
#ifdef USE_SYSLOG
if (getdef_bool ("SYSLOG_SG_ENAB"))
SYSLOG((LOG_INFO, "user `%s' switched to group `%s'\n",
name, group));
#endif
gid = grp->gr_gid;
#ifdef HAVE_SETGROUPS
/*
* i am going to try to add her new group id to her concurrent
* group set. if the group id is already present i'll just
* skip this part. if the group doesn't fit, i'll complain
* loudly and skip this part ...
*/
for (i = 0;i < ngroups;i++) {
if (gid == grouplist[i])
break;
}
if (i == ngroups) {
if (ngroups >= NGROUPS_MAX) {
fprintf (stderr, _("too many groups\n"));
} else {
grouplist[ngroups++] = gid;
if (setgroups(ngroups, grouplist)) {
perror("setgroups");
}
}
}
#endif
okay:
/*
* i set her group id either to the value she requested, or
* to the original value if the newgrp failed.
*/
if (setgid(gid))
perror("setgid");
if (setuid(getuid())) {
perror("setuid");
exit(1);
}
/*
* see if the "-c" flag was used. if it was, i just create a
* shell command for her using the argument that followed the
* "-c" flag.
*/
if (cflag) {
closelog();
execl("/bin/sh", "sh", "-c", command, (char *) 0);
if (errno == ENOENT) {
perror("/bin/sh");
exit(127);
} else {
perror("/bin/sh");
exit(126);
}
}
/*
* i have to get the pathname of her login shell. as a favor,
* i'll try her environment for a $SHELL value first, and
* then try the password file entry. obviously this shouldn't
* be in the restricted command directory since it could be
* used to leave the restricted environment.
*/
if (! initflag && (cp = getenv ("SHELL")))
prog = cp;
else if (pwd->pw_shell && pwd->pw_shell[0])
prog = pwd->pw_shell;
else
prog = "/bin/sh";
/*
* now i try to find the basename of the login shell. this
* will become argv[0] of the spawned command.
*/
cp = Basename((char *) prog);
#ifdef SHADOWPWD
endspent ();
#endif
#ifdef SHADOWGRP
endsgent ();
#endif
endpwent ();
endgrent ();
/*
* switch back to her home directory if i am doing login
* initialization.
*/
if (initflag) {
if (chdir (pwd->pw_dir))
perror("chdir");
while (*envp) {
if (strncmp (*envp, "PATH=", 5) == 0 ||
strncmp (*envp, "HOME=", 5) == 0 ||
strncmp (*envp, "SHELL=", 6) == 0 ||
strncmp (*envp, "TERM=", 5) == 0)
addenv(*envp, NULL);
envp++;
}
} else {
while (*envp)
addenv(*envp++, NULL);
}
/*
* exec the login shell and go away. we are trying to get
* back to the previous environment which should be the
* user's login shell.
*/
shell(prog, initflag ? (char *)0 : cp);
/*NOTREACHED*/
failure:
/*
* this is where all failures land. the group id will not
* have been set, so the setgid() below will set me to the
* original group id i had when i was invoked.
*/
/*
* only newgrp needs to re-exec the user's shell. that is
* because the shell doesn't recognize "sg", so it doesn't
* "exec" this command.
*/
if (!is_newgrp) {
closelog();
exit (1);
}
/*
* The GID is still set to the old value, so now I can
* give the user back her shell.
*/
goto okay;
}

571
src/newusers.c Normal file
View File

@ -0,0 +1,571 @@
/*
* Copyright 1990 - 1993, Julianne Frances Haugh
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Julianne F. Haugh nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* newusers - create users from a batch file
*
* newusers creates a collection of entries in /etc/passwd
* and related files by reading a passwd-format file and
* adding entries in the related directories.
*/
#include <config.h>
#include "rcsid.h"
RCSID(PKG_VER "$Id: newusers.c,v 1.10 1999/06/07 16:40:45 marekm Exp $")
#include <sys/types.h>
#include <sys/stat.h>
#include "prototypes.h"
#include "defines.h"
#include <stdio.h>
#include <pwd.h>
#include <grp.h>
#include <fcntl.h>
static char *Prog;
extern char *crypt_make_salt P_((void));
#include "getdef.h"
#include "pwio.h"
#include "groupio.h"
#ifdef SHADOWPWD
#include "shadowio.h"
static int is_shadow;
#endif
/* local function prototypes */
static void usage P_((void));
static int add_group P_((const char *, const char *, gid_t *));
static int add_user P_((const char *, const char *, uid_t *, gid_t));
static void update_passwd P_((struct passwd *, const char *));
static int add_passwd P_((struct passwd *, const char *));
int main P_((int, char **));
/*
* usage - display usage message and exit
*/
static void
usage(void)
{
fprintf(stderr, _("Usage: %s [ input ]\n"), Prog);
exit(1);
}
/*
* add_group - create a new group or add a user to an existing group
*/
static int
add_group(const char *name, const char *gid, gid_t *ngid)
{
const struct passwd *pwd;
const struct group *grp;
struct group grent;
char *members[2];
int i;
/*
* Start by seeing if the named group already exists. This
* will be very easy to deal with if it does.
*/
if ((grp = gr_locate (gid))) {
add_member:
grent = *grp;
*ngid = grent.gr_gid;
for (i = 0;grent.gr_mem[i] != (char *) 0;i++)
if (strcmp (grent.gr_mem[i], name) == 0)
return 0;
grent.gr_mem = (char **) xmalloc (sizeof (char *) * (i + 2));
memcpy (grent.gr_mem, grp->gr_mem, sizeof (char *) * (i + 2));
grent.gr_mem[i] = xstrdup (name);
grent.gr_mem[i + 1] = (char *) 0;
return ! gr_update (&grent);
}
/*
* The group did not exist, so I try to figure out what the
* GID is going to be. The gid parameter is probably "", meaning
* I figure out the GID from the password file. I want the UID
* and GID to match, unless the GID is already used.
*/
if (gid[0] == '\0') {
i = 100;
for (pw_rewind ();(pwd = pw_next ());) {
if (pwd->pw_uid >= i)
i = pwd->pw_uid + 1;
}
for (gr_rewind ();(grp = gr_next ());) {
if (grp->gr_gid == i) {
i = -1;
break;
}
}
} else if (gid[0] >= '0' && gid[0] <= '9') {
/*
* The GID is a number, which means either this is a brand new
* group, or an existing group. For existing groups I just add
* myself as a member, just like I did earlier.
*/
i = atoi (gid);
for (gr_rewind ();(grp = gr_next ());)
if (grp->gr_gid == i)
goto add_member;
} else
/*
* The last alternative is that the GID is a name which is not
* already the name of an existing group, and I need to figure
* out what group ID that group name is going to have.
*/
i = -1;
/*
* If I don't have a group ID by now, I'll go get the
* next one.
*/
if (i == -1) {
for (i = 100, gr_rewind ();(grp = gr_next ());)
if (grp->gr_gid >= i)
i = grp->gr_gid + 1;
}
/*
* Now I have all of the fields required to create the new
* group.
*/
if (gid[0] && (gid[0] <= '0' || gid[0] >= '9'))
grent.gr_name = xstrdup(gid);
else
grent.gr_name = xstrdup(name);
grent.gr_passwd = "x"; /* XXX warning: const */
grent.gr_gid = i;
members[0] = xstrdup(name);
members[1] = (char *) 0;
grent.gr_mem = members;
*ngid = grent.gr_gid;
return ! gr_update (&grent);
}
/*
* add_user - create a new user ID
*/
static int
add_user(const char *name, const char *uid, uid_t *nuid, gid_t gid)
{
const struct passwd *pwd;
struct passwd pwent;
uid_t i;
/*
* The first guess for the UID is either the numerical UID
* that the caller provided, or the next available UID.
*/
if (uid[0] >= '0' && uid[0] <= '9') {
i = atoi (uid);
} else if (uid[0] && (pwd = pw_locate (uid))) {
i = pwd->pw_uid;
} else {
i = 100;
for (pw_rewind ();(pwd = pw_next ());)
if (pwd->pw_uid >= i)
i = pwd->pw_uid + 1;
}
/*
* I don't want to fill in the entire password structure
* members JUST YET, since there is still more data to be
* added. So, I fill in the parts that I have.
*/
pwent.pw_name = xstrdup(name);
pwent.pw_passwd = "x"; /* XXX warning: const */
#ifdef ATT_AGE
pwent.pw_age = "";
#endif
#ifdef ATT_COMMENT
pwent.pw_comment = "";
#endif
#ifdef BSD_QUOTA
pwent.pw_quota = 0;
#endif
pwent.pw_uid = i;
pwent.pw_gid = gid;
pwent.pw_gecos = ""; /* XXX warning: const */
pwent.pw_dir = ""; /* XXX warning: const */
pwent.pw_shell = ""; /* XXX warning: const */
*nuid = i;
return ! pw_update (&pwent);
}
static void
update_passwd(struct passwd *pwd, const char *passwd)
{
pwd->pw_passwd = pw_encrypt(passwd, crypt_make_salt());
#ifdef ATT_AGE
if (strlen(pwd->pw_age) == 4) {
static char newage[5];
extern char *l64a();
strcpy(newage, pwd->pw_age);
strcpy(newage + 2, l64a(time((time_t *) 0) / WEEK));
pwd->pw_age = newage;
}
#endif
}
/*
* add_passwd - add or update the encrypted password
*/
static int
add_passwd(struct passwd *pwd, const char *passwd)
{
#ifdef SHADOWPWD
const struct spwd *sp;
struct spwd spent;
#endif
/*
* In the case of regular password files, this is real
* easy - pwd points to the entry in the password file.
* Shadow files are harder since there are zillions of
* things to do ...
*/
if (!is_shadow) {
update_passwd(pwd, passwd);
return 0;
}
#ifdef SHADOWPWD
/*
* Do the first and easiest shadow file case. The user
* already exists in the shadow password file.
*/
if ((sp = spw_locate (pwd->pw_name))) {
spent = *sp;
spent.sp_pwdp = pw_encrypt(passwd, crypt_make_salt());
return ! spw_update (&spent);
}
/*
* Pick the next easiest case - the user has an encrypted
* password which isn't equal to "x". The password was set
* to "x" earlier when the entry was created, so this user
* would have to have had the password set someplace else.
*/
if (strcmp (pwd->pw_passwd, "x") != 0) {
update_passwd(pwd, passwd);
return 0;
}
/*
* Now the really hard case - I need to create an entirely
* new shadow password file entry.
*/
spent.sp_namp = pwd->pw_name;
spent.sp_pwdp = pw_encrypt(passwd, crypt_make_salt());
spent.sp_lstchg = time((time_t *) 0) / SCALE;
spent.sp_min = getdef_num("PASS_MIN_DAYS", 0);
/* 10000 is infinity this week */
spent.sp_max = getdef_num("PASS_MAX_DAYS", 10000);
spent.sp_warn = getdef_num("PASS_WARN_AGE", -1);
spent.sp_inact = -1;
spent.sp_expire = -1;
spent.sp_flag = -1;
return ! spw_update (&spent);
#endif
}
int
main(int argc, char **argv)
{
char buf[BUFSIZ];
char *fields[8];
int nfields;
char *cp;
const struct passwd *pw;
struct passwd newpw;
int errors = 0;
int line = 0;
uid_t uid;
gid_t gid;
Prog = Basename(argv[0]);
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
if (argc > 1 && argv[1][0] == '-')
usage ();
if (argc == 2) {
if (! freopen (argv[1], "r", stdin)) {
snprintf(buf, sizeof buf, "%s: %s", Prog, argv[1]);
perror (buf);
exit (1);
}
}
/*
* Lock the password files and open them for update. This will
* bring all of the entries into memory where they may be
* searched for an modified, or new entries added. The password
* file is the key - if it gets locked, assume the others can
* be locked right away.
*/
if (!pw_lock()) {
fprintf (stderr, _("%s: can't lock /etc/passwd.\n"), Prog);
exit (1);
}
#ifdef SHADOWPWD
is_shadow = spw_file_present();
if ((is_shadow && !spw_lock()) || !gr_lock())
#else
if (!gr_lock())
#endif
{
fprintf (stderr, _("%s: can't lock files, try again later\n"),
Prog);
(void) pw_unlock ();
#ifdef SHADOWPWD
if (is_shadow)
spw_unlock();
#endif
exit (1);
}
#ifdef SHADOWPWD
if (!pw_open(O_RDWR) || (is_shadow && !spw_open(O_RDWR)) || !gr_open(O_RDWR))
#else
if (!pw_open(O_RDWR) || !gr_open(O_RDWR))
#endif
{
fprintf (stderr, _("%s: can't open files\n"), Prog);
(void) pw_unlock ();
#ifdef SHADOWPWD
if (is_shadow)
spw_unlock();
#endif
(void) gr_unlock ();
exit (1);
}
/*
* Read each line. The line has the same format as a password
* file entry, except that certain fields are not contrained to
* be numerical values. If a group ID is entered which does
* not already exist, an attempt is made to allocate the same
* group ID as the numerical user ID. Should that fail, the
* next available group ID over 100 is allocated. The pw_gid
* field will be updated with that value.
*/
while (fgets (buf, sizeof buf, stdin) != (char *) 0) {
line++;
if ((cp = strrchr (buf, '\n'))) {
*cp = '\0';
} else {
fprintf (stderr, _("%s: line %d: line too long\n"),
Prog, line);
errors++;
continue;
}
/*
* Break the string into fields and screw around with
* them. There MUST be 7 colon separated fields,
* although the values aren't that particular.
*/
for (cp = buf, nfields = 0;nfields < 7;nfields++) {
fields[nfields] = cp;
if ((cp = strchr (cp, ':')))
*cp++ = '\0';
else
break;
}
if (nfields != 6) {
fprintf (stderr, _("%s: line %d: invalid line\n"),
Prog, line);
continue;
}
/*
* Now the fields are processed one by one. The first
* field to be processed is the group name. A new
* group will be created if the group name is non-numeric
* and does not already exist. The named user will be
* the only member. If there is no named group to be a
* member of, the UID will be figured out and that value
* will be a candidate for a new group, if that group ID
* exists, a whole new group ID will be made up.
*/
if (! (pw = pw_locate (fields[0])) &&
add_group (fields[0], fields[3], &gid)) {
fprintf (stderr, _("%s: line %d: can't create GID\n"),
Prog, line);
errors++;
continue;
}
/*
* Now we work on the user ID. It has to be specified
* either as a numerical value, or left blank. If it
* is a numerical value, that value will be used, otherwise
* the next available user ID is computed and used. After
* this there will at least be a (struct passwd) for the
* user.
*/
if (! pw && add_user (fields[0], fields[2], &uid, gid)) {
fprintf (stderr, _("%s: line %d: can't create UID\n"),
Prog, line);
errors++;
continue;
}
/*
* The password, gecos field, directory, and shell fields
* all come next.
*/
if (! (pw = pw_locate (fields[0]))) {
fprintf (stderr, _("%s: line %d: cannot find user %s\n"),
Prog, line, fields[0]);
errors++;
continue;
}
newpw = *pw;
if (add_passwd (&newpw, fields[1])) {
fprintf (stderr, _("%s: line %d: can't update password\n"),
Prog, line);
errors++;
continue;
}
if (fields[4][0])
newpw.pw_gecos = fields[4];
if (fields[5][0])
newpw.pw_dir = fields[5];
if (fields[6][0])
newpw.pw_shell = fields[6];
if (newpw.pw_dir[0] && access(newpw.pw_dir, F_OK)) {
if (mkdir (newpw.pw_dir,
0777 & ~getdef_num("UMASK", 077)))
fprintf (stderr, _("%s: line %d: mkdir failed\n"),
Prog, line);
else if (chown (newpw.pw_dir,
newpw.pw_uid, newpw.pw_gid))
fprintf (stderr, _("%s: line %d: chown failed\n"),
Prog, line);
}
/*
* Update the password entry with the new changes made.
*/
if (! pw_update (&newpw)) {
fprintf (stderr, _("%s: line %d: can't update entry\n"),
Prog, line);
errors++;
continue;
}
}
/*
* Any detected errors will cause the entire set of changes
* to be aborted. Unlocking the password file will cause
* all of the changes to be ignored. Otherwise the file is
* closed, causing the changes to be written out all at
* once, and then unlocked afterwards.
*/
if (errors) {
fprintf (stderr, _("%s: error detected, changes ignored\n"), Prog);
(void) gr_unlock ();
#ifdef SHADOWPWD
if (is_shadow)
spw_unlock();
#endif
(void) pw_unlock ();
exit (1);
}
#ifdef SHADOWPWD
if (!pw_close() || (is_shadow && !spw_close()) || !gr_close())
#else
if (!pw_close() || ! gr_close())
#endif
{
fprintf (stderr, _("%s: error updating files\n"), Prog);
(void) gr_unlock ();
#ifdef SHADOWPWD
if (is_shadow)
spw_unlock();
#endif
(void) pw_unlock ();
exit (1);
}
(void) gr_unlock ();
#ifdef SHADOWPWD
if (is_shadow)
spw_unlock();
#endif
(void) pw_unlock ();
exit (0);
/*NOTREACHED*/
}

1417
src/passwd.c Normal file

File diff suppressed because it is too large Load Diff

58
src/patchlevel.h Normal file
View File

@ -0,0 +1,58 @@
/*
* Copyright 1991 - 1995, Julianne Frances Haugh
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Julianne F. Haugh nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Revision History
* 11/25/91 3.1.1 patchlevel 14
* Added "login.defs" to Makefile
* 12/02/91 3.1.2 patchlevel 15
* Bugs found by users
* 12/28/91 3.1.3 patchlevel 16
* Changes for SunOS 4.1.1
* 02/08/92 3.1.4 patchlevel 17
* Changes for SVR4, plus bug fixes
* 04/03/92 3.2.1 patchlevel 18
* Minor bug fixes, new baseline
* 07/07/92 3.2.2 patchlevel 20
* Added administrator defined authentication
* 11/04/92 3.2.3 patchlevel 21
* Bug fixes for SVR4
* 07/23/93 3.3.0 patchlevel 23
* New baseline release
* 08/23/93 3.3.1 patchlevel 24
* Bug fixes for SunOS 4.1.1
* 08/27/93 3.3.2 patchlevel 25
* Initial NIS support changes
* 12/03/95 3.3.3 patchlevel 26
* This is the Linux beta baseline. Marek will
* change the name some other day. -- jfh
* $Id: patchlevel.h,v 1.2 1997/05/01 23:07:16 marekm Exp $
*/
#define RELEASE 3
#define PATCHLEVEL 26
#define VERSION "3.3.3"

617
src/pwck.c Normal file
View File

@ -0,0 +1,617 @@
/*
* Copyright 1992 - 1994, Julianne Frances Haugh
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Julianne F. Haugh nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <config.h>
#include "rcsid.h"
RCSID(PKG_VER "$Id: pwck.c,v 1.13 1999/06/07 16:40:45 marekm Exp $")
#include <stdio.h>
#include <fcntl.h>
#include <grp.h>
#include "prototypes.h"
#include "defines.h"
#include "chkname.h"
#include <pwd.h>
#include "commonio.h"
#include "pwio.h"
extern void __pw_del_entry P_((const struct commonio_entry *));
extern struct commonio_entry *__pw_get_head P_((void));
#ifdef SHADOWPWD
#include "shadowio.h"
extern void __spw_del_entry P_((const struct commonio_entry *));
extern struct commonio_entry *__spw_get_head P_((void));
#endif
/*
* Exit codes
*/
#define E_OKAY 0
#define E_USAGE 1
#define E_BADENTRY 2
#define E_CANTOPEN 3
#define E_CANTLOCK 4
#define E_CANTUPDATE 5
/*
* Global variables
*/
extern int optind;
extern char *optarg;
/*
* Local variables
*/
static char *Prog;
static const char *pwd_file = PASSWD_FILE;
#ifdef SHADOWPWD
static const char *spw_file = SHADOW_FILE;
#endif
static int read_only = 0;
static int quiet = 0; /* don't report warnings, only errors */
/* local function prototypes */
static void usage P_((void));
static int yes_or_no P_((void));
int main P_((int, char **));
/*
* usage - print syntax message and exit
*/
static void
usage(void)
{
#ifdef SHADOWPWD
fprintf(stderr, _("Usage: %s [ -qr ] [ passwd [ shadow ] ]\n"), Prog);
#else
fprintf(stderr, _("Usage: %s [ -qr ] [ passwd ]\n"), Prog);
#endif
exit(E_USAGE);
}
/*
* yes_or_no - get answer to question from the user
*/
static int
yes_or_no(void)
{
char buf[80];
/*
* In read-only mode all questions are answered "no".
*/
if (read_only) {
puts(_("No"));
return 0;
}
/*
* Get a line and see what the first character is.
*/
if (fgets(buf, sizeof buf, stdin))
return buf[0] == 'y' || buf[0] == 'Y';
return 0;
}
/*
* pwck - verify password file integrity
*/
int
main(int argc, char **argv)
{
int arg;
int errors = 0;
int deleted = 0;
struct commonio_entry *pfe, *tpfe;
struct passwd *pwd;
#ifdef SHADOWPWD
struct commonio_entry *spe, *tspe;
struct spwd *spw;
int is_shadow = 0;
#endif
/*
* Get my name so that I can use it to report errors.
*/
Prog = Basename(argv[0]);
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
openlog(Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
/*
* Parse the command line arguments
*/
while ((arg = getopt(argc, argv, "eqr")) != EOF) {
switch (arg) {
case 'e': /* added for Debian shadow-961025-2 compatibility */
case 'q':
quiet = 1;
break;
case 'r':
read_only = 1;
break;
default:
usage();
}
}
/*
* Make certain we have the right number of arguments
*/
#ifdef SHADOWPWD
if (optind != argc && optind + 1 != argc && optind + 2 != argc)
#else
if (optind != argc && optind + 1 != argc)
#endif
usage();
/*
* If there are two left over filenames, use those as the
* password and shadow password filenames.
*/
if (optind != argc) {
pwd_file = argv[optind];
pw_name(pwd_file);
}
#ifdef SHADOWPWD
if (optind + 2 == argc) {
spw_file = argv[optind + 1];
spw_name(spw_file);
is_shadow = 1;
} else if (optind == argc)
is_shadow = spw_file_present();
#endif
/*
* Lock the files if we aren't in "read-only" mode
*/
if (!read_only) {
if (!pw_lock()) {
fprintf(stderr, _("%s: cannot lock file %s\n"),
Prog, pwd_file);
if (optind == argc)
SYSLOG((LOG_WARN,"cannot lock %s\n",pwd_file));
closelog();
exit(E_CANTLOCK);
}
#ifdef SHADOWPWD
if (is_shadow && !spw_lock()) {
fprintf(stderr, _("%s: cannot lock file %s\n"),
Prog, spw_file);
if (optind == argc)
SYSLOG((LOG_WARN,"cannot lock %s\n",spw_file));
closelog();
exit(E_CANTLOCK);
}
#endif
}
/*
* Open the files. Use O_RDONLY if we are in read_only mode,
* O_RDWR otherwise.
*/
if (!pw_open(read_only ? O_RDONLY:O_RDWR)) {
fprintf(stderr, _("%s: cannot open file %s\n"),
Prog, pwd_file);
if (optind == argc)
SYSLOG((LOG_WARN, "cannot open %s\n", pwd_file));
closelog();
exit(E_CANTOPEN);
}
#ifdef SHADOWPWD
if (is_shadow && !spw_open(read_only ? O_RDONLY : O_RDWR)) {
fprintf(stderr, _("%s: cannot open file %s\n"),
Prog, spw_file);
if (optind == argc)
SYSLOG((LOG_WARN, "cannot open %s\n", spw_file));
closelog();
exit(E_CANTOPEN);
}
#endif
/*
* Loop through the entire password file.
*/
for (pfe = __pw_get_head(); pfe; pfe = pfe->next) {
/*
* If this is a NIS line, skip it. You can't "know" what
* NIS is going to do without directly asking NIS ...
*/
if (pfe->line[0] == '+' || pfe->line[0] == '-')
continue;
/*
* Start with the entries that are completely corrupt.
* They have no (struct passwd) entry because they couldn't
* be parsed properly.
*/
if (!pfe->entry) {
/*
* Tell the user this entire line is bogus and
* ask them to delete it.
*/
printf(_("invalid password file entry\n"));
printf(_("delete line `%s'? "), pfe->line);
errors++;
/*
* prompt the user to delete the entry or not
*/
if (!yes_or_no())
continue;
/*
* All password file deletions wind up here. This
* code removes the current entry from the linked
* list. When done, it skips back to the top of
* the loop to try out the next list element.
*/
delete_pw:
SYSLOG((LOG_INFO, "delete passwd line `%s'\n",
pfe->line));
deleted++;
__pw_del_entry(pfe);
continue;
}
/*
* Password structure is good, start using it.
*/
pwd = pfe->entry;
/*
* Make sure this entry has a unique name.
*/
for (tpfe = __pw_get_head(); tpfe; tpfe = tpfe->next) {
const struct passwd *ent = tpfe->entry;
/*
* Don't check this entry
*/
if (tpfe == pfe)
continue;
/*
* Don't check invalid entries.
*/
if (!ent)
continue;
if (strcmp(pwd->pw_name, ent->pw_name) != 0)
continue;
/*
* Tell the user this entry is a duplicate of
* another and ask them to delete it.
*/
puts(_("duplicate password entry\n"));
printf(_("delete line `%s'? "), pfe->line);
errors++;
/*
* prompt the user to delete the entry or not
*/
if (yes_or_no())
goto delete_pw;
}
/*
* Check for invalid usernames. --marekm
*/
if (!check_user_name(pwd->pw_name)) {
printf(_("invalid user name `%s'\n"), pwd->pw_name);
errors++;
}
/*
* Check for a Slackware bug. Make sure UID is not -1
* (it has special meaning for some syscalls). --marekm
*/
if (pwd->pw_uid == (uid_t) -1) {
printf(_("user %s: bad UID (%d)\n"),
pwd->pw_name, (int) pwd->pw_uid);
errors++;
}
/*
* Make sure the primary group exists
*/
if (!quiet && !getgrgid(pwd->pw_gid)) {
/*
* No primary group, just give a warning
*/
printf(_("user %s: no group %d\n"),
pwd->pw_name, (int) pwd->pw_gid);
errors++;
}
/*
* Make sure the home directory exists
*/
if (!quiet && access(pwd->pw_dir, F_OK)) {
/*
* Home directory doesn't exist, give a warning
*/
printf(_("user %s: directory %s does not exist\n"),
pwd->pw_name, pwd->pw_dir);
errors++;
}
/*
* Make sure the login shell is executable
*/
if (!quiet && pwd->pw_shell[0] && access(pwd->pw_shell, F_OK)) {
/*
* Login shell doesn't exist, give a warning
*/
printf(_("user %s: program %s does not exist\n"),
pwd->pw_name, pwd->pw_shell);
errors++;
}
}
#ifdef SHADOWPWD
if (!is_shadow)
goto shadow_done;
/*
* Loop through the entire shadow password file.
*/
for (spe = __spw_get_head(); spe; spe = spe->next) {
/*
* If this is a NIS line, skip it. You can't "know" what
* NIS is going to do without directly asking NIS ...
*/
if (spe->line[0] == '+' || spe->line[0] == '-')
continue;
/*
* Start with the entries that are completely corrupt.
* They have no (struct spwd) entry because they couldn't
* be parsed properly.
*/
if (!spe->entry) {
/*
* Tell the user this entire line is bogus and
* ask them to delete it.
*/
printf(_("invalid shadow password file entry\n"));
printf(_("delete line `%s'? "), spe->line);
errors++;
/*
* prompt the user to delete the entry or not
*/
if (!yes_or_no())
continue;
/*
* All shadow file deletions wind up here. This
* code removes the current entry from the linked
* list. When done, it skips back to the top of
* the loop to try out the next list element.
*/
delete_spw:
SYSLOG((LOG_INFO, "delete shadow line `%s'\n",
spe->line));
deleted++;
__spw_del_entry(spe);
continue;
}
/*
* Shadow password structure is good, start using it.
*/
spw = spe->entry;
/*
* Make sure this entry has a unique name.
*/
for (tspe = __spw_get_head(); tspe; tspe = tspe->next) {
const struct spwd *ent = tspe->entry;
/*
* Don't check this entry
*/
if (tspe == spe)
continue;
/*
* Don't check invalid entries.
*/
if (!ent)
continue;
if (strcmp(spw->sp_namp, ent->sp_namp) != 0)
continue;
/*
* Tell the user this entry is a duplicate of
* another and ask them to delete it.
*/
puts(_("duplicate shadow password entry\n"));
printf(_("delete line `%s'? "), spe->line);
errors++;
/*
* prompt the user to delete the entry or not
*/
if (yes_or_no())
goto delete_spw;
}
/*
* Make sure this entry exists in the /etc/passwd
* file.
*/
if (!pw_locate(spw->sp_namp)) {
/*
* Tell the user this entry has no matching
* /etc/passwd entry and ask them to delete it.
*/
puts(_("no matching password file entry\n"));
printf(_("delete line `%s'? "), spe->line);
errors++;
/*
* prompt the user to delete the entry or not
*/
if (yes_or_no())
goto delete_spw;
}
/*
* Warn if last password change in the future. --marekm
*/
if (!quiet && spw->sp_lstchg > time((time_t *)0) / SCALE) {
printf(_("user %s: last password change in the future\n"), spw->sp_namp);
errors++;
}
}
shadow_done:
#endif
/*
* All done. If there were no deletions we can just abandon any
* changes to the files.
*/
if (deleted) {
if (!pw_close()) {
fprintf(stderr, _("%s: cannot update file %s\n"),
Prog, pwd_file);
SYSLOG((LOG_WARN, "cannot update %s\n", pwd_file));
closelog();
exit(E_CANTUPDATE);
}
#ifdef SHADOWPWD
if (is_shadow && !spw_close()) {
fprintf(stderr, _("%s: cannot update file %s\n"),
Prog, spw_file);
SYSLOG((LOG_WARN, "cannot update %s\n", spw_file));
closelog();
exit(E_CANTUPDATE);
}
#endif
}
/*
* Don't be anti-social - unlock the files when you're done.
*/
#ifdef SHADOWPWD
if (is_shadow)
spw_unlock();
#endif
(void) pw_unlock();
/*
* Tell the user what we did and exit.
*/
if (errors)
#ifdef NDBM
printf(deleted ?
_("%s: the files have been updated; run mkpasswd\n") :
_("%s: no changes\n"), Prog);
#else
printf(deleted ?
_("%s: the files have been updated\n") :
_("%s: no changes\n"), Prog);
#endif
closelog();
exit(errors ? E_BADENTRY : E_OKAY);
}

188
src/pwconv.c Normal file
View File

@ -0,0 +1,188 @@
/*
* pwconv - create or update /etc/shadow with information from
* /etc/passwd.
*
* It is more like SysV pwconv, slightly different from the
* original Shadow pwconv. Depends on "x" as password in
* /etc/passwd which means that the password has already been
* moved to /etc/shadow. There is no need to move /etc/npasswd
* to /etc/passwd, password files are updated using library
* routines with proper locking.
*
* Can be used to update /etc/shadow after adding/deleting users
* by editing /etc/passwd. There is no man page yet, but this
* program should be close to pwconv(1M) on Solaris 2.x.
*
* Warning: make sure that all users have "x" as the password in
* /etc/passwd before running this program for the first time on
* a system which already has shadow passwords. Anything else
* (like "*" from old versions of the shadow suite) will replace
* the user's encrypted password in /etc/shadow.
*
* Doesn't currently support pw_age information in /etc/passwd,
* and doesn't support DBM files. Add it if you need it...
*
* Copyright (C) 1996-1997, Marek Michalkiewicz
* <marekm@i17linuxb.ists.pwr.wroc.pl>
* This program may be freely used and distributed for any purposes.
* If you improve it, please send me your changes. Thanks!
*/
#include <config.h>
#include "rcsid.h"
RCSID(PKG_VER "$Id: pwconv.c,v 1.10 1999/06/07 16:40:45 marekm Exp $")
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <errno.h>
#include <pwd.h>
#include "prototypes.h"
#include "defines.h"
#include "pwio.h"
#include "shadowio.h"
#include "getdef.h"
/*
* exit status values
*/
#define E_SUCCESS 0 /* success */
#define E_NOPERM 1 /* permission denied */
#define E_USAGE 2 /* bad command syntax */
#define E_FAILURE 3 /* unexpected failure, nothing done */
#define E_MISSING 4 /* unexpected failure, passwd file missing */
#define E_PWDBUSY 5 /* passwd file(s) busy */
#define E_BADENTRY 6 /* bad shadow entry */
static int
shadow_locked = 0,
passwd_locked = 0;
/* local function prototypes */
static void fail_exit P_((int));
int main P_((int, char **));
static void
fail_exit(int status)
{
if (shadow_locked)
spw_unlock();
if (passwd_locked)
pw_unlock();
exit(status);
}
int
main(int argc, char **argv)
{
const struct passwd *pw;
struct passwd pwent;
const struct spwd *sp;
struct spwd spent;
char *Prog = argv[0];
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
if (!pw_lock()) {
fprintf(stderr, _("%s: can't lock passwd file\n"), Prog);
fail_exit(E_PWDBUSY);
}
passwd_locked++;
if (!pw_open(O_RDWR)) {
fprintf(stderr, _("%s: can't open passwd file\n"), Prog);
fail_exit(E_MISSING);
}
if (!spw_lock()) {
fprintf(stderr, _("%s: can't lock shadow file\n"), Prog);
fail_exit(E_PWDBUSY);
}
shadow_locked++;
if (!spw_open(O_CREAT | O_RDWR)) {
fprintf(stderr, _("%s: can't open shadow file\n"), Prog);
fail_exit(E_FAILURE);
}
/*
* Remove /etc/shadow entries for users not in /etc/passwd.
*/
spw_rewind();
while ((sp = spw_next())) {
if (pw_locate(sp->sp_namp))
continue;
if (!spw_remove(sp->sp_namp)) {
/*
* This shouldn't happen (the entry exists) but...
*/
fprintf(stderr,
_("%s: can't remove shadow entry for %s\n"),
Prog, sp->sp_namp);
fail_exit(E_FAILURE);
}
}
/*
* Update shadow entries which don't have "x" as pw_passwd.
* Add any missing shadow entries.
*/
pw_rewind();
while ((pw = pw_next())) {
sp = spw_locate(pw->pw_name);
if (sp) {
/* do we need to update this entry? */
if (strcmp(pw->pw_passwd, SHADOW_PASSWD_STRING) == 0)
continue;
/* update existing shadow entry */
spent = *sp;
} else {
/* add new shadow entry */
memset(&spent, 0, sizeof spent);
spent.sp_namp = pw->pw_name;
spent.sp_min = getdef_num("PASS_MIN_DAYS", -1);
spent.sp_max = getdef_num("PASS_MAX_DAYS", -1);
spent.sp_warn = getdef_num("PASS_WARN_AGE", -1);
spent.sp_inact = -1;
spent.sp_expire = -1;
spent.sp_flag = -1;
}
spent.sp_pwdp = pw->pw_passwd;
spent.sp_lstchg = time((time_t *) 0) / (24L*3600L);
if (!spw_update(&spent)) {
fprintf(stderr,
_("%s: can't update shadow entry for %s\n"),
Prog, spent.sp_namp);
fail_exit(E_FAILURE);
}
/* remove password from /etc/passwd */
pwent = *pw;
pwent.pw_passwd = SHADOW_PASSWD_STRING; /* XXX warning: const */
if (!pw_update(&pwent)) {
fprintf(stderr,
_("%s: can't update passwd entry for %s\n"),
Prog, pwent.pw_name);
fail_exit(E_FAILURE);
}
}
if (!spw_close()) {
fprintf(stderr, _("%s: can't update shadow file\n"), Prog);
fail_exit(E_FAILURE);
}
if (!pw_close()) {
fprintf(stderr, _("%s: can't update passwd file\n"), Prog);
fail_exit(E_FAILURE);
}
chmod(PASSWD_FILE "-", 0600); /* /etc/passwd- (backup file) */
spw_unlock();
pw_unlock();
exit(E_SUCCESS);
}

197
src/pwunconv.c Normal file
View File

@ -0,0 +1,197 @@
/*
* Copyright 1989 - 1994, Julianne Frances Haugh
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Julianne F. Haugh nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* pwunconv - restore old password file from shadow password file.
*
* Pwunconv copies the password file information from the shadow
* password file, merging entries from an optional existing shadow
* file.
*
* Modifed by Guy Maor <maor@debian.org> to acquire necessary locks
* and modify the files in place.
*/
#include <config.h>
#include "rcsid.h"
RCSID(PKG_VER "$Id: pwunconv.c,v 1.8 1999/06/07 16:40:45 marekm Exp $")
#include "defines.h"
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
#include <pwd.h>
#include <unistd.h>
#include "prototypes.h"
#include "pwio.h"
#include "shadowio.h"
#ifndef SHADOWPWD
int
main(int argc, char **argv)
{
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
fprintf(stderr, _("%s: Shadow passwords are not configured.\n"),
argv[0]);
exit(1);
}
#else /*{*/
char *l64a ();
static int shadow_locked = 0,
passwd_locked = 0;
/* local function prototypes */
static void fail_exit P_((int));
int main P_((int, char **));
static void
fail_exit(int status)
{
if (shadow_locked)
spw_unlock();
if (passwd_locked)
pw_unlock();
exit(status);
}
int
main(int argc, char **argv)
{
const struct passwd *pw;
struct passwd pwent;
const struct spwd *spwd;
#ifdef ATT_AGE
char newage[5];
#endif
char *Prog = argv[0];
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
if (!spw_file_present())
/* shadow not installed, do nothing */
exit(0);
if (!pw_lock()) {
fprintf(stderr, _("%s: can't lock passwd file\n"), Prog);
fail_exit(5);
}
passwd_locked++;
if (!pw_open(O_RDWR)) {
fprintf(stderr, _("%s: can't open passwd file\n"), Prog);
fail_exit(1);
}
if (!spw_lock()) {
fprintf(stderr, _("%s: can't open shadow file\n"), Prog);
fail_exit(5);
}
shadow_locked++;
if (!spw_open(O_RDWR)) {
fprintf(stderr, _("%s: can't open shadow file\n"), Prog);
fail_exit(1);
}
pw_rewind();
while ((pw = pw_next())) {
if (!(spwd = spw_locate(pw->pw_name)))
continue;
pwent = *pw;
/*
* Update password if non-shadow is "x".
*/
if (strcmp(pw->pw_passwd, SHADOW_PASSWD_STRING) == 0)
pwent.pw_passwd = spwd->sp_pwdp;
/*
* Password aging works differently in the two different systems.
* With shadow password files you apparently must have some aging
* information. The maxweeks or minweeks may not map exactly.
* In pwconv we set max == 10000, which is about 30 years. Here
* we have to undo that kludge. So, if maxdays == 10000, no aging
* information is put into the new file. Otherwise, the days are
* converted to weeks and so on.
*/
#ifdef ATT_AGE
if (spwd->sp_max > (63*WEEK/SCALE) && spwd->sp_max < 10000)
spwd->sp_max = (63*WEEK/SCALE); /* 10000 is infinity */
if (spwd->sp_min >= 0 && spwd->sp_min <= 63*7 &&
spwd->sp_max >= 0 && spwd->sp_max <= 63*7) {
if (spwd->sp_lstchg == -1)
spwd->sp_lstchg = 0;
spwd->sp_max /= WEEK/SCALE; /* turn it into weeks */
spwd->sp_min /= WEEK/SCALE;
spwd->sp_lstchg /= WEEK/SCALE;
strncpy (newage, l64a (spwd->sp_lstchg * (64L*64L) +
spwd->sp_min * (64L) + spwd->sp_max), 5);
pwent.pw_age = newage;
} else
pwent.pw_age = "";
#endif /* ATT_AGE */
if (!pw_update(&pwent)) {
fprintf(stderr,
_("%s: can't update entry for user %s\n"),
Prog, pwent.pw_name);
fail_exit(3);
}
}
if (!spw_close()) {
fprintf(stderr, _("%s: can't update shadow password file\n"), Prog);
fail_exit(3);
}
if (!pw_close()) {
fprintf(stderr, _("%s: can't update password file\n"), Prog);
fail_exit(3);
}
if (unlink(SHADOW) != 0) {
fprintf(stderr, _("%s: can't delete shadow password file\n"), Prog);
fail_exit(3);
}
spw_unlock();
pw_unlock();
return 0;
}
#endif

67
src/shadowconfig.sh Executable file
View File

@ -0,0 +1,67 @@
#!/bin/bash
# turn shadow passwords on or off on a Debian system
set -e
permfix () {
[ -f $1 ] || return 0
chown root:shadow $1
chmod 2755 $1
}
export -f permfix
shadowon () {
bash<<- EOF
set -e
permfix /usr/X11R6/bin/xlock
permfix /usr/X11R6/bin/xtrlock
permfix /bin/vlock
pwck -q
grpck
pwconv
grpconv
cd /etc
chown root:root passwd group
chmod 644 passwd group
chown root:shadow shadow gshadow
chmod 640 shadow gshadow
EOF
}
shadowoff () {
bash<<- EOF
set -e
pwck -q
grpck
pwunconv
grpunconv
cd /etc
# sometimes the passwd perms get munged
chown root:root passwd group
chmod 644 passwd group
EOF
}
case "$1" in
"on")
if shadowon ; then
echo Shadow passwords are now on.
else
echo Please correct the error and rerun \`$0 on\'
exit 1
fi
;;
"off")
if shadowoff ; then
echo Shadow passwords are now off.
else
echo Please correct the error and rerun \`$0 off\'
exit 1
fi
;;
*)
echo Usage: $0 on \| off
;;
esac

633
src/su.c Normal file
View File

@ -0,0 +1,633 @@
/*
* Copyright 1989 - 1994, Julianne Frances Haugh
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Julianne F. Haugh nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <config.h>
#include "rcsid.h"
RCSID(PKG_VER "$Id: su.c,v 1.13 1999/06/07 16:40:45 marekm Exp $")
#include <sys/types.h>
#include <stdio.h>
#ifdef USE_PAM
#include "pam_defs.h"
static const struct pam_conv conv = {
misc_conv,
NULL
};
static pam_handle_t *pamh = NULL;
#endif
#include "prototypes.h"
#include "defines.h"
#include <grp.h>
#include <signal.h>
#include <pwd.h>
#include "pwauth.h"
#include "getdef.h"
/*
* Assorted #defines to control su's behavior
*/
/*
* Global variables
*/
/* needed by sulog.c */
char name[BUFSIZ];
char oldname[BUFSIZ];
static char *Prog;
struct passwd pwent;
/*
* External identifiers
*/
extern char **newenvp;
extern size_t newenvc;
extern void sulog P_((const char *, int));
extern void subsystem P_((const struct passwd *));
extern char *tz P_((const char *));
extern int check_su_auth P_((const char *, const char *));
extern char **environ;
/* local function prototypes */
int main P_((int, char **));
#ifndef USE_PAM
static RETSIGTYPE die P_((int));
static int iswheel P_((const char *));
/*
* die - set or reset termio modes.
*
* die() is called before processing begins. signal() is then
* called with die() as the signal handler. If signal later
* calls die() with a signal number, the terminal modes are
* then reset.
*/
static RETSIGTYPE
die(int killed)
{
static TERMIO sgtty;
if (killed)
STTY(0, &sgtty);
else
GTTY(0, &sgtty);
if (killed) {
closelog();
exit(killed);
}
}
static int
iswheel(const char *username)
{
struct group *grp;
grp = getgrgid(0);
if (!grp || !grp->gr_mem)
return 0;
return is_on_list(grp->gr_mem, username);
}
#endif /* !USE_PAM */
static void
su_failure(const char *tty)
{
sulog(tty, 0); /* log failed attempt */
#ifdef USE_SYSLOG
if (getdef_bool("SYSLOG_SU_ENAB"))
SYSLOG((pwent.pw_uid ? LOG_INFO:LOG_NOTICE,
"- %s %s-%s\n", tty,
oldname[0] ? oldname:"???",
name[0] ? name:"???"));
closelog();
#endif
puts(_("Sorry."));
exit(1);
}
/*
* su - switch user id
*
* su changes the user's ids to the values for the specified user.
* if no new user name is specified, "root" is used by default.
*
* The only valid option is a "-" character, which is interpreted
* as requiring a new login session to be simulated.
*
* Any additional arguments are passed to the user's shell. In
* particular, the argument "-c" will cause the next argument to
* be interpreted as a command by the common shell programs.
*/
int
main(int argc, char **argv)
{
char *cp;
const char *tty = 0; /* Name of tty SU is run from */
int doshell = 0;
int fakelogin = 0;
int amroot = 0;
uid_t my_uid;
struct passwd *pw = 0;
char **envp = environ;
#ifdef USE_PAM
int ret;
#else /* !USE_PAM */
RETSIGTYPE (*oldsig)();
int is_console = 0;
#ifdef SHADOWPWD
struct spwd *spwd = 0;
#endif
#ifdef SU_ACCESS
char *oldpass;
#endif
#endif /* !USE_PAM */
sanitize_env();
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
/*
* Get the program name. The program name is used as a
* prefix to most error messages.
*/
Prog = Basename(argv[0]);
openlog("su", LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
initenv();
my_uid = getuid();
amroot = (my_uid == 0);
/*
* Get the tty name. Entries will be logged indicating that
* the user tried to change to the named new user from the
* current terminal.
*/
if (isatty(0) && (cp = ttyname(0))) {
if (strncmp (cp, "/dev/", 5) == 0)
tty = cp + 5;
else
tty = cp;
#ifndef USE_PAM
is_console = console(tty);
#endif
} else {
/*
* Be more paranoid, like su from SimplePAMApps. --marekm
*/
if (!amroot) {
fprintf(stderr, _("%s: must be run from a terminal\n"),
Prog);
exit(1);
}
tty = "???";
}
/*
* Process the command line arguments.
*/
argc--; argv++; /* shift out command name */
if (argc > 0 && strcmp(argv[0], "-") == 0) {
fakelogin = 1;
argc--; argv++; /* shift ... */
}
/*
* If a new login is being set up, the old environment will
* be ignored and a new one created later on.
*/
if (! fakelogin)
while (*envp)
addenv(*envp++, NULL);
if (fakelogin && (cp=getdef_str("ENV_TZ")))
addenv(*cp == '/' ? tz(cp) : cp, NULL);
/*
* The clock frequency will be reset to the login value if required
*/
if (fakelogin && (cp=getdef_str("ENV_HZ")) )
addenv(cp, NULL); /* set the default $HZ, if one */
/*
* The terminal type will be left alone if it is present in the
* environment already.
*/
if (fakelogin && (cp = getenv ("TERM")))
addenv("TERM", cp);
/*
* The next argument must be either a user ID, or some flag to
* a subshell. Pretty sticky since you can't have an argument
* which doesn't start with a "-" unless you specify the new user
* name. Any remaining arguments will be passed to the user's
* login shell.
*/
if (argc > 0 && argv[0][0] != '-') {
STRFCPY(name, argv[0]); /* use this login id */
argc--; argv++; /* shift ... */
}
if (! name[0]) /* use default user ID */
(void) strcpy (name, "root");
doshell = argc == 0; /* any arguments remaining? */
/*
* Get the user's real name. The current UID is used to determine
* who has executed su. That user ID must exist.
*/
pw = get_my_pwent();
if (!pw) {
SYSLOG((LOG_CRIT, "Unknown UID: %d\n", (int) my_uid));
su_failure(tty);
}
STRFCPY(oldname, pw->pw_name);
#ifndef USE_PAM
#ifdef SU_ACCESS
/*
* Sort out the password of user calling su, in case needed later
* -- chris
*/
#ifdef SHADOWPWD
if ((spwd = getspnam(oldname)))
pw->pw_passwd = spwd->sp_pwdp;
#endif
oldpass = xstrdup(pw->pw_passwd);
#endif /* SU_ACCESS */
#endif /* !USE_PAM */
#ifdef USE_PAM
ret = pam_start("su", name, &conv, &pamh);
if (ret != PAM_SUCCESS) {
SYSLOG((LOG_ERR, "pam_start: error %d\n", ret);
fprintf(stderr, _("%s: pam_start: error %d\n"), Prog, ret));
exit(1);
}
ret = pam_set_item(pamh, PAM_TTY, (const void *) tty);
if (ret == PAM_SUCCESS)
ret = pam_set_item(pamh, PAM_RUSER, (const void *) oldname);
if (ret != PAM_SUCCESS) {
SYSLOG((LOG_ERR, "pam_set_item: %s\n", PAM_STRERROR(pamh, ret)));
fprintf(stderr, "%s: %s\n", Prog, PAM_STRERROR(pamh, ret));
pam_end(pamh, ret);
exit(1);
}
#endif /* USE_PAM */
top:
/*
* This is the common point for validating a user whose name
* is known. It will be reached either by normal processing,
* or if the user is to be logged into a subsystem root.
*
* The password file entries for the user is gotten and the
* account validated.
*/
if (!(pw = getpwnam(name))) {
(void) fprintf (stderr, _("Unknown id: %s\n"), name);
closelog();
exit(1);
}
#ifndef USE_PAM
#ifdef SHADOWPWD
spwd = NULL;
if (strcmp(pw->pw_passwd, SHADOW_PASSWD_STRING) == 0 && (spwd = getspnam(name)))
pw->pw_passwd = spwd->sp_pwdp;
#endif
#endif /* !USE_PAM */
pwent = *pw;
#ifndef USE_PAM
/*
* BSD systems only allow "wheel" to SU to root. USG systems
* don't, so we make this a configurable option.
*/
/* The original Shadow 3.3.2 did this differently. Do it like BSD:
- check for uid 0 instead of name "root" - there are systems
with several root accounts under different names,
- check the contents of /etc/group instead of the current group
set (you must be listed as a member, GID 0 is not sufficient).
In addition to this traditional feature, we now have complete
su access control (allow, deny, no password, own password).
Thanks to Chris Evans <lady0110@sable.ox.ac.uk>. */
if (!amroot) {
if (pwent.pw_uid == 0 && getdef_bool("SU_WHEEL_ONLY")
&& !iswheel(oldname)) {
fprintf(stderr, _("You are not authorized to su %s\n"), name);
exit(1);
}
#ifdef SU_ACCESS
switch (check_su_auth(oldname, name)) {
case 0: /* normal su, require target user's password */
break;
case 1: /* require no password */
pwent.pw_passwd = ""; /* XXX warning: const */
break;
case 2: /* require own password */
puts(_("(Enter your own password.)"));
pwent.pw_passwd = oldpass;
break;
default: /* access denied (-1) or unexpected value */
fprintf(stderr, _("You are not authorized to su %s\n"), name);
exit(1);
}
#endif /* SU_ACCESS */
}
#endif /* !USE_PAM */
/*
* Set the default shell.
*/
#if 0
/*
* XXX - GNU and *BSD versions of su support the -m option.
* Need to add some option parsing code.
*/
if (mflg) {
if (!amroot && !check_shell(pwent.pw_shell)) {
fprintf(stderr, _("%s: permission denied (shell).\n"), Prog);
exit(1);
}
if ((cp = getenv("SHELL")))
pwent.pw_shell = cp;
}
#endif
if (pwent.pw_shell[0] == '\0')
pwent.pw_shell = "/bin/sh"; /* XXX warning: const */
#ifdef USE_PAM
ret = pam_authenticate(pamh, 0);
if (ret != PAM_SUCCESS) {
SYSLOG((LOG_ERR, "pam_authenticate: %s\n",
PAM_STRERROR(pamh, ret)));
fprintf(stderr, "%s: %s\n", Prog, PAM_STRERROR(pamh, ret));
pam_end(pamh, ret);
su_failure(tty);
}
ret = pam_acct_mgmt(pamh, 0);
if (ret != PAM_SUCCESS) {
if (amroot) {
fprintf(stderr, _("%s: %s\n(Ignored)\n"), Prog, PAM_STRERROR(pamh, ret));
} else {
SYSLOG((LOG_ERR, "pam_acct_mgmt: %s\n",
PAM_STRERROR(pamh, ret)));
fprintf(stderr, "%s: %s\n", Prog, PAM_STRERROR(pamh, ret));
pam_end(pamh, ret);
su_failure(tty);
}
}
#else /* !USE_PAM */
/*
* Set up a signal handler in case the user types QUIT.
*/
die (0);
oldsig = signal (SIGQUIT, die);
/*
* See if the system defined authentication method is being used.
* The first character of an administrator defined method is an
* '@' character.
*/
if (! amroot && pw_auth (pwent.pw_passwd, name, PW_SU, (char *) 0)) {
SYSLOG((pwent.pw_uid ? LOG_NOTICE:LOG_WARN,
"Authentication failed for %s\n", name));
su_failure(tty);
}
signal (SIGQUIT, oldsig);
/*
* Check to see if the account is expired. root gets to
* ignore any expired accounts, but normal users can't become
* a user with an expired password.
*/
if (! amroot) {
#ifdef SHADOWPWD
if (!spwd)
spwd = pwd_to_spwd(&pwent);
if (isexpired(&pwent, spwd)) {
SYSLOG((pwent.pw_uid ? LOG_WARN : LOG_CRIT,
"Expired account %s\n", name));
su_failure(tty);
}
#else
#if defined(ATT_AGE) && defined(AGING)
else if (pwent.pw_age[0] &&
isexpired (&pwent)) {
SYSLOG((pwent.pw_uid ? LOG_WARN:LOG_CRIT,
"Expired account %s\n", name));
su_failure(tty);
}
#endif /* ATT_AGE */
#endif
}
/*
* Check to see if the account permits "su". root gets to
* ignore any restricted accounts, but normal users can't become
* a user if there is a "SU" entry in the /etc/porttime file
* denying access to the account.
*/
if (! amroot) {
if (! isttytime (pwent.pw_name, "SU", time ((time_t *) 0))) {
SYSLOG((pwent.pw_uid ? LOG_WARN : LOG_CRIT,
"SU by %s to restricted account %s\n",
oldname, name));
su_failure(tty);
}
}
#endif /* !USE_PAM */
cp = getdef_str(pwent.pw_uid == 0 ? "ENV_SUPATH" : "ENV_PATH");
addenv(cp ? cp : "PATH=/bin:/usr/bin", NULL);
environ = newenvp; /* make new environment active */
if (getenv ("IFS")) /* don't export user IFS ... */
addenv("IFS= \t\n", NULL); /* ... instead, set a safe IFS */
if (pwent.pw_shell[0] == '*') { /* subsystem root required */
subsystem (&pwent); /* figure out what to execute */
endpwent ();
#ifdef SHADOWPWD
endspent ();
#endif
goto top;
}
sulog (tty, 1); /* save SU information */
endpwent ();
#ifdef SHADOWPWD
endspent ();
#endif
#ifdef USE_SYSLOG
if (getdef_bool("SYSLOG_SU_ENAB"))
SYSLOG((LOG_INFO, "+ %s %s-%s\n", tty,
oldname[0] ? oldname:"???", name[0] ? name:"???"));
#endif
#ifdef USE_PAM
/* set primary group id and supplementary groups */
if (setup_groups(&pwent)) {
pam_end(pamh, PAM_ABORT);
exit(1);
}
/* pam_setcred() may do things like resource limits, console groups,
and much more, depending on the configured modules */
ret = pam_setcred(pamh, PAM_ESTABLISH_CRED);
if (ret != PAM_SUCCESS) {
SYSLOG((LOG_ERR, "pam_setcred: %s\n", PAM_STRERROR(pamh, ret)));
fprintf(stderr, "%s: %s\n", Prog, PAM_STRERROR(pamh, ret));
pam_end(pamh, ret);
exit(1);
}
/* become the new user */
if (change_uid(&pwent)) {
pam_setcred(pamh, PAM_DELETE_CRED);
pam_end(pamh, PAM_ABORT);
exit(1);
}
/* now we are done using PAM */
pam_end(pamh, PAM_SUCCESS);
#else /* !USE_PAM */
if (!amroot) /* no limits if su from root */
setup_limits(&pwent);
if (setup_uid_gid(&pwent, is_console))
exit(1);
#endif /* !USE_PAM */
if (fakelogin)
setup_env(&pwent);
#if 1 /* Suggested by Joey Hess. XXX - is this right? */
else
addenv("HOME", pwent.pw_dir);
#endif
/*
* This is a workaround for Linux libc bug/feature (?) - the
* /dev/log file descriptor is open without the close-on-exec
* flag and used to be passed to the new shell. There is
* "fcntl(LogFile, F_SETFD, 1)" in libc/misc/syslog.c, but
* it is commented out (at least in 5.4.33). Why? --marekm
*/
closelog();
/*
* See if the user has extra arguments on the command line. In
* that case they will be provided to the new user's shell as
* arguments.
*/
if (! doshell) {
/*
* Use new user's shell from /etc/passwd and create an
* argv with the rest of the command line included.
*/
argv[-1] = pwent.pw_shell;
(void) execv (pwent.pw_shell, &argv[-1]);
(void) fprintf (stderr, _("No shell\n"));
SYSLOG((LOG_WARN, "Cannot execute %s\n", pwent.pw_shell));
closelog();
exit (1);
}
if (fakelogin) {
char *arg0;
#if 0 /* XXX - GNU su doesn't do this. --marekm */
if (! hushed (&pwent)) {
motd ();
mailcheck ();
}
#endif
cp = getdef_str("SU_NAME");
if (!cp)
cp = Basename(pwent.pw_shell);
arg0 = xmalloc(strlen(cp) + 2);
arg0[0] = '-';
strcpy(arg0 + 1, cp);
cp = arg0;
} else
cp = Basename(pwent.pw_shell);
shell(pwent.pw_shell, cp);
/*NOTREACHED*/
exit(1);
}

281
src/sulogin.c Normal file
View File

@ -0,0 +1,281 @@
/*
* Copyright 1989 - 1994, Julianne Frances Haugh
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Julianne F. Haugh nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <config.h>
#include "rcsid.h"
RCSID(PKG_VER "$Id: sulogin.c,v 1.9 1999/06/07 16:40:45 marekm Exp $")
#include "prototypes.h"
#include "defines.h"
#include "getdef.h"
#if HAVE_UTMPX_H
#include <utmpx.h>
#else
#include <utmp.h>
#endif
#include <signal.h>
#include <stdio.h>
#include <pwd.h>
#include <fcntl.h>
#include "pwauth.h"
static char name[BUFSIZ];
static char pass[BUFSIZ];
static struct passwd pwent;
#if 0
#if HAVE_UTMPX_H
static struct utmpx utent;
#else
static struct utmp utent;
#endif
#endif
extern char **newenvp;
extern size_t newenvc;
extern char **environ;
extern char *tz P_((const char *));
#ifndef ALARM
#define ALARM 60
#endif
/* local function prototypes */
static RETSIGTYPE catch P_((int));
int main P_((int, char **));
static RETSIGTYPE
catch(int sig)
{
exit(1);
}
/* syslogd is usually not running at the time when sulogin is typically
called, cluttering the screen with unnecessary messages. Suggested
by Ivan Nejgebauer <ian@unsux.ns.ac.yu>. --marekm */
#undef USE_SYSLOG
/*ARGSUSED*/
int
main(int argc, char **argv)
{
char *cp;
char **envp = environ;
TERMIO termio;
#ifdef USE_SGTTY
ioctl (0, TIOCGETP, &termio);
termio.sg_flags |= (ECHO|CRMOD);
termio.sg_flags &= ~(RAW|CBREAK);
ioctl (0, TIOCSETN, &termio);
#endif
#ifdef USE_TERMIO
ioctl (0, TCGETA, &termio);
termio.c_iflag |= (ICRNL|IXON);
termio.c_oflag |= (OPOST|ONLCR);
termio.c_cflag |= (CREAD);
termio.c_lflag |= (ISIG|ICANON|ECHO|ECHOE|ECHOK);
ioctl (0, TCSETAF, &termio);
#endif
#ifdef USE_TERMIOS
tcgetattr (0, &termio);
termio.c_iflag |= (ICRNL|IXON);
termio.c_oflag |= (CREAD);
termio.c_lflag |= (ECHO|ECHOE|ECHOK|ICANON|ISIG);
tcsetattr (0, TCSANOW, &termio);
#endif
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
#ifdef USE_SYSLOG
openlog ("sulogin", LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
#endif
initenv();
if (argc > 1) {
close (0);
close (1);
close (2);
if (open (argv[1], O_RDWR) >= 0) {
dup (0);
dup (0);
} else {
#ifdef USE_SYSLOG
syslog (LOG_WARN, "cannot open %s\n", argv[1]);
closelog ();
#endif
exit (1);
}
}
if (access(PASSWD_FILE, F_OK) == -1) { /* must be a password file! */
printf(_("No password file\n"));
#ifdef USE_SYSLOG
syslog(LOG_WARN, "No password file\n");
closelog ();
#endif
exit (1);
}
#if !defined(DEBUG) && defined(SULOGIN_ONLY_INIT)
if (getppid () != 1) { /* parent must be INIT */
#ifdef USE_SYSLOG
syslog (LOG_WARN, "Pid == %d, not 1\n", getppid ());
closelog ();
#endif
exit (1);
}
#endif
if (! isatty (0) || ! isatty (1) || ! isatty (2)) {
#ifdef USE_SYSLOG
closelog ();
#endif
exit (1); /* must be a terminal */
}
while (*envp) /* add inherited environment, */
addenv(*envp++, NULL); /* some variables change later */
if ((cp = getdef_str("ENV_TZ")))
addenv(*cp == '/' ? tz(cp) : cp, NULL);
if ((cp = getdef_str("ENV_HZ")))
addenv(cp, NULL); /* set the default $HZ, if one */
(void) strcpy (name, "root"); /* KLUDGE!!! */
signal (SIGALRM, catch); /* exit if the timer expires */
alarm (ALARM); /* only wait so long ... */
while (1) { /* repeatedly get login/password pairs */
entry (name, &pwent); /* get entry from password file */
if (pwent.pw_name == (char *) 0) {
/*
* Fail secure
*/
printf(_("No password entry for 'root'\n"));
#ifdef USE_SYSLOG
syslog (LOG_WARN, "No password entry for 'root'\n");
closelog ();
#endif
exit (1);
}
/*
* Here we prompt for the root password, or if no password is
* given we just exit.
*/
/* get a password for root */
cp = getpass(_("\nType control-d to proceed with normal startup,\n(or give root password for system maintenance):"));
/*
* XXX - can't enter single user mode if root password is empty.
* I think this doesn't happen very often :-). But it will work
* with standard getpass() (no NULL on EOF). --marekm
*/
if (!cp || !*cp) {
#ifdef USE_SYSLOG
syslog (LOG_INFO, "Normal startup\n");
closelog ();
#endif
puts("\n");
#ifdef TELINIT
execl (PATH_TELINIT, "telinit", RUNLEVEL, (char *) 0);
#endif
exit (0);
} else {
STRFCPY(pass, cp);
strzero(cp);
}
#ifdef AUTH_METHODS
if (pwent.pw_name && pwent.pw_passwd[0] == '@') {
if (pw_auth (pwent.pw_passwd + 1, name, PW_LOGIN, (char *) 0)) {
#ifdef USE_SYSLOG
syslog (LOG_WARN,
"Incorrect root authentication");
#endif
continue;
}
goto auth_done;
}
#endif
if (valid (pass, &pwent)) /* check encrypted passwords ... */
break; /* ... encrypted passwords matched */
#ifdef USE_SYSLOG
syslog (LOG_WARN, "Incorrect root password\n");
#endif
sleep(2);
puts (_("Login incorrect"));
}
#ifdef AUTH_METHODS
auth_done:
#endif
strzero(pass);
alarm (0);
signal (SIGALRM, SIG_DFL);
environ = newenvp; /* make new environment active */
puts(_("Entering System Maintenance Mode\n"));
#ifdef USE_SYSLOG
syslog (LOG_INFO, "System Maintenance Mode\n");
#endif
#if 0 /* do we need all this? we are logging in as root anyway... --marekm */
/*
* Normally there would be a utmp entry for login to mung on
* to get the tty name, date, etc. from. We don't need all that
* stuff because we won't update the utmp or wtmp files. BUT!,
* we do need the tty name so we can set the permissions and
* ownership.
*/
if ((cp = ttyname (0))) { /* found entry in /dev/ */
if (strncmp(cp, "/dev/", 5) == 0)
cp += 5;
strncpy(utent.ut_line, cp, sizeof utent.ut_line);
}
if (getenv ("IFS")) /* don't export user IFS ... */
addenv("IFS= \t\n", NULL); /* ... instead, set a safe IFS */
setup (&pwent, 0); /* set UID, GID, HOME, etc ... */
#endif
#ifdef USE_SYSLOG
closelog ();
#endif
shell (pwent.pw_shell, (char *) 0); /* exec the shell finally. */
/*NOTREACHED*/
return (0);
}

1746
src/useradd.c Normal file

File diff suppressed because it is too large Load Diff

816
src/userdel.c Normal file
View File

@ -0,0 +1,816 @@
/*
* Copyright 1991 - 1994, Julianne Frances Haugh
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Julianne F. Haugh nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <config.h>
#include "rcsid.h"
RCSID(PKG_VER "$Id: userdel.c,v 1.15 1999/06/07 16:40:45 marekm Exp $")
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>
#include <ctype.h>
#include <fcntl.h>
#include <utmp.h>
#include "prototypes.h"
#include "defines.h"
#include "getdef.h"
#include "pwauth.h"
/*
* exit status values
*/
#define E_SUCCESS 0
#define E_PW_UPDATE 1 /* can't update password file */
#define E_USAGE 2 /* bad command syntax */
#define E_NOTFOUND 6 /* specified user doesn't exist */
#define E_USER_BUSY 8 /* user currently logged in */
#define E_GRP_UPDATE 10 /* can't update group file */
#define E_HOMEDIR 12 /* can't remove home directory */
static char *user_name;
static uid_t user_id;
static char *user_home;
static char *Prog;
static int fflg = 0, rflg = 0;
#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
#include "groupio.h"
#include "pwio.h"
#ifdef SHADOWPWD
#include "shadowio.h"
#endif
#ifdef HAVE_TCFS
#include <tcfslib.h>
#include "tcfsio.h"
#endif
#ifdef SHADOWGRP
#include "sgroupio.h"
#endif
#ifdef SHADOWPWD
static int is_shadow_pwd;
#endif
#ifdef SHADOWGRP
static int is_shadow_grp;
#endif
extern int optind;
/* local function prototypes */
static void usage P_((void));
static void update_groups P_((void));
static void close_files P_((void));
static void fail_exit P_((int));
static void open_files P_((void));
static void update_user P_((void));
static void user_busy P_((const char *, uid_t));
static void user_cancel P_((const char *));
#ifdef EXTRA_CHECK_HOME_DIR
static int path_prefix P_((const char *, const char *));
#endif
static int is_owner P_((uid_t, const char *));
#ifndef NO_REMOVE_MAILBOX
static void remove_mailbox P_((void));
#endif
int main P_((int, char **));
/*
* usage - display usage message and exit
*/
static void
usage(void)
{
fprintf(stderr, _("usage: %s [-r] name\n"), Prog);
exit(E_USAGE);
}
/*
* 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.
*/
static void
update_groups(void)
{
const struct group *grp;
struct group *ngrp;
#ifdef SHADOWGRP
const struct sgrp *sgrp;
struct sgrp *nsgrp;
#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.
*/
if (!is_on_list(grp->gr_mem, user_name))
continue;
/*
* Delete the username from the list of group members and
* update the group entry to reflect the change.
*/
ngrp = __gr_dup(grp);
if (!ngrp) {
exit(13); /* XXX */
}
ngrp->gr_mem = del_list (ngrp->gr_mem, user_name);
if (!gr_update(ngrp))
fprintf(stderr, _("%s: error updating group entry\n"),
Prog);
/*
* Update the DBM group file with the new entry as well.
*/
#ifdef NDBM
if (!gr_dbm_update(ngrp))
fprintf(stderr,
_("%s: cannot update dbm group entry\n"),
Prog);
#endif /* NDBM */
SYSLOG((LOG_INFO, "delete `%s' from group `%s'\n",
user_name, ngrp->gr_name));
}
#ifdef NDBM
endgrent ();
#endif /* NDBM */
#ifdef SHADOWGRP
if (!is_shadow_grp)
return;
/*
* 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 was_member, was_admin;
/*
* See if the user specified this group as one of their
* concurrent groups.
*/
was_member = is_on_list(sgrp->sg_mem, user_name);
was_admin = is_on_list(sgrp->sg_adm, user_name);
if (!was_member && !was_admin)
continue;
nsgrp = __sgr_dup(sgrp);
if (!nsgrp) {
exit(13); /* XXX */
}
if (was_member)
nsgrp->sg_mem = del_list (nsgrp->sg_mem, user_name);
if (was_admin)
nsgrp->sg_adm = del_list (nsgrp->sg_adm, user_name);
if (!sgr_update(nsgrp))
fprintf(stderr, _("%s: error updating group entry\n"),
Prog);
#ifdef NDBM
/*
* Update the DBM group file with the new entry as well.
*/
if (!sg_dbm_update(nsgrp))
fprintf(stderr,
_("%s: cannot update dbm group entry\n"),
Prog);
#endif /* NDBM */
SYSLOG((LOG_INFO, "delete `%s' from shadow group `%s'\n",
user_name, nsgrp->sg_name));
}
#ifdef NDBM
endsgent ();
#endif /* NDBM */
#endif /* SHADOWGRP */
}
/*
* 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.
*/
static void
close_files(void)
{
if (!pw_close())
fprintf(stderr, _("%s: cannot rewrite password file\n"), Prog);
#ifdef SHADOWPWD
if (is_shadow_pwd && !spw_close())
fprintf(stderr, _("%s: cannot rewrite shadow password file\n"),
Prog);
#endif
#ifdef HAVE_TCFS
if (!tcfs_close())
fprintf(stderr, _("%s: cannot rewrite TCFS key file\n"), Prog);
#endif
if (! gr_close ())
fprintf(stderr, _("%s: cannot rewrite group file\n"),
Prog);
(void) gr_unlock ();
#ifdef SHADOWGRP
if (is_shadow_grp && !sgr_close())
fprintf(stderr, _("%s: cannot rewrite shadow group file\n"),
Prog);
if (is_shadow_grp)
(void) sgr_unlock();
#endif
#ifdef SHADOWPWD
if (is_shadow_pwd)
(void) spw_unlock();
#endif
#ifdef HAVE_TCFS
(void) tcfs_unlock();
#endif
(void) pw_unlock();
}
/*
* fail_exit - exit with a failure code after unlocking the files
*/
static void
fail_exit(int code)
{
(void) pw_unlock ();
(void) gr_unlock ();
#ifdef SHADOWPWD
if (is_shadow_pwd)
spw_unlock ();
#endif
#ifdef SHADOWGRP
if (is_shadow_grp)
sgr_unlock ();
#endif
#ifdef HAVE_TCFS
(void) tcfs_unlock ();
#endif
exit(code);
}
/*
* open_files - lock and open the password files
*
* open_files() opens the two password files.
*/
static void
open_files(void)
{
if (!pw_lock()) {
fprintf(stderr, _("%s: unable to lock password file\n"), Prog);
exit(E_PW_UPDATE);
}
if (! pw_open (O_RDWR)) {
fprintf(stderr, _("%s: unable to open password file\n"), Prog);
fail_exit(E_PW_UPDATE);
}
#ifdef SHADOWPWD
if (is_shadow_pwd && ! spw_lock ()) {
fprintf(stderr, _("%s: cannot lock shadow password file\n"),
Prog);
fail_exit(E_PW_UPDATE);
}
if (is_shadow_pwd && ! spw_open (O_RDWR)) {
fprintf(stderr, _("%s: cannot open shadow password file\n"),
Prog);
fail_exit(E_PW_UPDATE);
}
#endif
#ifdef HAVE_TCFS
if (!tcfs_lock()) {
fprintf(stderr, _("%s: cannot lock TCFS key file\n"), Prog);
fail_exit(E_PW_UPDATE);
}
if (!tcfs_open(O_RDWR)) {
fprintf(stderr, _("%s: cannot open TCFS key file\n"), Prog);
fail_exit(E_PW_UPDATE);
}
#endif
if (! gr_lock ()) {
fprintf(stderr, _("%s: unable to lock group file\n"), Prog);
fail_exit(E_GRP_UPDATE);
}
if (! gr_open (O_RDWR)) {
fprintf(stderr, _("%s: cannot open group file\n"), Prog);
fail_exit(E_GRP_UPDATE);
}
#ifdef SHADOWGRP
if (is_shadow_grp && ! sgr_lock ()) {
fprintf(stderr, _("%s: unable to lock shadow group file\n"),
Prog);
fail_exit(E_GRP_UPDATE);
}
if (is_shadow_grp && ! sgr_open (O_RDWR)) {
fprintf(stderr, _("%s: cannot open shadow group file\n"),
Prog);
fail_exit(E_GRP_UPDATE);
}
#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.
*/
static void
update_user(void)
{
#if defined(AUTH_METHODS) || defined(NDBM)
struct passwd *pwd;
#endif
#ifdef AUTH_METHODS
#ifdef SHADOWPWD
struct spwd *spwd;
if (is_shadow_pwd && (spwd = spw_locate (user_name)) &&
spwd->sp_pwdp[0] == '@') {
if (pw_auth (spwd->sp_pwdp + 1, user_name, PW_DELETE, (char *) 0)) {
SYSLOG((LOG_ERR,
"failed deleting auth `%s' for user `%s'\n",
spwd->sp_pwdp + 1, user_name));
fprintf(stderr,
_("%s: error deleting authentication\n"),
Prog);
} else {
SYSLOG((LOG_INFO,
"delete auth `%s' for user `%s'\n",
spwd->sp_pwdp + 1, user_name));
}
}
#endif /* SHADOWPWD */
if ((pwd = pw_locate(user_name)) && pwd->pw_passwd[0] == '@') {
if (pw_auth(pwd->pw_passwd + 1, user_name, PW_DELETE, (char *) 0)) {
SYSLOG((LOG_ERR,
"failed deleting auth `%s' for user `%s'\n",
pwd->pw_passwd + 1, user_name));
fprintf(stderr,
_("%s: error deleting authentication\n"),
Prog);
} else {
SYSLOG((LOG_INFO, "delete auth `%s' for user `%s'\n",
pwd->pw_passwd + 1, user_name);
}
}
#endif /* AUTH_METHODS */
if (!pw_remove(user_name))
fprintf(stderr, _("%s: error deleting password entry\n"), Prog);
#ifdef SHADOWPWD
if (is_shadow_pwd && ! spw_remove (user_name))
fprintf(stderr, _("%s: error deleting shadow password entry\n"),
Prog);
#endif
#ifdef HAVE_TCFS
if (tcfs_locate (user_name)) {
if (!tcfs_remove (user_name)) {
SYSLOG((LOG_ERR,
"failed deleting TCFS entry for user `%s'\n",
user_name));
fprintf(stderr, _("%s: error deleting TCFS entry\n"),
Prog);
} else {
SYSLOG((LOG_INFO,
"delete TCFS entry for user `%s'\n",
user_name));
}
}
#endif /* HAVE_TCFS */
#ifdef NDBM
if (pw_dbm_present()) {
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;
}
}
#ifdef SHADOWPWD
if (is_shadow_pwd && sp_dbm_present() && !sp_dbm_remove(user_name))
fprintf(stderr,
_("%s: error deleting shadow passwd dbm entry\n"),
Prog);
endspent ();
#endif
endpwent ();
#endif /* NDBM */
SYSLOG((LOG_INFO, "delete user `%s'\n", user_name));
}
/*
* user_busy - see if user is logged in.
*
* XXX - should probably check if there are any processes owned
* by this user. Also, I think this check should be in usermod
* as well (at least when changing username or uid). --marekm
*/
static void
user_busy(const char *name, uid_t uid)
{
struct utmp *utent;
/*
* We see if the user is logged in by looking for the user name
* in the utmp file.
*/
setutent ();
while ((utent = getutent ())) {
#ifdef USER_PROCESS
if (utent->ut_type != USER_PROCESS)
continue;
#else
if (utent->ut_user[0] == '\0')
continue;
#endif
if (strncmp(utent->ut_user, name, sizeof utent->ut_user))
continue;
fprintf(stderr, _("%s: user %s is currently logged in\n"),
Prog, name);
exit(E_USER_BUSY);
}
}
/*
* user_cancel - cancel cron and at jobs
*
* user_cancel removes the crontab and any at jobs for a user
*/
/*
* We used to have all this stuff hardcoded here, but now
* we just run an external script - it may need to do other
* things as well (like removing print jobs) and we may not
* want to recompile userdel too often. Below is a sample
* script (should work at least on Debian 1.1). --marekm
==========
#! /bin/sh
# Check for the required argument.
if [ $# != 1 ]; then
echo Usage: $0 username
exit 1
fi
# Remove cron jobs.
crontab -r -u $1
# Remove at jobs. XXX - will remove any jobs owned by the
# same UID, even if it was shared by a different username.
# at really should store the username somewhere, and atrm
# should support an option to remove all jobs owned by the
# specified user - for now we have to do this ugly hack...
find /var/spool/cron/atjobs -name "[^.]*" -type f -user $1 -exec rm {} \;
# Remove print jobs.
lprm $1
# All done.
exit 0
==========
*/
static void
user_cancel(const char *user)
{
char *cmd;
int pid, wpid;
int status;
if (!(cmd = getdef_str("USERDEL_CMD")))
return;
pid = fork();
if (pid == 0) {
execl(cmd, cmd, user, (char *) 0);
if (errno == ENOENT) {
perror(cmd);
_exit(127);
} else {
perror(cmd);
_exit(126);
}
} else if (pid == -1) {
perror("fork");
return;
}
do {
wpid = wait(&status);
} while (wpid != pid && wpid != -1);
}
#ifdef EXTRA_CHECK_HOME_DIR
static int
path_prefix(const char *s1, const char *s2)
{
return (strncmp(s2, s1, strlen(s1)) == 0);
}
#endif
static int
is_owner(uid_t uid, const char *path)
{
struct stat st;
if (stat(path, &st))
return -1;
return (st.st_uid == uid);
}
#ifndef NO_REMOVE_MAILBOX
static void
remove_mailbox(void)
{
const char *maildir;
char mailfile[1024];
int i;
maildir = getdef_str("MAIL_DIR");
#ifdef MAIL_SPOOL_DIR
if (!maildir && !getdef_str("MAIL_FILE"))
maildir = MAIL_SPOOL_DIR;
#endif
if (!maildir)
return;
snprintf(mailfile, sizeof mailfile, "%s/%s", maildir, user_name);
if (fflg) {
unlink(mailfile); /* always remove, ignore errors */
return;
}
i = is_owner(user_id, mailfile);
if (i == 0) {
fprintf(stderr,
_("%s: warning: %s not owned by %s, not removing\n"),
Prog, mailfile, user_name);
return;
} else if (i == -1)
return; /* mailbox doesn't exist */
if (unlink(mailfile)) {
fprintf(stderr, _("%s: warning: can't remove "), Prog);
perror(mailfile);
}
}
#endif
/*
* main - userdel command
*/
int
main(int argc, char **argv)
{
struct passwd *pwd;
int arg;
int errors = 0;
/*
* Get my name so that I can use it to report errors.
*/
Prog = Basename(argv[0]);
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
openlog(Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
#ifdef SHADOWPWD
is_shadow_pwd = spw_file_present();
#endif
#ifdef SHADOWGRP
is_shadow_grp = sgr_file_present();
#endif
/*
* The open routines for the DBM 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
while ((arg = getopt (argc, argv, "fr")) != EOF) {
switch (arg) {
case 'f': /* force remove even if not owned by user */
fflg++;
break;
case 'r': /* remove home dir and mailbox */
rflg++;
break;
default:
usage();
}
}
if (optind + 1 != argc)
usage ();
/*
* Start with a quick check to see if the user exists.
*/
user_name = argv[argc - 1];
if (! (pwd = getpwnam (user_name))) {
fprintf(stderr, _("%s: user %s does not exist\n"),
Prog, user_name);
exit(E_NOTFOUND);
}
#ifdef USE_NIS
/*
* Now make sure it isn't an NIS user.
*/
if (__ispwNIS ()) {
char *nis_domain;
char *nis_master;
fprintf(stderr, _("%s: user %s is a NIS user\n"),
Prog, user_name);
if (! yp_get_default_domain (&nis_domain) &&
! yp_master (nis_domain, "passwd.byname",
&nis_master)) {
fprintf(stderr, _("%s: %s is the NIS master\n"),
Prog, nis_master);
}
exit(E_NOTFOUND);
}
#endif
user_id = pwd->pw_uid;
user_home = xstrdup(pwd->pw_dir);
/*
* Check to make certain the user isn't logged in.
*/
user_busy (user_name, user_id);
/*
* 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 ();
#ifndef NO_REMOVE_MAILBOX
if (rflg)
remove_mailbox();
#endif
if (rflg && !fflg && !is_owner(user_id, user_home)) {
fprintf(stderr, _("%s: %s not owned by %s, not removing\n"),
Prog, user_home, user_name);
rflg = 0;
errors++;
}
/* This may be slow, the above should be good enough. */
#ifdef EXTRA_CHECK_HOME_DIR
if (rflg && !fflg) {
/*
* For safety, refuse to remove the home directory
* if it would result in removing some other user's
* home directory. Still not perfect so be careful,
* but should prevent accidents if someone has /home
* or / as home directory... --marekm
*/
setpwent();
while ((pwd = getpwent())) {
if (strcmp(pwd->pw_name, user_name) == 0)
continue;
if (path_prefix(user_home, pwd->pw_dir)) {
fprintf(stderr,
_("%s: not removing directory %s (would remove home of user %s)\n"),
Prog, user_home, pwd->pw_name);
rflg = 0;
errors++;
break;
}
}
}
#endif
if (rflg) {
if (remove_tree(user_home) || rmdir(user_home)) {
fprintf(stderr, _("%s: error removing directory %s\n"),
Prog, user_home);
errors++;
}
}
/*
* Cancel any crontabs or at jobs. Have to do this before we
* remove the entry from /etc/passwd.
*/
user_cancel(user_name);
close_files ();
exit(errors ? E_HOMEDIR : E_SUCCESS);
/*NOTREACHED*/
}

1664
src/usermod.c Normal file

File diff suppressed because it is too large Load Diff

239
src/vipw.c Normal file
View File

@ -0,0 +1,239 @@
/*
vipw, vigr edit the password or group file
with -s will edit shadow or gshadow file
Copyright (C) 1997 Guy Maor <maor@ece.utexas.edu>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <config.h>
#include "rcsid.h"
RCSID(PKG_VER "$Id: vipw.c,v 1.1 1999/07/09 18:02:43 marekm Exp $")
#include "defines.h"
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
#include <utime.h>
#include "prototypes.h"
#include "pwio.h"
#include "shadowio.h"
#include "groupio.h"
#include "sgroupio.h"
static const char *progname, *filename, *fileeditname;
static int filelocked = 0, createedit = 0;
static int (*unlock)();
/* local function prototypes */
static int create_backup_file P_((FILE *, const char *, struct stat *));
static void vipwexit P_((const char *, int, int));
static void vipwedit P_((const char *, int (*) P_((void)), int (*) P_((void))));
int main P_((int, char **));
static int
create_backup_file(FILE *fp, const char *backup, struct stat *sb)
{
struct utimbuf ub;
FILE *bkfp;
int c;
mode_t mask;
mask = umask(077);
bkfp = fopen(backup, "w");
umask(mask);
if (!bkfp) return -1;
rewind(fp);
while ((c = getc(fp)) != EOF) {
if (putc(c, bkfp) == EOF) break;
}
if (c != EOF || fflush(bkfp)) {
fclose(bkfp);
unlink(backup);
return -1;
}
if (fclose(bkfp)) {
unlink(backup);
return -1;
}
ub.actime = sb->st_atime;
ub.modtime = sb->st_mtime;
if (utime(backup, &ub) ||
chmod(backup, sb->st_mode) ||
chown(backup, sb->st_uid, sb->st_gid)) {
unlink(backup);
return -1;
}
return 0;
}
static void
vipwexit(const char *msg, int syserr, int ret)
{
int err = errno;
if (filelocked) (*unlock)();
if (createedit) unlink(fileeditname);
if (msg) fprintf(stderr, "%s: %s", progname, msg);
if (syserr) fprintf(stderr, ": %s", strerror(err));
fprintf(stderr, _("\n%s: %s is unchanged\n"), progname, filename);
exit(ret);
}
#ifndef DEFAULT_EDITOR
#define DEFAULT_EDITOR "vi"
#endif
static void
vipwedit(const char *file, int (*file_lock) P_((void)), int (*file_unlock) P_((void)))
{
const char *editor;
pid_t pid;
struct stat st1, st2;
int status;
FILE *f;
char filebackup[1024], fileedit[1024];
snprintf(filebackup, sizeof filebackup, "%s-", file);
snprintf(fileedit, sizeof fileedit, "%s.edit", file);
unlock = file_unlock;
filename = file;
fileeditname = fileedit;
if (access(file, F_OK)) vipwexit(file, 1, 1);
if (!file_lock()) vipwexit(_("Couldn't lock file"), errno, 5);
filelocked = 1;
/* edited copy has same owners, perm */
if (stat(file, &st1)) vipwexit(file, 1, 1);
if (!(f = fopen(file, "r"))) vipwexit(file, 1, 1);
if (create_backup_file(f, fileedit, &st1))
vipwexit(_("Couldn't make backup"), errno, 1);
createedit = 1;
editor = getenv("VISUAL");
if (!editor)
editor = getenv("EDITOR");
if (!editor)
editor = DEFAULT_EDITOR;
if ((pid = fork()) == -1) vipwexit("fork", 1, 1);
else if (!pid) {
execlp(editor, editor, fileedit, (char *) 0);
fprintf(stderr, "%s: %s: %s\n", progname, editor, strerror(errno));
exit(1);
}
for (;;) {
pid = waitpid(pid, &status, WUNTRACED);
if (WIFSTOPPED(status)) {
kill(getpid(), SIGSTOP);
kill(getpid(), SIGCONT);
}
else break;
}
if (pid == -1 || !WIFEXITED(status) || WEXITSTATUS(status))
vipwexit(editor, 1, 1);
if (stat(fileedit, &st2)) vipwexit(fileedit, 1, 1);
if (st1.st_mtime == st2.st_mtime) vipwexit(0, 0, 0);
/* XXX - here we should check fileedit for errors; if there are any,
ask the user what to do (edit again, save changes anyway, or quit
without saving). Use pwck or grpck to do the check. --marekm */
createedit = 0;
unlink(filebackup);
link(file, filebackup);
if (rename(fileedit, file) == -1) {
fprintf(stderr, _("%s: can't restore %s: %s (your changes are in %s)\n"),
progname, file, strerror(errno), fileedit);
vipwexit(0,0,1);
}
(*file_unlock)();
}
int
main(int argc, char **argv)
{
int flag;
int editshadow = 0;
char *c;
int e = 1;
int do_vipw;
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
progname = ((c = strrchr(*argv, '/')) ? c+1 : *argv);
do_vipw = (strcmp(progname, "vigr") != 0);
while ((flag = getopt(argc, argv, "ghps")) != EOF) {
switch (flag) {
case 'p':
do_vipw = 1;
break;
case 'g':
do_vipw = 0;
break;
case 's':
editshadow = 1;
break;
case 'h':
e = 0;
default:
printf(_("Usage:\n\
`vipw' edits /etc/passwd `vipw -s' edits /etc/shadow\n\
`vigr' edits /etc/group `vigr -s' edits /etc/gshadow\n\
"));
exit(e);
}
}
if (do_vipw) {
#ifdef SHADOWPWD
if (editshadow)
vipwedit(SHADOW_FILE, spw_lock, spw_unlock);
else
#endif
vipwedit(PASSWD_FILE, pw_lock, pw_unlock);
}
else {
#ifdef SHADOWGRP
if (editshadow)
vipwedit(SGROUP_FILE, sgr_lock, sgr_unlock);
else
#endif
vipwedit(GROUP_FILE, gr_lock, gr_unlock);
}
return 0;
}