Rework source layout in preparation for modular sudo.

This commit is contained in:
Todd C. Miller
2010-02-20 09:14:01 -05:00
parent 28c24027ec
commit e90fa482f9
151 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,111 @@
#
# Copyright (c) 1996, 1998-2002,2004 Todd C. Miller <Todd.Miller@courtesan.com>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
srcdir = .
# Which install program?
INSTALL = $(srcdir)/install-sh -c
# For installing the shared lib
LIBTOOL = $(srcdir)/libtool
# Where to install things...
prefix = @prefix@
exec_prefix = @exec_prefix@
bindir = @bindir@
sbindir = @sbindir@
sysconfdir = @sysconfdir@
mandir = @mandir@
noexecdir = @NOEXECDIR@
# Directory in which to install sudo.
sudodir = $(bindir)
# Directory in which to install visudo
visudodir = $(sbindir)
# Directory in which to install the sudoers file
sudoersdir = $(sysconfdir)
# Directory in which to install the man page
mantype = @MANTYPE@
mansectsu = @mansectsu@
mansectform = @mansectform@
mandirsu = $(mandir)/$(mantype)$(mansectsu)
mandirform = $(mandir)/$(mantype)$(mansectform)
# User and group ids the installed files should be "owned" by
install_uid = 0
install_gid = 0
# User, group, and mode the sudoers file should be "owned" by
sudoers_uid = @SUDOERS_UID@
sudoers_gid = @SUDOERS_GID@
sudoers_mode = @SUDOERS_MODE@
SHELL = /bin/sh
PROGS = @PROGS@
all: $(PROGS)
@echo run 'make install' to install sudo
install: install-dirs install-binaries @INSTALL_NOEXEC@ install-sudoers install-man
install-dirs:
$(SHELL) $(srcdir)/mkinstalldirs $(DESTDIR)$(sudodir) \
$(DESTDIR)$(visudodir) $(DESTDIR)$(sudoersdir) \
$(DESTDIR)$(mandirsu) $(DESTDIR)$(mandirform) \
$(DESTDIR)$(noexecdir)
install-binaries: $(PROGS)
$(INSTALL) -O $(install_uid) -G $(install_gid) -M 4111 sudo $(DESTDIR)$(sudodir)/sudo
rm -f $(DESTDIR)$(sudodir)/sudoedit
ln $(DESTDIR)$(sudodir)/sudo $(DESTDIR)$(sudodir)/sudoedit
$(INSTALL) -O $(install_uid) -G $(install_gid) -M 0111 visudo $(DESTDIR)$(visudodir)/visudo
install-noexec: sudo_noexec.la
$(LIBTOOL) --mode=install $(INSTALL) sudo_noexec.la $(DESTDIR)$(noexecdir)
install-sudoers:
test -f $(DESTDIR)$(sudoersdir)/sudoers || \
$(INSTALL) -O $(sudoers_uid) -G $(sudoers_gid) -M $(sudoers_mode) \
$(srcdir)/sudoers $(DESTDIR)$(sudoersdir)/sudoers
install-man:
$(INSTALL) -O $(install_uid) -G $(install_gid) -M 0444 @mansrcdir@/sudo.$(mantype) $(DESTDIR)$(mandirsu)/sudo.$(mansectsu)
@rm -f $(DESTDIR)$(mandirsu)/sudoedit.$(mansectsu)
ln $(DESTDIR)$(mandirsu)/sudo.$(mansectsu) $(DESTDIR)$(mandirsu)/sudoedit.$(mansectsu)
$(INSTALL) -O $(install_uid) -G $(install_gid) -M 0444 @mansrcdir@/visudo.$(mantype) $(DESTDIR)$(mandirsu)/visudo.$(mansectsu)
$(INSTALL) -O $(install_uid) -G $(install_gid) -M 0444 @mansrcdir@/sudoers.$(mantype) $(DESTDIR)$(mandirform)/sudoers.$(mansectform)
@MAN_POSTINSTALL@
check:
@echo nothing to check
clean:
@echo nothing to clean
mostlyclean: clean
distclean: clean
clobber: clean
realclean: clean
cleandir: clean

577
plugins/sudoers/Makefile.in Normal file
View File

@@ -0,0 +1,577 @@
#
# Copyright (c) 1996, 1998-2005, 2007-2009
# Todd C. Miller <Todd.Miller@courtesan.com>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Sponsored in part by the Defense Advanced Research Projects
# Agency (DARPA) and Air Force Research Laboratory, Air Force
# Materiel Command, USAF, under agreement number F39502-99-1-0512.
#
# @configure_input@
#
#### Start of system configuration section. ####
srcdir = @srcdir@
devdir = @devdir@
authdir = $(srcdir)/auth
top_builddir = .
# Compiler & tools to use
CC = @CC@
FLEX = @FLEX@
YACC = @YACC@
NROFF = nroff -Tascii
LIBTOOL = @LIBTOOL@
AR=@AR@
RANLIB=@RANLIB@
# Our install program supports extra flags...
INSTALL = $(SHELL) $(srcdir)/install-sh -c
# Libraries
LIBS = @LIBS@
NET_LIBS = @NET_LIBS@
SUDO_LIBS = @SUDO_LIBS@ @AFS_LIBS@ @GETGROUPS_LIB@ $(LIBS) $(NET_LIBS)
# C preprocessor flags
CPPFLAGS = -I. -I$(srcdir) @CPPFLAGS@
# Usually -O and/or -g
CFLAGS = @CFLAGS@
# Flags to pass to the link stage
LDFLAGS = -L. @LDFLAGS@
SUDO_LDFLAGS = @SUDO_LDFLAGS@ $(LDFLAGS)
# Where to install things...
prefix = @prefix@
exec_prefix = @exec_prefix@
bindir = @bindir@
sbindir = @sbindir@
sysconfdir = @sysconfdir@
libexecdir = @libexecdir@
datarootdir = @datarootdir@
mandir = @mandir@
noexecfile = @NOEXECFILE@
noexecdir = @NOEXECDIR@
# Directory in which to install sudo.
sudodir = $(bindir)
# Directory in which to install visudo
visudodir = $(sbindir)
# Directory in which to install the sudoers file
sudoersdir = $(sysconfdir)
# Directory in which to install the man page
mantype = @MANTYPE@
mansectsu = @mansectsu@
mansectform = @mansectform@
mandirsu = $(mandir)/$(mantype)$(mansectsu)
mandirform = $(mandir)/$(mantype)$(mansectform)
# User and group ids the installed files should be "owned" by
install_uid = 0
install_gid = 0
# User, group, and mode the sudoers file should be "owned" by (configure)
sudoers_uid = @SUDOERS_UID@
sudoers_gid = @SUDOERS_GID@
sudoers_mode = @SUDOERS_MODE@
# Pass in paths and uid/gid + OS dependent defined
DEFS = @OSDEFS@ -D_PATH_SUDOERS=\"$(sudoersdir)/sudoers\" -DSUDOERS_UID=$(sudoers_uid) -DSUDOERS_GID=$(sudoers_gid) -DSUDOERS_MODE=$(sudoers_mode)
#### End of system configuration section. ####
SHELL = /bin/sh
PROGS = @PROGS@
SRCS = aix.c alias.c alloc.c audit.c boottime.c bsm_audit.c check.c \
closefrom.c def_data.c defaults.c env.c error.c fileops.c find_path.c \
fnmatch.c getcwd.c getprogname.c getspwuid.c gettime.c glob.c \
goodpath.c gram.c gram.y interfaces.c isblank.c lbuf.c ldap.c list.c \
logging.c match.c mkstemp.c memrchr.c nanosleep.c parse.c pwutil.c \
pty.c script.c set_perms.c sigaction.c snprintf.c strcasecmp.c \
strerror.c strlcat.c strlcpy.c strsignal.c sudo.c sudo_noexec.c \
sudo_edit.c sudo_nss.c term.c testsudoers.c tgetpass.c toke.c toke.l \
tsgetgrpw.c utimes.c vasgroups.c visudo.c zero_bytes.c redblack.c \
selinux.c sesh.c sudoreplay.c getdate.c getdate.y getline.c \
timestr.c $(AUTH_SRCS)
AUTH_SRCS = auth/afs.c auth/aix_auth.c auth/bsdauth.c auth/dce.c auth/fwtk.c \
auth/kerb4.c auth/kerb5.c auth/pam.c auth/passwd.c auth/rfc1938.c \
auth/secureware.c auth/securid.c auth/securid5.c auth/sia.c \
auth/sudo_auth.c
HDRS = alloc.h bsm_audit.h compat.h def_data.h defaults.h error.h ins_2001.h \
ins_classic.h ins_csops.h ins_goons.h insults.h interfaces.h lbuf.h \
list.h logging.h missing.h nonunix.h redblack.h parse.h sudo.h \
sudo_nss.h gram.h auth/sudo_auth.h emul/charclass.h emul/fnmatch.h \
emul/glob.h emul/timespec.h emul/utime.h
AUTH_OBJS = sudo_auth.o @AUTH_OBJS@
COMMON_OBJS = alias.o alloc.o defaults.o error.o getline.o gram.o \
list.o match.o pwutil.o timestr.o toke.o redblack.o \
term.o zero_bytes.o @NONUNIX_GROUPS_IMPL@
SUDO_OBJS = $(AUTH_OBJS) @SUDO_OBJS@ audit.o boottime.o check.o \
env.o getspwuid.o gettime.o goodpath.o fileops.o find_path.o \
interfaces.o lbuf.o logging.o parse.o set_perms.o sudo.o \
sudo_edit.o sudo_nss.o tgetpass.o
VISUDO_OBJS = visudo.o fileops.o gettime.o goodpath.o find_path.o
REPLAY_OBJS = getdate.o sudoreplay.o
TEST_OBJS = interfaces.o testsudoers.o tsgetgrpw.o
LIB_OBJS = @LIBOBJS@
VERSION = @PACKAGE_VERSION@
DISTFILES = $(SRCS) $(HDRS) ChangeLog HISTORY INSTALL INSTALL.configure \
LICENSE Makefile.in PORTING README README.LDAP TROUBLESHOOTING \
UPGRADE WHATSNEW aclocal.m4 acsite.m4 aixcrypt.exp config.guess \
config.h.in config.sub configure configure.in def_data.in \
indent.pro install-sh ltmain.sh mkdefaults mkinstalldirs \
pathnames.h.in sample.pam sample.syslog.conf sample.sudoers \
schema.ActiveDirectory schema.OpenLDAP schema.iPlanet sudo.cat \
sudo.man.in sudo.pod sudo.psf sudo_usage.h.in sudoers sudoers.cat \
sudoers.man.in sudoers.pod sudoers.ldap.cat sudoers.ldap.man.in \
sudoers.ldap.pod sudoers2ldif sudoreplay.cat sudoreplay.man.in \
sudoreplay.pod visudo.cat visudo.man.in visudo.pod auth/API \
sudo.man.pl sudoers.man.pl
BINFILES= ChangeLog HISTORY LICENSE README TROUBLESHOOTING \
UPGRADE install-sh mkinstalldirs sample.syslog.conf sample.sudoers \
sudo sudo.cat sudo.man sudo.pod sudoers sudoers.cat sudoers.man \
sudoers.pod sudoreplay.cat sudoreplay.man sudoreplay.pod \
visudo visudo.cat visudo.man visudo.pod
BINSPECIAL= INSTALL.binary Makefile.binary.in libtool
SUDODEP = $(srcdir)/sudo.h $(srcdir)/alloc.h $(srcdir)/compat.h \
$(srcdir)/defaults.h $(srcdir)/error.h $(srcdir)/list.h \
$(srcdir)/logging.h $(srcdir)/missing.h $(srcdir)/sudo_nss.h \
$(devdir)/def_data.h pathnames.h config.h
AUTHDEP = $(SUDODEP) $(authdir)/sudo_auth.h
INSDEP = $(srcdir)/ins_2001.h $(srcdir)/ins_classic.h $(srcdir)/ins_csops.h \
$(srcdir)/ins_goons.h $(srcdir)/insults.h
all: $(PROGS)
.SUFFIXES: .o .c .h .l .y .man .cat .lo
.c.o:
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $<
.c.lo:
$(LIBTOOL) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $<
.man.cat:
@rm -f $@
sed '1s/^/.if n .ll 78n/' $< | $(NROFF) -man > $@
libsudo.a: $(LIB_OBJS) $(COMMON_OBJS)
$(AR) rv $@ $(LIB_OBJS) $(COMMON_OBJS)
$(RANLIB) $@
sudo: libsudo.a $(SUDO_OBJS)
$(CC) -o $@ $(SUDO_OBJS) $(SUDO_LDFLAGS) -lsudo $(SUDO_LIBS) @ZLIB@
visudo: libsudo.a $(VISUDO_OBJS)
$(CC) -o $@ $(VISUDO_OBJS) $(LDFLAGS) -lsudo $(LIBS) $(NET_LIBS)
sudoreplay: libsudo.a $(REPLAY_OBJS)
$(CC) -o $@ $(REPLAY_OBJS) $(LDFLAGS) -lsudo $(LIBS) @ZLIB@
testsudoers: $(TEST_OBJS)
$(CC) -o $@ $(TEST_OBJS) $(LDFLAGS) -lsudo $(LIBS) $(NET_LIBS)
sudo_noexec.lo: $(srcdir)/sudo_noexec.c
$(LIBTOOL) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/sudo_noexec.c
sudo_noexec.la: sudo_noexec.lo
$(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -o $@ sudo_noexec.lo -avoid-version -rpath $(noexecdir)
# Uncomment the following if you want "make distclean" to clean the parser
@DEV@GENERATED = gram.h gram.c toke.c def_data.c def_data.h getdate
# Uncomment the lines before -@true if you intend to modify gram.y
$(devdir)/gram.c $(devdir)/gram.h: $(srcdir)/gram.y
@DEV@ $(YACC) -d $(srcdir)/gram.y
@DEV@ mv -f y.tab.c gram.c
@DEV@ if cmp -s y.tab.h gram.h; then rm -f y.tab.h; else mv -f y.tab.h gram.h; fi
-@true
# Uncomment the lines before -@true if you intend to modify toke.l
$(devdir)/toke.c: $(srcdir)/toke.l
@DEV@ $(FLEX) $(srcdir)/toke.l
@DEV@ mv -f lex.yy.c toke.c
-@true
# Uncomment the lines before -@true if you intend to modify getdate.y
$(devdir)/getdate.c: $(srcdir)/getdate.y
@DEV@ echo "expect 10 shift/reduce conflicts"
@DEV@ $(YACC) $(srcdir)/getdate.y
@DEV@ mv -f y.tab.c getdate.c
-@true
# Uncomment the following if you intend to modify def_data.in
@DEV@$(devdir)/def_data.h $(devdir)/def_data.c: $(srcdir)/def_data.in
@DEV@ perl $(srcdir)/mkdefaults -o def_data $(srcdir)/def_data.in
# Dependencies (not counting auth functions)
aix.o: $(srcdir)/aix.c
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/aix.c
alias.o: $(srcdir)/alias.c $(SUDODEP) $(srcdir)/parse.h $(srcdir)/list.h $(srcdir)/redblack.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/alias.c
alloc.o: $(srcdir)/alloc.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/alloc.c
audit.o: $(srcdir)/audit.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/audit.c
boottime.o: $(srcdir)/boottime.c config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/boottime.c
bsm_audit.o: $(srcdir)/bsm_audit.c $(SUDODEP) bsm_audit.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/bsm_audit.c
check.o: $(srcdir)/check.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/check.c
closefrom.o: $(srcdir)/closefrom.c config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/closefrom.c
defaults.o: $(srcdir)/defaults.c $(SUDODEP) $(srcdir)/def_data.c $(authdir)/sudo_auth.h $(devdir)/gram.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/defaults.c
env.o: $(srcdir)/env.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/env.c
error.o: $(srcdir)/error.c $(srcdir)/compat.h $(srcdir)/error.h config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/error.c
fileops.o: $(srcdir)/fileops.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/fileops.c
find_path.o: $(srcdir)/find_path.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/find_path.c
fnmatch.o: $(srcdir)/fnmatch.c $(srcdir)/emul/fnmatch.h $(srcdir)/compat.h config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/fnmatch.c
getcwd.o: $(srcdir)/getcwd.c $(srcdir)/compat.h config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/getcwd.c
getdate.o: $(srcdir)/getdate.c $(srcdir)/compat.h config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/getdate.c
getline.o: $(srcdir)/getline.c config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/getline.c
getprogname.o: $(srcdir)/getprogname.c config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/getprogname.c
getspwuid.o: $(srcdir)/getspwuid.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/getspwuid.c
gettime.o: $(srcdir)/gettime.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/gettime.c
glob.o: $(srcdir)/glob.c $(srcdir)/emul/glob.h $(srcdir)/compat.h config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/glob.c
goodpath.o: $(srcdir)/goodpath.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/goodpath.c
gram.o: $(devdir)/gram.c $(SUDODEP) $(srcdir)/parse.h $(srcdir)/list.h $(devdir)/gram.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(devdir)/gram.c
interfaces.o: $(srcdir)/interfaces.c $(SUDODEP) $(srcdir)/interfaces.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/interfaces.c
isblank.o: $(srcdir)/isblank.c $(srcdir)/compat.h config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/isblank.c
lbuf.o: $(srcdir)/lbuf.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/lbuf.c
ldap.o: $(srcdir)/ldap.c $(SUDODEP) $(srcdir)/parse.h $(srcdir)/list.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/ldap.c
list.o: $(srcdir)/list.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/list.c
logging.o: $(srcdir)/logging.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/logging.c
match.o: $(srcdir)/match.c $(SUDODEP) $(srcdir)/parse.h $(srcdir)/list.h $(srcdir)/interfaces.h $(devdir)/gram.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/match.c
memrchr.o: $(srcdir)/memrchr.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/memrchr.c
mkstemp.o: $(srcdir)/mkstemp.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/mkstemp.c
nanosleep.o: $(srcdir)/nanosleep.c $(srcdir)/compat.h config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/nanosleep.c
parse.o: $(srcdir)/parse.c $(SUDODEP) $(srcdir)/parse.h $(srcdir)/list.h $(devdir)/gram.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/parse.c
pwutil.o: $(srcdir)/pwutil.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/pwutil.c
pty.o: $(srcdir)/pty.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/pty.c
redblack.o: $(srcdir)/redblack.c $(SUDODEP) $(srcdir)/redblack.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/redblack.c
script.o: $(srcdir)/script.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/script.c
set_perms.o: $(srcdir)/set_perms.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/set_perms.c
sigaction.o: $(srcdir)/sigaction.c $(srcdir)/compat.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/sigaction.c
snprintf.o: $(srcdir)/snprintf.c $(srcdir)/compat.h config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/snprintf.c
strcasecmp.o: $(srcdir)/strcasecmp.c $(srcdir)/compat.h config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/strcasecmp.c
strerror.o: $(srcdir)/strerror.c $(srcdir)/compat.h config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/strerror.c
strlcat.o: $(srcdir)/strlcat.c $(srcdir)/compat.h config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/strlcat.c
strlcpy.o: $(srcdir)/strlcpy.c $(srcdir)/compat.h config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/strlcpy.c
strsignal.o: $(srcdir)/strsignal.c $(srcdir)/compat.h config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/strsignal.c
selinux.o: $(srcdir)/selinux.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/selinux.c
sudo.o: $(srcdir)/sudo.c $(SUDODEP) sudo_usage.h $(srcdir)/interfaces.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/sudo.c
sudoreplay.o: $(srcdir)/sudoreplay.c $(srcdir)/alloc.h $(srcdir)/compat.h $(srcdir)/error.h $(srcdir)/missing.h config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/sudoreplay.c
sudo_edit.o: $(srcdir)/sudo_edit.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/sudo_edit.c
sudo_noexec.o: $(srcdir)/sudo_noexec.c $(srcdir)/compat.h config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/sudo_noexec.c
sudo_nss.o: $(srcdir)/sudo_nss.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/sudo_nss.c
term.o: $(srcdir)/term.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/term.c
testsudoers.o: $(srcdir)/testsudoers.c $(SUDODEP) $(srcdir)/parse.h $(srcdir)/list.h $(srcdir)/interfaces.h $(devdir)/gram.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/testsudoers.c
tgetpass.o: $(srcdir)/tgetpass.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/tgetpass.c
timestr.o: $(srcdir)/timestr.c $(srcdir)/compat.h config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/timestr.c
toke.o: $(devdir)/toke.c $(SUDODEP) $(srcdir)/parse.h $(srcdir)/list.h $(devdir)/gram.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(devdir)/toke.c
tsgetgrpw.o: $(srcdir)/tsgetgrpw.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/tsgetgrpw.c
utimes.o: $(srcdir)/utimes.c $(srcdir)/compat.h $(srcdir)/emul/utime.h config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/utimes.c
vasgroups.o: $(srcdir)/vasgroups.c $(srcdir)/nonunix.h $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/vasgroups.c
visudo.o: $(srcdir)/visudo.c $(SUDODEP) $(devdir)/gram.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/visudo.c
zero_bytes.o: $(srcdir)/zero_bytes.c $(srcdir)/compat.h config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/zero_bytes.c
sudo_auth.o: $(authdir)/sudo_auth.c $(AUTHDEP) $(INSDEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(authdir)/sudo_auth.c
afs.o: $(authdir)/afs.c $(AUTHDEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(authdir)/afs.c
aix_auth.o: $(authdir)/aix_auth.c $(AUTHDEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(authdir)/aix_auth.c
bsdauth.o: $(authdir)/bsdauth.c $(AUTHDEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(authdir)/bsdauth.c
dce.o: $(authdir)/dce.c $(AUTHDEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(authdir)/dce.c
fwtk.o: $(authdir)/fwtk.c $(AUTHDEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(authdir)/fwtk.c
kerb4.o: $(authdir)/kerb4.c $(AUTHDEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(authdir)/kerb4.c
kerb5.o: $(authdir)/kerb5.c $(AUTHDEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(authdir)/kerb5.c
pam.o: $(authdir)/pam.c $(AUTHDEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(authdir)/pam.c
passwd.o: $(authdir)/passwd.c $(AUTHDEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(authdir)/passwd.c
rfc1938.o: $(authdir)/rfc1938.c $(AUTHDEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(authdir)/rfc1938.c
secureware.o: $(authdir)/secureware.c $(AUTHDEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(authdir)/secureware.c
securid.o: $(authdir)/securid.c $(AUTHDEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(authdir)/securid.c
securid5.o: $(authdir)/securid5.c $(AUTHDEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(authdir)/securid5.c
sia.o: $(authdir)/sia.c $(AUTHDEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(authdir)/sia.c
sudo.man.in: $(srcdir)/sudo.pod
@rm -f $(srcdir)/$@
( cd $(srcdir); mansectsu=`echo @MANSECTSU@|tr A-Z a-z`; mansectform=`echo @MANSECTFORM@|tr A-Z a-z`; sed -n -e '/^=pod/q' -e 's/^/.\\" /p' sudo.pod > $@; pod2man --quotes=none --date="`date '+%B %e, %Y'`" --section=$$mansectsu --release=$(VERSION) --center="MAINTENANCE COMMANDS" sudo.pod | sed -e "s/(5)/($$mansectform)/" -e "s/(8)/($$mansectsu)/" | perl -p sudo.man.pl >> $@ )
sudo.man: sudo.man.in
CONFIG_FILES=$@ CONFIG_HEADERS= sh ./config.status
sudo.cat: sudo.man
visudo.man.in: $(srcdir)/visudo.pod
@rm -f $(srcdir)/$@
( cd $(srcdir); mansectsu=`echo @MANSECTSU@|tr A-Z a-z`; mansectform=`echo @MANSECTFORM@|tr A-Z a-z`; sed -n -e '/^=pod/q' -e 's/^/.\\" /p' visudo.pod > $@; pod2man --quotes=none --date="`date '+%B %e, %Y'`" --section=$$mansectsu --release=$(VERSION) --center="MAINTENANCE COMMANDS" visudo.pod | sed -e "s/(5)/($$mansectform)/" -e "s/(8)/($$mansectsu)/" -e 's|\\fI\\f\((CW*\)*I@\([^@]*\)\\fI@|\\fI@\2@|g' >> $@ )
visudo.man: visudo.man.in
CONFIG_FILES=$@ CONFIG_HEADERS= sh ./config.status
visudo.cat: visudo.man
sudoers.man.in: $(srcdir)/sudoers.pod
@rm -f $(srcdir)/$@
( cd $(srcdir); mansectsu=`echo @MANSECTSU@|tr A-Z a-z`; mansectform=`echo @MANSECTFORM@|tr A-Z a-z`; sed -n -e '/^=pod/q' -e 's/^/.\\" /p' sudoers.pod > $@; pod2man --quotes=none --date="`date '+%B %e, %Y'`" --section=$$mansectform --release=$(VERSION) --center="MAINTENANCE COMMANDS" sudoers.pod | sed -e "s/(5)/($$mansectform)/" -e "s/(8)/($$mansectsu)/" | perl -p sudoers.man.pl >> $@ )
sudoers.man:: sudoers.man.in
CONFIG_FILES=$@ CONFIG_HEADERS= sh ./config.status
sudoers.cat: sudoers.man
sudoers.ldap.man.in: $(srcdir)/sudoers.ldap.pod
@rm -f $(srcdir)/$@
( cd $(srcdir); mansectsu=`echo @MANSECTSU@|tr A-Z a-z`; mansectform=`echo @MANSECTFORM@|tr A-Z a-z`; sed -n -e '/^=pod/q' -e 's/^/.\\" /p' sudoers.ldap.pod > $@; pod2man --quotes=none --date="`date '+%B %e, %Y'`" --section=$$mansectform --release=$(VERSION) --center="MAINTENANCE COMMANDS" sudoers.ldap.pod | sed -e "s/(5)/($$mansectform)/" -e "s/(8)/($$mansectsu)/" -e 's|\\fI\\f\((CW*\)*I@\([^@]*\)\\fI@|\\fI@\2@|g' >> $@ )
sudoers.ldap.man:: sudoers.ldap.man.in
CONFIG_FILES=$@ CONFIG_HEADERS= sh ./config.status
sudoers.ldap.cat: sudoers.ldap.man
sudoreplay.man.in: $(srcdir)/sudoreplay.pod
@rm -f $(srcdir)/$@
( cd $(srcdir); mansectsu=`echo @MANSECTSU@|tr A-Z a-z`; mansectform=`echo @MANSECTFORM@|tr A-Z a-z`; sed -n -e '/^=pod/q' -e 's/^/.\\" /p' sudoreplay.pod > $@; pod2man --quotes=none --date="`date '+%B %e, %Y'`" --section=$$mansectsu --release=$(VERSION) --center="MAINTENANCE COMMANDS" sudoreplay.pod | sed -e "s/(5)/($$mansectform)/" -e "s/(8)/($$mansectsu)/" -e 's|\\fI\\f\((CW*\)*I@\([^@]*\)\\fI@|\\fI@\2@|g' >> $@ )
sudoreplay.man:: sudoreplay.man.in
CONFIG_FILES=$@ CONFIG_HEADERS= sh ./config.status
sudoers.cat: sudoers.man
@DEV@HISTORY: history.pod
@DEV@ pod2text -l -i0 $> > $@
@DEV@
@DEV@LICENSE: license.pod
@DEV@ pod2text -l -i0 $> | sed '1,2d' > $@
ChangeLog:
hg log --style=changelog -b default > $@
install: install-dirs install-binaries @INSTALL_NOEXEC@ install-sudoers install-man
install-dirs:
$(SHELL) $(srcdir)/mkinstalldirs $(DESTDIR)$(sudodir) \
$(DESTDIR)$(visudodir) $(DESTDIR)$(sudoersdir) \
$(DESTDIR)$(mandirsu) $(DESTDIR)$(mandirform) \
$(DESTDIR)$(noexecdir)
install-binaries: install-dirs $(PROGS)
$(INSTALL) -O $(install_uid) -G $(install_gid) -M 4111 -s sudo $(DESTDIR)$(sudodir)/sudo
rm -f $(DESTDIR)$(sudodir)/sudoedit
ln $(DESTDIR)$(sudodir)/sudo $(DESTDIR)$(sudodir)/sudoedit
if [ -f sudoreplay ]; then $(INSTALL) -O $(install_uid) -G $(install_gid) -M 0111 -s sudoreplay $(DESTDIR)$(sudodir)/sudoreplay; fi
$(INSTALL) -O $(install_uid) -G $(install_gid) -M 0111 -s visudo $(DESTDIR)$(visudodir)/visudo
if [ -f sesh ]; then $(INSTALL) -O $(install_uid) -G $(install_gid) -M 0111 -s sesh $(DESTDIR)$(libexecdir)/sesh; fi
install-noexec: install-dirs sudo_noexec.la
if [ -f .libs/$(noexecfile) ]; then $(INSTALL) -O $(install_uid) -G $(install_gid) -M 0755 .libs/$(noexecfile) $(DESTDIR)$(noexecdir); fi
install-sudoers: install-dirs
test -f $(DESTDIR)$(sudoersdir)/sudoers || \
$(INSTALL) -O $(sudoers_uid) -G $(sudoers_gid) -M $(sudoers_mode) \
$(srcdir)/sudoers $(DESTDIR)$(sudoersdir)/sudoers
install-man: install-dirs
$(INSTALL) -O $(install_uid) -G $(install_gid) -M 0444 @mansrcdir@/sudo.$(mantype) $(DESTDIR)$(mandirsu)/sudo.$(mansectsu)
@rm -f $(DESTDIR)$(mandirsu)/sudoedit.$(mansectsu)
ln $(DESTDIR)$(mandirsu)/sudo.$(mansectsu) $(DESTDIR)$(mandirsu)/sudoedit.$(mansectsu)
@REPLAY@$(INSTALL) -O $(install_uid) -G $(install_gid) -M 0444 @mansrcdir@/sudoreplay.$(mantype) $(DESTDIR)$(mandirsu)/sudoreplay.$(mansectsu)
$(INSTALL) -O $(install_uid) -G $(install_gid) -M 0444 @mansrcdir@/visudo.$(mantype) $(DESTDIR)$(mandirsu)/visudo.$(mansectsu)
$(INSTALL) -O $(install_uid) -G $(install_gid) -M 0444 @mansrcdir@/sudoers.$(mantype) $(DESTDIR)$(mandirform)/sudoers.$(mansectform)
@LDAP@$(INSTALL) -O $(install_uid) -G $(install_gid) -M 0444 @mansrcdir@/sudoers.ldap.$(mantype) $(DESTDIR)$(mandirform)/sudoers.ldap.$(mansectform)
@MAN_POSTINSTALL@
check:
@echo nothing to check
clean:
-rm -f *.a *.o *.lo stamp-* $(PROGS) testsudoers core *.core core.*
mostlyclean: clean
distclean: clean
-rm -rf Makefile pathnames.h config.h config.status config.cache \
config.log libtool sudo_noexec.lo .libs $(GENERATED) \
sudo.man sudoers.man sudoers.ldap.man visudo.man sudo_usage.h \
Makefile.binary
clobber: distclean
realclean: distclean
rm -f TAGS tags
cleandir: realclean
dist: ChangeLog
pax -w -x ustar -s '/^/sudo-$(VERSION)\//' -f ../sudo-$(VERSION).tar \
$(DISTFILES)
gzip -9f ../sudo-$(VERSION).tar
ls -l ../sudo-$(VERSION).tar.gz
bindist: ChangeLog
( \
ARCH=`uname -m|sed 's:/:_:g'`+`uname -sr|sed 's/ /_/g'` ; \
mkdir tmp.$$ARCH ; \
tdir=tmp.$$ARCH/sudo-$(VERSION) ; \
mkdir $$tdir ; \
for i in $(BINFILES) ; do \
if [ -f $$i ]; then \
cp $$i $$tdir ; \
elif [ -f $(srcdir)/$$i ]; then \
cp $(srcdir)/$$i $$tdir ; \
else \
echo cannot find $$i ; \
exit 1 ; \
fi ; \
done ; \
if [ -f sudo_noexec.la ]; then \
cp libtool $$tdir ; \
$(LIBTOOL) --mode=install $(INSTALL) sudo_noexec.la `pwd`/$$tdir ; \
ln $$tdir/sudo_noexec.la $$tdir/sudo_noexec.lai ; \
ln -s . $$tdir/.libs ; \
fi ; \
cp $(srcdir)/INSTALL.binary $$tdir/INSTALL ; \
sh ./config.status --file=Makefile.binary && cp Makefile.binary $$tdir/Makefile ; \
strip $$tdir/sudo ; \
strip $$tdir/visudo ; \
cd tmp.$$ARCH && tar Ocf ../sudo-$(VERSION)-$$ARCH.tar sudo-$(VERSION) && cd .. ; \
gzip -f --best sudo-$(VERSION)-$$ARCH.tar ; \
rm -rf tmp.$$ARCH ; \
)
depot:
( \
tdir=tmp.depot ; \
mkdir $$tdir ; \
for i in sudo visudo sudo.man visudo.man sudoers.man sudoers ChangeLog HISTORY LICENSE README TROUBLESHOOTING UPGRADE sample.syslog.conf sample.sudoers; do \
if [ -f $$i ]; then \
cp $$i $$tdir ; \
elif [ -f $(srcdir)/$$i ]; then \
cp $(srcdir)/$$i $$tdir ; \
else \
echo cannot find $$i ; \
exit 1 ; \
fi ; \
done ; \
if [ -f sudo_noexec.la ]; then \
cp libtool $$tdir ; \
$(LIBTOOL) --mode=install $(INSTALL) sudo_noexec.la `pwd`/$$tdir ; \
fi ; \
sed 's/@VERSION@/$(VERSION)/g' <$(srcdir)/sudo.psf >$$tdir/sudo.psf ; \
printf '#!/sbin/sh\nrm -f /usr/local/bin/sudoedit\nln /usr/local/bin/sudo /usr/local/bin/sudoedit\n' > $$tdir/sudo-exec.postinstall ; \
printf '#!/sbin/sh\nrm -f /usr/local/man/man1m/sudoedit.1m\nln /usr/local/man/man1m/sudo.1m /usr/local/man/man1m/sudoedit.1m\n' > $$tdir/sudo-man.postinstall ; \
printf '#!/sbin/sh\nif [ ! -s /etc/sudoers ]; then\n\techo installing /usr/local/doc/sudo/sudoers as /etc/sudoers\n\techo use /usr/local/sbin/visudo to configure sudo\n\tcp /usr/local/doc/sudo/sudoers /etc/sudoers\n\tchmod 440 /etc/sudoers\n\tchown root:root /etc/sudoers\nfi\n' > $$tdir/sudo-config.postinstall ; \
chmod 755 $$tdir/sudo-exec.postinstall $$tdir/sudo-man.postinstall $$tdir/sudo-config.postinstall ; \
strip $$tdir/sudo ; \
strip $$tdir/visudo ; \
cd $$tdir ; \
swpackage -x target_type=tape -d ../sudo-$(VERSION).depot -s sudo.psf ; \
cd .. ; \
gzip -f --best sudo-$(VERSION).depot; \
rm -rf tmp.depot ; \
)
.PHONY: ChangeLog

View File

@@ -0,0 +1,4 @@
#!
__setkey
__encrypt
__crypt

202
plugins/sudoers/alias.c Normal file
View File

@@ -0,0 +1,202 @@
/*
* Copyright (c) 2004-2005m, 2007-2009
* Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
* 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 <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include "sudo.h"
#include "parse.h"
#include "redblack.h"
#include <gram.h>
/*
* Globals
*/
struct rbtree *aliases;
unsigned int alias_seqno;
/*
* Comparison function for the red-black tree.
* Aliases are sorted by name with the type used as a tie-breaker.
*/
int
alias_compare(v1, v2)
const void *v1, *v2;
{
const struct alias *a1 = (const struct alias *)v1;
const struct alias *a2 = (const struct alias *)v2;
int res;
if (v1 == NULL)
res = -1;
else if (v2 == NULL)
res = 1;
else if ((res = strcmp(a1->name, a2->name)) == 0)
res = a1->type - a2->type;
return(res);
}
/*
* Search the tree for an alias with the specified name and type.
* Returns a pointer to the alias structure or NULL if not found.
*/
struct alias *
alias_find(name, type)
char *name;
int type;
{
struct alias key;
struct rbnode *node;
struct alias *a = NULL;
key.name = name;
key.type = type;
if ((node = rbfind(aliases, &key)) != NULL) {
/*
* Compare the global sequence number with the one stored
* in the alias. If they match then we've seen this alias
* before and found a loop.
*/
a = node->data;
if (a->seqno == alias_seqno)
return(NULL);
a->seqno = alias_seqno;
}
return(a);
}
/*
* Add an alias to the aliases redblack tree.
* Returns NULL on success and an error string on failure.
*/
char *
alias_add(name, type, members)
char *name;
int type;
struct member *members;
{
static char errbuf[512];
struct alias *a;
a = emalloc(sizeof(*a));
a->name = name;
a->type = type;
a->seqno = 0;
list2tq(&a->members, members);
if (rbinsert(aliases, a)) {
alias_free(a);
snprintf(errbuf, sizeof(errbuf), "Alias `%s' already defined", name);
return(errbuf);
}
return(NULL);
}
/*
* Apply a function to each alias entry and pass in a cookie.
*/
void
alias_apply(func, cookie)
int (*func) __P((void *, void *));
void *cookie;
{
rbapply(aliases, func, cookie, inorder);
}
/*
* Returns TRUE if there are no aliases, else FALSE.
*/
int
no_aliases()
{
return(rbisempty(aliases));
}
/*
* Free memory used by an alias struct and its members.
*/
void
alias_free(v)
void *v;
{
struct alias *a = (struct alias *)v;
struct member *m;
struct sudo_command *c;
void *next;
efree(a->name);
for (m = a->members.first; m != NULL; m = next) {
next = m->next;
if (m->type == COMMAND) {
c = (struct sudo_command *) m->name;
efree(c->cmnd);
efree(c->args);
}
efree(m->name);
efree(m);
}
efree(a);
}
/*
* Find the named alias, remove it from the tree and return it.
*/
struct alias *
alias_remove(name, type)
char *name;
int type;
{
struct rbnode *node;
struct alias key, *a;
key.name = name;
key.type = type;
if ((node = rbfind(aliases, &key)) == NULL)
return(NULL);
a = rbdelete(aliases, node);
return(a);
}
void
init_aliases()
{
if (aliases != NULL)
rbdestroy(aliases, alias_free);
aliases = rbcreate(alias_compare);
}

128
plugins/sudoers/auth/API Normal file
View File

@@ -0,0 +1,128 @@
NOTE: the Sudo auth API is subject to change
Purpose: to provide a simple API for authentication methods that
encapsulates things nicely without turning into a maze
of #ifdef's
The sudo_auth struct looks like this:
typedef struct sudo_auth {
short flags; /* various flags, see below */
short status; /* status from verify routine */
char *name; /* name of the method in string form */
void *data; /* method-specific data pointer */
int (*init) __P((struct passwd *pw, char **prompt, sudo_auth *auth));
int (*setup) __P((struct passwd *pw, char **prompt, sudo_auth *auth));
int (*verify) __P((struct passwd *pw, char *p, sudo_auth *auth));
int (*cleanup) __P((struct passwd *pw, sudo_auth *auth));
} sudo_auth;
The variables in the struct are as follows:
flags Bitwise binary flags, see below.
status Contains the return value from the last run of
the "verify" function. Starts out as AUTH_FAILURE.
name The name of the authentication method as a C string.
data A pointer to method-specific data. This is passed to
all the functions of an auth method and is usually
initialized in the "init" or "setup" routines.
Possible values of sudo_auth.flags:
FLAG_USER Whether or not the auth functions should run with
the euid of the invoking user instead of 0.
FLAG_CONFIGURED If set then the auth method is assumed to have been
configured successfully. All auth methods start out
with this set. If an "init" or "setup" function
fails, this bit is cleared.
FLAG_ONEANDONLY If set, this indicates that the method is the
only one in use. Can be used by auth functions
to determine whether to return a fatal or nonfatal
error.
The member functions can return the following values:
AUTH_SUCCESS Function succeeded. For a ``verify'' function
this means the user correctly authenticated.
AUTH_FAILURE Function failed. If this is an ``init'' or
``setup'' routine, the auth method will be
marked as !configured.
AUTH_FATAL A fatal error occurred. The routine should have
written an error message to stderr and optionally
sent mail to the administrator. (If log_error()
is called to do this, the NO_EXIT flag must be used.)
When verify_user() gets AUTH_FATAL from an auth
function it does an exit(1).
The functions in the struct are as follows:
int init(struct passwd *pw, char **prompt, sudo_auth *auth)
Function to do any one-time initialization for the auth
method. All of the "init" functions are run before anything
else. A pointer to the prompt string may be used to add
method-specific info to the prompt.
int setup(struct passwd *pw, char **prompt, sudo_auth *auth)
Function to do method-specific setup. All the "setup"
routines are run before any of the "verify" routines. A
pointer to the prompt string may be used to add method-specific
info to the prompt.
int verify(struct passwd *pw, char *p, sudo_auth *auth)
Function to do user verification for this auth method. For
standalone auth methods ``p'' is the prompt string. For
normal auth methods, ``p'' is the password the user entered.
Note that standalone auth methods are responsible for
rerading the password themselves.
int cleanup(struct passwd *pw, sudo_auth *auth)
Function to do per-auth method cleanup. This is only run
at the end of the authentication process, after the user
has completely failed or succeeded to authenticate.
The ``auth->status'' variable contains the result of the
last authentication attempt which may be interesting.
A note about standalone methods. Some authentication methods can't
coexist with any others. This may be because they encapsulate other
methods (pam, sia) or because they have a special way of interacting
with the user (securid).
Adding a new authentication method:
Each method should live in its own file. Add prototypes for the functions
in sudo_auth.h.
If this is a standalone method, add it to the standalone #if cascade
in sudo_auth.h. For instance, for a method, ``fooauth'', add:
#elif defined(HAVE_FOOAUTH)
# define AUTH_STANDALONE \
AUTH_ENTRY(0, "foo", \
foo_init, foo_setup, foo_verify, foo_cleanup)
If the method needs to run as the user, not root, replace the first
parameter to AUTH_ENTRY (0) with FLAG_USER. If you don't have a
init/setup/cleanup routine, just use a NULL for that field.
For a normal authentication method, add it to the ``auth_switch'' in
sudo_auth.c. If ``fooauth'' is a normal auth method, its entry
would look like:
# ifdef HAVE_FOOAUTH
AUTH_ENTRY(0, "foo", foo_init, foo_setup, foo_verify, foo_cleanup)
# endif
Again, if the method doesn't need to run as root, replace the 0 with
FLAG_USER. Likewise, if you don't have a init/setup/cleanup routine,
just use a NULL for that field.
NOTE: You should not make a method both ``standalone'' and
``normal''. Just use the --without-passwd configure argument
to disable passwd/shadow file checking and then have your
auth routines check the FLAG_ONEANDONLY flag to see if
they are running standalone and act accordingly.

View File

@@ -0,0 +1,87 @@
/*
* Copyright (c) 1999, 2001-2005, 2007
* Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#include <config.h>
#include <sys/param.h>
#include <sys/types.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <pwd.h>
#include "sudo.h"
#include "sudo_auth.h"
#include <afs/stds.h>
#include <afs/kautils.h>
int
afs_verify(pw, pass, auth)
struct passwd *pw;
char *pass;
sudo_auth *auth;
{
struct ktc_encryptionKey afs_key;
struct ktc_token afs_token;
/* Try to just check the password */
ka_StringToKey(pass, NULL, &afs_key);
if (ka_GetAdminToken(pw->pw_name, /* name */
NULL, /* instance */
NULL, /* realm */
&afs_key, /* key (contains password) */
0, /* lifetime */
&afs_token, /* token */
0) == 0) /* new */
return(AUTH_SUCCESS);
/* Fall back on old method XXX - needed? */
setpag();
if (ka_UserAuthenticateGeneral(KA_USERAUTH_VERSION+KA_USERAUTH_DOSETPAG,
pw->pw_name, /* name */
NULL, /* instance */
NULL, /* realm */
pass, /* password */
0, /* lifetime */
NULL, /* expiration ptr (unused) */
0, /* spare */
NULL) == 0) /* reason */
return(AUTH_SUCCESS);
return(AUTH_FAILURE);
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright (c) 1999-2005, 2007-2009 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <pwd.h>
#include "sudo.h"
#include "sudo_auth.h"
/*
* For a description of the AIX authentication API, see
* http://publib16.boulder.ibm.com/doc_link/en_US/a_doc_lib/libs/basetrf1/authenticate.htm
*/
int
aixauth_verify(pw, prompt, auth)
struct passwd *pw;
char *prompt;
sudo_auth *auth;
{
char *pass;
char *message = NULL;
int reenter = 1;
int rval = AUTH_FAILURE;
pass = tgetpass(prompt, def_passwd_timeout * 60, tgetpass_flags);
if (pass) {
/* XXX - should probably print message on failure. */
if (authenticate(pw->pw_name, pass, &reenter, &message) == 0)
rval = AUTH_SUCCESS;
free(message);
zero_bytes(pass, strlen(pass));
}
return(rval);
}
int
aixauth_cleanup(pw, auth)
struct passwd *pw;
sudo_auth *auth;
{
/* Unset AUTHSTATE as it may not be correct for the runas user. */
unsetenv("AUTHSTATE");
return(AUTH_SUCCESS);
}

View File

@@ -0,0 +1,169 @@
/*
* Copyright (c) 2000-2005, 2007-2008 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <ctype.h>
#include <pwd.h>
#include <signal.h>
#include <login_cap.h>
#include <bsd_auth.h>
#include "sudo.h"
#include "sudo_auth.h"
extern char *login_style; /* from sudo.c */
int
bsdauth_init(pw, promptp, auth)
struct passwd *pw;
char **promptp;
sudo_auth *auth;
{
static auth_session_t *as;
extern login_cap_t *lc; /* from sudo.c */
if ((as = auth_open()) == NULL) {
log_error(USE_ERRNO|NO_EXIT|NO_MAIL,
"unable to begin bsd authentication");
return(AUTH_FATAL);
}
/* XXX - maybe sanity check the auth style earlier? */
login_style = login_getstyle(lc, login_style, "auth-sudo");
if (login_style == NULL) {
log_error(NO_EXIT|NO_MAIL, "invalid authentication type");
auth_close(as);
return(AUTH_FATAL);
}
if (auth_setitem(as, AUTHV_STYLE, login_style) < 0 ||
auth_setitem(as, AUTHV_NAME, pw->pw_name) < 0 ||
auth_setitem(as, AUTHV_CLASS, login_class) < 0) {
log_error(NO_EXIT|NO_MAIL, "unable to setup authentication");
auth_close(as);
return(AUTH_FATAL);
}
auth->data = (void *) as;
return(AUTH_SUCCESS);
}
int
bsdauth_verify(pw, prompt, auth)
struct passwd *pw;
char *prompt;
sudo_auth *auth;
{
char *pass;
char *s;
size_t len;
int authok = 0;
sigaction_t sa, osa;
auth_session_t *as = (auth_session_t *) auth->data;
/* save old signal handler */
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = SIG_DFL;
(void) sigaction(SIGCHLD, &sa, &osa);
/*
* If there is a challenge then print that instead of the normal
* prompt. If the user just hits return we prompt again with echo
* turned on, which is useful for challenge/response things like
* S/Key.
*/
if ((s = auth_challenge(as)) == NULL) {
pass = tgetpass(prompt, def_passwd_timeout * 60, tgetpass_flags);
} else {
pass = tgetpass(s, def_passwd_timeout * 60, tgetpass_flags);
if (pass && *pass == '\0') {
if ((prompt = strrchr(s, '\n')))
prompt++;
else
prompt = s;
/*
* Append '[echo on]' to the last line of the challenge and
* reprompt with echo turned on.
*/
len = strlen(prompt) - 1;
while (isspace(prompt[len]) || prompt[len] == ':')
prompt[len--] = '\0';
easprintf(&s, "%s [echo on]: ", prompt);
pass = tgetpass(s, def_passwd_timeout * 60,
tgetpass_flags | TGP_ECHO);
free(s);
}
}
if (pass) {
authok = auth_userresponse(as, pass, 1);
zero_bytes(pass, strlen(pass));
}
/* restore old signal handler */
(void) sigaction(SIGCHLD, &osa, NULL);
if (authok)
return(AUTH_SUCCESS);
if (!pass)
return(AUTH_INTR);
if ((s = auth_getvalue(as, "errormsg")) != NULL)
log_error(NO_EXIT|NO_MAIL, "%s", s);
return(AUTH_FAILURE);
}
int
bsdauth_cleanup(pw, auth)
struct passwd *pw;
sudo_auth *auth;
{
auth_session_t *as = (auth_session_t *) auth->data;
auth_close(as);
return(AUTH_SUCCESS);
}

202
plugins/sudoers/auth/dce.c Normal file
View File

@@ -0,0 +1,202 @@
/*
* Copyright (c) 1996, 1998-2005 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
* 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.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
/*
* The code below basically comes from the examples supplied on
* the OSF DCE 1.0.3 manpages for the sec_login routines, with
* enough additional polishing to make the routine work with the
* rest of sudo.
*
* This code is known to work on HP 700 and 800 series systems
* running HP-UX 9.X and 10.X, with either HP's version 1.2.1 of DCE.
* (aka, OSF DCE 1.0.3) or with HP's version 1.4 of DCE (aka, OSF
* DCE 1.1).
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <pwd.h>
#include <dce/rpc.h>
#include <dce/sec_login.h>
#include <dce/dce_error.h> /* required to call dce_error_inq_text routine */
#include "sudo.h"
#include "sudo_auth.h"
static int check_dce_status __P((error_status_t, char *));
int
dce_verify(pw, plain_pw, auth)
struct passwd *pw;
char *plain_pw;
sudo_auth *auth;
{
struct passwd temp_pw;
sec_passwd_rec_t password_rec;
sec_login_handle_t login_context;
boolean32 reset_passwd;
sec_login_auth_src_t auth_src;
error_status_t status;
/*
* Create the local context of the DCE principal necessary
* to perform authenticated network operations. The network
* identity set up by this operation cannot be used until it
* is validated via sec_login_validate_identity().
*/
if (sec_login_setup_identity((unsigned_char_p_t) pw->pw_name,
sec_login_no_flags, &login_context, &status)) {
if (check_dce_status(status, "sec_login_setup_identity(1):"))
return(AUTH_FAILURE);
password_rec.key.key_type = sec_passwd_plain;
password_rec.key.tagged_union.plain = (idl_char *) plain_pw;
password_rec.pepper = NULL;
password_rec.version_number = sec_passwd_c_version_none;
/* Validate the login context with the password */
if (sec_login_validate_identity(login_context, &password_rec,
&reset_passwd, &auth_src, &status)) {
if (check_dce_status(status, "sec_login_validate_identity(1):"))
return(AUTH_FAILURE);
/*
* Certify that the DCE Security Server used to set
* up and validate a login context is legitimate. Makes
* sure that we didn't get spoofed by another DCE server.
*/
if (!sec_login_certify_identity(login_context, &status)) {
(void) fprintf(stderr, "Whoa! Bogus authentication server!\n");
(void) check_dce_status(status,"sec_login_certify_identity(1):");
return(AUTH_FAILURE);
}
if (check_dce_status(status, "sec_login_certify_identity(2):"))
return(AUTH_FAILURE);
/*
* Sets the network credentials to those specified
* by the now validated login context.
*/
sec_login_set_context(login_context, &status);
if (check_dce_status(status, "sec_login_set_context:"))
return(AUTH_FAILURE);
/*
* Oops, your credentials were no good. Possibly
* caused by clock times out of adjustment between
* DCE client and DCE security server...
*/
if (auth_src != sec_login_auth_src_network) {
(void) fprintf(stderr,
"You have no network credentials.\n");
return(AUTH_FAILURE);
}
/* Check if the password has aged and is thus no good */
if (reset_passwd) {
(void) fprintf(stderr,
"Your DCE password needs resetting.\n");
return(AUTH_FAILURE);
}
/*
* We should be a valid user by this point. Pull the
* user's password structure from the DCE security
* server just to make sure. If we get it with no
* problems, then we really are legitimate...
*/
sec_login_get_pwent(login_context, (sec_login_passwd_t) &temp_pw,
&status);
if (check_dce_status(status, "sec_login_get_pwent:"))
return(AUTH_FAILURE);
/*
* If we get to here, then the pwent above properly fetched
* the password structure from the DCE registry, so the user
* must be valid. We don't really care what the user's
* registry password is, just that the user could be
* validated. In fact, if we tried to compare the local
* password to the DCE entry at this point, the operation
* would fail if the hidden password feature is turned on,
* because the password field would contain an asterisk.
* Also go ahead and destroy the user's DCE login context
* before we leave here (and don't bother checking the
* status), in order to clean up credentials files in
* /opt/dcelocal/var/security/creds. By doing this, we are
* assuming that the user will not need DCE authentication
* later in the program, only local authentication. If this
* is not true, then the login_context will have to be
* returned to the calling program, and the context purged
* somewhere later in the program.
*/
sec_login_purge_context(&login_context, &status);
return(AUTH_SUCCESS);
} else {
if(check_dce_status(status, "sec_login_validate_identity(2):"))
return(AUTH_FAILURE);
sec_login_purge_context(&login_context, &status);
if(check_dce_status(status, "sec_login_purge_context:"))
return(AUTH_FAILURE);
}
}
(void) check_dce_status(status, "sec_login_setup_identity(2):");
return(AUTH_FAILURE);
}
/* Returns 0 for DCE "ok" status, 1 otherwise */
static int
check_dce_status(input_status, comment)
error_status_t input_status;
char *comment;
{
int error_stat;
unsigned char error_string[dce_c_error_string_len];
if (input_status == rpc_s_ok)
return(0);
dce_error_inq_text(input_status, error_string, &error_stat);
(void) fprintf(stderr, "%s %s\n", comment, error_string);
return(1);
}

159
plugins/sudoers/auth/fwtk.c Normal file
View File

@@ -0,0 +1,159 @@
/*
* Copyright (c) 1999-2005, 2008 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <pwd.h>
#include <auth.h>
#include <firewall.h>
#include "sudo.h"
#include "sudo_auth.h"
int
fwtk_init(pw, promptp, auth)
struct passwd *pw;
char **promptp;
sudo_auth *auth;
{
static Cfg *confp; /* Configuration entry struct */
char resp[128]; /* Response from the server */
if ((confp = cfg_read("sudo")) == (Cfg *)-1) {
warningx("cannot read fwtk config");
return(AUTH_FATAL);
}
if (auth_open(confp)) {
warningx("cannot connect to authentication server");
return(AUTH_FATAL);
}
/* Get welcome message from auth server */
if (auth_recv(resp, sizeof(resp))) {
warningx("lost connection to authentication server");
return(AUTH_FATAL);
}
if (strncmp(resp, "Authsrv ready", 13) != 0) {
warningx("authentication server error:\n%s", resp);
return(AUTH_FATAL);
}
return(AUTH_SUCCESS);
}
int
fwtk_verify(pw, prompt, auth)
struct passwd *pw;
char *prompt;
sudo_auth *auth;
{
char *pass; /* Password from the user */
char buf[SUDO_PASS_MAX + 12]; /* General prupose buffer */
char resp[128]; /* Response from the server */
int error;
/* Send username to authentication server. */
(void) snprintf(buf, sizeof(buf), "authorize %s 'sudo'", pw->pw_name);
restart:
if (auth_send(buf) || auth_recv(resp, sizeof(resp))) {
warningx("lost connection to authentication server");
return(AUTH_FATAL);
}
/* Get the password/response from the user. */
if (strncmp(resp, "challenge ", 10) == 0) {
(void) snprintf(buf, sizeof(buf), "%s\nResponse: ", &resp[10]);
pass = tgetpass(buf, def_passwd_timeout * 60, tgetpass_flags);
if (pass && *pass == '\0') {
pass = tgetpass("Response [echo on]: ",
def_passwd_timeout * 60, tgetpass_flags | TGP_ECHO);
}
} else if (strncmp(resp, "chalnecho ", 10) == 0) {
pass = tgetpass(&resp[10], def_passwd_timeout * 60, tgetpass_flags);
} else if (strncmp(resp, "password", 8) == 0) {
pass = tgetpass(prompt, def_passwd_timeout * 60,
tgetpass_flags);
} else if (strncmp(resp, "display ", 8) == 0) {
fprintf(stderr, "%s\n", &resp[8]);
strlcpy(buf, "response dummy", sizeof(buf));
goto restart;
} else {
warningx("%s", resp);
return(AUTH_FATAL);
}
if (!pass) { /* ^C or error */
return(AUTH_INTR);
}
/* Send the user's response to the server */
(void) snprintf(buf, sizeof(buf), "response '%s'", pass);
if (auth_send(buf) || auth_recv(resp, sizeof(resp))) {
warningx("lost connection to authentication server");
error = AUTH_FATAL;
goto done;
}
if (strncmp(resp, "ok", 2) == 0) {
error = AUTH_SUCCESS;
goto done;
}
/* Main loop prints "Permission Denied" or insult. */
if (strcmp(resp, "Permission Denied.") != 0)
warningx("%s", resp);
error = AUTH_FAILURE;
done:
zero_bytes(pass, strlen(pass));
zero_bytes(buf, strlen(buf));
return(error);
}
int
fwtk_cleanup(pw, auth)
struct passwd *pw;
sudo_auth *auth;
{
auth_close();
return(AUTH_SUCCESS);
}

View File

@@ -0,0 +1,108 @@
/*
* Copyright (c) 1999-2005, 2007 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <pwd.h>
#include <krb.h>
#include "sudo.h"
#include "sudo_auth.h"
int
kerb4_init(pw, promptp, auth)
struct passwd *pw;
char **promptp;
sudo_auth *auth;
{
static char realm[REALM_SZ];
/* Don't try to verify root */
if (pw->pw_uid == 0)
return(AUTH_FAILURE);
/* Get the local realm, or retrun failure (no krb.conf) */
if (krb_get_lrealm(realm, 1) != KSUCCESS)
return(AUTH_FAILURE);
/* Stash a pointer to the realm (used in kerb4_verify) */
auth->data = (void *) realm;
return(AUTH_SUCCESS);
}
int
kerb4_verify(pw, pass, auth)
struct passwd *pw;
char *pass;
sudo_auth *auth;
{
char tkfile[sizeof(_PATH_SUDO_TIMEDIR) + 4 + MAX_UID_T_LEN];
char *realm = (char *) auth->data;
int error;
/*
* Set the ticket file to be in sudo sudo timedir so we don't
* wipe out other (real) kerberos tickets.
*/
(void) snprintf(tkfile, sizeof(tkfile), "%s/tkt%lu",
_PATH_SUDO_TIMEDIR, (unsigned long) pw->pw_uid);
(void) krb_set_tkt_string(tkfile);
/* Convert the password to a ticket given. */
error = krb_get_pw_in_tkt(pw->pw_name, "", realm, "krbtgt", realm,
DEFAULT_TKT_LIFE, pass);
switch (error) {
case INTK_OK:
dest_tkt(); /* we are done with the temp ticket */
return(AUTH_SUCCESS);
break;
case INTK_BADPW:
case KDC_PR_UNKNOWN:
break;
default:
(void) fprintf(stderr, "Warning: Kerberos error: %s\n",
krb_err_txt[error]);
}
return(AUTH_FAILURE);
}

View File

@@ -0,0 +1,319 @@
/*
* Copyright (c) 1999-2005, 2007-2008 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
* 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.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <pwd.h>
#include <krb5.h>
#ifdef HAVE_HEIMDAL
#include <com_err.h>
#endif
#include "sudo.h"
#include "sudo_auth.h"
#ifdef HAVE_HEIMDAL
# define extract_name(c, p) krb5_principal_get_comp_string(c, p, 1)
# define krb5_free_data_contents(c, d) krb5_data_free(d)
#else
# define extract_name(c, p) (krb5_princ_component(c, p, 1)->data)
#endif
#ifndef HAVE_KRB5_VERIFY_USER
static int verify_krb_v5_tgt __P((krb5_context, krb5_creds *, char *));
#endif
static struct _sudo_krb5_data {
krb5_context sudo_context;
krb5_principal princ;
krb5_ccache ccache;
} sudo_krb5_data = { NULL, NULL, NULL };
typedef struct _sudo_krb5_data *sudo_krb5_datap;
#ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC
static krb5_error_code
krb5_get_init_creds_opt_alloc(context, opts)
krb5_context context;
krb5_get_init_creds_opt **opts;
{
*opts = emalloc(sizeof(krb5_get_init_creds_opt));
krb5_get_init_creds_opt_init(*opts);
return 0;
}
static void
krb5_get_init_creds_opt_free(opts)
krb5_get_init_creds_opt *opts;
{
free(opts);
}
#endif
int
kerb5_init(pw, promptp, auth)
struct passwd *pw;
char **promptp;
sudo_auth *auth;
{
krb5_context sudo_context;
krb5_ccache ccache;
krb5_principal princ;
krb5_error_code error;
char cache_name[64];
char *pname;
auth->data = (void *) &sudo_krb5_data; /* Stash all our data here */
#ifdef HAVE_KRB5_INIT_SECURE_CONTEXT
error = krb5_init_secure_context(&(sudo_krb5_data.sudo_context));
#else
error = krb5_init_context(&(sudo_krb5_data.sudo_context));
#endif
if (error)
return(AUTH_FAILURE);
sudo_context = sudo_krb5_data.sudo_context;
if ((error = krb5_parse_name(sudo_context, pw->pw_name,
&(sudo_krb5_data.princ)))) {
log_error(NO_EXIT|NO_MAIL,
"%s: unable to parse '%s': %s", auth->name, pw->pw_name,
error_message(error));
return(AUTH_FAILURE);
}
princ = sudo_krb5_data.princ;
/*
* Really, we need to tell the caller not to prompt for password.
* The API does not currently provide this unless the auth is standalone.
*/
#if 1
if ((error = krb5_unparse_name(sudo_context, princ, &pname))) {
log_error(NO_EXIT|NO_MAIL,
"%s: unable to unparse princ ('%s'): %s", auth->name,
pw->pw_name, error_message(error));
return(AUTH_FAILURE);
}
/* Only rewrite prompt if user didn't specify their own. */
/*if (!strcmp(prompt, PASSPROMPT)) { */
easprintf(promptp, "Password for %s: ", pname);
/*}*/
free(pname);
#endif
(void) snprintf(cache_name, sizeof(cache_name), "MEMORY:sudocc_%ld",
(long) getpid());
if ((error = krb5_cc_resolve(sudo_context, cache_name,
&(sudo_krb5_data.ccache)))) {
log_error(NO_EXIT|NO_MAIL,
"%s: unable to resolve ccache: %s", auth->name,
error_message(error));
return(AUTH_FAILURE);
}
ccache = sudo_krb5_data.ccache;
return(AUTH_SUCCESS);
}
#ifdef HAVE_KRB5_VERIFY_USER
int
kerb5_verify(pw, pass, auth)
struct passwd *pw;
char *pass;
sudo_auth *auth;
{
krb5_context sudo_context;
krb5_principal princ;
krb5_ccache ccache;
krb5_error_code error;
sudo_context = ((sudo_krb5_datap) auth->data)->sudo_context;
princ = ((sudo_krb5_datap) auth->data)->princ;
ccache = ((sudo_krb5_datap) auth->data)->ccache;
error = krb5_verify_user(sudo_context, princ, ccache, pass, 1, NULL);
return (error ? AUTH_FAILURE : AUTH_SUCCESS);
}
#else
int
kerb5_verify(pw, pass, auth)
struct passwd *pw;
char *pass;
sudo_auth *auth;
{
krb5_context sudo_context;
krb5_principal princ;
krb5_creds credbuf, *creds = NULL;
krb5_ccache ccache;
krb5_error_code error;
krb5_get_init_creds_opt *opts = NULL;
sudo_context = ((sudo_krb5_datap) auth->data)->sudo_context;
princ = ((sudo_krb5_datap) auth->data)->princ;
ccache = ((sudo_krb5_datap) auth->data)->ccache;
/* Set default flags based on the local config file. */
error = krb5_get_init_creds_opt_alloc(sudo_context, &opts);
if (error) {
log_error(NO_EXIT|NO_MAIL,
"%s: unable to allocate options: %s", auth->name,
error_message(error));
goto done;
}
#ifdef HAVE_HEIMDAL
krb5_get_init_creds_opt_set_default_flags(sudo_context, NULL,
krb5_principal_get_realm(sudo_context, princ), opts);
#endif
/* Note that we always obtain a new TGT to verify the user */
if ((error = krb5_get_init_creds_password(sudo_context, &credbuf, princ,
pass, krb5_prompter_posix,
NULL, 0, NULL, opts))) {
/* Don't print error if just a bad password */
if (error != KRB5KRB_AP_ERR_BAD_INTEGRITY)
log_error(NO_EXIT|NO_MAIL,
"%s: unable to get credentials: %s", auth->name,
error_message(error));
goto done;
}
creds = &credbuf;
/* Verify the TGT to prevent spoof attacks. */
if ((error = verify_krb_v5_tgt(sudo_context, creds, auth->name)))
goto done;
/* Store cred in cred cache. */
if ((error = krb5_cc_initialize(sudo_context, ccache, princ))) {
log_error(NO_EXIT|NO_MAIL,
"%s: unable to initialize ccache: %s", auth->name,
error_message(error));
} else if ((error = krb5_cc_store_cred(sudo_context, ccache, creds))) {
log_error(NO_EXIT|NO_MAIL,
"%s: unable to store cred in ccache: %s", auth->name,
error_message(error));
}
done:
if (opts) {
#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_FREE_TWO_ARGS
krb5_get_init_creds_opt_free(sudo_context, opts);
#else
krb5_get_init_creds_opt_free(opts);
#endif
}
if (creds)
krb5_free_cred_contents(sudo_context, creds);
return (error ? AUTH_FAILURE : AUTH_SUCCESS);
}
#endif
int
kerb5_cleanup(pw, auth)
struct passwd *pw;
sudo_auth *auth;
{
krb5_context sudo_context;
krb5_principal princ;
krb5_ccache ccache;
sudo_context = ((sudo_krb5_datap) auth->data)->sudo_context;
princ = ((sudo_krb5_datap) auth->data)->princ;
ccache = ((sudo_krb5_datap) auth->data)->ccache;
if (sudo_context) {
if (ccache)
krb5_cc_destroy(sudo_context, ccache);
if (princ)
krb5_free_principal(sudo_context, princ);
krb5_free_context(sudo_context);
}
return(AUTH_SUCCESS);
}
#ifndef HAVE_KRB5_VERIFY_USER
/*
* Verify the Kerberos ticket-granting ticket just retrieved for the
* user. If the Kerberos server doesn't respond, assume the user is
* trying to fake us out (since we DID just get a TGT from what is
* supposedly our KDC).
*
* Returns 0 for successful authentication, non-zero for failure.
*/
static int
verify_krb_v5_tgt(sudo_context, cred, auth_name)
krb5_context sudo_context;
krb5_creds *cred;
char *auth_name; /* For error reporting */
{
krb5_error_code error;
krb5_principal server;
krb5_verify_init_creds_opt vopt;
/*
* Get the server principal for the local host.
* (Use defaults of "host" and canonicalized local name.)
*/
if ((error = krb5_sname_to_principal(sudo_context, NULL, NULL,
KRB5_NT_SRV_HST, &server))) {
log_error(NO_EXIT|NO_MAIL,
"%s: unable to get host principal: %s", auth_name,
error_message(error));
return(-1);
}
/* Initialize verify opts and set secure mode */
krb5_verify_init_creds_opt_init(&vopt);
krb5_verify_init_creds_opt_set_ap_req_nofail(&vopt, 1);
/* verify the Kerberos ticket-granting ticket we just retrieved */
error = krb5_verify_init_creds(sudo_context, cred, server, NULL,
NULL, &vopt);
krb5_free_principal(sudo_context, server);
if (error)
log_error(NO_EXIT|NO_MAIL,
"%s: Cannot verify TGT! Possible attack!: %s", auth_name,
error_message(error));
return(error);
}
#endif

336
plugins/sudoers/auth/pam.c Normal file
View File

@@ -0,0 +1,336 @@
/*
* Copyright (c) 1999-2005, 2007-2009 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
# include <memory.h>
# endif
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <pwd.h>
#include <errno.h>
#ifdef HAVE_PAM_PAM_APPL_H
# include <pam/pam_appl.h>
#else
# include <security/pam_appl.h>
#endif
#ifdef HAVE_DGETTEXT
# include <libintl.h>
# if defined(__LINUX_PAM__)
# define PAM_TEXT_DOMAIN "Linux-PAM"
# elif defined(__sun__)
# define PAM_TEXT_DOMAIN "SUNW_OST_SYSOSPAM"
# endif
#endif
#include "sudo.h"
#include "sudo_auth.h"
/* Only OpenPAM and Linux PAM use const qualifiers. */
#if defined(_OPENPAM) || defined(__LIBPAM_VERSION) || defined(__LINUX_PAM__)
# define PAM_CONST const
#else
# define PAM_CONST
#endif
static int sudo_conv __P((int, PAM_CONST struct pam_message **,
struct pam_response **, void *));
static char *def_prompt = "Password:";
static int gotintr;
#ifndef PAM_DATA_SILENT
#define PAM_DATA_SILENT 0
#endif
static pam_handle_t *pamh; /* global due to pam_prep_user() */
int
pam_init(pw, promptp, auth)
struct passwd *pw;
char **promptp;
sudo_auth *auth;
{
static struct pam_conv pam_conv;
static int pam_status;
/* Initial PAM setup */
if (auth != NULL)
auth->data = (void *) &pam_status;
pam_conv.conv = sudo_conv;
pam_status = pam_start("sudo", pw->pw_name, &pam_conv, &pamh);
if (pam_status != PAM_SUCCESS) {
log_error(USE_ERRNO|NO_EXIT|NO_MAIL, "unable to initialize PAM");
return(AUTH_FATAL);
}
/*
* Set PAM_RUSER to the invoking user (the "from" user).
* We set PAM_RHOST to avoid a bug in Solaris 7 and below.
*/
(void) pam_set_item(pamh, PAM_RUSER, user_name);
(void) pam_set_item(pamh, PAM_RHOST, user_host);
/*
* Some versions of pam_lastlog have a bug that
* will cause a crash if PAM_TTY is not set so if
* there is no tty, set PAM_TTY to the empty string.
*/
if (user_ttypath == NULL)
(void) pam_set_item(pamh, PAM_TTY, "");
else
(void) pam_set_item(pamh, PAM_TTY, user_ttypath);
return(AUTH_SUCCESS);
}
int
pam_verify(pw, prompt, auth)
struct passwd *pw;
char *prompt;
sudo_auth *auth;
{
const char *s;
int *pam_status = (int *) auth->data;
def_prompt = prompt; /* for sudo_conv */
/* PAM_SILENT prevents the authentication service from generating output. */
*pam_status = pam_authenticate(pamh, PAM_SILENT);
switch (*pam_status) {
case PAM_SUCCESS:
*pam_status = pam_acct_mgmt(pamh, PAM_SILENT);
switch (*pam_status) {
case PAM_SUCCESS:
return(AUTH_SUCCESS);
case PAM_AUTH_ERR:
log_error(NO_EXIT|NO_MAIL, "pam_acct_mgmt: %d",
*pam_status);
return(AUTH_FAILURE);
case PAM_NEW_AUTHTOK_REQD:
log_error(NO_EXIT|NO_MAIL, "%s, %s",
"Account or password is expired",
"reset your password and try again");
*pam_status = pam_chauthtok(pamh,
PAM_CHANGE_EXPIRED_AUTHTOK);
if (*pam_status == PAM_SUCCESS)
return(AUTH_SUCCESS);
if ((s = pam_strerror(pamh, *pam_status)))
log_error(NO_EXIT|NO_MAIL, "pam_chauthtok: %s", s);
return(AUTH_FAILURE);
case PAM_AUTHTOK_EXPIRED:
log_error(NO_EXIT|NO_MAIL,
"Password expired, contact your system administrator");
return(AUTH_FATAL);
case PAM_ACCT_EXPIRED:
log_error(NO_EXIT|NO_MAIL, "%s %s",
"Account expired or PAM config lacks an \"account\"",
"section for sudo, contact your system administrator");
return(AUTH_FATAL);
}
/* FALLTHROUGH */
case PAM_AUTH_ERR:
if (gotintr) {
/* error or ^C from tgetpass() */
return(AUTH_INTR);
}
case PAM_MAXTRIES:
case PAM_PERM_DENIED:
return(AUTH_FAILURE);
default:
if ((s = pam_strerror(pamh, *pam_status)))
log_error(NO_EXIT|NO_MAIL, "pam_authenticate: %s", s);
return(AUTH_FATAL);
}
}
int
pam_cleanup(pw, auth)
struct passwd *pw;
sudo_auth *auth;
{
int *pam_status = (int *) auth->data;
/* If successful, we can't close the session until pam_prep_user() */
if (auth->status == AUTH_SUCCESS)
return(AUTH_SUCCESS);
*pam_status = pam_end(pamh, *pam_status | PAM_DATA_SILENT);
return(*pam_status == PAM_SUCCESS ? AUTH_SUCCESS : AUTH_FAILURE);
}
int
pam_prep_user(pw)
struct passwd *pw;
{
int eval;
if (pamh == NULL)
pam_init(pw, NULL, NULL);
/*
* Update PAM_USER to reference the user we are running the command
* as, as opposed to the user we authenticated as.
*/
(void) pam_set_item(pamh, PAM_USER, pw->pw_name);
/*
* Set credentials (may include resource limits, device ownership, etc).
* We don't check the return value here because in Linux-PAM 0.75
* it returns the last saved return code, not the return code
* for the setcred module. Because we haven't called pam_authenticate(),
* this is not set and so pam_setcred() returns PAM_PERM_DENIED.
* We can't call pam_acct_mgmt() with Linux-PAM for a similar reason.
*/
(void) pam_setcred(pamh, PAM_ESTABLISH_CRED);
#ifndef NO_PAM_SESSION
/*
* To fully utilize PAM sessions we would need to keep a
* sudo process around until the command exits. However, we
* can at least cause pam_limits to be run by opening and then
* immediately closing the session.
*/
if ((eval = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
(void) pam_end(pamh, eval | PAM_DATA_SILENT);
return(AUTH_FAILURE);
}
(void) pam_close_session(pamh, 0);
#endif
if (pam_end(pamh, PAM_SUCCESS | PAM_DATA_SILENT) == PAM_SUCCESS)
return(AUTH_SUCCESS);
else
return(AUTH_FAILURE);
}
/*
* ``Conversation function'' for PAM.
* XXX - does not handle PAM_BINARY_PROMPT
*/
static int
sudo_conv(num_msg, msg, response, appdata_ptr)
int num_msg;
PAM_CONST struct pam_message **msg;
struct pam_response **response;
void *appdata_ptr;
{
struct pam_response *pr;
PAM_CONST struct pam_message *pm;
const char *prompt;
char *pass;
int n, flags, std_prompt;
if ((*response = malloc(num_msg * sizeof(struct pam_response))) == NULL)
return(PAM_SYSTEM_ERR);
zero_bytes(*response, num_msg * sizeof(struct pam_response));
for (pr = *response, pm = *msg, n = num_msg; n--; pr++, pm++) {
flags = tgetpass_flags;
switch (pm->msg_style) {
case PAM_PROMPT_ECHO_ON:
SET(flags, TGP_ECHO);
case PAM_PROMPT_ECHO_OFF:
prompt = def_prompt;
/* Is the sudo prompt standard? (If so, we'l just use PAM's) */
std_prompt = strncmp(def_prompt, "Password:", 9) == 0 &&
(def_prompt[9] == '\0' ||
(def_prompt[9] == ' ' && def_prompt[10] == '\0'));
/* Only override PAM prompt if it matches /^Password: ?/ */
#if defined(PAM_TEXT_DOMAIN) && defined(HAVE_DGETTEXT)
if (!def_passprompt_override && (std_prompt ||
(strcmp(pm->msg, dgettext(PAM_TEXT_DOMAIN, "Password: ")) &&
strcmp(pm->msg, dgettext(PAM_TEXT_DOMAIN, "Password:")))))
prompt = pm->msg;
#else
if (!def_passprompt_override && (std_prompt ||
strncmp(pm->msg, "Password:", 9) || (pm->msg[9] != '\0'
&& (pm->msg[9] != ' ' || pm->msg[10] != '\0'))))
prompt = pm->msg;
#endif
/* Read the password unless interrupted. */
pass = tgetpass(prompt, def_passwd_timeout * 60, flags);
if (pass == NULL) {
/* We got ^C instead of a password; abort quickly. */
if (errno == EINTR)
gotintr = 1;
#if defined(__darwin__) || defined(__APPLE__)
pass = "";
#else
goto err;
#endif
}
pr->resp = estrdup(pass);
zero_bytes(pass, strlen(pass));
break;
case PAM_TEXT_INFO:
if (pm->msg)
(void) puts(pm->msg);
break;
case PAM_ERROR_MSG:
if (pm->msg) {
(void) fputs(pm->msg, stderr);
(void) fputc('\n', stderr);
}
break;
default:
goto err;
}
}
return(PAM_SUCCESS);
err:
/* Zero and free allocated memory and return an error. */
for (pr = *response, n = num_msg; n--; pr++) {
if (pr->resp != NULL) {
zero_bytes(pr->resp, strlen(pr->resp));
free(pr->resp);
pr->resp = NULL;
}
}
zero_bytes(*response, num_msg * sizeof(struct pam_response));
free(*response);
*response = NULL;
return(gotintr ? PAM_AUTH_ERR : PAM_CONV_ERR);
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright (c) 1999-2005 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <pwd.h>
#include "sudo.h"
#include "sudo_auth.h"
#define DESLEN 13
#define HAS_AGEINFO(p, l) (l == 18 && p[DESLEN] == ',')
int
passwd_init(pw, promptp, auth)
struct passwd *pw;
char **promptp;
sudo_auth *auth;
{
#ifdef HAVE_SKEYACCESS
if (skeyaccess(pw, user_tty, NULL, NULL) == 0)
return(AUTH_FAILURE);
#endif
return(AUTH_SUCCESS);
}
int
passwd_verify(pw, pass, auth)
struct passwd *pw;
char *pass;
sudo_auth *auth;
{
char sav, *epass;
size_t pw_len;
int error;
pw_len = strlen(pw->pw_passwd);
#ifdef HAVE_GETAUTHUID
/* Ultrix shadow passwords may use crypt16() */
error = strcmp(pw->pw_passwd, (char *) crypt16(pass, pw->pw_passwd));
if (!error)
return(AUTH_SUCCESS);
#endif /* HAVE_GETAUTHUID */
/*
* Truncate to 8 chars if standard DES since not all crypt()'s do this.
* If this turns out not to be safe we will have to use OS #ifdef's (sigh).
*/
sav = pass[8];
if (pw_len == DESLEN || HAS_AGEINFO(pw->pw_passwd, pw_len))
pass[8] = '\0';
/*
* Normal UN*X password check.
* HP-UX may add aging info (separated by a ',') at the end so
* only compare the first DESLEN characters in that case.
*/
epass = (char *) crypt(pass, pw->pw_passwd);
pass[8] = sav;
if (HAS_AGEINFO(pw->pw_passwd, pw_len) && strlen(epass) == DESLEN)
error = strncmp(pw->pw_passwd, epass, DESLEN);
else
error = strcmp(pw->pw_passwd, epass);
return(error ? AUTH_FAILURE : AUTH_SUCCESS);
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright (c) 1994-1996, 1998-2005
* Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <pwd.h>
#if defined(HAVE_SKEY)
# include <skey.h>
# define RFC1938 skey
# ifdef __NetBSD__
# define rfc1938challenge(a,b,c,d) skeychallenge((a),(b),(c),(d))
# else
# define rfc1938challenge(a,b,c,d) skeychallenge((a),(b),(c))
# endif
# define rfc1938verify(a,b) skeyverify((a),(b))
#elif defined(HAVE_OPIE)
# include <opie.h>
# define RFC1938 opie
# define rfc1938challenge(a,b,c,d) opiechallenge((a),(b),(c))
# define rfc1938verify(a,b) opieverify((a),(b))
#endif
#include "sudo.h"
#include "sudo_auth.h"
int
rfc1938_setup(pw, promptp, auth)
struct passwd *pw;
char **promptp;
sudo_auth *auth;
{
char challenge[256];
static char *orig_prompt = NULL, *new_prompt = NULL;
static int op_len, np_size;
static struct RFC1938 rfc1938;
/* Stash a pointer to the rfc1938 struct if we have not initialized */
if (!auth->data)
auth->data = &rfc1938;
/* Save the original prompt */
if (orig_prompt == NULL) {
orig_prompt = *promptp;
op_len = strlen(orig_prompt);
/* Ignore trailing colon (we will add our own) */
if (orig_prompt[op_len - 1] == ':')
op_len--;
else if (op_len >= 2 && orig_prompt[op_len - 1] == ' '
&& orig_prompt[op_len - 2] == ':')
op_len -= 2;
}
#ifdef HAVE_SKEY
/* Close old stream */
if (rfc1938.keyfile)
(void) fclose(rfc1938.keyfile);
#endif
/*
* Look up the user and get the rfc1938 challenge.
* If the user is not in the OTP db, only post a fatal error if
* we are running alone (since they may just use a normal passwd).
*/
if (rfc1938challenge(&rfc1938, pw->pw_name, challenge, sizeof(challenge))) {
if (IS_ONEANDONLY(auth)) {
warningx("you do not exist in the %s database", auth->name);
return(AUTH_FATAL);
} else {
return(AUTH_FAILURE);
}
}
/* Get space for new prompt with embedded challenge */
if (np_size < op_len + strlen(challenge) + 7) {
np_size = op_len + strlen(challenge) + 7;
new_prompt = (char *) erealloc(new_prompt, np_size);
}
if (def_long_otp_prompt)
(void) snprintf(new_prompt, np_size, "%s\n%s", challenge, orig_prompt);
else
(void) snprintf(new_prompt, np_size, "%.*s [ %s ]:", op_len,
orig_prompt, challenge);
*promptp = new_prompt;
return(AUTH_SUCCESS);
}
int
rfc1938_verify(pw, pass, auth)
struct passwd *pw;
char *pass;
sudo_auth *auth;
{
if (rfc1938verify((struct RFC1938 *) auth->data, pass) == 0)
return(AUTH_SUCCESS);
else
return(AUTH_FAILURE);
}

View File

@@ -0,0 +1,98 @@
/*
* Copyright (c) 1998-2005 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <pwd.h>
#ifdef __hpux
# undef MAXINT
# include <hpsecurity.h>
#else
# include <sys/security.h>
#endif /* __hpux */
#include <prot.h>
#include "sudo.h"
#include "sudo_auth.h"
int
secureware_init(pw, promptp, auth)
struct passwd *pw;
char **promptp;
sudo_auth *auth;
{
#ifdef __alpha
extern int crypt_type;
if (crypt_type == INT_MAX)
return(AUTH_FAILURE); /* no shadow */
#endif
return(AUTH_SUCCESS);
}
int
secureware_verify(pw, pass, auth)
struct passwd *pw;
char *pass;
sudo_auth *auth;
{
#ifdef __alpha
extern int crypt_type;
# ifdef HAVE_DISPCRYPT
if (strcmp(user_passwd, dispcrypt(pass, user_passwd, crypt_type)) == 0)
return(AUTH_SUCCESS);
# else
if (crypt_type == AUTH_CRYPT_BIGCRYPT) {
if (strcmp(user_passwd, bigcrypt(pass, user_passwd)) == 0)
return(AUTH_SUCCESS);
} else if (crypt_type == AUTH_CRYPT_CRYPT16) {
if (strcmp(user_passwd, crypt(pass, user_passwd)) == 0)
return(AUTH_SUCCESS);
}
# endif /* HAVE_DISPCRYPT */
#elif defined(HAVE_BIGCRYPT)
if (strcmp(user_passwd, bigcrypt(pass, user_passwd)) == 0)
return(AUTH_SUCCESS);
#endif /* __alpha */
return(AUTH_FAILURE);
}

View File

@@ -0,0 +1,108 @@
/*
* Copyright (c) 1999-2005, 2007 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
* 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.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <pwd.h>
#include <sdi_athd.h>
#include <sdconf.h>
#include <sdacmvls.h>
#include "sudo.h"
#include "sudo_auth.h"
union config_record configure;
int
securid_init(pw, promptp, auth)
struct passwd *pw;
char **promptp;
sudo_auth *auth;
{
static struct SD_CLIENT sd_dat; /* SecurID data block */
auth->data = (void *) &sd_dat; /* For method-specific data */
if (creadcfg() == 0)
return(AUTH_SUCCESS);
else
return(AUTH_FATAL);
}
int
securid_setup(pw, promptp, auth)
struct passwd *pw;
char **promptp;
sudo_auth *auth;
{
struct SD_CLIENT *sd = (struct SD_CLIENT *) auth->data;
/* Re-initialize SecurID every time. */
if (sd_init(sd) == 0) {
/* The programmer's guide says username is 32 bytes */
strlcpy(sd->username, pw->pw_name, 32);
return(AUTH_SUCCESS);
} else {
warningx("unable to contact the SecurID server");
return(AUTH_FATAL);
}
}
int
securid_verify(pw, pass, auth)
struct passwd *pw;
char *pass;
sudo_auth *auth;
{
struct SD_CLIENT *sd = (struct SD_CLIENT *) auth->data;
int rval;
rval = sd_auth(sd);
sd_close();
if (rval == ACM_OK)
return(AUTH_SUCCESS);
else
return(AUTH_FAILURE);
}

View File

@@ -0,0 +1,232 @@
/*
* Copyright (c) 1999-2005, 2007 Todd C. Miller <Todd.Miller@courtesan.com>
* Copyright (c) 2002 Michael Stroucken <michael@stroucken.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
* 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.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <pwd.h>
/* Needed for SecurID v5.0 Authentication on UNIX */
#define UNIX 1
#include <acexport.h>
#include <sdacmvls.h>
#include "sudo.h"
#include "sudo_auth.h"
/*
* securid_init - Initialises communications with ACE server
* Arguments in:
* pw - UNUSED
* promptp - UNUSED
* auth - sudo authentication structure
*
* Results out:
* auth - auth->data contains pointer to new SecurID handle
* return code - Fatal if initialization unsuccessful, otherwise
* success.
*/
int
securid_init(pw, promptp, auth)
struct passwd *pw;
char **promptp;
sudo_auth *auth;
{
static SDI_HANDLE sd_dat; /* SecurID handle */
auth->data = (void *) &sd_dat; /* For method-specific data */
/* Start communications */
if (AceInitialize() != SD_FALSE)
return(AUTH_SUCCESS);
warningx("failed to initialise the ACE API library");
return(AUTH_FATAL);
}
/*
* securid_setup - Initialises a SecurID transaction and locks out other
* ACE servers
*
* Arguments in:
* pw - struct passwd for username
* promptp - UNUSED
* auth - sudo authentication structure for SecurID handle
*
* Results out:
* return code - Success if transaction started correctly, fatal
* otherwise
*/
int
securid_setup(pw, promptp, auth)
struct passwd *pw;
char **promptp;
sudo_auth *auth;
{
SDI_HANDLE *sd = (SDI_HANDLE *) auth->data;
int retval;
/* Re-initialize SecurID every time. */
if (SD_Init(sd) != ACM_OK) {
warningx("unable to contact the SecurID server");
return(AUTH_FATAL);
}
/* Lock new PIN code */
retval = SD_Lock(*sd, pw->pw_name);
switch (retval) {
case ACM_OK:
warningx("User ID locked for SecurID Authentication");
return(AUTH_SUCCESS);
case ACE_UNDEFINED_USERNAME:
warningx("invalid username length for SecurID");
return(AUTH_FATAL);
case ACE_ERR_INVALID_HANDLE:
warningx("invalid Authentication Handle for SecurID");
return(AUTH_FATAL);
case ACM_ACCESS_DENIED:
warningx("SecurID communication failed");
return(AUTH_FATAL);
default:
warningx("unknown SecurID error");
return(AUTH_FATAL);
}
}
/*
* securid_verify - Authenticates user and handles ACE responses
*
* Arguments in:
* pw - struct passwd for username
* pass - UNUSED
* auth - sudo authentication structure for SecurID handle
*
* Results out:
* return code - Success on successful authentication, failure on
* incorrect authentication, fatal on errors
*/
int
securid_verify(pw, pass, auth)
struct passwd *pw;
char *pass;
sudo_auth *auth;
{
SDI_HANDLE *sd = (SDI_HANDLE *) auth->data;
int rval;
pass = (char *) tgetpass("Enter your PASSCODE: ",
def_passwd_timeout * 60, tgetpass_flags);
/* Have ACE verify password */
switch (SD_Check(*sd, pass, pw->pw_name)) {
case ACM_OK:
rval = AUTH_SUCESS;
break;
case ACE_UNDEFINED_PASSCODE:
warningx("invalid passcode length for SecurID");
rval = AUTH_FATAL;
break;
case ACE_UNDEFINED_USERNAME:
warningx("invalid username length for SecurID");
rval = AUTH_FATAL;
break;
case ACE_ERR_INVALID_HANDLE:
warningx("invalid Authentication Handle for SecurID");
rval = AUTH_FATAL;
break;
case ACM_ACCESS_DENIED:
rval = AUTH_FAILURE;
break;
case ACM_NEXT_CODE_REQUIRED:
/* Sometimes (when current token close to expire?)
ACE challenges for the next token displayed
(entered without the PIN) */
pass = (char *) tgetpass("\
!!! ATTENTION !!!\n\
Wait for the token code to change, \n\
then enter the new token code.\n", \
def_passwd_timeout * 60, tgetpass_flags);
if (SD_Next(*sd, pass) == ACM_OK) {
rval = AUTH_SUCCESS;
break;
}
rval = AUTH_FAILURE;
break;
case ACM_NEW_PIN_REQUIRED:
/*
* This user's SecurID has not been activated yet,
* or the pin has been reset
*/
/* XXX - Is setting up a new PIN within sudo's scope? */
SD_Pin(*sd, "");
fprintf(stderr, "Your SecurID access has not yet been set up.\n");
fprintf(stderr, "Please set up a PIN before you try to authenticate.\n");
rval = AUTH_FATAL;
break;
default:
warningx("unknown SecurID error");
rval = AUTH_FATAL;
break;
}
/* Free resources */
SD_Close(*sd);
/* Return stored state to calling process */
return(rval);
}

137
plugins/sudoers/auth/sia.c Normal file
View File

@@ -0,0 +1,137 @@
/*
* Copyright (c) 1999-2005, 2007 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <pwd.h>
#include <siad.h>
#include "sudo.h"
#include "sudo_auth.h"
static int sudo_collect __P((int, int, uchar_t *, int, prompt_t *));
static char *def_prompt;
/*
* Collection routine (callback) for limiting the timeouts in SIA
* prompts and (possibly) setting a custom prompt.
*/
static int
sudo_collect(timeout, rendition, title, nprompts, prompts)
int timeout;
int rendition;
uchar_t *title;
int nprompts;
prompt_t *prompts;
{
switch (rendition) {
case SIAFORM:
case SIAONELINER:
if (timeout <= 0 || timeout > def_passwd_timeout * 60)
timeout = def_passwd_timeout * 60;
/*
* Substitute custom prompt if a) the sudo prompt is not "Password:"
* and b) the SIA prompt is "Password:" (so we know it is safe).
* This keeps us from overwriting things like S/Key challenges.
*/
if (strcmp((char *)prompts[0].prompt, "Password:") == 0 &&
strcmp(def_prompt, "Password:") != 0)
prompts[0].prompt = (unsigned char *)def_prompt;
break;
default:
break;
}
return sia_collect_trm(timeout, rendition, title, nprompts, prompts);
}
int
sia_setup(pw, promptp, auth)
struct passwd *pw;
char **promptp;
sudo_auth *auth;
{
SIAENTITY *siah = NULL;
extern int Argc;
extern char **Argv;
if (sia_ses_init(&siah, Argc, Argv, NULL, pw->pw_name, ttyname(0), 1, NULL)
!= SIASUCCESS) {
log_error(USE_ERRNO|NO_EXIT|NO_MAIL,
"unable to initialize SIA session");
return(AUTH_FATAL);
}
auth->data = (void *) siah;
return(AUTH_SUCCESS);
}
int
sia_verify(pw, prompt, auth)
struct passwd *pw;
char *prompt;
sudo_auth *auth;
{
SIAENTITY *siah = (SIAENTITY *) auth->data;
def_prompt = prompt; /* for sudo_collect */
/* XXX - need a way to detect user hitting return or EOF at prompt */
if (sia_ses_reauthent(sudo_collect, siah) == SIASUCCESS)
return(AUTH_SUCCESS);
else
return(AUTH_FAILURE);
}
int
sia_cleanup(pw, auth)
struct passwd *pw;
sudo_auth *auth;
{
SIAENTITY *siah = (SIAENTITY *) auth->data;
(void) sia_ses_release(&siah);
return(AUTH_SUCCESS);
}

View File

@@ -0,0 +1,269 @@
/*
* Copyright (c) 1999-2005, 2008-2009 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
# include <memory.h>
# endif
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <pwd.h>
#include <time.h>
#include <signal.h>
#include "sudo.h"
#include "sudo_auth.h"
#include "insults.h"
sudo_auth auth_switch[] = {
#ifdef AUTH_STANDALONE
AUTH_STANDALONE
#else
# ifndef WITHOUT_PASSWD
AUTH_ENTRY(0, "passwd", passwd_init, NULL, passwd_verify, NULL)
# endif
# if defined(HAVE_GETPRPWNAM) && !defined(WITHOUT_PASSWD)
AUTH_ENTRY(0, "secureware", secureware_init, NULL, secureware_verify, NULL)
# endif
# ifdef HAVE_AFS
AUTH_ENTRY(0, "afs", NULL, NULL, afs_verify, NULL)
# endif
# ifdef HAVE_DCE
AUTH_ENTRY(0, "dce", NULL, NULL, dce_verify, NULL)
# endif
# ifdef HAVE_KERB4
AUTH_ENTRY(0, "kerb4", kerb4_init, NULL, kerb4_verify, NULL)
# endif
# ifdef HAVE_KERB5
AUTH_ENTRY(0, "kerb5", kerb5_init, NULL, kerb5_verify, kerb5_cleanup)
# endif
# ifdef HAVE_SKEY
AUTH_ENTRY(0, "S/Key", NULL, rfc1938_setup, rfc1938_verify, NULL)
# endif
# ifdef HAVE_OPIE
AUTH_ENTRY(0, "OPIE", NULL, rfc1938_setup, rfc1938_verify, NULL)
# endif
#endif /* AUTH_STANDALONE */
AUTH_ENTRY(0, NULL, NULL, NULL, NULL, NULL)
};
void
verify_user(pw, prompt)
struct passwd *pw;
char *prompt;
{
int counter = def_passwd_tries + 1;
int success = AUTH_FAILURE;
int status;
int flags;
char *p;
sudo_auth *auth;
sigaction_t sa, osa;
#ifdef HAVE_BSM_AUDIT
extern char **NewArgv;
#endif
/* Enable suspend during password entry. */
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = SIG_DFL;
(void) sigaction(SIGTSTP, &sa, &osa);
/* Make sure we have at least one auth method. */
if (auth_switch[0].name == NULL) {
#ifdef HAVE_BSM_AUDIT
audit_failure(NewArgv, "no authentication methods");
#endif
log_error(0, "%s %s %s",
"There are no authentication methods compiled into sudo!",
"If you want to turn off authentication, use the",
"--disable-authentication configure option.");
}
/* Set FLAG_ONEANDONLY if there is only one auth method. */
if (auth_switch[1].name == NULL)
SET(auth_switch[0].flags, FLAG_ONEANDONLY);
/* Initialize auth methods and unconfigure the method if necessary. */
for (auth = auth_switch; auth->name; auth++) {
if (auth->init && IS_CONFIGURED(auth)) {
if (NEEDS_USER(auth))
set_perms(PERM_USER);
status = (auth->init)(pw, &prompt, auth);
if (status == AUTH_FAILURE)
CLR(auth->flags, FLAG_CONFIGURED);
else if (status == AUTH_FATAL) { /* XXX log */
#ifdef HAVE_BSM_AUDIT
audit_failure(NewArgv, "authentication failure");
#endif
exit(1); /* assume error msg already printed */
}
if (NEEDS_USER(auth))
set_perms(PERM_ROOT);
}
}
while (--counter) {
/* Do any per-method setup and unconfigure the method if needed */
for (auth = auth_switch; auth->name; auth++) {
if (auth->setup && IS_CONFIGURED(auth)) {
if (NEEDS_USER(auth))
set_perms(PERM_USER);
status = (auth->setup)(pw, &prompt, auth);
if (status == AUTH_FAILURE)
CLR(auth->flags, FLAG_CONFIGURED);
else if (status == AUTH_FATAL) {/* XXX log */
#ifdef HAVE_BSM_AUDIT
audit_failure(NewArgv, "authentication failure");
#endif
exit(1); /* assume error msg already printed */
}
if (NEEDS_USER(auth))
set_perms(PERM_ROOT);
}
}
/* Get the password unless the auth function will do it for us */
#ifdef AUTH_STANDALONE
p = prompt;
#else
p = (char *) tgetpass(prompt, def_passwd_timeout * 60,
tgetpass_flags);
#endif /* AUTH_STANDALONE */
/* Call authentication functions. */
for (auth = auth_switch; p && auth->name; auth++) {
if (!IS_CONFIGURED(auth))
continue;
if (NEEDS_USER(auth))
set_perms(PERM_USER);
success = auth->status = (auth->verify)(pw, (char *)p, auth);
if (NEEDS_USER(auth))
set_perms(PERM_ROOT);
if (auth->status != AUTH_FAILURE)
goto cleanup;
}
#ifndef AUTH_STANDALONE
if (p)
zero_bytes(p, strlen(p));
#endif
if (!ISSET(tgetpass_flags, TGP_ASKPASS))
pass_warn(stderr);
}
cleanup:
/* Call cleanup routines. */
for (auth = auth_switch; auth->name; auth++) {
if (auth->cleanup && IS_CONFIGURED(auth)) {
if (NEEDS_USER(auth))
set_perms(PERM_USER);
status = (auth->cleanup)(pw, auth);
if (status == AUTH_FATAL) { /* XXX log */
#ifdef HAVE_BSM_AUDIT
audit_failure(NewArgv, "authentication failure");
#endif
exit(1); /* assume error msg already printed */
}
if (NEEDS_USER(auth))
set_perms(PERM_ROOT);
}
}
switch (success) {
case AUTH_SUCCESS:
(void) sigaction(SIGTSTP, &osa, NULL);
return;
case AUTH_INTR:
case AUTH_FAILURE:
if (counter != def_passwd_tries) {
if (def_mail_badpass || def_mail_always)
flags = 0;
else
flags = NO_MAIL;
#ifdef HAVE_BSM_AUDIT
audit_failure(NewArgv, "authentication failure");
#endif
log_error(flags, "%d incorrect password attempt%s",
def_passwd_tries - counter,
(def_passwd_tries - counter == 1) ? "" : "s");
}
/* FALLTHROUGH */
case AUTH_FATAL:
#ifdef HAVE_BSM_AUDIT
audit_failure(NewArgv, "authentication failure");
#endif
exit(1);
}
/* NOTREACHED */
}
void
pass_warn(fp)
FILE *fp;
{
#ifdef INSULT
if (def_insults)
(void) fprintf(fp, "%s\n", INSULT);
else
#endif
(void) fprintf(fp, "%s\n", def_badpass_message);
}
void
dump_auth_methods()
{
sudo_auth *auth;
(void) fputs("Authentication methods:", stdout);
for (auth = auth_switch; auth->name; auth++)
(void) printf(" '%s'", auth->name);
(void) putchar('\n');
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (c) 1999-2005, 2007-2009 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef SUDO_AUTH_H
#define SUDO_AUTH_H
/* Auth function return values. */
#define AUTH_SUCCESS 0
#define AUTH_FAILURE 1
#define AUTH_INTR 2
#define AUTH_FATAL 3
typedef struct sudo_auth {
short flags; /* various flags, see below */
short status; /* status from verify routine */
char *name; /* name of the method as a string */
void *data; /* method-specific data pointer */
int (*init) __P((struct passwd *pw, char **prompt, struct sudo_auth *auth));
int (*setup) __P((struct passwd *pw, char **prompt, struct sudo_auth *auth));
int (*verify) __P((struct passwd *pw, char *p, struct sudo_auth *auth));
int (*cleanup) __P((struct passwd *pw, struct sudo_auth *auth));
} sudo_auth;
/* Values for sudo_auth.flags. */
/* XXX - these names are too long for my liking */
#define FLAG_USER 0x01 /* functions must run as the user, not root */
#define FLAG_CONFIGURED 0x02 /* method configured ok */
#define FLAG_ONEANDONLY 0x04 /* one and only auth method */
/* Shortcuts for using the flags above. */
#define NEEDS_USER(x) ((x)->flags & FLAG_USER)
#define IS_CONFIGURED(x) ((x)->flags & FLAG_CONFIGURED)
#define IS_ONEANDONLY(x) ((x)->flags & FLAG_ONEANDONLY)
/* Prototypes for standalone methods */
int fwtk_init __P((struct passwd *pw, char **prompt, sudo_auth *auth));
int fwtk_verify __P((struct passwd *pw, char *prompt, sudo_auth *auth));
int fwtk_cleanup __P((struct passwd *pw, sudo_auth *auth));
int pam_init __P((struct passwd *pw, char **prompt, sudo_auth *auth));
int pam_verify __P((struct passwd *pw, char *prompt, sudo_auth *auth));
int pam_cleanup __P((struct passwd *pw, sudo_auth *auth));
int sia_setup __P((struct passwd *pw, char **prompt, sudo_auth *auth));
int sia_verify __P((struct passwd *pw, char *prompt, sudo_auth *auth));
int sia_cleanup __P((struct passwd *pw, sudo_auth *auth));
int aixauth_verify __P((struct passwd *pw, char *pass, sudo_auth *auth));
int aixauth_cleanup __P((struct passwd *pw, sudo_auth *auth));
int bsdauth_init __P((struct passwd *pw, char **prompt, sudo_auth *auth));
int bsdauth_verify __P((struct passwd *pw, char *prompt, sudo_auth *auth));
int bsdauth_cleanup __P((struct passwd *pw, sudo_auth *auth));
/* Prototypes for normal methods */
int passwd_init __P((struct passwd *pw, char **prompt, sudo_auth *auth));
int passwd_verify __P((struct passwd *pw, char *pass, sudo_auth *auth));
int secureware_init __P((struct passwd *pw, char **prompt, sudo_auth *auth));
int secureware_verify __P((struct passwd *pw, char *pass, sudo_auth *auth));
int rfc1938_setup __P((struct passwd *pw, char **prompt, sudo_auth *auth));
int rfc1938_verify __P((struct passwd *pw, char *pass, sudo_auth *auth));
int afs_verify __P((struct passwd *pw, char *pass, sudo_auth *auth));
int dce_verify __P((struct passwd *pw, char *pass, sudo_auth *auth));
int kerb4_init __P((struct passwd *pw, char **prompt, sudo_auth *auth));
int kerb4_verify __P((struct passwd *pw, char *pass, sudo_auth *auth));
int kerb5_init __P((struct passwd *pw, char **prompt, sudo_auth *auth));
int kerb5_verify __P((struct passwd *pw, char *pass, sudo_auth *auth));
int kerb5_cleanup __P((struct passwd *pw, sudo_auth *auth));
int securid_init __P((struct passwd *pw, char **prompt, sudo_auth *auth));
int securid_setup __P((struct passwd *pw, char **prompt, sudo_auth *auth));
int securid_verify __P((struct passwd *pw, char *pass, sudo_auth *auth));
/* Fields: need_root, name, init, setup, verify, cleanup */
#define AUTH_ENTRY(r, n, i, s, v, c) \
{ (r|FLAG_CONFIGURED), AUTH_FAILURE, n, NULL, i, s, v, c },
/* Some methods cannots (or should not) interoperate with any others */
#if defined(HAVE_PAM)
# define AUTH_STANDALONE \
AUTH_ENTRY(0, "pam", \
pam_init, NULL, pam_verify, pam_cleanup)
#elif defined(HAVE_SECURID)
# define AUTH_STANDALONE \
AUTH_ENTRY(0, "SecurId", \
securid_init, securid_setup, securid_verify, NULL)
#elif defined(HAVE_SIA_SES_INIT)
# define AUTH_STANDALONE \
AUTH_ENTRY(0, "sia", \
NULL, sia_setup, sia_verify, sia_cleanup)
#elif defined(HAVE_AIXAUTH)
# define AUTH_STANDALONE \
AUTH_ENTRY(0, "aixauth", \
NULL, NULL, aixauth_verify, aixauth_cleanup)
#elif defined(HAVE_FWTK)
# define AUTH_STANDALONE \
AUTH_ENTRY(0, "fwtk", \
fwtk_init, NULL, fwtk_verify, fwtk_cleanup)
#elif defined(HAVE_BSD_AUTH_H)
# define AUTH_STANDALONE \
AUTH_ENTRY(0, "bsdauth", \
bsdauth_init, NULL, bsdauth_verify, bsdauth_cleanup)
#endif
#endif /* SUDO_AUTH_H */

135
plugins/sudoers/boottime.c Normal file
View File

@@ -0,0 +1,135 @@
/*
* Copyright (c) 2009 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <config.h>
#include <sys/param.h>
#include <sys/types.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#include <limits.h>
#ifdef HAVE_SYSCTL
# include <sys/sysctl.h>
#endif
#include "compat.h"
#include "missing.h"
#if defined(__linux__)
time_t
get_boottime()
{
time_t boottime = 0;
char *line = NULL;
size_t linesize = 0;
ssize_t len;
FILE * fp;
/* read btime from /proc/stat */
fp = fopen("/proc/stat", "r");
if (fp != NULL) {
while ((len = getline(&line, &linesize, fp)) != -1) {
if (strncmp(line, "btime ", 6) == 0) {
boottime = atoi(line + 6);
break;
}
}
fclose(fp);
free(line);
}
return(boottime);
}
#elif defined(HAVE_SYSCTL) && defined(KERN_BOOTTIME)
time_t
get_boottime()
{
struct timeval tv;
time_t boottime = 0;
size_t size;
int mib[2];
mib[0] = CTL_KERN;
mib[1] = KERN_BOOTTIME;
size = sizeof(tv);
if (sysctl(mib, 2, &tv, &size, NULL, 0) != -1)
boottime = tv.tv_sec;
return(boottime);
}
#elif defined(HAVE_GETUTXID)
#include <utmpx.h>
time_t
get_boottime()
{
time_t boottime = 0;
struct utmpx *ut, key;
zero_bytes(&key, sizeof(key));
key.ut_type = BOOT_TIME;
if ((ut = getutxid(&key)) != NULL) {
boottime = ut->ut_tv.tv_sec;
endutxent();
}
return(boottime);
}
#elif defined(HAVE_GETUTID)
#include <utmp.h>
time_t
get_boottime()
{
time_t boottime = 0;
struct utmp *ut, key;
zero_bytes(&key, sizeof(key));
key.ut_type = BOOT_TIME;
if ((ut = getutid(&key)) != NULL) {
boottime = ut->ut_time;
endutent();
}
return(boottime);
}
#else
time_t
get_boottime()
{
return(0);
}
#endif

605
plugins/sudoers/check.c Normal file
View File

@@ -0,0 +1,605 @@
/*
* Copyright (c) 1993-1996,1998-2005, 2007-2009
* Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#ifndef __TANDEM
# include <sys/file.h>
#endif
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <time.h>
#include <pwd.h>
#include <grp.h>
#ifndef HAVE_TIMESPEC
# include <emul/timespec.h>
#endif
#include "sudo.h"
/* Status codes for timestamp_status() */
#define TS_CURRENT 0
#define TS_OLD 1
#define TS_MISSING 2
#define TS_NOFILE 3
#define TS_ERROR 4
/* Flags for timestamp_status() */
#define TS_MAKE_DIRS 1
#define TS_REMOVE 2
static void build_timestamp __P((char **, char **));
static int timestamp_status __P((char *, char *, char *, int));
static char *expand_prompt __P((char *, char *, char *));
static void lecture __P((int));
static void update_timestamp __P((char *, char *));
/*
* This function only returns if the user can successfully
* verify who he/she is.
*/
void
check_user(validated, mode)
int validated;
int mode;
{
char *timestampdir = NULL;
char *timestampfile = NULL;
char *prompt;
int status;
/* Always prompt for a password when -k was specified with the command. */
if (ISSET(mode, MODE_INVALIDATE)) {
SET(validated, FLAG_CHECK_USER);
} else {
if (user_uid == 0 || user_uid == runas_pw->pw_uid || user_is_exempt())
return;
}
build_timestamp(&timestampdir, &timestampfile);
status = timestamp_status(timestampdir, timestampfile, user_name,
TS_MAKE_DIRS);
if (status != TS_CURRENT || ISSET(validated, FLAG_CHECK_USER)) {
/* Bail out if we are non-interactive and a password is required */
if (ISSET(mode, MODE_NONINTERACTIVE))
errorx(1, "sorry, a password is required to run %s", getprogname());
/* If user specified -A, make sure we have an askpass helper. */
if (ISSET(tgetpass_flags, TGP_ASKPASS)) {
if (user_askpass == NULL)
log_error(NO_MAIL,
"no askpass program specified, try setting SUDO_ASKPASS");
} else if (!ISSET(tgetpass_flags, TGP_STDIN)) {
/* If no tty but DISPLAY is set, use askpass if we have it. */
if (!user_ttypath && !tty_present()) {
if (user_askpass && user_display && *user_display != '\0') {
SET(tgetpass_flags, TGP_ASKPASS);
} else if (!def_visiblepw) {
log_error(NO_MAIL,
"no tty present and no askpass program specified");
}
}
}
if (!ISSET(tgetpass_flags, TGP_ASKPASS))
lecture(status);
/* Expand any escapes in the prompt. */
prompt = expand_prompt(user_prompt ? user_prompt : def_passprompt,
user_name, user_shost);
verify_user(auth_pw, prompt);
}
/* Only update timestamp if user was validated. */
if (ISSET(validated, VALIDATE_OK) && !ISSET(mode, MODE_INVALIDATE) && status != TS_ERROR)
update_timestamp(timestampdir, timestampfile);
efree(timestampdir);
efree(timestampfile);
}
/*
* Standard sudo lecture.
*/
static void
lecture(status)
int status;
{
FILE *fp;
char buf[BUFSIZ];
ssize_t nread;
if (def_lecture == never ||
(def_lecture == once && status != TS_MISSING && status != TS_ERROR))
return;
if (def_lecture_file && (fp = fopen(def_lecture_file, "r")) != NULL) {
while ((nread = fread(buf, sizeof(char), sizeof(buf), fp)) != 0)
fwrite(buf, nread, 1, stderr);
fclose(fp);
} else {
(void) fputs("\n\
We trust you have received the usual lecture from the local System\n\
Administrator. It usually boils down to these three things:\n\
\n\
#1) Respect the privacy of others.\n\
#2) Think before you type.\n\
#3) With great power comes great responsibility.\n\n",
stderr);
}
}
/*
* Update the time on the timestamp file/dir or create it if necessary.
*/
static void
update_timestamp(timestampdir, timestampfile)
char *timestampdir;
char *timestampfile;
{
if (timestamp_uid != 0)
set_perms(PERM_TIMESTAMP);
if (touch(-1, timestampfile ? timestampfile : timestampdir, NULL) == -1) {
if (timestampfile) {
int fd = open(timestampfile, O_WRONLY|O_CREAT|O_TRUNC, 0600);
if (fd == -1)
log_error(NO_EXIT|USE_ERRNO, "Can't open %s", timestampfile);
else
close(fd);
} else {
if (mkdir(timestampdir, 0700) == -1)
log_error(NO_EXIT|USE_ERRNO, "Can't mkdir %s", timestampdir);
}
}
if (timestamp_uid != 0)
set_perms(PERM_ROOT);
}
/*
* Expand %h and %u escapes in the prompt and pass back the dynamically
* allocated result. Returns the same string if there are no escapes.
*/
static char *
expand_prompt(old_prompt, user, host)
char *old_prompt;
char *user;
char *host;
{
size_t len, n;
int subst;
char *p, *np, *new_prompt, *endp;
/* How much space do we need to malloc for the prompt? */
subst = 0;
for (p = old_prompt, len = strlen(old_prompt); *p; p++) {
if (p[0] =='%') {
switch (p[1]) {
case 'h':
p++;
len += strlen(user_shost) - 2;
subst = 1;
break;
case 'H':
p++;
len += strlen(user_host) - 2;
subst = 1;
break;
case 'p':
p++;
if (def_rootpw)
len += 2;
else if (def_targetpw || def_runaspw)
len += strlen(runas_pw->pw_name) - 2;
else
len += strlen(user_name) - 2;
subst = 1;
break;
case 'u':
p++;
len += strlen(user_name) - 2;
subst = 1;
break;
case 'U':
p++;
len += strlen(runas_pw->pw_name) - 2;
subst = 1;
break;
case '%':
p++;
len--;
subst = 1;
break;
default:
break;
}
}
}
if (subst) {
new_prompt = (char *) emalloc(++len);
endp = new_prompt + len;
for (p = old_prompt, np = new_prompt; *p; p++) {
if (p[0] =='%') {
switch (p[1]) {
case 'h':
p++;
n = strlcpy(np, user_shost, np - endp);
if (n >= np - endp)
goto oflow;
np += n;
continue;
case 'H':
p++;
n = strlcpy(np, user_host, np - endp);
if (n >= np - endp)
goto oflow;
np += n;
continue;
case 'p':
p++;
if (def_rootpw)
n = strlcpy(np, "root", np - endp);
else if (def_targetpw || def_runaspw)
n = strlcpy(np, runas_pw->pw_name, np - endp);
else
n = strlcpy(np, user_name, np - endp);
if (n >= np - endp)
goto oflow;
np += n;
continue;
case 'u':
p++;
n = strlcpy(np, user_name, np - endp);
if (n >= np - endp)
goto oflow;
np += n;
continue;
case 'U':
p++;
n = strlcpy(np, runas_pw->pw_name, np - endp);
if (n >= np - endp)
goto oflow;
np += n;
continue;
case '%':
/* convert %% -> % */
p++;
break;
default:
/* no conversion */
break;
}
}
*np++ = *p;
if (np >= endp)
goto oflow;
}
*np = '\0';
} else
new_prompt = old_prompt;
return(new_prompt);
oflow:
/* We pre-allocate enough space, so this should never happen. */
errorx(1, "internal error, expand_prompt() overflow");
}
/*
* Checks if the user is exempt from supplying a password.
*/
int
user_is_exempt()
{
if (!def_exempt_group)
return(FALSE);
return(user_in_group(sudo_user.pw, def_exempt_group));
}
/*
* Fills in timestampdir as well as timestampfile if using tty tickets.
*/
static void
build_timestamp(timestampdir, timestampfile)
char **timestampdir;
char **timestampfile;
{
char *dirparent;
int len;
dirparent = def_timestampdir;
len = easprintf(timestampdir, "%s/%s", dirparent, user_name);
if (len >= PATH_MAX)
log_error(0, "timestamp path too long: %s", *timestampdir);
/*
* Timestamp file may be a file in the directory or NUL to use
* the directory as the timestamp.
*/
if (def_tty_tickets) {
char *p;
if ((p = strrchr(user_tty, '/')))
p++;
else
p = user_tty;
if (def_targetpw)
len = easprintf(timestampfile, "%s/%s/%s:%s", dirparent, user_name,
p, runas_pw->pw_name);
else
len = easprintf(timestampfile, "%s/%s/%s", dirparent, user_name, p);
if (len >= PATH_MAX)
log_error(0, "timestamp path too long: %s", *timestampfile);
} else if (def_targetpw) {
len = easprintf(timestampfile, "%s/%s/%s", dirparent, user_name,
runas_pw->pw_name);
if (len >= PATH_MAX)
log_error(0, "timestamp path too long: %s", *timestampfile);
} else
*timestampfile = NULL;
}
/*
* Check the timestamp file and directory and return their status.
*/
static int
timestamp_status(timestampdir, timestampfile, user, flags)
char *timestampdir;
char *timestampfile;
char *user;
int flags;
{
struct stat sb;
time_t boottime, now;
char *dirparent = def_timestampdir;
int status = TS_ERROR; /* assume the worst */
if (timestamp_uid != 0)
set_perms(PERM_TIMESTAMP);
/*
* Sanity check dirparent and make it if it doesn't already exist.
* We start out assuming the worst (that the dir is not sane) and
* if it is ok upgrade the status to ``no timestamp file''.
* Note that we don't check the parent(s) of dirparent for
* sanity since the sudo dir is often just located in /tmp.
*/
if (lstat(dirparent, &sb) == 0) {
if (!S_ISDIR(sb.st_mode))
log_error(NO_EXIT, "%s exists but is not a directory (0%o)",
dirparent, (unsigned int) sb.st_mode);
else if (sb.st_uid != timestamp_uid)
log_error(NO_EXIT, "%s owned by uid %lu, should be uid %lu",
dirparent, (unsigned long) sb.st_uid,
(unsigned long) timestamp_uid);
else if ((sb.st_mode & 0000022))
log_error(NO_EXIT,
"%s writable by non-owner (0%o), should be mode 0700",
dirparent, (unsigned int) sb.st_mode);
else {
if ((sb.st_mode & 0000777) != 0700)
(void) chmod(dirparent, 0700);
status = TS_MISSING;
}
} else if (errno != ENOENT) {
log_error(NO_EXIT|USE_ERRNO, "can't stat %s", dirparent);
} else {
/* No dirparent, try to make one. */
if (ISSET(flags, TS_MAKE_DIRS)) {
if (mkdir(dirparent, S_IRWXU))
log_error(NO_EXIT|USE_ERRNO, "can't mkdir %s",
dirparent);
else
status = TS_MISSING;
}
}
if (status == TS_ERROR) {
if (timestamp_uid != 0)
set_perms(PERM_ROOT);
return(status);
}
/*
* Sanity check the user's ticket dir. We start by downgrading
* the status to TS_ERROR. If the ticket dir exists and is sane
* this will be upgraded to TS_OLD. If the dir does not exist,
* it will be upgraded to TS_MISSING.
*/
status = TS_ERROR; /* downgrade status again */
if (lstat(timestampdir, &sb) == 0) {
if (!S_ISDIR(sb.st_mode)) {
if (S_ISREG(sb.st_mode)) {
/* convert from old style */
if (unlink(timestampdir) == 0)
status = TS_MISSING;
} else
log_error(NO_EXIT, "%s exists but is not a directory (0%o)",
timestampdir, (unsigned int) sb.st_mode);
} else if (sb.st_uid != timestamp_uid)
log_error(NO_EXIT, "%s owned by uid %lu, should be uid %lu",
timestampdir, (unsigned long) sb.st_uid,
(unsigned long) timestamp_uid);
else if ((sb.st_mode & 0000022))
log_error(NO_EXIT,
"%s writable by non-owner (0%o), should be mode 0700",
timestampdir, (unsigned int) sb.st_mode);
else {
if ((sb.st_mode & 0000777) != 0700)
(void) chmod(timestampdir, 0700);
status = TS_OLD; /* do date check later */
}
} else if (errno != ENOENT) {
log_error(NO_EXIT|USE_ERRNO, "can't stat %s", timestampdir);
} else
status = TS_MISSING;
/*
* If there is no user ticket dir, AND we are in tty ticket mode,
* AND the TS_MAKE_DIRS flag is set, create the user ticket dir.
*/
if (status == TS_MISSING && timestampfile && ISSET(flags, TS_MAKE_DIRS)) {
if (mkdir(timestampdir, S_IRWXU) == -1) {
status = TS_ERROR;
log_error(NO_EXIT|USE_ERRNO, "can't mkdir %s", timestampdir);
}
}
/*
* Sanity check the tty ticket file if it exists.
*/
if (timestampfile && status != TS_ERROR) {
if (status != TS_MISSING)
status = TS_NOFILE; /* dir there, file missing */
if (!user_ttypath)
goto done; /* no tty, always prompt */
if (lstat(timestampfile, &sb) == 0) {
if (!S_ISREG(sb.st_mode)) {
status = TS_ERROR;
log_error(NO_EXIT, "%s exists but is not a regular file (0%o)",
timestampfile, (unsigned int) sb.st_mode);
} else {
/* If bad uid or file mode, complain and kill the bogus file. */
if (sb.st_uid != timestamp_uid) {
log_error(NO_EXIT,
"%s owned by uid %lu, should be uid %lu",
timestampfile, (unsigned long) sb.st_uid,
(unsigned long) timestamp_uid);
(void) unlink(timestampfile);
} else if ((sb.st_mode & 0000022)) {
log_error(NO_EXIT,
"%s writable by non-owner (0%o), should be mode 0600",
timestampfile, (unsigned int) sb.st_mode);
(void) unlink(timestampfile);
} else {
/* If not mode 0600, fix it. */
if ((sb.st_mode & 0000777) != 0600)
(void) chmod(timestampfile, 0600);
status = TS_OLD; /* actually check mtime below */
}
}
} else if (errno != ENOENT) {
log_error(NO_EXIT|USE_ERRNO, "can't stat %s", timestampfile);
status = TS_ERROR;
}
}
/*
* If the file/dir exists and we are not removing it, check its mtime.
*/
if (status == TS_OLD && !ISSET(flags, TS_REMOVE)) {
/* Negative timeouts only expire manually (sudo -k). */
if (def_timestamp_timeout < 0 && sb.st_mtime != 0)
status = TS_CURRENT;
else {
/* XXX - should use timespec here */
now = time(NULL);
boottime = get_boottime();
if (def_timestamp_timeout &&
now - sb.st_mtime < 60 * def_timestamp_timeout) {
/*
* Check for bogus time on the stampfile. The clock may
* have been set back or someone could be trying to spoof us.
*/
if (sb.st_mtime > now + 60 * def_timestamp_timeout * 2) {
log_error(NO_EXIT,
"timestamp too far in the future: %20.20s",
4 + ctime(&sb.st_mtime));
if (timestampfile)
(void) unlink(timestampfile);
else
(void) rmdir(timestampdir);
status = TS_MISSING;
} else if (sb.st_mtime < boottime) {
status = TS_OLD;
} else {
status = TS_CURRENT;
}
}
}
}
done:
if (timestamp_uid != 0)
set_perms(PERM_ROOT);
return(status);
}
/*
* Remove the timestamp ticket file/dir.
*/
void
remove_timestamp(remove)
int remove;
{
struct timespec ts;
char *timestampdir, *timestampfile, *path;
int status;
build_timestamp(&timestampdir, &timestampfile);
status = timestamp_status(timestampdir, timestampfile, user_name,
TS_REMOVE);
if (status == TS_OLD || status == TS_CURRENT) {
path = timestampfile ? timestampfile : timestampdir;
if (remove) {
if (timestampfile)
status = unlink(timestampfile);
else
status = rmdir(timestampdir);
if (status == -1 && errno != ENOENT) {
log_error(NO_EXIT, "can't remove %s (%s), will reset to Epoch",
path, strerror(errno));
remove = FALSE;
}
} else {
timespecclear(&ts);
if (touch(-1, path, &ts) == -1)
error(1, "can't reset %s to Epoch", path);
}
}
efree(timestampdir);
efree(timestampfile);
}

328
plugins/sudoers/def_data.c Normal file
View File

@@ -0,0 +1,328 @@
static struct def_values def_data_lecture[] = {
{ "never", never },
{ "once", once },
{ "always", always },
{ NULL, 0 },
};
static struct def_values def_data_listpw[] = {
{ "never", never },
{ "any", any },
{ "all", all },
{ "always", always },
{ NULL, 0 },
};
static struct def_values def_data_verifypw[] = {
{ "never", never },
{ "all", all },
{ "any", any },
{ "always", always },
{ NULL, 0 },
};
struct sudo_defs_types sudo_defs_table[] = {
{
"syslog", T_LOGFAC|T_BOOL,
"Syslog facility if syslog is being used for logging: %s",
NULL,
}, {
"syslog_goodpri", T_LOGPRI,
"Syslog priority to use when user authenticates successfully: %s",
NULL,
}, {
"syslog_badpri", T_LOGPRI,
"Syslog priority to use when user authenticates unsuccessfully: %s",
NULL,
}, {
"long_otp_prompt", T_FLAG,
"Put OTP prompt on its own line",
NULL,
}, {
"ignore_dot", T_FLAG,
"Ignore '.' in $PATH",
NULL,
}, {
"mail_always", T_FLAG,
"Always send mail when sudo is run",
NULL,
}, {
"mail_badpass", T_FLAG,
"Send mail if user authentication fails",
NULL,
}, {
"mail_no_user", T_FLAG,
"Send mail if the user is not in sudoers",
NULL,
}, {
"mail_no_host", T_FLAG,
"Send mail if the user is not in sudoers for this host",
NULL,
}, {
"mail_no_perms", T_FLAG,
"Send mail if the user is not allowed to run a command",
NULL,
}, {
"tty_tickets", T_FLAG,
"Use a separate timestamp for each user/tty combo",
NULL,
}, {
"lecture", T_TUPLE|T_BOOL,
"Lecture user the first time they run sudo",
def_data_lecture,
}, {
"lecture_file", T_STR|T_PATH|T_BOOL,
"File containing the sudo lecture: %s",
NULL,
}, {
"authenticate", T_FLAG,
"Require users to authenticate by default",
NULL,
}, {
"root_sudo", T_FLAG,
"Root may run sudo",
NULL,
}, {
"log_host", T_FLAG,
"Log the hostname in the (non-syslog) log file",
NULL,
}, {
"log_year", T_FLAG,
"Log the year in the (non-syslog) log file",
NULL,
}, {
"shell_noargs", T_FLAG,
"If sudo is invoked with no arguments, start a shell",
NULL,
}, {
"set_home", T_FLAG,
"Set $HOME to the target user when starting a shell with -s",
NULL,
}, {
"always_set_home", T_FLAG,
"Always set $HOME to the target user's home directory",
NULL,
}, {
"path_info", T_FLAG,
"Allow some information gathering to give useful error messages",
NULL,
}, {
"fqdn", T_FLAG,
"Require fully-qualified hostnames in the sudoers file",
NULL,
}, {
"insults", T_FLAG,
"Insult the user when they enter an incorrect password",
NULL,
}, {
"requiretty", T_FLAG,
"Only allow the user to run sudo if they have a tty",
NULL,
}, {
"env_editor", T_FLAG,
"Visudo will honor the EDITOR environment variable",
NULL,
}, {
"rootpw", T_FLAG,
"Prompt for root's password, not the users's",
NULL,
}, {
"runaspw", T_FLAG,
"Prompt for the runas_default user's password, not the users's",
NULL,
}, {
"targetpw", T_FLAG,
"Prompt for the target user's password, not the users's",
NULL,
}, {
"use_loginclass", T_FLAG,
"Apply defaults in the target user's login class if there is one",
NULL,
}, {
"set_logname", T_FLAG,
"Set the LOGNAME and USER environment variables",
NULL,
}, {
"stay_setuid", T_FLAG,
"Only set the effective uid to the target user, not the real uid",
NULL,
}, {
"preserve_groups", T_FLAG,
"Don't initialize the group vector to that of the target user",
NULL,
}, {
"loglinelen", T_UINT|T_BOOL,
"Length at which to wrap log file lines (0 for no wrap): %d",
NULL,
}, {
"timestamp_timeout", T_FLOAT|T_BOOL,
"Authentication timestamp timeout: %.1f minutes",
NULL,
}, {
"passwd_timeout", T_FLOAT|T_BOOL,
"Password prompt timeout: %.1f minutes",
NULL,
}, {
"passwd_tries", T_UINT,
"Number of tries to enter a password: %d",
NULL,
}, {
"umask", T_MODE|T_BOOL,
"Umask to use or 0777 to use user's: 0%o",
NULL,
}, {
"logfile", T_STR|T_BOOL|T_PATH,
"Path to log file: %s",
NULL,
}, {
"mailerpath", T_STR|T_BOOL|T_PATH,
"Path to mail program: %s",
NULL,
}, {
"mailerflags", T_STR|T_BOOL,
"Flags for mail program: %s",
NULL,
}, {
"mailto", T_STR|T_BOOL,
"Address to send mail to: %s",
NULL,
}, {
"mailfrom", T_STR|T_BOOL,
"Address to send mail from: %s",
NULL,
}, {
"mailsub", T_STR,
"Subject line for mail messages: %s",
NULL,
}, {
"badpass_message", T_STR,
"Incorrect password message: %s",
NULL,
}, {
"timestampdir", T_STR|T_PATH,
"Path to authentication timestamp dir: %s",
NULL,
}, {
"timestampowner", T_STR,
"Owner of the authentication timestamp dir: %s",
NULL,
}, {
"exempt_group", T_STR|T_BOOL,
"Users in this group are exempt from password and PATH requirements: %s",
NULL,
}, {
"passprompt", T_STR,
"Default password prompt: %s",
NULL,
}, {
"passprompt_override", T_FLAG,
"If set, passprompt will override system prompt in all cases.",
NULL,
}, {
"runas_default", T_STR,
"Default user to run commands as: %s",
NULL,
}, {
"secure_path", T_STR|T_BOOL,
"Value to override user's $PATH with: %s",
NULL,
}, {
"editor", T_STR|T_PATH,
"Path to the editor for use by visudo: %s",
NULL,
}, {
"listpw", T_TUPLE|T_BOOL,
"When to require a password for 'list' pseudocommand: %s",
def_data_listpw,
}, {
"verifypw", T_TUPLE|T_BOOL,
"When to require a password for 'verify' pseudocommand: %s",
def_data_verifypw,
}, {
"noexec", T_FLAG,
"Preload the dummy exec functions contained in 'noexec_file'",
NULL,
}, {
"noexec_file", T_STR|T_PATH,
"File containing dummy exec functions: %s",
NULL,
}, {
"ignore_local_sudoers", T_FLAG,
"If LDAP directory is up, do we ignore local sudoers file",
NULL,
}, {
"closefrom", T_INT,
"File descriptors >= %d will be closed before executing a command",
NULL,
}, {
"closefrom_override", T_FLAG,
"If set, users may override the value of `closefrom' with the -C option",
NULL,
}, {
"setenv", T_FLAG,
"Allow users to set arbitrary environment variables",
NULL,
}, {
"env_reset", T_FLAG,
"Reset the environment to a default set of variables",
NULL,
}, {
"env_check", T_LIST|T_BOOL,
"Environment variables to check for sanity:",
NULL,
}, {
"env_delete", T_LIST|T_BOOL,
"Environment variables to remove:",
NULL,
}, {
"env_keep", T_LIST|T_BOOL,
"Environment variables to preserve:",
NULL,
}, {
"role", T_STR,
"SELinux role to use in the new security context: %s",
NULL,
}, {
"type", T_STR,
"SELinux type to use in the new security context: %s",
NULL,
}, {
"askpass", T_STR|T_PATH|T_BOOL,
"Path to the askpass helper program: %s",
NULL,
}, {
"env_file", T_STR|T_PATH|T_BOOL,
"Path to the sudo-specific environment file: %s",
NULL,
}, {
"sudoers_locale", T_STR,
"Locale to use while parsing sudoers: %s",
NULL,
}, {
"visiblepw", T_FLAG,
"Allow sudo to prompt for a password even if it would be visisble",
NULL,
}, {
"pwfeedback", T_FLAG,
"Provide visual feedback at the password prompt when there is user input",
NULL,
}, {
"fast_glob", T_FLAG,
"Use faster globbing that is less accurate but does not access the filesystem",
NULL,
}, {
"umask_override", T_FLAG,
"The umask specified in sudoers will override the user's, even if it is more permissive",
NULL,
}, {
"transcript", T_FLAG,
"Log a transcript of the command being run",
NULL,
}, {
"compress_transcript", T_FLAG,
"Compress session transcripts with zlib",
NULL,
}, {
NULL, 0, NULL
}
};

158
plugins/sudoers/def_data.h Normal file
View File

@@ -0,0 +1,158 @@
#define def_syslog (sudo_defs_table[0].sd_un.ival)
#define I_SYSLOG 0
#define def_syslog_goodpri (sudo_defs_table[1].sd_un.ival)
#define I_SYSLOG_GOODPRI 1
#define def_syslog_badpri (sudo_defs_table[2].sd_un.ival)
#define I_SYSLOG_BADPRI 2
#define def_long_otp_prompt (sudo_defs_table[3].sd_un.flag)
#define I_LONG_OTP_PROMPT 3
#define def_ignore_dot (sudo_defs_table[4].sd_un.flag)
#define I_IGNORE_DOT 4
#define def_mail_always (sudo_defs_table[5].sd_un.flag)
#define I_MAIL_ALWAYS 5
#define def_mail_badpass (sudo_defs_table[6].sd_un.flag)
#define I_MAIL_BADPASS 6
#define def_mail_no_user (sudo_defs_table[7].sd_un.flag)
#define I_MAIL_NO_USER 7
#define def_mail_no_host (sudo_defs_table[8].sd_un.flag)
#define I_MAIL_NO_HOST 8
#define def_mail_no_perms (sudo_defs_table[9].sd_un.flag)
#define I_MAIL_NO_PERMS 9
#define def_tty_tickets (sudo_defs_table[10].sd_un.flag)
#define I_TTY_TICKETS 10
#define def_lecture (sudo_defs_table[11].sd_un.tuple)
#define I_LECTURE 11
#define def_lecture_file (sudo_defs_table[12].sd_un.str)
#define I_LECTURE_FILE 12
#define def_authenticate (sudo_defs_table[13].sd_un.flag)
#define I_AUTHENTICATE 13
#define def_root_sudo (sudo_defs_table[14].sd_un.flag)
#define I_ROOT_SUDO 14
#define def_log_host (sudo_defs_table[15].sd_un.flag)
#define I_LOG_HOST 15
#define def_log_year (sudo_defs_table[16].sd_un.flag)
#define I_LOG_YEAR 16
#define def_shell_noargs (sudo_defs_table[17].sd_un.flag)
#define I_SHELL_NOARGS 17
#define def_set_home (sudo_defs_table[18].sd_un.flag)
#define I_SET_HOME 18
#define def_always_set_home (sudo_defs_table[19].sd_un.flag)
#define I_ALWAYS_SET_HOME 19
#define def_path_info (sudo_defs_table[20].sd_un.flag)
#define I_PATH_INFO 20
#define def_fqdn (sudo_defs_table[21].sd_un.flag)
#define I_FQDN 21
#define def_insults (sudo_defs_table[22].sd_un.flag)
#define I_INSULTS 22
#define def_requiretty (sudo_defs_table[23].sd_un.flag)
#define I_REQUIRETTY 23
#define def_env_editor (sudo_defs_table[24].sd_un.flag)
#define I_ENV_EDITOR 24
#define def_rootpw (sudo_defs_table[25].sd_un.flag)
#define I_ROOTPW 25
#define def_runaspw (sudo_defs_table[26].sd_un.flag)
#define I_RUNASPW 26
#define def_targetpw (sudo_defs_table[27].sd_un.flag)
#define I_TARGETPW 27
#define def_use_loginclass (sudo_defs_table[28].sd_un.flag)
#define I_USE_LOGINCLASS 28
#define def_set_logname (sudo_defs_table[29].sd_un.flag)
#define I_SET_LOGNAME 29
#define def_stay_setuid (sudo_defs_table[30].sd_un.flag)
#define I_STAY_SETUID 30
#define def_preserve_groups (sudo_defs_table[31].sd_un.flag)
#define I_PRESERVE_GROUPS 31
#define def_loglinelen (sudo_defs_table[32].sd_un.ival)
#define I_LOGLINELEN 32
#define def_timestamp_timeout (sudo_defs_table[33].sd_un.fval)
#define I_TIMESTAMP_TIMEOUT 33
#define def_passwd_timeout (sudo_defs_table[34].sd_un.fval)
#define I_PASSWD_TIMEOUT 34
#define def_passwd_tries (sudo_defs_table[35].sd_un.ival)
#define I_PASSWD_TRIES 35
#define def_umask (sudo_defs_table[36].sd_un.mode)
#define I_UMASK 36
#define def_logfile (sudo_defs_table[37].sd_un.str)
#define I_LOGFILE 37
#define def_mailerpath (sudo_defs_table[38].sd_un.str)
#define I_MAILERPATH 38
#define def_mailerflags (sudo_defs_table[39].sd_un.str)
#define I_MAILERFLAGS 39
#define def_mailto (sudo_defs_table[40].sd_un.str)
#define I_MAILTO 40
#define def_mailfrom (sudo_defs_table[41].sd_un.str)
#define I_MAILFROM 41
#define def_mailsub (sudo_defs_table[42].sd_un.str)
#define I_MAILSUB 42
#define def_badpass_message (sudo_defs_table[43].sd_un.str)
#define I_BADPASS_MESSAGE 43
#define def_timestampdir (sudo_defs_table[44].sd_un.str)
#define I_TIMESTAMPDIR 44
#define def_timestampowner (sudo_defs_table[45].sd_un.str)
#define I_TIMESTAMPOWNER 45
#define def_exempt_group (sudo_defs_table[46].sd_un.str)
#define I_EXEMPT_GROUP 46
#define def_passprompt (sudo_defs_table[47].sd_un.str)
#define I_PASSPROMPT 47
#define def_passprompt_override (sudo_defs_table[48].sd_un.flag)
#define I_PASSPROMPT_OVERRIDE 48
#define def_runas_default (sudo_defs_table[49].sd_un.str)
#define I_RUNAS_DEFAULT 49
#define def_secure_path (sudo_defs_table[50].sd_un.str)
#define I_SECURE_PATH 50
#define def_editor (sudo_defs_table[51].sd_un.str)
#define I_EDITOR 51
#define def_listpw (sudo_defs_table[52].sd_un.tuple)
#define I_LISTPW 52
#define def_verifypw (sudo_defs_table[53].sd_un.tuple)
#define I_VERIFYPW 53
#define def_noexec (sudo_defs_table[54].sd_un.flag)
#define I_NOEXEC 54
#define def_noexec_file (sudo_defs_table[55].sd_un.str)
#define I_NOEXEC_FILE 55
#define def_ignore_local_sudoers (sudo_defs_table[56].sd_un.flag)
#define I_IGNORE_LOCAL_SUDOERS 56
#define def_closefrom (sudo_defs_table[57].sd_un.ival)
#define I_CLOSEFROM 57
#define def_closefrom_override (sudo_defs_table[58].sd_un.flag)
#define I_CLOSEFROM_OVERRIDE 58
#define def_setenv (sudo_defs_table[59].sd_un.flag)
#define I_SETENV 59
#define def_env_reset (sudo_defs_table[60].sd_un.flag)
#define I_ENV_RESET 60
#define def_env_check (sudo_defs_table[61].sd_un.list)
#define I_ENV_CHECK 61
#define def_env_delete (sudo_defs_table[62].sd_un.list)
#define I_ENV_DELETE 62
#define def_env_keep (sudo_defs_table[63].sd_un.list)
#define I_ENV_KEEP 63
#define def_role (sudo_defs_table[64].sd_un.str)
#define I_ROLE 64
#define def_type (sudo_defs_table[65].sd_un.str)
#define I_TYPE 65
#define def_askpass (sudo_defs_table[66].sd_un.str)
#define I_ASKPASS 66
#define def_env_file (sudo_defs_table[67].sd_un.str)
#define I_ENV_FILE 67
#define def_sudoers_locale (sudo_defs_table[68].sd_un.str)
#define I_SUDOERS_LOCALE 68
#define def_visiblepw (sudo_defs_table[69].sd_un.flag)
#define I_VISIBLEPW 69
#define def_pwfeedback (sudo_defs_table[70].sd_un.flag)
#define I_PWFEEDBACK 70
#define def_fast_glob (sudo_defs_table[71].sd_un.flag)
#define I_FAST_GLOB 71
#define def_umask_override (sudo_defs_table[72].sd_un.flag)
#define I_UMASK_OVERRIDE 72
#define def_transcript (sudo_defs_table[73].sd_un.flag)
#define I_TRANSCRIPT 73
#define def_compress_transcript (sudo_defs_table[74].sd_un.flag)
#define I_COMPRESS_TRANSCRIPT 74
enum def_tupple {
never,
once,
always,
any,
all
};

240
plugins/sudoers/def_data.in Normal file
View File

@@ -0,0 +1,240 @@
#
# Format:
#
# var_name
# TYPE
# description (or NULL)
# array of struct def_values if TYPE == T_TUPLE
#
# NOTE: for tuples that can be used in a boolean context the first
# value corresponds to boolean FALSE and the second to TRUE.
#
syslog
T_LOGFAC|T_BOOL
"Syslog facility if syslog is being used for logging: %s"
syslog_goodpri
T_LOGPRI
"Syslog priority to use when user authenticates successfully: %s"
syslog_badpri
T_LOGPRI
"Syslog priority to use when user authenticates unsuccessfully: %s"
long_otp_prompt
T_FLAG
"Put OTP prompt on its own line"
ignore_dot
T_FLAG
"Ignore '.' in $PATH"
mail_always
T_FLAG
"Always send mail when sudo is run"
mail_badpass
T_FLAG
"Send mail if user authentication fails"
mail_no_user
T_FLAG
"Send mail if the user is not in sudoers"
mail_no_host
T_FLAG
"Send mail if the user is not in sudoers for this host"
mail_no_perms
T_FLAG
"Send mail if the user is not allowed to run a command"
tty_tickets
T_FLAG
"Use a separate timestamp for each user/tty combo"
lecture
T_TUPLE|T_BOOL
"Lecture user the first time they run sudo"
never once always
lecture_file
T_STR|T_PATH|T_BOOL
"File containing the sudo lecture: %s"
authenticate
T_FLAG
"Require users to authenticate by default"
root_sudo
T_FLAG
"Root may run sudo"
log_host
T_FLAG
"Log the hostname in the (non-syslog) log file"
log_year
T_FLAG
"Log the year in the (non-syslog) log file"
shell_noargs
T_FLAG
"If sudo is invoked with no arguments, start a shell"
set_home
T_FLAG
"Set $HOME to the target user when starting a shell with -s"
always_set_home
T_FLAG
"Always set $HOME to the target user's home directory"
path_info
T_FLAG
"Allow some information gathering to give useful error messages"
fqdn
T_FLAG
"Require fully-qualified hostnames in the sudoers file"
insults
T_FLAG
"Insult the user when they enter an incorrect password"
requiretty
T_FLAG
"Only allow the user to run sudo if they have a tty"
env_editor
T_FLAG
"Visudo will honor the EDITOR environment variable"
rootpw
T_FLAG
"Prompt for root's password, not the users's"
runaspw
T_FLAG
"Prompt for the runas_default user's password, not the users's"
targetpw
T_FLAG
"Prompt for the target user's password, not the users's"
use_loginclass
T_FLAG
"Apply defaults in the target user's login class if there is one"
set_logname
T_FLAG
"Set the LOGNAME and USER environment variables"
stay_setuid
T_FLAG
"Only set the effective uid to the target user, not the real uid"
preserve_groups
T_FLAG
"Don't initialize the group vector to that of the target user"
loglinelen
T_UINT|T_BOOL
"Length at which to wrap log file lines (0 for no wrap): %d"
timestamp_timeout
T_FLOAT|T_BOOL
"Authentication timestamp timeout: %.1f minutes"
passwd_timeout
T_FLOAT|T_BOOL
"Password prompt timeout: %.1f minutes"
passwd_tries
T_UINT
"Number of tries to enter a password: %d"
umask
T_MODE|T_BOOL
"Umask to use or 0777 to use user's: 0%o"
logfile
T_STR|T_BOOL|T_PATH
"Path to log file: %s"
mailerpath
T_STR|T_BOOL|T_PATH
"Path to mail program: %s"
mailerflags
T_STR|T_BOOL
"Flags for mail program: %s"
mailto
T_STR|T_BOOL
"Address to send mail to: %s"
mailfrom
T_STR|T_BOOL
"Address to send mail from: %s"
mailsub
T_STR
"Subject line for mail messages: %s"
badpass_message
T_STR
"Incorrect password message: %s"
timestampdir
T_STR|T_PATH
"Path to authentication timestamp dir: %s"
timestampowner
T_STR
"Owner of the authentication timestamp dir: %s"
exempt_group
T_STR|T_BOOL
"Users in this group are exempt from password and PATH requirements: %s"
passprompt
T_STR
"Default password prompt: %s"
passprompt_override
T_FLAG
"If set, passprompt will override system prompt in all cases."
runas_default
T_STR
"Default user to run commands as: %s"
secure_path
T_STR|T_BOOL
"Value to override user's $PATH with: %s"
editor
T_STR|T_PATH
"Path to the editor for use by visudo: %s"
listpw
T_TUPLE|T_BOOL
"When to require a password for 'list' pseudocommand: %s"
never any all always
verifypw
T_TUPLE|T_BOOL
"When to require a password for 'verify' pseudocommand: %s"
never all any always
noexec
T_FLAG
"Preload the dummy exec functions contained in 'noexec_file'"
noexec_file
T_STR|T_PATH
"File containing dummy exec functions: %s"
ignore_local_sudoers
T_FLAG
"If LDAP directory is up, do we ignore local sudoers file"
closefrom
T_INT
"File descriptors >= %d will be closed before executing a command"
closefrom_override
T_FLAG
"If set, users may override the value of `closefrom' with the -C option"
setenv
T_FLAG
"Allow users to set arbitrary environment variables"
env_reset
T_FLAG
"Reset the environment to a default set of variables"
env_check
T_LIST|T_BOOL
"Environment variables to check for sanity:"
env_delete
T_LIST|T_BOOL
"Environment variables to remove:"
env_keep
T_LIST|T_BOOL
"Environment variables to preserve:"
role
T_STR
"SELinux role to use in the new security context: %s"
type
T_STR
"SELinux type to use in the new security context: %s"
askpass
T_STR|T_PATH|T_BOOL
"Path to the askpass helper program: %s"
env_file
T_STR|T_PATH|T_BOOL
"Path to the sudo-specific environment file: %s"
sudoers_locale
T_STR
"Locale to use while parsing sudoers: %s"
visiblepw
T_FLAG
"Allow sudo to prompt for a password even if it would be visisble"
pwfeedback
T_FLAG
"Provide visual feedback at the password prompt when there is user input"
fast_glob
T_FLAG
"Use faster globbing that is less accurate but does not access the filesystem"
umask_override
T_FLAG
"The umask specified in sudoers will override the user's, even if it is more permissive"
transcript
T_FLAG
"Log a transcript of the command being run"
compress_transcript
T_FLAG
"Compress session transcripts with zlib"

843
plugins/sudoers/defaults.c Normal file
View File

@@ -0,0 +1,843 @@
/*
* Copyright (c) 1999-2005, 2007-2008
* Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
# ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <pwd.h>
#include <ctype.h>
#include "sudo.h"
#include "parse.h"
#include <gram.h>
/*
* For converting between syslog numbers and strings.
*/
struct strmap {
char *name;
int num;
};
#ifdef LOG_NFACILITIES
static struct strmap facilities[] = {
#ifdef LOG_AUTHPRIV
{ "authpriv", LOG_AUTHPRIV },
#endif
{ "auth", LOG_AUTH },
{ "daemon", LOG_DAEMON },
{ "user", LOG_USER },
{ "local0", LOG_LOCAL0 },
{ "local1", LOG_LOCAL1 },
{ "local2", LOG_LOCAL2 },
{ "local3", LOG_LOCAL3 },
{ "local4", LOG_LOCAL4 },
{ "local5", LOG_LOCAL5 },
{ "local6", LOG_LOCAL6 },
{ "local7", LOG_LOCAL7 },
{ NULL, -1 }
};
#endif /* LOG_NFACILITIES */
static struct strmap priorities[] = {
{ "alert", LOG_ALERT },
{ "crit", LOG_CRIT },
{ "debug", LOG_DEBUG },
{ "emerg", LOG_EMERG },
{ "err", LOG_ERR },
{ "info", LOG_INFO },
{ "notice", LOG_NOTICE },
{ "warning", LOG_WARNING },
{ NULL, -1 }
};
/*
* Local prototypes.
*/
static int store_int __P((char *, struct sudo_defs_types *, int));
static int store_list __P((char *, struct sudo_defs_types *, int));
static int store_mode __P((char *, struct sudo_defs_types *, int));
static int store_str __P((char *, struct sudo_defs_types *, int));
static int store_syslogfac __P((char *, struct sudo_defs_types *, int));
static int store_syslogpri __P((char *, struct sudo_defs_types *, int));
static int store_tuple __P((char *, struct sudo_defs_types *, int));
static int store_uint __P((char *, struct sudo_defs_types *, int));
static int store_float __P((char *, struct sudo_defs_types *, int));
static void list_op __P((char *, size_t, struct sudo_defs_types *, enum list_ops));
static const char *logfac2str __P((int));
static const char *logpri2str __P((int));
/*
* Table describing compile-time and run-time options.
*/
#include <def_data.c>
/*
* Print version and configure info.
*/
void
dump_defaults()
{
struct sudo_defs_types *cur;
struct list_member *item;
struct def_values *def;
for (cur = sudo_defs_table; cur->name; cur++) {
if (cur->desc) {
switch (cur->type & T_MASK) {
case T_FLAG:
if (cur->sd_un.flag)
puts(cur->desc);
break;
case T_STR:
if (cur->sd_un.str) {
(void) printf(cur->desc, cur->sd_un.str);
putchar('\n');
}
break;
case T_LOGFAC:
if (cur->sd_un.ival) {
(void) printf(cur->desc, logfac2str(cur->sd_un.ival));
putchar('\n');
}
break;
case T_LOGPRI:
if (cur->sd_un.ival) {
(void) printf(cur->desc, logpri2str(cur->sd_un.ival));
putchar('\n');
}
break;
case T_UINT:
case T_INT:
(void) printf(cur->desc, cur->sd_un.ival);
putchar('\n');
break;
case T_FLOAT:
(void) printf(cur->desc, cur->sd_un.fval);
putchar('\n');
break;
case T_MODE:
(void) printf(cur->desc, cur->sd_un.mode);
putchar('\n');
break;
case T_LIST:
if (cur->sd_un.list) {
puts(cur->desc);
for (item = cur->sd_un.list; item; item = item->next)
printf("\t%s\n", item->value);
}
break;
case T_TUPLE:
for (def = cur->values; def->sval; def++) {
if (cur->sd_un.ival == def->ival) {
(void) printf(cur->desc, def->sval);
break;
}
}
putchar('\n');
break;
}
}
}
}
/*
* List each option along with its description.
*/
void
list_options()
{
struct sudo_defs_types *cur;
char *p;
(void) puts("Available options in a sudoers ``Defaults'' line:\n");
for (cur = sudo_defs_table; cur->name; cur++) {
if (cur->name && cur->desc) {
switch (cur->type & T_MASK) {
case T_FLAG:
(void) printf("%s: %s\n", cur->name, cur->desc);
break;
default:
p = strrchr(cur->desc, ':');
if (p)
(void) printf("%s: %.*s\n", cur->name,
(int) (p - cur->desc), cur->desc);
else
(void) printf("%s: %s\n", cur->name, cur->desc);
break;
}
}
}
}
/*
* Sets/clears an entry in the defaults structure
* If a variable that takes a value is used in a boolean
* context with op == 0, disable that variable.
* Eg. you may want to turn off logging to a file for some hosts.
* This is only meaningful for variables that are *optional*.
*/
int
set_default(var, val, op)
char *var;
char *val;
int op; /* TRUE or FALSE */
{
struct sudo_defs_types *cur;
int num;
for (cur = sudo_defs_table, num = 0; cur->name; cur++, num++) {
if (strcmp(var, cur->name) == 0)
break;
}
if (!cur->name) {
warningx("unknown defaults entry `%s'", var);
return(FALSE);
}
switch (cur->type & T_MASK) {
case T_LOGFAC:
if (!store_syslogfac(val, cur, op)) {
if (val)
warningx("value `%s' is invalid for option `%s'", val, var);
else
warningx("no value specified for `%s'", var);
return(FALSE);
}
break;
case T_LOGPRI:
if (!store_syslogpri(val, cur, op)) {
if (val)
warningx("value `%s' is invalid for option `%s'", val, var);
else
warningx("no value specified for `%s'", var);
return(FALSE);
}
break;
case T_STR:
if (!val) {
/* Check for bogus boolean usage or lack of a value. */
if (!ISSET(cur->type, T_BOOL) || op != FALSE) {
warningx("no value specified for `%s'", var);
return(FALSE);
}
}
if (ISSET(cur->type, T_PATH) && val && *val != '/') {
warningx("values for `%s' must start with a '/'", var);
return(FALSE);
}
if (!store_str(val, cur, op)) {
warningx("value `%s' is invalid for option `%s'", val, var);
return(FALSE);
}
break;
case T_INT:
if (!val) {
/* Check for bogus boolean usage or lack of a value. */
if (!ISSET(cur->type, T_BOOL) || op != FALSE) {
warningx("no value specified for `%s'", var);
return(FALSE);
}
}
if (!store_int(val, cur, op)) {
warningx("value `%s' is invalid for option `%s'", val, var);
return(FALSE);
}
break;
case T_UINT:
if (!val) {
/* Check for bogus boolean usage or lack of a value. */
if (!ISSET(cur->type, T_BOOL) || op != FALSE) {
warningx("no value specified for `%s'", var);
return(FALSE);
}
}
if (!store_uint(val, cur, op)) {
warningx("value `%s' is invalid for option `%s'", val, var);
return(FALSE);
}
break;
case T_FLOAT:
if (!val) {
/* Check for bogus boolean usage or lack of a value. */
if (!ISSET(cur->type, T_BOOL) || op != FALSE) {
warningx("no value specified for `%s'", var);
return(FALSE);
}
}
if (!store_float(val, cur, op)) {
warningx("value `%s' is invalid for option `%s'", val, var);
return(FALSE);
}
break;
case T_MODE:
if (!val) {
/* Check for bogus boolean usage or lack of a value. */
if (!ISSET(cur->type, T_BOOL) || op != FALSE) {
warningx("no value specified for `%s'", var);
return(FALSE);
}
}
if (!store_mode(val, cur, op)) {
warningx("value `%s' is invalid for option `%s'", val, var);
return(FALSE);
}
break;
case T_FLAG:
if (val) {
warningx("option `%s' does not take a value", var);
return(FALSE);
}
cur->sd_un.flag = op;
/* Special action for I_FQDN. Move to own switch if we get more */
if (num == I_FQDN && op)
set_fqdn();
break;
case T_LIST:
if (!val) {
/* Check for bogus boolean usage or lack of a value. */
if (!ISSET(cur->type, T_BOOL) || op != FALSE) {
warningx("no value specified for `%s'", var);
return(FALSE);
}
}
if (!store_list(val, cur, op)) {
warningx("value `%s' is invalid for option `%s'", val, var);
return(FALSE);
}
break;
case T_TUPLE:
if (!val && !ISSET(cur->type, T_BOOL)) {
warningx("no value specified for `%s'", var);
return(FALSE);
}
if (!store_tuple(val, cur, op)) {
warningx("value `%s' is invalid for option `%s'", val, var);
return(FALSE);
}
break;
}
return(TRUE);
}
/*
* Set default options to compiled-in values.
* Any of these may be overridden at runtime by a "Defaults" file.
*/
void
init_defaults()
{
static int firsttime = 1;
struct sudo_defs_types *def;
/* Clear any old settings. */
if (!firsttime) {
for (def = sudo_defs_table; def->name; def++) {
switch (def->type & T_MASK) {
case T_STR:
efree(def->sd_un.str);
def->sd_un.str = NULL;
break;
case T_LIST:
list_op(NULL, 0, def, freeall);
break;
}
zero_bytes(&def->sd_un, sizeof(def->sd_un));
}
}
/* First initialize the flags. */
#ifdef LONG_OTP_PROMPT
def_long_otp_prompt = TRUE;
#endif
#ifdef IGNORE_DOT_PATH
def_ignore_dot = TRUE;
#endif
#ifdef ALWAYS_SEND_MAIL
def_mail_always = TRUE;
#endif
#ifdef SEND_MAIL_WHEN_NO_USER
def_mail_no_user = TRUE;
#endif
#ifdef SEND_MAIL_WHEN_NO_HOST
def_mail_no_host = TRUE;
#endif
#ifdef SEND_MAIL_WHEN_NOT_OK
def_mail_no_perms = TRUE;
#endif
#ifdef USE_TTY_TICKETS
def_tty_tickets = TRUE;
#endif
#ifndef NO_LECTURE
def_lecture = once;
#endif
#ifndef NO_AUTHENTICATION
def_authenticate = TRUE;
#endif
#ifndef NO_ROOT_SUDO
def_root_sudo = TRUE;
#endif
#ifdef HOST_IN_LOG
def_log_host = TRUE;
#endif
#ifdef SHELL_IF_NO_ARGS
def_shell_noargs = TRUE;
#endif
#ifdef SHELL_SETS_HOME
def_set_home = TRUE;
#endif
#ifndef DONT_LEAK_PATH_INFO
def_path_info = TRUE;
#endif
#ifdef FQDN
def_fqdn = TRUE;
#endif
#ifdef USE_INSULTS
def_insults = TRUE;
#endif
#ifdef ENV_EDITOR
def_env_editor = TRUE;
#endif
#ifdef _PATH_SUDO_ASKPASS
def_askpass = estrdup(_PATH_SUDO_ASKPASS);
#endif
def_sudoers_locale = estrdup("C");
def_env_reset = TRUE;
def_set_logname = TRUE;
def_closefrom = STDERR_FILENO + 1;
/* Syslog options need special care since they both strings and ints */
#if (LOGGING & SLOG_SYSLOG)
(void) store_syslogfac(LOGFAC, &sudo_defs_table[I_SYSLOG], TRUE);
(void) store_syslogpri(PRI_SUCCESS, &sudo_defs_table[I_SYSLOG_GOODPRI],
TRUE);
(void) store_syslogpri(PRI_FAILURE, &sudo_defs_table[I_SYSLOG_BADPRI],
TRUE);
#endif
/* Password flags also have a string and integer component. */
(void) store_tuple("any", &sudo_defs_table[I_LISTPW], TRUE);
(void) store_tuple("all", &sudo_defs_table[I_VERIFYPW], TRUE);
/* Then initialize the int-like things. */
#ifdef SUDO_UMASK
def_umask = SUDO_UMASK;
#else
def_umask = 0777;
#endif
def_loglinelen = MAXLOGFILELEN;
def_timestamp_timeout = TIMEOUT;
def_passwd_timeout = PASSWORD_TIMEOUT;
def_passwd_tries = TRIES_FOR_PASSWORD;
#ifdef HAVE_ZLIB
def_compress_transcript = TRUE;
#endif
/* Now do the strings */
def_mailto = estrdup(MAILTO);
def_mailsub = estrdup(MAILSUBJECT);
def_badpass_message = estrdup(INCORRECT_PASSWORD);
def_timestampdir = estrdup(_PATH_SUDO_TIMEDIR);
def_passprompt = estrdup(PASSPROMPT);
def_runas_default = estrdup(RUNAS_DEFAULT);
#ifdef _PATH_SUDO_SENDMAIL
def_mailerpath = estrdup(_PATH_SUDO_SENDMAIL);
def_mailerflags = estrdup("-t");
#endif
#if (LOGGING & SLOG_FILE)
def_logfile = estrdup(_PATH_SUDO_LOGFILE);
#endif
#ifdef EXEMPTGROUP
def_exempt_group = estrdup(EXEMPTGROUP);
#endif
#ifdef SECURE_PATH
def_secure_path = estrdup(SECURE_PATH);
#endif
def_editor = estrdup(EDITOR);
#ifdef _PATH_SUDO_NOEXEC
def_noexec_file = estrdup(_PATH_SUDO_NOEXEC);
#endif
/* Finally do the lists (currently just environment tables). */
init_envtables();
firsttime = 0;
}
/*
* Update the defaults based on what was set by sudoers.
* Pass in a an OR'd list of which default types to update.
*/
int
update_defaults(what)
int what;
{
struct defaults *def;
tq_foreach_fwd(&defaults, def) {
switch (def->type) {
case DEFAULTS:
if (ISSET(what, SETDEF_GENERIC) &&
!set_default(def->var, def->val, def->op))
return(FALSE);
break;
case DEFAULTS_USER:
if (ISSET(what, SETDEF_USER) &&
userlist_matches(sudo_user.pw, &def->binding) == ALLOW &&
!set_default(def->var, def->val, def->op))
return(FALSE);
break;
case DEFAULTS_RUNAS:
if (ISSET(what, SETDEF_RUNAS) &&
runaslist_matches(&def->binding, NULL) == ALLOW &&
!set_default(def->var, def->val, def->op))
return(FALSE);
break;
case DEFAULTS_HOST:
if (ISSET(what, SETDEF_HOST) &&
hostlist_matches(&def->binding) == ALLOW &&
!set_default(def->var, def->val, def->op))
return(FALSE);
break;
case DEFAULTS_CMND:
if (ISSET(what, SETDEF_CMND) &&
cmndlist_matches(&def->binding) == ALLOW &&
!set_default(def->var, def->val, def->op))
return(FALSE);
break;
}
}
return(TRUE);
}
static int
store_int(val, def, op)
char *val;
struct sudo_defs_types *def;
int op;
{
char *endp;
long l;
if (op == FALSE) {
def->sd_un.ival = 0;
} else {
l = strtol(val, &endp, 10);
if (*endp != '\0')
return(FALSE);
/* XXX - should check against INT_MAX */
def->sd_un.ival = (int)l;
}
if (def->callback)
return(def->callback(val));
return(TRUE);
}
static int
store_uint(val, def, op)
char *val;
struct sudo_defs_types *def;
int op;
{
char *endp;
long l;
if (op == FALSE) {
def->sd_un.ival = 0;
} else {
l = strtol(val, &endp, 10);
if (*endp != '\0' || l < 0)
return(FALSE);
/* XXX - should check against INT_MAX */
def->sd_un.ival = (unsigned int)l;
}
if (def->callback)
return(def->callback(val));
return(TRUE);
}
static int
store_float(val, def, op)
char *val;
struct sudo_defs_types *def;
int op;
{
char *endp;
double d;
if (op == FALSE) {
def->sd_un.fval = 0.0;
} else {
d = strtod(val, &endp);
if (*endp != '\0')
return(FALSE);
/* XXX - should check against HUGE_VAL */
def->sd_un.fval = d;
}
if (def->callback)
return(def->callback(val));
return(TRUE);
}
static int
store_tuple(val, def, op)
char *val;
struct sudo_defs_types *def;
int op;
{
struct def_values *v;
/*
* Since enums are really just ints we store the value as an ival.
* In the future, there may be multiple enums for different tuple
* types we want to avoid and special knowledge of the tuple type.
* This does assume that the first entry in the tuple enum will
* be the equivalent to a boolean "false".
*/
if (!val) {
def->sd_un.ival = (op == FALSE) ? 0 : 1;
} else {
for (v = def->values; v->sval != NULL; v++) {
if (strcmp(v->sval, val) == 0) {
def->sd_un.ival = v->ival;
break;
}
}
if (v->sval == NULL)
return(FALSE);
}
if (def->callback)
return(def->callback(val));
return(TRUE);
}
static int
store_str(val, def, op)
char *val;
struct sudo_defs_types *def;
int op;
{
efree(def->sd_un.str);
if (op == FALSE)
def->sd_un.str = NULL;
else
def->sd_un.str = estrdup(val);
if (def->callback)
return(def->callback(val));
return(TRUE);
}
static int
store_list(str, def, op)
char *str;
struct sudo_defs_types *def;
int op;
{
char *start, *end;
/* Remove all old members. */
if (op == FALSE || op == TRUE)
list_op(NULL, 0, def, freeall);
/* Split str into multiple space-separated words and act on each one. */
if (op != FALSE) {
end = str;
do {
/* Remove leading blanks, if nothing but blanks we are done. */
for (start = end; isblank(*start); start++)
;
if (*start == '\0')
break;
/* Find end position and perform operation. */
for (end = start; *end && !isblank(*end); end++)
;
list_op(start, end - start, def, op == '-' ? delete : add);
} while (*end++ != '\0');
}
return(TRUE);
}
static int
store_syslogfac(val, def, op)
char *val;
struct sudo_defs_types *def;
int op;
{
struct strmap *fac;
if (op == FALSE) {
def->sd_un.ival = FALSE;
return(TRUE);
}
#ifdef LOG_NFACILITIES
if (!val)
return(FALSE);
for (fac = facilities; fac->name && strcmp(val, fac->name); fac++)
;
if (fac->name == NULL)
return(FALSE); /* not found */
def->sd_un.ival = fac->num;
#else
def->sd_un.ival = -1;
#endif /* LOG_NFACILITIES */
return(TRUE);
}
static const char *
logfac2str(n)
int n;
{
#ifdef LOG_NFACILITIES
struct strmap *fac;
for (fac = facilities; fac->name && fac->num != n; fac++)
;
return(fac->name);
#else
return("default");
#endif /* LOG_NFACILITIES */
}
static int
store_syslogpri(val, def, op)
char *val;
struct sudo_defs_types *def;
int op;
{
struct strmap *pri;
if (op == FALSE || !val)
return(FALSE);
for (pri = priorities; pri->name && strcmp(val, pri->name); pri++)
;
if (pri->name == NULL)
return(FALSE); /* not found */
def->sd_un.ival = pri->num;
return(TRUE);
}
static const char *
logpri2str(n)
int n;
{
struct strmap *pri;
for (pri = priorities; pri->name && pri->num != n; pri++)
;
return(pri->name);
}
static int
store_mode(val, def, op)
char *val;
struct sudo_defs_types *def;
int op;
{
char *endp;
long l;
if (op == FALSE) {
def->sd_un.mode = (mode_t)0777;
} else {
l = strtol(val, &endp, 8);
if (*endp != '\0' || l < 0 || l > 0777)
return(FALSE);
def->sd_un.mode = (mode_t)l;
}
if (def->callback)
return(def->callback(val));
return(TRUE);
}
static void
list_op(val, len, def, op)
char *val;
size_t len;
struct sudo_defs_types *def;
enum list_ops op;
{
struct list_member *cur, *prev, *tmp;
if (op == freeall) {
for (cur = def->sd_un.list; cur; ) {
tmp = cur;
cur = tmp->next;
efree(tmp->value);
efree(tmp);
}
def->sd_un.list = NULL;
return;
}
for (cur = def->sd_un.list, prev = NULL; cur; prev = cur, cur = cur->next) {
if ((strncmp(cur->value, val, len) == 0 && cur->value[len] == '\0')) {
if (op == add)
return; /* already exists */
/* Delete node */
if (prev != NULL)
prev->next = cur->next;
else
def->sd_un.list = cur->next;
efree(cur->value);
efree(cur);
break;
}
}
/* Add new node to the head of the list. */
if (op == add) {
cur = emalloc(sizeof(struct list_member));
cur->value = emalloc(len + 1);
(void) memcpy(cur->value, val, len);
cur->value[len] = '\0';
cur->next = def->sd_un.list;
def->sd_un.list = cur;
}
}

116
plugins/sudoers/defaults.h Normal file
View File

@@ -0,0 +1,116 @@
/*
* Copyright (c) 1999-2005, 2008
* Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#ifndef _SUDO_DEFAULTS_H
#define _SUDO_DEFAULTS_H
#include <def_data.h>
struct list_member {
char *value;
struct list_member *next;
};
struct def_values {
char *sval; /* string value */
int ival; /* actually an enum */
};
enum list_ops {
add,
delete,
freeall
};
/*
* Structure describing compile-time and run-time options.
*/
struct sudo_defs_types {
char *name;
int type;
char *desc;
struct def_values *values;
int (*callback) __P((char *));
union {
int flag;
int ival;
double fval;
enum def_tupple tuple;
char *str;
mode_t mode;
struct list_member *list;
} sd_un;
};
/*
* Four types of defaults: strings, integers, and flags.
* Also, T_INT, T_FLOAT or T_STR may be ANDed with T_BOOL to indicate that
* a value is not required. Flags are boolean by nature...
*/
#undef T_INT
#define T_INT 0x001
#undef T_UINT
#define T_UINT 0x002
#undef T_STR
#define T_STR 0x003
#undef T_FLAG
#define T_FLAG 0x004
#undef T_MODE
#define T_MODE 0x005
#undef T_LIST
#define T_LIST 0x006
#undef T_LOGFAC
#define T_LOGFAC 0x007
#undef T_LOGPRI
#define T_LOGPRI 0x008
#undef T_TUPLE
#define T_TUPLE 0x009
#undef T_FLOAT
#define T_FLOAT 0x010
#undef T_MASK
#define T_MASK 0x0FF
#undef T_BOOL
#define T_BOOL 0x100
#undef T_PATH
#define T_PATH 0x200
/*
* Argument to update_defaults()
*/
#define SETDEF_GENERIC 0x01
#define SETDEF_HOST 0x02
#define SETDEF_USER 0x04
#define SETDEF_RUNAS 0x08
#define SETDEF_CMND 0x10
#define SETDEF_ALL (SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS|SETDEF_CMND)
/*
* Prototypes
*/
void dump_default __P((void));
int set_default __P((char *, char *, int));
void init_defaults __P((void));
int update_defaults __P((int));
void list_options __P((void));
extern struct sudo_defs_types sudo_defs_table[];
#endif /* _SUDO_DEFAULTS_H */

935
plugins/sudoers/env.c Normal file
View File

@@ -0,0 +1,935 @@
/*
* Copyright (c) 2000-2005, 2007-2009
* Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <ctype.h>
#include <errno.h>
#include <pwd.h>
#include "sudo.h"
/*
* Flags used in rebuild_env()
*/
#undef DID_TERM
#define DID_TERM 0x0001
#undef DID_PATH
#define DID_PATH 0x0002
#undef DID_HOME
#define DID_HOME 0x0004
#undef DID_SHELL
#define DID_SHELL 0x0008
#undef DID_LOGNAME
#define DID_LOGNAME 0x0010
#undef DID_USER
#define DID_USER 0x0020
#undef DID_USERNAME
#define DID_USERNAME 0x0040
#undef DID_MAX
#define DID_MAX 0x00ff
#undef KEPT_TERM
#define KEPT_TERM 0x0100
#undef KEPT_PATH
#define KEPT_PATH 0x0200
#undef KEPT_HOME
#define KEPT_HOME 0x0400
#undef KEPT_SHELL
#define KEPT_SHELL 0x0800
#undef KEPT_LOGNAME
#define KEPT_LOGNAME 0x1000
#undef KEPT_USER
#define KEPT_USER 0x2000
#undef KEPT_USERNAME
#define KEPT_USERNAME 0x4000
#undef KEPT_MAX
#define KEPT_MAX 0xff00
#undef VNULL
#define VNULL (void *)NULL
struct environment {
char **envp; /* pointer to the new environment */
size_t env_size; /* size of new_environ in char **'s */
size_t env_len; /* number of slots used, not counting NULL */
};
/*
* Prototypes
*/
void rebuild_env __P((int, int));
static void sudo_setenv __P((const char *, const char *, int));
static void sudo_putenv __P((char *, int, int));
extern char **environ; /* global environment */
/*
* Copy of the sudo-managed environment.
*/
static struct environment env;
/*
* Default table of "bad" variables to remove from the environment.
* XXX - how to omit TERMCAP if it starts with '/'?
*/
static const char *initial_badenv_table[] = {
"IFS",
"CDPATH",
"LOCALDOMAIN",
"RES_OPTIONS",
"HOSTALIASES",
"NLSPATH",
"PATH_LOCALE",
"LD_*",
"_RLD*",
#ifdef __hpux
"SHLIB_PATH",
#endif /* __hpux */
#ifdef _AIX
"LDR_*",
"LIBPATH",
"AUTHSTATE",
#endif
#ifdef __APPLE__
"DYLD_*",
#endif
#ifdef HAVE_KERB4
"KRB_CONF*",
"KRBCONFDIR",
"KRBTKFILE",
#endif /* HAVE_KERB4 */
#ifdef HAVE_KERB5
"KRB5_CONFIG*",
"KRB5_KTNAME",
#endif /* HAVE_KERB5 */
#ifdef HAVE_SECURID
"VAR_ACE",
"USR_ACE",
"DLC_ACE",
#endif /* HAVE_SECURID */
"TERMINFO", /* terminfo, exclusive path to terminfo files */
"TERMINFO_DIRS", /* terminfo, path(s) to terminfo files */
"TERMPATH", /* termcap, path(s) to termcap files */
"TERMCAP", /* XXX - only if it starts with '/' */
"ENV", /* ksh, file to source before script runs */
"BASH_ENV", /* bash, file to source before script runs */
"PS4", /* bash, prefix for lines in xtrace mode */
"GLOBIGNORE", /* bash, globbing patterns to ignore */
"SHELLOPTS", /* bash, extra command line options */
"JAVA_TOOL_OPTIONS", /* java, extra command line options */
"PERLIO_DEBUG ", /* perl, debugging output file */
"PERLLIB", /* perl, search path for modules/includes */
"PERL5LIB", /* perl 5, search path for modules/includes */
"PERL5OPT", /* perl 5, extra command line options */
"PERL5DB", /* perl 5, command used to load debugger */
"FPATH", /* ksh, search path for functions */
"NULLCMD", /* zsh, command for null file redirection */
"READNULLCMD", /* zsh, command for null file redirection */
"ZDOTDIR", /* zsh, search path for dot files */
"TMPPREFIX", /* zsh, prefix for temporary files */
"PYTHONHOME", /* python, module search path */
"PYTHONPATH", /* python, search path */
"PYTHONINSPECT", /* python, allow inspection */
"RUBYLIB", /* ruby, library load path */
"RUBYOPT", /* ruby, extra command line options */
NULL
};
/*
* Default table of variables to check for '%' and '/' characters.
*/
static const char *initial_checkenv_table[] = {
"COLORTERM",
"LANG",
"LANGUAGE",
"LC_*",
"LINGUAS",
"TERM",
NULL
};
/*
* Default table of variables to preserve in the environment.
*/
static const char *initial_keepenv_table[] = {
"COLORS",
"DISPLAY",
"HOME",
"HOSTNAME",
"KRB5CCNAME",
"LS_COLORS",
"MAIL",
"PATH",
"PS1",
"PS2",
"TZ",
"XAUTHORITY",
"XAUTHORIZATION",
NULL
};
/*
* Similar to setenv(3) but operates on sudo's private copy of the environment
* (not environ) and it always overwrites. The dupcheck param determines
* whether we need to verify that the variable is not already set.
*/
static void
sudo_setenv(var, val, dupcheck)
const char *var;
const char *val;
int dupcheck;
{
char *estring;
size_t esize;
esize = strlen(var) + 1 + strlen(val) + 1;
estring = emalloc(esize);
/* Build environment string and insert it. */
if (strlcpy(estring, var, esize) >= esize ||
strlcat(estring, "=", esize) >= esize ||
strlcat(estring, val, esize) >= esize) {
errorx(1, "internal error, sudo_setenv() overflow");
}
sudo_putenv(estring, dupcheck, TRUE);
}
/*
* Version of setenv(3) that uses our own environ pointer.
* Will sync with environ as needed.
*/
int
setenv(var, val, overwrite)
const char *var;
const char *val;
int overwrite;
{
char *estring, *ep;
const char *cp;
size_t esize;
if (!var || *var == '\0')
return(EINVAL);
/*
* POSIX says a var name with '=' is an error but BSD
* just ignores the '=' and anything after it.
*/
for (cp = var; *cp && *cp != '='; cp++)
;
esize = (size_t)(cp - var) + 2;
if (val) {
esize += strlen(val); /* glibc treats a NULL val as "" */
}
/* Allocate and fill in estring. */
estring = ep = emalloc(esize);
for (cp = var; *cp && *cp != '='; cp++)
*ep++ = *cp;
*ep++ = '=';
if (val) {
for (cp = val; *cp; cp++)
*ep++ = *cp;
}
*ep = '\0';
/* Sync env.envp with environ as needed. */
if (env.envp != environ) {
char **ep;
size_t len;
for (ep = environ; *ep != NULL; ep++)
continue;
len = ep - environ;
if (len + 2 > env.env_size) {
efree(env.envp);
env.env_size = len + 2 + 128;
env.envp = emalloc2(env.env_size, sizeof(char *));
#ifdef ENV_DEBUG
memset(env.envp, 0, env.env_size * sizeof(char *));
#endif
}
memcpy(env.envp, environ, len * sizeof(char *));
env.envp[len] = NULL;
env.env_len = len;
environ = env.envp;
#ifdef ENV_DEBUG
} else {
if (env.envp[env.env_len] != NULL)
errorx(1, "setenv: corrupted envp, len mismatch");
#endif
}
sudo_putenv(estring, TRUE, overwrite);
return(0);
}
/*
* Version of unsetenv(3) that uses our own environ pointer.
* Will sync with environ as needed.
*/
#ifdef UNSETENV_VOID
void
#else
int
#endif
unsetenv(var)
const char *var;
{
char **ep;
size_t len;
if (strchr(var, '=') != NULL) {
errno = EINVAL;
#ifdef UNSETENV_VOID
return;
#else
return(-1);
#endif
}
/* Make sure we are operating on the current environment. */
/* XXX - this could be optimized to include the search */
if (env.envp != environ) {
for (ep = environ; *ep != NULL; ep++)
continue;
len = ep - environ;
if (len + 1 > env.env_size) {
efree(env.envp);
env.env_size = len + 1 + 128;
env.envp = emalloc2(env.env_size, sizeof(char *));
#ifdef ENV_DEBUG
memset(env.envp, 0, env.env_size * sizeof(char *));
#endif
}
memcpy(env.envp, environ, len * sizeof(char *));
env.envp[len] = NULL;
env.env_len = len;
environ = env.envp;
#ifdef ENV_DEBUG
} else {
if (env.envp[env.env_len] != NULL)
errorx(1, "unsetenv: corrupted envp, len mismatch");
#endif
}
len = strlen(var);
for (ep = env.envp; *ep; ep++) {
if (strncmp(var, *ep, len) == 0 && (*ep)[len] == '=') {
/* Found it; shift remainder + NULL over by one and update len. */
memmove(ep, ep + 1,
(env.env_len - (ep - env.envp)) * sizeof(char *));
env.env_len--;
break;
}
}
#ifndef UNSETENV_VOID
return(0);
#endif
}
/*
* Version of putenv(3) that uses our own environ pointer.
* Will sync with environ as needed.
*/
int
#ifdef PUTENV_CONST
putenv(const char *string)
#else
putenv(string)
char *string;
#endif
{
if (strchr(string, '=') == NULL) {
errno = EINVAL;
return(-1);
}
/* Sync env.envp with environ as needed. */
if (env.envp != environ) {
char **ep;
size_t len;
for (ep = environ; *ep != NULL; ep++)
continue;
len = ep - environ;
if (len + 2 > env.env_size) {
efree(env.envp);
env.env_size = len + 2 + 128;
env.envp = emalloc2(env.env_size, sizeof(char *));
#ifdef ENV_DEBUG
memset(env.envp, 0, env.env_size * sizeof(char *));
#endif
}
memcpy(env.envp, environ, len * sizeof(char *));
env.envp[len] = NULL;
env.env_len = len;
environ = env.envp;
#ifdef ENV_DEBUG
} else {
if (env.envp[env.env_len] != NULL)
errorx(1, "putenv: corrupted envp, len mismatch");
#endif
}
sudo_putenv((char *)string, TRUE, TRUE);
return(0);
}
/*
* Similar to putenv(3) but operates on sudo's private copy of the
* environment (not environ) and it always overwrites. The dupcheck param
* determines whether we need to verify that the variable is not already set.
* Will only overwrite an existing variable if overwrite is set.
*/
static void
sudo_putenv(str, dupcheck, overwrite)
char *str;
int dupcheck;
int overwrite;
{
char **ep;
size_t len;
/* Make sure there is room for the new entry plus a NULL. */
if (env.env_len + 2 > env.env_size) {
env.env_size += 128;
env.envp = erealloc3(env.envp, env.env_size, sizeof(char *));
#ifdef ENV_DEBUG
memset(env.envp + env.env_len, 0,
(env.env_size - env.env_len) * sizeof(char *));
#endif
environ = env.envp;
}
#ifdef ENV_DEBUG
if (env.envp[env.env_len] != NULL)
errorx(1, "sudo_putenv: corrupted envp, len mismatch");
#endif
if (dupcheck) {
len = (strchr(str, '=') - str) + 1;
for (ep = env.envp; *ep; ep++) {
if (strncmp(str, *ep, len) == 0) {
if (overwrite)
*ep = str;
return;
}
}
} else
ep = env.envp + env.env_len;
env.env_len++;
*ep++ = str;
*ep = NULL;
}
/*
* Check the env_delete blacklist.
* Returns TRUE if the variable was found, else false.
*/
static int
matches_env_delete(var)
const char *var;
{
struct list_member *cur;
size_t len;
int iswild, match = FALSE;
/* Skip anything listed in env_delete. */
for (cur = def_env_delete; cur; cur = cur->next) {
len = strlen(cur->value);
/* Deal with '*' wildcard */
if (cur->value[len - 1] == '*') {
len--;
iswild = TRUE;
} else
iswild = FALSE;
if (strncmp(cur->value, var, len) == 0 &&
(iswild || var[len] == '=')) {
match = TRUE;
break;
}
}
return(match);
}
/*
* Apply the env_check list.
* Returns TRUE if the variable is allowed, FALSE if denied
* or -1 if no match.
*/
static int
matches_env_check(var)
const char *var;
{
struct list_member *cur;
size_t len;
int iswild, keepit = -1;
for (cur = def_env_check; cur; cur = cur->next) {
len = strlen(cur->value);
/* Deal with '*' wildcard */
if (cur->value[len - 1] == '*') {
len--;
iswild = TRUE;
} else
iswild = FALSE;
if (strncmp(cur->value, var, len) == 0 &&
(iswild || var[len] == '=')) {
keepit = !strpbrk(var, "/%");
break;
}
}
return(keepit);
}
/*
* Check the env_keep list.
* Returns TRUE if the variable is allowed else FALSE.
*/
static int
matches_env_keep(var)
const char *var;
{
struct list_member *cur;
size_t len;
int iswild, keepit = FALSE;
for (cur = def_env_keep; cur; cur = cur->next) {
len = strlen(cur->value);
/* Deal with '*' wildcard */
if (cur->value[len - 1] == '*') {
len--;
iswild = TRUE;
} else
iswild = FALSE;
if (strncmp(cur->value, var, len) == 0 &&
(iswild || var[len] == '=')) {
keepit = TRUE;
break;
}
}
return(keepit);
}
/*
* Build a new environment and ether clear potentially dangerous
* variables from the old one or start with a clean slate.
* Also adds sudo-specific variables (SUDO_*).
*/
void
rebuild_env(sudo_mode, noexec)
int sudo_mode;
int noexec;
{
char **old_envp, **ep, *cp, *ps1;
char idbuf[MAX_UID_T_LEN];
unsigned int didvar;
/*
* Either clean out the environment or reset to a safe default.
*/
ps1 = NULL;
didvar = 0;
env.env_len = 0;
env.env_size = 128;
old_envp = env.envp;
env.envp = emalloc2(env.env_size, sizeof(char *));
#ifdef ENV_DEBUG
memset(env.envp, 0, env.env_size * sizeof(char *));
#endif
if (def_env_reset || ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
/* Pull in vars we want to keep from the old environment. */
for (ep = environ; *ep; ep++) {
int keepit;
/* Skip variables with values beginning with () (bash functions) */
if ((cp = strchr(*ep, '=')) != NULL) {
if (strncmp(cp, "=() ", 3) == 0)
continue;
}
/*
* First check certain variables for '%' and '/' characters.
* If no match there, check the keep list.
* If nothing matched, we remove it from the environment.
*/
keepit = matches_env_check(*ep);
if (keepit == -1)
keepit = matches_env_keep(*ep);
/* For SUDO_PS1 -> PS1 conversion. */
if (strncmp(*ep, "SUDO_PS1=", 8) == 0)
ps1 = *ep + 5;
if (keepit) {
/* Preserve variable. */
switch (**ep) {
case 'H':
if (strncmp(*ep, "HOME=", 5) == 0)
SET(didvar, DID_HOME);
break;
case 'L':
if (strncmp(*ep, "LOGNAME=", 8) == 0)
SET(didvar, DID_LOGNAME);
break;
case 'P':
if (strncmp(*ep, "PATH=", 5) == 0)
SET(didvar, DID_PATH);
break;
case 'S':
if (strncmp(*ep, "SHELL=", 6) == 0)
SET(didvar, DID_SHELL);
break;
case 'T':
if (strncmp(*ep, "TERM=", 5) == 0)
SET(didvar, DID_TERM);
break;
case 'U':
if (strncmp(*ep, "USER=", 5) == 0)
SET(didvar, DID_USER);
if (strncmp(*ep, "USERNAME=", 5) == 0)
SET(didvar, DID_USERNAME);
break;
}
sudo_putenv(*ep, FALSE, FALSE);
}
}
didvar |= didvar << 8; /* convert DID_* to KEPT_* */
/*
* Add in defaults. In -i mode these come from the runas user,
* otherwise they may be from the user's environment (depends
* on sudoers options).
*/
if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
sudo_setenv("HOME", runas_pw->pw_dir, ISSET(didvar, DID_HOME));
sudo_setenv("SHELL", runas_pw->pw_shell, ISSET(didvar, DID_SHELL));
sudo_setenv("LOGNAME", runas_pw->pw_name,
ISSET(didvar, DID_LOGNAME));
sudo_setenv("USER", runas_pw->pw_name, ISSET(didvar, DID_USER));
sudo_setenv("USERNAME", runas_pw->pw_name,
ISSET(didvar, DID_USERNAME));
} else {
if (!ISSET(didvar, DID_HOME))
sudo_setenv("HOME", user_dir, FALSE);
if (!ISSET(didvar, DID_SHELL))
sudo_setenv("SHELL", sudo_user.pw->pw_shell, FALSE);
if (!ISSET(didvar, DID_LOGNAME))
sudo_setenv("LOGNAME", user_name, FALSE);
if (!ISSET(didvar, DID_USER))
sudo_setenv("USER", user_name, FALSE);
if (!ISSET(didvar, DID_USERNAME))
sudo_setenv("USERNAME", user_name, FALSE);
}
} else {
/*
* Copy environ entries as long as they don't match env_delete or
* env_check.
*/
for (ep = environ; *ep; ep++) {
int okvar;
/* Skip variables with values beginning with () (bash functions) */
if ((cp = strchr(*ep, '=')) != NULL) {
if (strncmp(cp, "=() ", 3) == 0)
continue;
}
/*
* First check variables against the blacklist in env_delete.
* If no match there check for '%' and '/' characters.
*/
okvar = matches_env_delete(*ep) != TRUE;
if (okvar)
okvar = matches_env_check(*ep) != FALSE;
if (okvar) {
if (strncmp(*ep, "SUDO_PS1=", 9) == 0)
ps1 = *ep + 5;
else if (strncmp(*ep, "PATH=", 5) == 0)
SET(didvar, DID_PATH);
else if (strncmp(*ep, "TERM=", 5) == 0)
SET(didvar, DID_TERM);
sudo_putenv(*ep, FALSE, FALSE);
}
}
}
/* Replace the PATH envariable with a secure one? */
if (def_secure_path && !user_is_exempt()) {
sudo_setenv("PATH", def_secure_path, TRUE);
SET(didvar, DID_PATH);
}
/* Set $USER, $LOGNAME and $USERNAME to target if "set_logname" is true. */
/* XXX - not needed for MODE_LOGIN_SHELL */
if (def_set_logname && runas_pw->pw_name) {
if (!ISSET(didvar, KEPT_LOGNAME))
sudo_setenv("LOGNAME", runas_pw->pw_name, TRUE);
if (!ISSET(didvar, KEPT_USER))
sudo_setenv("USER", runas_pw->pw_name, TRUE);
if (!ISSET(didvar, KEPT_USERNAME))
sudo_setenv("USERNAME", runas_pw->pw_name, TRUE);
}
/* Set $HOME for `sudo -H'. Only valid at PERM_FULL_RUNAS. */
/* XXX - not needed for MODE_LOGIN_SHELL */
if (runas_pw->pw_dir) {
if (ISSET(sudo_mode, MODE_RESET_HOME) ||
(ISSET(sudo_mode, MODE_RUN) && (def_always_set_home ||
(ISSET(sudo_mode, MODE_SHELL) && def_set_home))))
sudo_setenv("HOME", runas_pw->pw_dir, TRUE);
}
/* Provide default values for $TERM and $PATH if they are not set. */
if (!ISSET(didvar, DID_TERM))
sudo_putenv("TERM=unknown", FALSE, FALSE);
if (!ISSET(didvar, DID_PATH))
sudo_setenv("PATH", _PATH_DEFPATH, FALSE);
/*
* Preload a noexec file? For a list of LD_PRELOAD-alikes, see
* http://www.fortran-2000.com/ArnaudRecipes/sharedlib.html
* XXX - should prepend to original value, if any
*/
if (noexec && def_noexec_file != NULL) {
#if defined(__darwin__) || defined(__APPLE__)
sudo_setenv("DYLD_INSERT_LIBRARIES", def_noexec_file, TRUE);
sudo_setenv("DYLD_FORCE_FLAT_NAMESPACE", "", TRUE);
#else
# if defined(__osf__) || defined(__sgi)
easprintf(&cp, "%s:DEFAULT", def_noexec_file);
sudo_setenv("_RLD_LIST", cp, TRUE);
efree(cp);
# else
# ifdef _AIX
sudo_setenv("LDR_PRELOAD", def_noexec_file, TRUE);
# else
sudo_setenv("LD_PRELOAD", def_noexec_file, TRUE);
# endif /* _AIX */
# endif /* __osf__ || __sgi */
#endif /* __darwin__ || __APPLE__ */
}
/* Set PS1 if SUDO_PS1 is set. */
if (ps1 != NULL)
sudo_putenv(ps1, TRUE, TRUE);
/* Add the SUDO_COMMAND envariable (cmnd + args). */
if (user_args) {
easprintf(&cp, "%s %s", user_cmnd, user_args);
sudo_setenv("SUDO_COMMAND", cp, TRUE);
efree(cp);
} else
sudo_setenv("SUDO_COMMAND", user_cmnd, TRUE);
/* Add the SUDO_USER, SUDO_UID, SUDO_GID environment variables. */
sudo_setenv("SUDO_USER", user_name, TRUE);
snprintf(idbuf, sizeof(idbuf), "%lu", (unsigned long) user_uid);
sudo_setenv("SUDO_UID", idbuf, TRUE);
snprintf(idbuf, sizeof(idbuf), "%lu", (unsigned long) user_gid);
sudo_setenv("SUDO_GID", idbuf, TRUE);
/* Install new environment. */
environ = env.envp;
efree(old_envp);
}
void
insert_env_vars(env_vars)
struct list_member *env_vars;
{
struct list_member *cur;
if (env_vars == NULL)
return;
/* Add user-specified environment variables. */
for (cur = env_vars; cur != NULL; cur = cur->next)
putenv(cur->value);
}
/*
* Validate the list of environment variables passed in on the command
* line against env_delete, env_check, and env_keep.
* Calls log_error() if any specified variables are not allowed.
*/
void
validate_env_vars(env_vars)
struct list_member *env_vars;
{
struct list_member *var;
char *eq, *bad = NULL;
size_t len, blen = 0, bsize = 0;
int okvar;
for (var = env_vars; var != NULL; var = var->next) {
if (def_secure_path && !user_is_exempt() &&
strncmp(var->value, "PATH=", 5) == 0) {
okvar = FALSE;
} else if (def_env_reset) {
okvar = matches_env_check(var->value);
if (okvar == -1)
okvar = matches_env_keep(var->value);
} else {
okvar = matches_env_delete(var->value) == FALSE;
if (okvar == FALSE)
okvar = matches_env_check(var->value) != FALSE;
}
if (okvar == FALSE) {
/* Not allowed, add to error string, allocating as needed. */
if ((eq = strchr(var->value, '=')) != NULL)
*eq = '\0';
len = strlen(var->value) + 2;
if (blen + len >= bsize) {
do {
bsize += 1024;
} while (blen + len >= bsize);
bad = erealloc(bad, bsize);
bad[blen] = '\0';
}
strlcat(bad, var->value, bsize);
strlcat(bad, ", ", bsize);
blen += len;
if (eq != NULL)
*eq = '=';
}
}
if (bad != NULL) {
bad[blen - 2] = '\0'; /* remove trailing ", " */
log_error(NO_MAIL,
"sorry, you are not allowed to set the following environment variables: %s", bad);
/* NOTREACHED */
efree(bad);
}
}
/*
* Read in /etc/environment ala AIX and Linux.
* Lines may be in either of three formats:
* NAME=VALUE
* NAME="VALUE"
* NAME='VALUE'
* with an optional "export" prefix so the shell can source the file.
* Invalid lines, blank lines, or lines consisting solely of a comment
* character are skipped.
*/
void
read_env_file(path, overwrite)
const char *path;
int overwrite;
{
FILE *fp;
char *cp, *var, *val;
size_t var_len, val_len;
if ((fp = fopen(path, "r")) == NULL)
return;
while ((var = sudo_parseln(fp)) != NULL) {
/* Skip blank or comment lines */
if (*var == '\0')
continue;
/* Skip optional "export " */
if (strncmp(var, "export", 6) == 0 && isspace((unsigned char) var[6])) {
var += 7;
while (isspace((unsigned char) *var)) {
var++;
}
}
/* Must be of the form name=["']value['"] */
for (val = var; *val != '\0' && *val != '='; val++)
;
if (var == val || *val != '=')
continue;
var_len = (size_t)(val - var);
val_len = strlen(++val);
/* Strip leading and trailing single/double quotes */
if ((val[0] == '\'' || val[0] == '\"') && val[0] == val[val_len - 1]) {
val[val_len - 1] = '\0';
val++;
val_len -= 2;
}
cp = emalloc(var_len + 1 + val_len + 1);
memcpy(cp, var, var_len + 1); /* includes '=' */
memcpy(cp + var_len + 1, val, val_len + 1); /* includes NUL */
sudo_putenv(cp, TRUE, overwrite);
}
fclose(fp);
}
void
init_envtables()
{
struct list_member *cur;
const char **p;
/* Fill in the "env_delete" list. */
for (p = initial_badenv_table; *p; p++) {
cur = emalloc(sizeof(struct list_member));
cur->value = estrdup(*p);
cur->next = def_env_delete;
def_env_delete = cur;
}
/* Fill in the "env_check" list. */
for (p = initial_checkenv_table; *p; p++) {
cur = emalloc(sizeof(struct list_member));
cur->value = estrdup(*p);
cur->next = def_env_check;
def_env_check = cur;
}
/* Fill in the "env_keep" list. */
for (p = initial_keepenv_table; *p; p++) {
cur = emalloc(sizeof(struct list_member));
cur->value = estrdup(*p);
cur->next = def_env_keep;
def_env_keep = cur;
}
}

135
plugins/sudoers/find_path.c Normal file
View File

@@ -0,0 +1,135 @@
/*
* Copyright (c) 1996, 1998-2005 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include "sudo.h"
/*
* This function finds the full pathname for a command and
* stores it in a statically allocated array, filling in a pointer
* to the array. Returns FOUND if the command was found, NOT_FOUND
* if it was not found, or NOT_FOUND_DOT if it would have been found
* but it is in '.' and IGNORE_DOT is set.
*/
int
find_path(infile, outfile, sbp, path)
char *infile; /* file to find */
char **outfile; /* result parameter */
struct stat *sbp; /* stat result parameter */
char *path; /* path to search */
{
static char command[PATH_MAX]; /* qualified filename */
char *n; /* for traversing path */
char *origpath; /* so we can free path later */
char *result = NULL; /* result of path/file lookup */
int checkdot = 0; /* check current dir? */
int len; /* length parameter */
if (strlen(infile) >= PATH_MAX)
errorx(1, "%s: File name too long", infile);
/*
* If we were given a fully qualified or relative path
* there is no need to look at $PATH.
*/
if (strchr(infile, '/')) {
strlcpy(command, infile, sizeof(command)); /* paranoia */
if (sudo_goodpath(command, sbp)) {
*outfile = command;
return(FOUND);
} else
return(NOT_FOUND);
}
/* Use PATH passed in unless SECURE_PATH is in effect. */
if (def_secure_path && !user_is_exempt())
path = def_secure_path;
else if (path == NULL)
return(NOT_FOUND);
path = estrdup(path);
origpath = path;
do {
if ((n = strchr(path, ':')))
*n = '\0';
/*
* Search current dir last if it is in PATH This will miss sneaky
* things like using './' or './/'
*/
if (*path == '\0' || (*path == '.' && *(path + 1) == '\0')) {
checkdot = 1;
path = n + 1;
continue;
}
/*
* Resolve the path and exit the loop if found.
*/
len = snprintf(command, sizeof(command), "%s/%s", path, infile);
if (len <= 0 || len >= sizeof(command))
errorx(1, "%s: File name too long", infile);
if ((result = sudo_goodpath(command, sbp)))
break;
path = n + 1;
} while (n);
efree(origpath);
/*
* Check current dir if dot was in the PATH
*/
if (!result && checkdot) {
result = sudo_goodpath(infile, sbp);
if (result && def_ignore_dot)
return(NOT_FOUND_DOT);
}
if (result) {
*outfile = result;
return(FOUND);
} else
return(NOT_FOUND);
}

1608
plugins/sudoers/getdate.c Normal file

File diff suppressed because it is too large Load Diff

967
plugins/sudoers/getdate.y Normal file
View File

@@ -0,0 +1,967 @@
%{
/*
** Originally written by Steven M. Bellovin <smb@research.att.com> while
** at the University of North Carolina at Chapel Hill. Later tweaked by
** a couple of people on Usenet. Completely overhauled by Rich $alz
** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
**
** This grammar has 10 shift/reduce conflicts.
**
** This code is in the public domain and has no copyright.
*/
/* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */
/* SUPPRESS 288 on yyerrlab *//* Label unused */
#include <config.h>
#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
# include <memory.h>
# endif
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#if TIME_WITH_SYS_TIME
# include <time.h>
#endif
#include <ctype.h>
#include "compat.h"
#define EPOCH 1970
#define HOUR(x) ((time_t)(x) * 60)
#define SECSPERDAY (24L * 60L * 60L)
/*
** An entry in the lexical lookup table.
*/
typedef struct _TABLE {
char *name;
int type;
time_t value;
} TABLE;
/*
** Daylight-savings mode: on, off, or not yet known.
*/
typedef enum _DSTMODE {
DSTon, DSToff, DSTmaybe
} DSTMODE;
/*
** Meridian: am, pm, or 24-hour style.
*/
typedef enum _MERIDIAN {
MERam, MERpm, MER24
} MERIDIAN;
/*
** Global variables. We could get rid of most of these by using a good
** union as the yacc stack. (This routine was originally written before
** yacc had the %union construct.) Maybe someday; right now we only use
** the %union very rarely.
*/
static char *yyInput;
static DSTMODE yyDSTmode;
static time_t yyDayOrdinal;
static time_t yyDayNumber;
static int yyHaveDate;
static int yyHaveDay;
static int yyHaveRel;
static int yyHaveTime;
static int yyHaveZone;
static time_t yyTimezone;
static time_t yyDay;
static time_t yyHour;
static time_t yyMinutes;
static time_t yyMonth;
static time_t yySeconds;
static time_t yyYear;
static MERIDIAN yyMeridian;
static time_t yyRelMonth;
static time_t yyRelSeconds;
static int yyerror __P((char *s));
static int yylex __P((void));
static int yyparse __P((void));
%}
%union {
time_t Number;
enum _MERIDIAN Meridian;
}
%token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
%token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
%type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
%type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE
%type <Meridian> tMERIDIAN o_merid
%%
spec : /* NULL */
| spec item
;
item : time {
yyHaveTime++;
}
| zone {
yyHaveZone++;
}
| date {
yyHaveDate++;
}
| day {
yyHaveDay++;
}
| rel {
yyHaveRel++;
}
| number
;
time : tUNUMBER tMERIDIAN {
yyHour = $1;
yyMinutes = 0;
yySeconds = 0;
yyMeridian = $2;
}
| tUNUMBER ':' tUNUMBER o_merid {
yyHour = $1;
yyMinutes = $3;
yySeconds = 0;
yyMeridian = $4;
}
| tUNUMBER ':' tUNUMBER tSNUMBER {
yyHour = $1;
yyMinutes = $3;
yyMeridian = MER24;
yyDSTmode = DSToff;
yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
}
| tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
yyHour = $1;
yyMinutes = $3;
yySeconds = $5;
yyMeridian = $6;
}
| tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
yyHour = $1;
yyMinutes = $3;
yySeconds = $5;
yyMeridian = MER24;
yyDSTmode = DSToff;
yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
}
;
zone : tZONE {
yyTimezone = $1;
yyDSTmode = DSToff;
}
| tDAYZONE {
yyTimezone = $1;
yyDSTmode = DSTon;
}
|
tZONE tDST {
yyTimezone = $1;
yyDSTmode = DSTon;
}
;
day : tDAY {
yyDayOrdinal = 1;
yyDayNumber = $1;
}
| tDAY ',' {
yyDayOrdinal = 1;
yyDayNumber = $1;
}
| tUNUMBER tDAY {
yyDayOrdinal = $1;
yyDayNumber = $2;
}
;
date : tUNUMBER '/' tUNUMBER {
yyMonth = $1;
yyDay = $3;
}
| tUNUMBER '/' tUNUMBER '/' tUNUMBER {
if ($1 >= 100) {
yyYear = $1;
yyMonth = $3;
yyDay = $5;
} else {
yyMonth = $1;
yyDay = $3;
yyYear = $5;
}
}
| tUNUMBER tSNUMBER tSNUMBER {
/* ISO 8601 format. yyyy-mm-dd. */
yyYear = $1;
yyMonth = -$2;
yyDay = -$3;
}
| tUNUMBER tMONTH tSNUMBER {
/* e.g. 17-JUN-1992. */
yyDay = $1;
yyMonth = $2;
yyYear = -$3;
}
| tMONTH tUNUMBER {
yyMonth = $1;
yyDay = $2;
}
| tMONTH tUNUMBER ',' tUNUMBER {
yyMonth = $1;
yyDay = $2;
yyYear = $4;
}
| tUNUMBER tMONTH {
yyMonth = $2;
yyDay = $1;
}
| tUNUMBER tMONTH tUNUMBER {
yyMonth = $2;
yyDay = $1;
yyYear = $3;
}
;
rel : relunit tAGO {
yyRelSeconds = -yyRelSeconds;
yyRelMonth = -yyRelMonth;
}
| relunit
;
relunit : tUNUMBER tMINUTE_UNIT {
yyRelSeconds += $1 * $2 * 60L;
}
| tSNUMBER tMINUTE_UNIT {
yyRelSeconds += $1 * $2 * 60L;
}
| tMINUTE_UNIT {
yyRelSeconds += $1 * 60L;
}
| tSNUMBER tSEC_UNIT {
yyRelSeconds += $1;
}
| tUNUMBER tSEC_UNIT {
yyRelSeconds += $1;
}
| tSEC_UNIT {
yyRelSeconds++;
}
| tSNUMBER tMONTH_UNIT {
yyRelMonth += $1 * $2;
}
| tUNUMBER tMONTH_UNIT {
yyRelMonth += $1 * $2;
}
| tMONTH_UNIT {
yyRelMonth += $1;
}
;
number : tUNUMBER {
if (yyHaveTime && yyHaveDate && !yyHaveRel)
yyYear = $1;
else {
if($1>10000) {
yyHaveDate++;
yyDay= ($1)%100;
yyMonth= ($1/100)%100;
yyYear = $1/10000;
}
else {
yyHaveTime++;
if ($1 < 100) {
yyHour = $1;
yyMinutes = 0;
}
else {
yyHour = $1 / 100;
yyMinutes = $1 % 100;
}
yySeconds = 0;
yyMeridian = MER24;
}
}
}
;
o_merid : /* NULL */ {
$$ = MER24;
}
| tMERIDIAN {
$$ = $1;
}
;
%%
/* Month and day table. */
static TABLE const MonthDayTable[] = {
{ "january", tMONTH, 1 },
{ "february", tMONTH, 2 },
{ "march", tMONTH, 3 },
{ "april", tMONTH, 4 },
{ "may", tMONTH, 5 },
{ "june", tMONTH, 6 },
{ "july", tMONTH, 7 },
{ "august", tMONTH, 8 },
{ "september", tMONTH, 9 },
{ "sept", tMONTH, 9 },
{ "october", tMONTH, 10 },
{ "november", tMONTH, 11 },
{ "december", tMONTH, 12 },
{ "sunday", tDAY, 0 },
{ "monday", tDAY, 1 },
{ "tuesday", tDAY, 2 },
{ "tues", tDAY, 2 },
{ "wednesday", tDAY, 3 },
{ "wednes", tDAY, 3 },
{ "thursday", tDAY, 4 },
{ "thur", tDAY, 4 },
{ "thurs", tDAY, 4 },
{ "friday", tDAY, 5 },
{ "saturday", tDAY, 6 },
{ NULL }
};
/* Time units table. */
static TABLE const UnitsTable[] = {
{ "year", tMONTH_UNIT, 12 },
{ "month", tMONTH_UNIT, 1 },
{ "fortnight", tMINUTE_UNIT, 14 * 24 * 60 },
{ "week", tMINUTE_UNIT, 7 * 24 * 60 },
{ "day", tMINUTE_UNIT, 1 * 24 * 60 },
{ "hour", tMINUTE_UNIT, 60 },
{ "minute", tMINUTE_UNIT, 1 },
{ "min", tMINUTE_UNIT, 1 },
{ "second", tSEC_UNIT, 1 },
{ "sec", tSEC_UNIT, 1 },
{ NULL }
};
/* Assorted relative-time words. */
static TABLE const OtherTable[] = {
{ "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
{ "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
{ "today", tMINUTE_UNIT, 0 },
{ "now", tMINUTE_UNIT, 0 },
{ "last", tUNUMBER, -1 },
{ "this", tMINUTE_UNIT, 0 },
{ "next", tUNUMBER, 2 },
{ "first", tUNUMBER, 1 },
/* { "second", tUNUMBER, 2 }, */
{ "third", tUNUMBER, 3 },
{ "fourth", tUNUMBER, 4 },
{ "fifth", tUNUMBER, 5 },
{ "sixth", tUNUMBER, 6 },
{ "seventh", tUNUMBER, 7 },
{ "eighth", tUNUMBER, 8 },
{ "ninth", tUNUMBER, 9 },
{ "tenth", tUNUMBER, 10 },
{ "eleventh", tUNUMBER, 11 },
{ "twelfth", tUNUMBER, 12 },
{ "ago", tAGO, 1 },
{ NULL }
};
/* The timezone table. */
/* Some of these are commented out because a time_t can't store a float. */
static TABLE const TimezoneTable[] = {
{ "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
{ "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
{ "utc", tZONE, HOUR( 0) },
{ "wet", tZONE, HOUR( 0) }, /* Western European */
{ "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
{ "wat", tZONE, HOUR( 1) }, /* West Africa */
{ "at", tZONE, HOUR( 2) }, /* Azores */
#if 0
/* For completeness. BST is also British Summer, and GST is
* also Guam Standard. */
{ "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
{ "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
#endif
#if 0
{ "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
{ "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
{ "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
#endif
{ "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
{ "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
{ "est", tZONE, HOUR( 5) }, /* Eastern Standard */
{ "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
{ "cst", tZONE, HOUR( 6) }, /* Central Standard */
{ "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
{ "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
{ "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
{ "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
{ "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
{ "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
{ "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
{ "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
{ "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
{ "cat", tZONE, HOUR(10) }, /* Central Alaska */
{ "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
{ "nt", tZONE, HOUR(11) }, /* Nome */
{ "idlw", tZONE, HOUR(12) }, /* International Date Line West */
{ "cet", tZONE, -HOUR(1) }, /* Central European */
{ "met", tZONE, -HOUR(1) }, /* Middle European */
{ "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
{ "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
{ "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
{ "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
{ "fwt", tZONE, -HOUR(1) }, /* French Winter */
{ "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
{ "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */
{ "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */
#if 0
{ "it", tZONE, -HOUR(3.5) },/* Iran */
#endif
{ "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
{ "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
#if 0
{ "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
#endif
{ "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
#if 0
/* For completeness. NST is also Newfoundland Stanard, and SST is
* also Swedish Summer. */
{ "nst", tZONE, -HOUR(6.5) },/* North Sumatra */
{ "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */
#endif /* 0 */
{ "wast", tZONE, -HOUR(7) }, /* West Australian Standard */
{ "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */
#if 0
{ "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
#endif
{ "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
{ "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
#if 0
{ "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
{ "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
#endif
{ "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
{ "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
{ "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
{ "nzt", tZONE, -HOUR(12) }, /* New Zealand */
{ "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
{ "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
{ "idle", tZONE, -HOUR(12) }, /* International Date Line East */
{ NULL }
};
/* Military timezone table. */
static TABLE const MilitaryTable[] = {
{ "a", tZONE, HOUR( 1) },
{ "b", tZONE, HOUR( 2) },
{ "c", tZONE, HOUR( 3) },
{ "d", tZONE, HOUR( 4) },
{ "e", tZONE, HOUR( 5) },
{ "f", tZONE, HOUR( 6) },
{ "g", tZONE, HOUR( 7) },
{ "h", tZONE, HOUR( 8) },
{ "i", tZONE, HOUR( 9) },
{ "k", tZONE, HOUR( 10) },
{ "l", tZONE, HOUR( 11) },
{ "m", tZONE, HOUR( 12) },
{ "n", tZONE, HOUR(- 1) },
{ "o", tZONE, HOUR(- 2) },
{ "p", tZONE, HOUR(- 3) },
{ "q", tZONE, HOUR(- 4) },
{ "r", tZONE, HOUR(- 5) },
{ "s", tZONE, HOUR(- 6) },
{ "t", tZONE, HOUR(- 7) },
{ "u", tZONE, HOUR(- 8) },
{ "v", tZONE, HOUR(- 9) },
{ "w", tZONE, HOUR(-10) },
{ "x", tZONE, HOUR(-11) },
{ "y", tZONE, HOUR(-12) },
{ "z", tZONE, HOUR( 0) },
{ NULL }
};
/* ARGSUSED */
static int
yyerror(s)
char *s;
{
return 0;
}
static time_t
ToSeconds(Hours, Minutes, Seconds, Meridian)
time_t Hours;
time_t Minutes;
time_t Seconds;
MERIDIAN Meridian;
{
if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
return -1;
switch (Meridian) {
case MER24:
if (Hours < 0 || Hours > 23)
return -1;
return (Hours * 60L + Minutes) * 60L + Seconds;
case MERam:
if (Hours < 1 || Hours > 12)
return -1;
if (Hours == 12)
Hours = 0;
return (Hours * 60L + Minutes) * 60L + Seconds;
case MERpm:
if (Hours < 1 || Hours > 12)
return -1;
if (Hours == 12)
Hours = 0;
return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
default:
abort ();
}
/* NOTREACHED */
}
/* Year is either
* A negative number, which means to use its absolute value (why?)
* A number from 0 to 99, which means a year from 1900 to 1999, or
* The actual year (>=100). */
static time_t
Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode)
time_t Month;
time_t Day;
time_t Year;
time_t Hours;
time_t Minutes;
time_t Seconds;
MERIDIAN Meridian;
DSTMODE DSTmode;
{
static int DaysInMonth[12] = {
31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
time_t tod;
time_t Julian;
int i;
if (Year < 0)
Year = -Year;
if (Year < 69)
Year += 2000;
else if (Year < 100) {
Year += 1900;
if (Year < EPOCH)
Year += 100;
}
DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
? 29 : 28;
/* Checking for 2038 bogusly assumes that time_t is 32 bits. But
I'm too lazy to try to check for time_t overflow in another way. */
if (Year < EPOCH || Year > 2038
|| Month < 1 || Month > 12
/* Lint fluff: "conversion from long may lose accuracy" */
|| Day < 1 || Day > DaysInMonth[(int)--Month])
return -1;
for (Julian = Day - 1, i = 0; i < Month; i++)
Julian += DaysInMonth[i];
for (i = EPOCH; i < Year; i++)
Julian += 365 + (i % 4 == 0);
Julian *= SECSPERDAY;
Julian += yyTimezone * 60L;
if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
return -1;
Julian += tod;
if (DSTmode == DSTon
|| (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
Julian -= 60 * 60;
return Julian;
}
static time_t
DSTcorrect(Start, Future)
time_t Start;
time_t Future;
{
time_t StartDay;
time_t FutureDay;
StartDay = (localtime(&Start)->tm_hour + 1) % 24;
FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
}
static time_t
RelativeDate(Start, DayOrdinal, DayNumber)
time_t Start;
time_t DayOrdinal;
time_t DayNumber;
{
struct tm *tm;
time_t now;
now = Start;
tm = localtime(&now);
now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
return DSTcorrect(Start, now);
}
static time_t
RelativeMonth(Start, RelMonth)
time_t Start;
time_t RelMonth;
{
struct tm *tm;
time_t Month;
time_t Year;
if (RelMonth == 0)
return 0;
tm = localtime(&Start);
Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
Year = Month / 12;
Month = Month % 12 + 1;
return DSTcorrect(Start,
Convert(Month, (time_t)tm->tm_mday, Year,
(time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
MER24, DSTmaybe));
}
static int
LookupWord(buff)
char *buff;
{
char *p;
char *q;
const TABLE *tp;
int i;
int abbrev;
/* Make it lowercase. */
for (p = buff; *p; p++)
if (isupper(*p))
*p = tolower(*p);
if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
yylval.Meridian = MERam;
return tMERIDIAN;
}
if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
yylval.Meridian = MERpm;
return tMERIDIAN;
}
/* See if we have an abbreviation for a month. */
if (strlen(buff) == 3)
abbrev = 1;
else if (strlen(buff) == 4 && buff[3] == '.') {
abbrev = 1;
buff[3] = '\0';
}
else
abbrev = 0;
for (tp = MonthDayTable; tp->name; tp++) {
if (abbrev) {
if (strncmp(buff, tp->name, 3) == 0) {
yylval.Number = tp->value;
return tp->type;
}
}
else if (strcmp(buff, tp->name) == 0) {
yylval.Number = tp->value;
return tp->type;
}
}
for (tp = TimezoneTable; tp->name; tp++)
if (strcmp(buff, tp->name) == 0) {
yylval.Number = tp->value;
return tp->type;
}
if (strcmp(buff, "dst") == 0)
return tDST;
for (tp = UnitsTable; tp->name; tp++)
if (strcmp(buff, tp->name) == 0) {
yylval.Number = tp->value;
return tp->type;
}
/* Strip off any plural and try the units table again. */
i = strlen(buff) - 1;
if (buff[i] == 's') {
buff[i] = '\0';
for (tp = UnitsTable; tp->name; tp++)
if (strcmp(buff, tp->name) == 0) {
yylval.Number = tp->value;
return tp->type;
}
buff[i] = 's'; /* Put back for "this" in OtherTable. */
}
for (tp = OtherTable; tp->name; tp++)
if (strcmp(buff, tp->name) == 0) {
yylval.Number = tp->value;
return tp->type;
}
/* Military timezones. */
if (buff[1] == '\0' && isalpha(*buff)) {
for (tp = MilitaryTable; tp->name; tp++)
if (strcmp(buff, tp->name) == 0) {
yylval.Number = tp->value;
return tp->type;
}
}
/* Drop out any periods and try the timezone table again. */
for (i = 0, p = q = buff; *q; q++)
if (*q != '.')
*p++ = *q;
else
i++;
*p = '\0';
if (i)
for (tp = TimezoneTable; tp->name; tp++)
if (strcmp(buff, tp->name) == 0) {
yylval.Number = tp->value;
return tp->type;
}
return tID;
}
static int
yylex()
{
char c;
char *p;
char buff[20];
int Count;
int sign;
for ( ; ; ) {
while (isspace(*yyInput))
yyInput++;
if (isdigit(c = *yyInput) || c == '-' || c == '+') {
if (c == '-' || c == '+') {
sign = c == '-' ? -1 : 1;
if (!isdigit(*++yyInput))
/* skip the '-' sign */
continue;
}
else
sign = 0;
for (yylval.Number = 0; isdigit(c = *yyInput++); )
yylval.Number = 10 * yylval.Number + c - '0';
yyInput--;
if (sign < 0)
yylval.Number = -yylval.Number;
return sign ? tSNUMBER : tUNUMBER;
}
if (isalpha(c)) {
for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
if (p < &buff[sizeof buff - 1])
*p++ = c;
*p = '\0';
yyInput--;
return LookupWord(buff);
}
if (c != '(')
return *yyInput++;
Count = 0;
do {
c = *yyInput++;
if (c == '\0')
return c;
if (c == '(')
Count++;
else if (c == ')')
Count--;
} while (Count > 0);
}
}
#define TM_YEAR_ORIGIN 1900
/* Yield A - B, measured in seconds. */
static long
difftm (a, b)
struct tm *a, *b;
{
int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
int days = (
/* difference in day of year */
a->tm_yday - b->tm_yday
/* + intervening leap days */
+ ((ay >> 2) - (by >> 2))
- (ay/100 - by/100)
+ ((ay/100 >> 2) - (by/100 >> 2))
/* + difference in years * 365 */
+ (long)(ay-by) * 365
);
return (60*(60*(24*days + (a->tm_hour - b->tm_hour))
+ (a->tm_min - b->tm_min))
+ (a->tm_sec - b->tm_sec));
}
time_t
get_date(p)
char *p;
{
struct tm *tm, gmt;
time_t Start;
time_t tod;
time_t now;
time_t timezone;
yyInput = p;
{
struct tm *gmt_ptr;
/* XXX - eliminate timeb */
(void)time (&now);
gmt_ptr = gmtime (&now);
if (gmt_ptr != NULL)
{
/* Make a copy, in case localtime modifies *tm (I think
that comment now applies to *gmt_ptr, but I am too
lazy to dig into how gmtime and locatime allocate the
structures they return pointers to). */
gmt = *gmt_ptr;
}
if (! (tm = localtime (&now)))
return -1;
if (gmt_ptr != NULL)
timezone = difftm (&gmt, tm) / 60;
else
/* We are on a system like VMS, where the system clock is
in local time and the system has no concept of timezones.
Hopefully we can fake this out (for the case in which the
user specifies no timezone) by just saying the timezone
is zero. */
timezone = 0;
if(tm->tm_isdst)
timezone += 60;
}
tm = localtime(&now);
yyYear = tm->tm_year + 1900;
yyMonth = tm->tm_mon + 1;
yyDay = tm->tm_mday;
yyTimezone = timezone;
yyDSTmode = DSTmaybe;
yyHour = 0;
yyMinutes = 0;
yySeconds = 0;
yyMeridian = MER24;
yyRelSeconds = 0;
yyRelMonth = 0;
yyHaveDate = 0;
yyHaveDay = 0;
yyHaveRel = 0;
yyHaveTime = 0;
yyHaveZone = 0;
if (yyparse()
|| yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
return -1;
if (yyHaveDate || yyHaveTime || yyHaveDay) {
Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
yyMeridian, yyDSTmode);
if (Start < 0)
return -1;
}
else {
Start = now;
if (!yyHaveRel)
Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
}
Start += yyRelSeconds;
Start += RelativeMonth(Start, yyRelMonth);
if (yyHaveDay && !yyHaveDate) {
tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
Start += tod;
}
/* Have to do *something* with a legitimate -1 so it's distinguishable
* from the error return value. (Alternately could set errno on error.) */
return Start == -1 ? 0 : Start;
}
#if defined(TEST)
/* ARGSUSED */
int
main(ac, av)
int ac;
char *av[];
{
char buff[128];
time_t d;
(void)printf("Enter date, or blank line to exit.\n\t> ");
(void)fflush(stdout);
while (gets(buff) && buff[0]) {
d = get_date(buff, (struct timeb *)NULL);
if (d == -1)
(void)printf("Bad format - couldn't convert.\n");
else
(void)printf("%s", ctime(&d));
(void)printf("\t> ");
(void)fflush(stdout);
}
exit(0);
/* NOTREACHED */
}
#endif /* defined(TEST) */

198
plugins/sudoers/getspwuid.c Normal file
View File

@@ -0,0 +1,198 @@
/*
* Copyright (c) 1996, 1998-2005 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
# include <memory.h>
# endif
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <pwd.h>
#include <grp.h>
#ifdef HAVE_GETSPNAM
# include <shadow.h>
#endif /* HAVE_GETSPNAM */
#ifdef HAVE_GETPRPWNAM
# ifdef __hpux
# undef MAXINT
# include <hpsecurity.h>
# else
# include <sys/security.h>
# endif /* __hpux */
# include <prot.h>
#endif /* HAVE_GETPRPWNAM */
#ifdef HAVE_GETPWANAM
# include <sys/label.h>
# include <sys/audit.h>
# include <pwdadj.h>
#endif /* HAVE_GETPWANAM */
#ifdef HAVE_GETAUTHUID
# include <auth.h>
#endif /* HAVE_GETAUTHUID */
#include "sudo.h"
/*
* Exported for auth/secureware.c
*/
#if defined(HAVE_GETPRPWNAM) && defined(__alpha)
int crypt_type = INT_MAX;
#endif /* HAVE_GETPRPWNAM && __alpha */
/*
* Return a copy of the encrypted password for the user described by pw.
* If shadow passwords are in use, look in the shadow file.
*/
char *
sudo_getepw(pw)
const struct passwd *pw;
{
char *epw;
/* If there is a function to check for shadow enabled, use it... */
#ifdef HAVE_ISCOMSEC
if (!iscomsec())
return(estrdup(pw->pw_passwd));
#endif /* HAVE_ISCOMSEC */
#ifdef HAVE_ISSECURE
if (!issecure())
return(estrdup(pw->pw_passwd));
#endif /* HAVE_ISSECURE */
epw = NULL;
#ifdef HAVE_GETPRPWNAM
{
struct pr_passwd *spw;
if ((spw = getprpwnam(pw->pw_name)) && spw->ufld.fd_encrypt) {
# ifdef __alpha
crypt_type = spw->ufld.fd_oldcrypt;
# endif /* __alpha */
epw = estrdup(spw->ufld.fd_encrypt);
}
if (epw)
return(epw);
}
#endif /* HAVE_GETPRPWNAM */
#ifdef HAVE_GETSPNAM
{
struct spwd *spw;
if ((spw = getspnam(pw->pw_name)) && spw->sp_pwdp)
epw = estrdup(spw->sp_pwdp);
if (epw)
return(epw);
}
#endif /* HAVE_GETSPNAM */
#ifdef HAVE_GETSPWUID
{
struct s_passwd *spw;
if ((spw = getspwuid(pw->pw_uid)) && spw->pw_passwd)
epw = estrdup(spw->pw_passwd);
if (epw)
return(epw);
}
#endif /* HAVE_GETSPWUID */
#ifdef HAVE_GETPWANAM
{
struct passwd_adjunct *spw;
if ((spw = getpwanam(pw->pw_name)) && spw->pwa_passwd)
epw = estrdup(spw->pwa_passwd);
if (epw)
return(epw);
}
#endif /* HAVE_GETPWANAM */
#ifdef HAVE_GETAUTHUID
{
AUTHORIZATION *spw;
if ((spw = getauthuid(pw->pw_uid)) && spw->a_password)
epw = estrdup(spw->a_password);
if (epw)
return(epw);
}
#endif /* HAVE_GETAUTHUID */
/* Fall back on normal password. */
return(estrdup(pw->pw_passwd));
}
void
sudo_setspent()
{
#ifdef HAVE_GETPRPWNAM
setprpwent();
#endif
#ifdef HAVE_GETSPNAM
setspent();
#endif
#ifdef HAVE_GETSPWUID
setspwent();
#endif
#ifdef HAVE_GETPWANAM
setpwaent();
#endif
#ifdef HAVE_GETAUTHUID
setauthent();
#endif
}
void
sudo_endspent()
{
#ifdef HAVE_GETPRPWNAM
endprpwent();
#endif
#ifdef HAVE_GETSPNAM
endspent();
#endif
#ifdef HAVE_GETSPWUID
endspwent();
#endif
#ifdef HAVE_GETPWANAM
endpwaent();
#endif
#ifdef HAVE_GETAUTHUID
endauthent();
#endif
}

51
plugins/sudoers/gettime.c Normal file
View File

@@ -0,0 +1,51 @@
/*
* Copyright (c) 2004-2005, 2008 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#if TIME_WITH_SYS_TIME
# include <time.h>
#endif
#ifndef HAVE_TIMESPEC
# include <emul/timespec.h>
#endif
#include <compat.h>
/*
* Get the current time via gettimeofday() for systems with
* timespecs in struct stat or, otherwise, using time().
*/
int
gettime(ts)
struct timespec *ts;
{
int rval;
#if defined(HAVE_GETTIMEOFDAY) && (defined(HAVE_ST_MTIM) || defined(HAVE_ST_MTIMESPEC))
struct timeval tv;
rval = gettimeofday(&tv, NULL);
ts->tv_sec = tv.tv_sec;
ts->tv_nsec = tv.tv_usec * 1000;
#else
rval = (int)time(&ts->tv_sec);
ts->tv_nsec = 0;
#endif
return (rval);
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (c) 1996, 1998-2005 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <stdio.h>
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <errno.h>
#include "sudo.h"
/*
* Verify that path is a normal file and executable by root.
*/
char *
sudo_goodpath(path, sbp)
const char *path;
struct stat *sbp;
{
struct stat sb;
/* Check for brain damage */
if (path == NULL || path[0] == '\0')
return(NULL);
if (stat(path, &sb))
return(NULL);
/* Make sure path describes an executable regular file. */
if (!S_ISREG(sb.st_mode) || !(sb.st_mode & 0000111)) {
errno = EACCES;
return(NULL);
}
if (sbp != NULL)
(void) memcpy(sbp, &sb, sizeof(struct stat));
return((char *)path);
}

1643
plugins/sudoers/gram.c Normal file

File diff suppressed because it is too large Load Diff

45
plugins/sudoers/gram.h Normal file
View File

@@ -0,0 +1,45 @@
#define COMMAND 257
#define ALIAS 258
#define DEFVAR 259
#define NTWKADDR 260
#define NETGROUP 261
#define USERGROUP 262
#define WORD 263
#define DEFAULTS 264
#define DEFAULTS_HOST 265
#define DEFAULTS_USER 266
#define DEFAULTS_RUNAS 267
#define DEFAULTS_CMND 268
#define NOPASSWD 269
#define PASSWD 270
#define NOEXEC 271
#define EXEC 272
#define SETENV 273
#define NOSETENV 274
#define TRANSCRIPT 275
#define NOTRANSCRIPT 276
#define ALL 277
#define COMMENT 278
#define HOSTALIAS 279
#define CMNDALIAS 280
#define USERALIAS 281
#define RUNASALIAS 282
#define ERROR 283
#define TYPE 284
#define ROLE 285
#ifndef YYSTYPE_DEFINED
#define YYSTYPE_DEFINED
typedef union {
struct cmndspec *cmndspec;
struct defaults *defaults;
struct member *member;
struct runascontainer *runas;
struct privilege *privilege;
struct sudo_command command;
struct cmndtag tag;
struct selinux_info seinfo;
char *string;
int tok;
} YYSTYPE;
#endif /* YYSTYPE_DEFINED */
extern YYSTYPE yylval;

785
plugins/sudoers/gram.y Normal file
View File

@@ -0,0 +1,785 @@
%{
/*
* Copyright (c) 1996, 1998-2005, 2007-2009
* Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#if defined(YYBISON) && defined(HAVE_ALLOCA_H) && !defined(__GNUC__)
# include <alloca.h>
#endif /* YYBISON && HAVE_ALLOCA_H && !__GNUC__ */
#include <limits.h>
#include "sudo.h"
#include "parse.h"
/*
* We must define SIZE_MAX for yacc's skeleton.c.
* If there is no SIZE_MAX or SIZE_T_MAX we have to assume that size_t
* could be signed (as it is on SunOS 4.x).
*/
#ifndef SIZE_MAX
# ifdef SIZE_T_MAX
# define SIZE_MAX SIZE_T_MAX
# else
# define SIZE_MAX INT_MAX
# endif /* SIZE_T_MAX */
#endif /* SIZE_MAX */
/*
* Globals
*/
extern int sudolineno;
extern char *sudoers;
int parse_error;
int pedantic = FALSE;
int verbose = FALSE;
int errorlineno = -1;
char *errorfile = NULL;
struct defaults_list defaults;
struct userspec_list userspecs;
/*
* Local protoypes
*/
static void add_defaults __P((int, struct member *, struct defaults *));
static void add_userspec __P((struct member *, struct privilege *));
static struct defaults *new_default __P((char *, char *, int));
static struct member *new_member __P((char *, int));
void yyerror __P((const char *));
void
yyerror(s)
const char *s;
{
/* Save the line the first error occurred on. */
if (errorlineno == -1) {
errorlineno = sudolineno ? sudolineno - 1 : 0;
errorfile = estrdup(sudoers);
}
if (verbose && s != NULL) {
#ifndef TRACELEXER
(void) fprintf(stderr, ">>> %s: %s near line %d <<<\n", sudoers, s,
sudolineno ? sudolineno - 1 : 0);
#else
(void) fprintf(stderr, "<*> ");
#endif
}
parse_error = TRUE;
}
%}
%union {
struct cmndspec *cmndspec;
struct defaults *defaults;
struct member *member;
struct runascontainer *runas;
struct privilege *privilege;
struct sudo_command command;
struct cmndtag tag;
struct selinux_info seinfo;
char *string;
int tok;
}
%start file /* special start symbol */
%token <command> COMMAND /* absolute pathname w/ optional args */
%token <string> ALIAS /* an UPPERCASE alias name */
%token <string> DEFVAR /* a Defaults variable name */
%token <string> NTWKADDR /* ipv4 or ipv6 address */
%token <string> NETGROUP /* a netgroup (+NAME) */
%token <string> USERGROUP /* a usergroup (%NAME) */
%token <string> WORD /* a word */
%token <tok> DEFAULTS /* Defaults entry */
%token <tok> DEFAULTS_HOST /* Host-specific defaults entry */
%token <tok> DEFAULTS_USER /* User-specific defaults entry */
%token <tok> DEFAULTS_RUNAS /* Runas-specific defaults entry */
%token <tok> DEFAULTS_CMND /* Command-specific defaults entry */
%token <tok> NOPASSWD /* no passwd req for command */
%token <tok> PASSWD /* passwd req for command (default) */
%token <tok> NOEXEC /* preload dummy execve() for cmnd */
%token <tok> EXEC /* don't preload dummy execve() */
%token <tok> SETENV /* user may set environment for cmnd */
%token <tok> NOSETENV /* user may not set environment */
%token <tok> TRANSCRIPT /* log a transcript of the cmnd */
%token <tok> NOTRANSCRIPT /* don't log a transcript of the cmnd */
%token <tok> ALL /* ALL keyword */
%token <tok> COMMENT /* comment and/or carriage return */
%token <tok> HOSTALIAS /* Host_Alias keyword */
%token <tok> CMNDALIAS /* Cmnd_Alias keyword */
%token <tok> USERALIAS /* User_Alias keyword */
%token <tok> RUNASALIAS /* Runas_Alias keyword */
%token <tok> ':' '=' ',' '!' '+' '-' /* union member tokens */
%token <tok> '(' ')' /* runas tokens */
%token <tok> ERROR
%token <tok> TYPE /* SELinux type */
%token <tok> ROLE /* SELinux role */
%type <cmndspec> cmndspec
%type <cmndspec> cmndspeclist
%type <defaults> defaults_entry
%type <defaults> defaults_list
%type <member> cmnd
%type <member> opcmnd
%type <member> cmndlist
%type <member> host
%type <member> hostlist
%type <member> ophost
%type <member> opuser
%type <member> user
%type <member> userlist
%type <member> opgroup
%type <member> group
%type <member> grouplist
%type <runas> runasspec
%type <runas> runaslist
%type <privilege> privilege
%type <privilege> privileges
%type <tag> cmndtag
%type <seinfo> selinux
%type <string> rolespec
%type <string> typespec
%%
file : { ; }
| line
;
line : entry
| line entry
;
entry : COMMENT {
;
}
| error COMMENT {
yyerrok;
}
| userlist privileges {
add_userspec($1, $2);
}
| USERALIAS useraliases {
;
}
| HOSTALIAS hostaliases {
;
}
| CMNDALIAS cmndaliases {
;
}
| RUNASALIAS runasaliases {
;
}
| DEFAULTS defaults_list {
add_defaults(DEFAULTS, NULL, $2);
}
| DEFAULTS_USER userlist defaults_list {
add_defaults(DEFAULTS_USER, $2, $3);
}
| DEFAULTS_RUNAS userlist defaults_list {
add_defaults(DEFAULTS_RUNAS, $2, $3);
}
| DEFAULTS_HOST hostlist defaults_list {
add_defaults(DEFAULTS_HOST, $2, $3);
}
| DEFAULTS_CMND cmndlist defaults_list {
add_defaults(DEFAULTS_CMND, $2, $3);
}
;
defaults_list : defaults_entry
| defaults_list ',' defaults_entry {
list_append($1, $3);
$$ = $1;
}
;
defaults_entry : DEFVAR {
$$ = new_default($1, NULL, TRUE);
}
| '!' DEFVAR {
$$ = new_default($2, NULL, FALSE);
}
| DEFVAR '=' WORD {
$$ = new_default($1, $3, TRUE);
}
| DEFVAR '+' WORD {
$$ = new_default($1, $3, '+');
}
| DEFVAR '-' WORD {
$$ = new_default($1, $3, '-');
}
;
privileges : privilege
| privileges ':' privilege {
list_append($1, $3);
$$ = $1;
}
;
privilege : hostlist '=' cmndspeclist {
struct privilege *p = emalloc(sizeof(*p));
list2tq(&p->hostlist, $1);
list2tq(&p->cmndlist, $3);
p->prev = p;
p->next = NULL;
$$ = p;
}
;
ophost : host {
$$ = $1;
$$->negated = FALSE;
}
| '!' host {
$$ = $2;
$$->negated = TRUE;
}
;
host : ALIAS {
$$ = new_member($1, ALIAS);
}
| ALL {
$$ = new_member(NULL, ALL);
}
| NETGROUP {
$$ = new_member($1, NETGROUP);
}
| NTWKADDR {
$$ = new_member($1, NTWKADDR);
}
| WORD {
$$ = new_member($1, WORD);
}
;
cmndspeclist : cmndspec
| cmndspeclist ',' cmndspec {
list_append($1, $3);
#ifdef HAVE_SELINUX
/* propagate role and type */
if ($3->role == NULL)
$3->role = $3->prev->role;
if ($3->type == NULL)
$3->type = $3->prev->type;
#endif /* HAVE_SELINUX */
/* propagate tags and runas list */
if ($3->tags.nopasswd == UNSPEC)
$3->tags.nopasswd = $3->prev->tags.nopasswd;
if ($3->tags.noexec == UNSPEC)
$3->tags.noexec = $3->prev->tags.noexec;
if ($3->tags.setenv == UNSPEC &&
$3->prev->tags.setenv != IMPLIED)
$3->tags.setenv = $3->prev->tags.setenv;
if ($3->tags.transcript == UNSPEC)
$3->tags.transcript = $3->prev->tags.transcript;
if ((tq_empty(&$3->runasuserlist) &&
tq_empty(&$3->runasgrouplist)) &&
(!tq_empty(&$3->prev->runasuserlist) ||
!tq_empty(&$3->prev->runasgrouplist))) {
$3->runasuserlist = $3->prev->runasuserlist;
$3->runasgrouplist = $3->prev->runasgrouplist;
}
$$ = $1;
}
;
cmndspec : runasspec selinux cmndtag opcmnd {
struct cmndspec *cs = emalloc(sizeof(*cs));
if ($1 != NULL) {
list2tq(&cs->runasuserlist, $1->runasusers);
list2tq(&cs->runasgrouplist, $1->runasgroups);
efree($1);
} else {
tq_init(&cs->runasuserlist);
tq_init(&cs->runasgrouplist);
}
#ifdef HAVE_SELINUX
cs->role = $2.role;
cs->type = $2.type;
#endif
cs->tags = $3;
cs->cmnd = $4;
cs->prev = cs;
cs->next = NULL;
/* sudo "ALL" implies the SETENV tag */
if (cs->cmnd->type == ALL && !cs->cmnd->negated &&
cs->tags.setenv == UNSPEC)
cs->tags.setenv = IMPLIED;
$$ = cs;
}
;
opcmnd : cmnd {
$$ = $1;
$$->negated = FALSE;
}
| '!' cmnd {
$$ = $2;
$$->negated = TRUE;
}
;
rolespec : ROLE '=' WORD {
$$ = $3;
}
;
typespec : TYPE '=' WORD {
$$ = $3;
}
;
selinux : /* empty */ {
$$.role = NULL;
$$.type = NULL;
}
| rolespec {
$$.role = $1;
$$.type = NULL;
}
| typespec {
$$.type = $1;
$$.role = NULL;
}
| rolespec typespec {
$$.role = $1;
$$.type = $2;
}
| typespec rolespec {
$$.type = $1;
$$.role = $2;
}
;
runasspec : /* empty */ {
$$ = NULL;
}
| '(' runaslist ')' {
$$ = $2;
}
;
runaslist : userlist {
$$ = emalloc(sizeof(struct runascontainer));
$$->runasusers = $1;
$$->runasgroups = NULL;
}
| userlist ':' grouplist {
$$ = emalloc(sizeof(struct runascontainer));
$$->runasusers = $1;
$$->runasgroups = $3;
}
| ':' grouplist {
$$ = emalloc(sizeof(struct runascontainer));
$$->runasusers = NULL;
$$->runasgroups = $2;
}
;
cmndtag : /* empty */ {
$$.nopasswd = $$.noexec = $$.setenv = $$.transcript = UNSPEC;
}
| cmndtag NOPASSWD {
$$.nopasswd = TRUE;
}
| cmndtag PASSWD {
$$.nopasswd = FALSE;
}
| cmndtag NOEXEC {
$$.noexec = TRUE;
}
| cmndtag EXEC {
$$.noexec = FALSE;
}
| cmndtag SETENV {
$$.setenv = TRUE;
}
| cmndtag NOSETENV {
$$.setenv = FALSE;
}
| cmndtag TRANSCRIPT {
$$.transcript = TRUE;
}
| cmndtag NOTRANSCRIPT {
$$.transcript = FALSE;
}
;
cmnd : ALL {
$$ = new_member(NULL, ALL);
}
| ALIAS {
$$ = new_member($1, ALIAS);
}
| COMMAND {
struct sudo_command *c = emalloc(sizeof(*c));
c->cmnd = $1.cmnd;
c->args = $1.args;
$$ = new_member((char *)c, COMMAND);
}
;
hostaliases : hostalias
| hostaliases ':' hostalias
;
hostalias : ALIAS '=' hostlist {
char *s;
if ((s = alias_add($1, HOSTALIAS, $3)) != NULL) {
yyerror(s);
YYERROR;
}
}
;
hostlist : ophost
| hostlist ',' ophost {
list_append($1, $3);
$$ = $1;
}
;
cmndaliases : cmndalias
| cmndaliases ':' cmndalias
;
cmndalias : ALIAS '=' cmndlist {
char *s;
if ((s = alias_add($1, CMNDALIAS, $3)) != NULL) {
yyerror(s);
YYERROR;
}
}
;
cmndlist : opcmnd
| cmndlist ',' opcmnd {
list_append($1, $3);
$$ = $1;
}
;
runasaliases : runasalias
| runasaliases ':' runasalias
;
runasalias : ALIAS '=' userlist {
char *s;
if ((s = alias_add($1, RUNASALIAS, $3)) != NULL) {
yyerror(s);
YYERROR;
}
}
;
useraliases : useralias
| useraliases ':' useralias
;
useralias : ALIAS '=' userlist {
char *s;
if ((s = alias_add($1, USERALIAS, $3)) != NULL) {
yyerror(s);
YYERROR;
}
}
;
userlist : opuser
| userlist ',' opuser {
list_append($1, $3);
$$ = $1;
}
;
opuser : user {
$$ = $1;
$$->negated = FALSE;
}
| '!' user {
$$ = $2;
$$->negated = TRUE;
}
;
user : ALIAS {
$$ = new_member($1, ALIAS);
}
| ALL {
$$ = new_member(NULL, ALL);
}
| NETGROUP {
$$ = new_member($1, NETGROUP);
}
| USERGROUP {
$$ = new_member($1, USERGROUP);
}
| WORD {
$$ = new_member($1, WORD);
}
;
grouplist : opgroup
| grouplist ',' opgroup {
list_append($1, $3);
$$ = $1;
}
;
opgroup : group {
$$ = $1;
$$->negated = FALSE;
}
| '!' group {
$$ = $2;
$$->negated = TRUE;
}
;
group : ALIAS {
$$ = new_member($1, ALIAS);
}
| ALL {
$$ = new_member(NULL, ALL);
}
| WORD {
$$ = new_member($1, WORD);
}
;
%%
static struct defaults *
new_default(var, val, op)
char *var;
char *val;
int op;
{
struct defaults *d;
d = emalloc(sizeof(struct defaults));
d->var = var;
d->val = val;
tq_init(&d->binding);
d->type = 0;
d->op = op;
d->prev = d;
d->next = NULL;
return(d);
}
static struct member *
new_member(name, type)
char *name;
int type;
{
struct member *m;
m = emalloc(sizeof(struct member));
m->name = name;
m->type = type;
m->prev = m;
m->next = NULL;
return(m);
}
/*
* Add a list of defaults structures to the defaults list.
* The binding, if non-NULL, specifies a list of hosts, users, or
* runas users the entries apply to (specified by the type).
*/
static void
add_defaults(type, bmem, defs)
int type;
struct member *bmem;
struct defaults *defs;
{
struct defaults *d;
struct member_list binding;
/*
* We can only call list2tq once on bmem as it will zero
* out the prev pointer when it consumes bmem.
*/
list2tq(&binding, bmem);
/*
* Set type and binding (who it applies to) for new entries.
*/
for (d = defs; d != NULL; d = d->next) {
d->type = type;
d->binding = binding;
}
tq_append(&defaults, defs);
}
/*
* Allocate a new struct userspec, populate it, and insert it at the
* and of the userspecs list.
*/
static void
add_userspec(members, privs)
struct member *members;
struct privilege *privs;
{
struct userspec *u;
u = emalloc(sizeof(*u));
list2tq(&u->users, members);
list2tq(&u->privileges, privs);
u->prev = u;
u->next = NULL;
tq_append(&userspecs, u);
}
/*
* Free up space used by data structures from a previous parser run and sets
* the current sudoers file to path.
*/
void
init_parser(path, quiet)
char *path;
int quiet;
{
struct defaults *d;
struct member *m, *binding;
struct userspec *us;
struct privilege *priv;
struct cmndspec *cs;
struct sudo_command *c;
while ((us = tq_pop(&userspecs)) != NULL) {
while ((m = tq_pop(&us->users)) != NULL) {
efree(m->name);
efree(m);
}
while ((priv = tq_pop(&us->privileges)) != NULL) {
struct member *runasuser = NULL, *runasgroup = NULL;
#ifdef HAVE_SELINUX
char *role = NULL, *type = NULL;
#endif /* HAVE_SELINUX */
while ((m = tq_pop(&priv->hostlist)) != NULL) {
efree(m->name);
efree(m);
}
while ((cs = tq_pop(&priv->cmndlist)) != NULL) {
#ifdef HAVE_SELINUX
/* Only free the first instance of a role/type. */
if (cs->role != role) {
role = cs->role;
efree(cs->role);
}
if (cs->type != type) {
type = cs->type;
efree(cs->type);
}
#endif /* HAVE_SELINUX */
if (tq_last(&cs->runasuserlist) != runasuser) {
runasuser = tq_last(&cs->runasuserlist);
while ((m = tq_pop(&cs->runasuserlist)) != NULL) {
efree(m->name);
efree(m);
}
}
if (tq_last(&cs->runasgrouplist) != runasgroup) {
runasgroup = tq_last(&cs->runasgrouplist);
while ((m = tq_pop(&cs->runasgrouplist)) != NULL) {
efree(m->name);
efree(m);
}
}
if (cs->cmnd->type == COMMAND) {
c = (struct sudo_command *) cs->cmnd->name;
efree(c->cmnd);
efree(c->args);
}
efree(cs->cmnd->name);
efree(cs->cmnd);
efree(cs);
}
efree(priv);
}
efree(us);
}
tq_init(&userspecs);
binding = NULL;
while ((d = tq_pop(&defaults)) != NULL) {
if (tq_last(&d->binding) != binding) {
binding = tq_last(&d->binding);
while ((m = tq_pop(&d->binding)) != NULL) {
if (m->type == COMMAND) {
c = (struct sudo_command *) m->name;
efree(c->cmnd);
efree(c->args);
}
efree(m->name);
efree(m);
}
}
efree(d->var);
efree(d->val);
efree(d);
}
tq_init(&defaults);
init_aliases();
init_lexer();
efree(sudoers);
sudoers = path ? estrdup(path) : NULL;
parse_error = FALSE;
errorlineno = -1;
errorfile = NULL;
sudolineno = 1;
verbose = !quiet;
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 1996, 1998, 1999 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _SUDO_INS_2001_H
#define _SUDO_INS_2001_H
/*
* HAL insults (paraphrased) from 2001.
*/
"Just what do you think you're doing Dave?",
"It can only be attributed to human error.",
"That's something I cannot allow to happen.",
"My mind is going. I can feel it.",
"Sorry about this, I know it's a bit silly.",
"Take a stress pill and think things over.",
"This mission is too important for me to allow you to jeopardize it.",
"I feel much better now.",
#endif /* _SUDO_INS_2001_H */

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 1996, 1998, 1999 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _SUDO_INS_CLASSIC_H
#define _SUDO_INS_CLASSIC_H
/*
* Insults from the original sudo(8).
*/
"Wrong! You cheating scum!",
#ifdef PC_INSULTS
"And you call yourself a Rocket Scientist!",
#else
"No soap, honkie-lips.",
#endif
"Where did you learn to type?",
"Are you on drugs?",
"My pet ferret can type better than you!",
"You type like i drive.",
"Do you think like you type?",
"Your mind just hasn't been the same since the electro-shock, has it?",
#endif /* _SUDO_INS_CLASSIC_H */

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 1996, 1998, 1999, 2004
* Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _SUDO_INS_CSOPS_H
#define _SUDO_INS_CSOPS_H
/*
* CSOps insults (may be site dependent).
*/
"Maybe if you used more than just two fingers...",
"BOB says: You seem to have forgotten your passwd, enter another!",
"stty: unknown mode: doofus",
"I can't hear you -- I'm using the scrambler.",
"The more you drive -- the dumber you get.",
#ifdef PC_INSULTS
"Listen, broccoli brains, I don't have time to listen to this trash.",
#else
"Listen, burrito brains, I don't have time to listen to this trash.",
#endif
"I've seen penguins that can type better than that.",
"Have you considered trying to match wits with a rutabaga?",
"You speak an infinite deal of nothing",
#endif /* _SUDO_INS_CSOPS_H */

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) 1996, 1998, 1999 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _SUDO_INS_GOONS_H
#define _SUDO_INS_GOONS_H
/*
* Insults from the "Goon Show."
*/
"You silly, twisted boy you.",
"He has fallen in the water!",
"We'll all be murdered in our beds!",
"You can't come in. Our tiger has got flu",
"I don't wish to know that.",
"What, what, what, what, what, what, what, what, what, what?",
"You can't get the wood, you know.",
"You'll starve!",
"... and it used to be so popular...",
"Pauses for audience applause, not a sausage",
"Hold it up to the light --- not a brain in sight!",
"Have a gorilla...",
"There must be cure for it!",
"There's a lot of it about, you know.",
"You do that again and see what happens...",
"Ying Tong Iddle I Po",
"Harm can come to a young lad like that!",
"And with that remarks folks, the case of the Crown vs yourself was proven.",
"Speak English you fool --- there are no subtitles in this scene.",
"You gotta go owwwww!",
"I have been called worse.",
"It's only your word against mine.",
"I think ... err ... I think ... I think I'll go home",
#endif /* _SUDO_INS_GOONS_H */

227
plugins/sudoers/install-sh Executable file
View File

@@ -0,0 +1,227 @@
#! /bin/sh
## (From INN-1.4, written by Rich Salz)
## $Revision$
## A script to install files and directories.
PROGNAME=`basename $0`
## Paths to programs. CHOWN, STRIP and WHOAMI are checked below.
CHOWN=chown
CHGRP=chgrp
CHMOD=chmod
CP=cp
LN=ln
MKDIR=mkdir
MV=mv
RM=rm
STRIP=strip
WHOAMI="echo root"
## Some systems don't support -x, so we have to use -f.
for d in /sbin /etc /usr/sbin /usr/etc; do
if [ -f $d/chown ]; then
CHOWN=${d}/chown
break
fi
done
for d in /usr/bin /bin /usr/ucb /usr/bsd; do
if [ -f $d/whoami ]; then
WHOAMI=${d}/whoami
break
elif [ -f $d/id ]; then
WHOAMI=${d}/id | sed -n 's/^[^(]*(\([^)]*\)).*/\1/p'
fi
done
for d in /usr/ccs/bin /usr/bin /bin; do
if [ -f $d/strip ]; then
STRIP=${d}/strip
break
fi
done
## Defaults.
CHOWNIT=false
CHGROUPIT=false
CHMODIT=false
STRIPIT=false
BACKIT=false
TOUCHIT=true
SAVESRC=false
case `${WHOAMI}` in
root)
ROOT=true
;;
*)
ROOT=false
;;
esac
## Process JCL.
MORETODO=true
while ${MORETODO} ; do
case X"$1" in
X-b)
BACKIT=true
BACKUP="$2"
shift
;;
X-b*)
BACKIT=true
BACKUP=`expr "$1" : '-b\(.*\)'`
;;
X-c)
SAVESRC=true
;;
X-g)
GROUP="$2"
CHGROUPIT=true
shift
;;
X-g*)
GROUP=`expr "$1" : '-g\(.*\)'`
CHGROUPIT=true
;;
X-G)
GROUP="$2"
shift
${ROOT} && CHGROUPIT=true
;;
X-G*)
if ${ROOT} ; then
GROUP=`expr "$1" : '-g\(.*\)'`
CHGROUPIT=true
fi
;;
X-m)
MODE="$2"
CHMODIT=true
shift
;;
X-m*)
MODE=`expr "$1" : '-m\(.*\)'`
CHMODIT=true
;;
X-M)
MODE="$2"
${ROOT} && CHMODIT=true
shift
;;
X-M*)
MODE=`expr "$1" : '-m\(.*\)'`
${ROOT} && CHMODIT=true
;;
X-n)
TOUCHIT=false
;;
X-o)
OWNER="$2"
CHOWNIT=true
shift
;;
X-o*)
OWNER=`expr "$1" : '-o\(.*\)'`
CHOWNIT=true
;;
X-O)
OWNER="$2"
shift
${ROOT} && CHOWNIT=true
;;
X-O*)
if ${ROOT} ; then
OWNER=`expr "$1" : '-o\(.*\)'`
CHOWNIT=true
fi
;;
X-s)
STRIPIT=true
;;
X--)
shift
MORETODO=false
;;
X-*)
echo "${PROGNAME}: Unknown flag $1" 1>&2
exit 1
;;
*)
MORETODO=false
;;
esac
${MORETODO} && shift
done
## Process arguments.
if [ $# -ne 2 ] ; then
echo "Usage: ${PROGNAME} [flags] source destination"
exit 1
fi
## Making a directory?
if [ X"$1" = X. ] ; then
DEST="$2"
if [ ! -d "${DEST}" ] ; then
${MKDIR} "${DEST}" || exit 1
fi
if ${CHOWNIT} ; then
${CHOWN} "${OWNER}" "${DEST}" || exit 1
fi
if ${CHGROUPIT} ; then
${CHGRP} "${GROUP}" "${DEST}" || exit 1
fi
if ${CHMODIT} ; then
umask 0
${CHMOD} "${MODE}" "${DEST}" || exit 1
fi
exit 0
fi
## Get the destination and a temp file in the destination diretory.
if [ -d "$2" ] ; then
DEST="$2/`basename $1`"
TEMP="$2/$$.tmp"
else
DEST="$2"
TEMP="`expr "$2" : '\(.*\)/.*'`/$$.tmp"
fi
## If not given the same name, we must try to copy.
if [ X"$1" != X"$2" -o $SAVESRC ] ; then
if cmp -s "$1" "${DEST}" ; then
## Files are same; touch or not.
${TOUCHIT} && touch "${DEST}"
else
## If destination exists and we wish to backup, link to backup.
if [ -f "${DEST}" ] ; then
if ${BACKIT} ; then
${RM} -f "${DEST}${BACKUP}"
${LN} "${DEST}" "${DEST}${BACKUP}"
fi
fi
## Copy source to the right dir, then move to right spot.
## Done in two parts so we can hope for atomicity.
${RM} -f "${TEMP}" || exit 1
${CP} "$1" "${TEMP}" || exit 1
${MV} -f "${TEMP}" "${DEST}" || exit 1
fi
fi
## Strip and set the modes.
if ${STRIPIT} ; then
${STRIP} "${DEST}" || exit 1
fi
if ${CHOWNIT} ; then
${CHOWN} "${OWNER}" "${DEST}" || exit 1
fi
if ${CHGROUPIT} ; then
${CHGRP} "${GROUP}" "${DEST}" || exit 1
fi
if ${CHMODIT} ; then
umask 0
${CHMOD} "${MODE}" "${DEST}" || exit 1
fi
exit 0

61
plugins/sudoers/insults.h Normal file
View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) 1994-1996, 1998-1999, 2004
* Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _SUDO_INSULTS_H
#define _SUDO_INSULTS_H
#if defined(HAL_INSULTS) || defined(GOONS_INSULTS) || defined(CLASSIC_INSULTS) || defined(CSOPS_INSULTS)
/*
* Use one or more set of insults as determined by configure
*/
char *insults[] = {
# ifdef HAL_INSULTS
# include "ins_2001.h"
# endif
# ifdef GOONS_INSULTS
# include "ins_goons.h"
# endif
# ifdef CLASSIC_INSULTS
# include "ins_classic.h"
# endif
# ifdef CSOPS_INSULTS
# include "ins_csops.h"
# endif
(char *) 0
};
/*
* How may I insult you? Let me count the ways...
*/
#define NOFINSULTS (sizeof(insults) / sizeof(insults[0]) - 1)
/*
* return a pseudo-random insult.
*/
#define INSULT (insults[time(NULL) % NOFINSULTS])
#endif /* HAL_INSULTS || GOONS_INSULTS || CLASSIC_INSULTS || CSOPS_INSULTS */
#endif /* _SUDO_INSULTS_H */

View File

@@ -0,0 +1,354 @@
/*
* Copyright (c) 1996, 1998-2005, 2007-2009
* Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
/*
* Suppress a warning w/ gcc on Digital UN*X.
* The system headers should really do this....
*/
#if defined(__osf__) && !defined(__cplusplus)
struct mbuf;
struct rtentry;
#endif
#include <config.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#if defined(HAVE_SYS_SOCKIO_H) && !defined(SIOCGIFCONF)
# include <sys/sockio.h>
#endif
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
# include <memory.h>
# endif
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <netdb.h>
#include <errno.h>
#ifdef _ISC
# include <sys/stream.h>
# include <sys/sioctl.h>
# include <sys/stropts.h>
# define STRSET(cmd, param, len) {strioctl.ic_cmd=(cmd);\
strioctl.ic_dp=(param);\
strioctl.ic_timout=0;\
strioctl.ic_len=(len);}
#endif /* _ISC */
#ifdef _MIPS
# include <net/soioctl.h>
#endif /* _MIPS */
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#ifdef HAVE_GETIFADDRS
# include <ifaddrs.h>
#endif
#include "sudo.h"
#include "interfaces.h"
/* Minix apparently lacks IFF_LOOPBACK */
#ifndef IFF_LOOPBACK
# define IFF_LOOPBACK 0
#endif
#ifdef HAVE_GETIFADDRS
/*
* Allocate and fill in the interfaces global variable with the
* machine's ip addresses and netmasks.
*/
void
load_interfaces()
{
struct ifaddrs *ifa, *ifaddrs;
struct sockaddr_in *sin;
#ifdef HAVE_IN6_ADDR
struct sockaddr_in6 *sin6;
#endif
int i;
if (getifaddrs(&ifaddrs))
return;
/* Allocate space for the interfaces list. */
for (ifa = ifaddrs; ifa != NULL; ifa = ifa -> ifa_next) {
/* Skip interfaces marked "down" and "loopback". */
if (ifa->ifa_addr == NULL || !ISSET(ifa->ifa_flags, IFF_UP) ||
ISSET(ifa->ifa_flags, IFF_LOOPBACK))
continue;
switch(ifa->ifa_addr->sa_family) {
case AF_INET:
#ifdef HAVE_IN6_ADDR
case AF_INET6:
#endif
num_interfaces++;
break;
}
}
if (num_interfaces == 0)
return;
interfaces =
(struct interface *) emalloc2(num_interfaces, sizeof(struct interface));
/* Store the ip addr / netmask pairs. */
for (ifa = ifaddrs, i = 0; ifa != NULL; ifa = ifa -> ifa_next) {
/* Skip interfaces marked "down" and "loopback". */
if (ifa->ifa_addr == NULL || !ISSET(ifa->ifa_flags, IFF_UP) ||
ISSET(ifa->ifa_flags, IFF_LOOPBACK))
continue;
switch(ifa->ifa_addr->sa_family) {
case AF_INET:
sin = (struct sockaddr_in *)ifa->ifa_addr;
if (sin == NULL)
continue;
memcpy(&interfaces[i].addr, &sin->sin_addr,
sizeof(struct in_addr));
sin = (struct sockaddr_in *)ifa->ifa_netmask;
if (sin == NULL)
continue;
memcpy(&interfaces[i].netmask, &sin->sin_addr,
sizeof(struct in_addr));
interfaces[i].family = AF_INET;
i++;
break;
#ifdef HAVE_IN6_ADDR
case AF_INET6:
sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
if (sin6 == NULL)
continue;
memcpy(&interfaces[i].addr, &sin6->sin6_addr,
sizeof(struct in6_addr));
sin6 = (struct sockaddr_in6 *)ifa->ifa_netmask;
if (sin6 == NULL)
continue;
memcpy(&interfaces[i].netmask, &sin6->sin6_addr,
sizeof(struct in6_addr));
interfaces[i].family = AF_INET6;
i++;
break;
#endif /* HAVE_IN6_ADDR */
}
}
#ifdef HAVE_FREEIFADDRS
freeifaddrs(ifaddrs);
#else
efree(ifaddrs);
#endif
}
#elif defined(SIOCGIFCONF) && !defined(STUB_LOAD_INTERFACES)
/*
* Allocate and fill in the interfaces global variable with the
* machine's ip addresses and netmasks.
*/
void
load_interfaces()
{
struct ifconf *ifconf;
struct ifreq *ifr, ifr_tmp;
struct sockaddr_in *sin;
int sock, n, i;
size_t len = sizeof(struct ifconf) + BUFSIZ;
char *previfname = "", *ifconf_buf = NULL;
#ifdef _ISC
struct strioctl strioctl;
#endif /* _ISC */
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0)
error(1, "cannot open socket");
/*
* Get interface configuration or return (leaving num_interfaces == 0)
*/
for (;;) {
ifconf_buf = erealloc(ifconf_buf, len);
ifconf = (struct ifconf *) ifconf_buf;
ifconf->ifc_len = len - sizeof(struct ifconf);
ifconf->ifc_buf = (caddr_t) (ifconf_buf + sizeof(struct ifconf));
#ifdef _ISC
STRSET(SIOCGIFCONF, (caddr_t) ifconf, len);
if (ioctl(sock, I_STR, (caddr_t) &strioctl) < 0) {
#else
/* Note that some kernels return EINVAL if the buffer is too small */
if (ioctl(sock, SIOCGIFCONF, (caddr_t) ifconf) < 0 && errno != EINVAL) {
#endif /* _ISC */
efree(ifconf_buf);
(void) close(sock);
return;
}
/* Break out of loop if we have a big enough buffer. */
if (ifconf->ifc_len + sizeof(struct ifreq) < len)
break;
len += BUFSIZ;
}
/* Allocate space for the maximum number of interfaces that could exist. */
if ((n = ifconf->ifc_len / sizeof(struct ifreq)) == 0)
return;
interfaces = (struct interface *) emalloc2(n, sizeof(struct interface));
/* For each interface, store the ip address and netmask. */
for (i = 0; i < ifconf->ifc_len; ) {
/* Get a pointer to the current interface. */
ifr = (struct ifreq *) &ifconf->ifc_buf[i];
/* Set i to the subscript of the next interface. */
i += sizeof(struct ifreq);
#ifdef HAVE_SA_LEN
if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_addr))
i += ifr->ifr_addr.sa_len - sizeof(struct sockaddr);
#endif /* HAVE_SA_LEN */
/* Skip duplicates and interfaces with NULL addresses. */
sin = (struct sockaddr_in *) &ifr->ifr_addr;
if (sin->sin_addr.s_addr == 0 ||
strncmp(previfname, ifr->ifr_name, sizeof(ifr->ifr_name) - 1) == 0)
continue;
if (ifr->ifr_addr.sa_family != AF_INET)
continue;
#ifdef SIOCGIFFLAGS
zero_bytes(&ifr_tmp, sizeof(ifr_tmp));
strncpy(ifr_tmp.ifr_name, ifr->ifr_name, sizeof(ifr_tmp.ifr_name) - 1);
if (ioctl(sock, SIOCGIFFLAGS, (caddr_t) &ifr_tmp) < 0)
#endif
ifr_tmp = *ifr;
/* Skip interfaces marked "down" and "loopback". */
if (!ISSET(ifr_tmp.ifr_flags, IFF_UP) ||
ISSET(ifr_tmp.ifr_flags, IFF_LOOPBACK))
continue;
sin = (struct sockaddr_in *) &ifr->ifr_addr;
interfaces[num_interfaces].addr.ip4.s_addr = sin->sin_addr.s_addr;
/* Stash the name of the interface we saved. */
previfname = ifr->ifr_name;
/* Get the netmask. */
zero_bytes(&ifr_tmp, sizeof(ifr_tmp));
strncpy(ifr_tmp.ifr_name, ifr->ifr_name, sizeof(ifr_tmp.ifr_name) - 1);
#ifdef SIOCGIFNETMASK
#ifdef _ISC
STRSET(SIOCGIFNETMASK, (caddr_t) &ifr_tmp, sizeof(ifr_tmp));
if (ioctl(sock, I_STR, (caddr_t) &strioctl) == 0) {
#else
if (ioctl(sock, SIOCGIFNETMASK, (caddr_t) &ifr_tmp) == 0) {
#endif /* _ISC */
sin = (struct sockaddr_in *) &ifr_tmp.ifr_addr;
interfaces[num_interfaces].netmask.ip4.s_addr = sin->sin_addr.s_addr;
} else {
#else
{
#endif /* SIOCGIFNETMASK */
if (IN_CLASSC(interfaces[num_interfaces].addr.ip4.s_addr))
interfaces[num_interfaces].netmask.ip4.s_addr = htonl(IN_CLASSC_NET);
else if (IN_CLASSB(interfaces[num_interfaces].addr.ip4.s_addr))
interfaces[num_interfaces].netmask.ip4.s_addr = htonl(IN_CLASSB_NET);
else
interfaces[num_interfaces].netmask.ip4.s_addr = htonl(IN_CLASSA_NET);
}
/* Only now can we be sure it was a good/interesting interface. */
interfaces[num_interfaces].family = AF_INET;
num_interfaces++;
}
/* If the expected size < real size, realloc the array. */
if (n != num_interfaces) {
if (num_interfaces != 0)
interfaces = (struct interface *) erealloc3(interfaces,
num_interfaces, sizeof(struct interface));
else
efree(interfaces);
}
efree(ifconf_buf);
(void) close(sock);
}
#else /* !SIOCGIFCONF || STUB_LOAD_INTERFACES */
/*
* Stub function for those without SIOCGIFCONF
*/
void
load_interfaces()
{
return;
}
#endif /* SIOCGIFCONF && !STUB_LOAD_INTERFACES */
void
dump_interfaces()
{
int i;
#ifdef HAVE_IN6_ADDR
char addrbuf[INET6_ADDRSTRLEN], maskbuf[INET6_ADDRSTRLEN];
#endif
puts("Local IP address and netmask pairs:");
for (i = 0; i < num_interfaces; i++) {
switch(interfaces[i].family) {
case AF_INET:
printf("\t%s / ", inet_ntoa(interfaces[i].addr.ip4));
puts(inet_ntoa(interfaces[i].netmask.ip4));
break;
#ifdef HAVE_IN6_ADDR
case AF_INET6:
inet_ntop(AF_INET6, &interfaces[i].addr.ip6,
addrbuf, sizeof(addrbuf));
inet_ntop(AF_INET6, &interfaces[i].netmask.ip6,
maskbuf, sizeof(maskbuf));
printf("\t%s / %s\n", addrbuf, maskbuf);
break;
#endif /* HAVE_IN6_ADDR */
}
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (c) 1996, 1998-2005, 2007
* Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#ifndef _SUDO_INTERFACES_H
#define _SUDO_INTERFACES_H
/*
* IP address and netmask pairs for checking against local interfaces.
*/
struct interface {
int family; /* AF_INET or AF_INET6 */
union {
struct in_addr ip4;
#ifdef HAVE_IN6_ADDR
struct in6_addr ip6;
#endif
} addr;
union {
struct in_addr ip4;
#ifdef HAVE_IN6_ADDR
struct in6_addr ip6;
#endif
} netmask;
};
/*
* Prototypes for external functions.
*/
void load_interfaces __P((void));
void dump_interfaces __P((void));
/*
* Definitions for external variables.
*/
#ifndef _SUDO_MAIN
extern struct interface *interfaces;
extern int num_interfaces;
#endif
#endif /* _SUDO_INTERFACES_H */

1945
plugins/sudoers/ldap.c Normal file

File diff suppressed because it is too large Load Diff

735
plugins/sudoers/logging.c Normal file
View File

@@ -0,0 +1,735 @@
/*
* Copyright (c) 1994-1996, 1998-2009 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#ifdef __TANDEM
# include <floss.h>
#endif
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <pwd.h>
#include <grp.h>
#include <signal.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include "sudo.h"
static void do_syslog __P((int, char *));
static void do_logfile __P((char *));
static void send_mail __P((char *));
static int should_mail __P((int));
static void mysyslog __P((int, const char *, ...));
static char *new_logline __P((const char *, int));
#define MAXSYSLOGTRIES 16 /* num of retries for broken syslogs */
/*
* We do an openlog(3)/closelog(3) for each message because some
* authentication methods (notably PAM) use syslog(3) for their
* own nefarious purposes and may call openlog(3) and closelog(3).
* Note that because we don't want to assume that all systems have
* vsyslog(3) (HP-UX doesn't) "%m" will not be expanded.
* Sadly this is a maze of #ifdefs.
*/
static void
#ifdef __STDC__
mysyslog(int pri, const char *fmt, ...)
#else
mysyslog(pri, fmt, va_alist)
int pri;
const char *fmt;
va_dcl
#endif
{
#ifdef BROKEN_SYSLOG
int i;
#endif
char buf[MAXSYSLOGLEN+1];
va_list ap;
#ifdef __STDC__
va_start(ap, fmt);
#else
va_start(ap);
#endif
#ifdef LOG_NFACILITIES
openlog("sudo", 0, def_syslog);
#else
openlog("sudo", 0);
#endif
vsnprintf(buf, sizeof(buf), fmt, ap);
#ifdef BROKEN_SYSLOG
/*
* Some versions of syslog(3) don't guarantee success and return
* an int (notably HP-UX < 10.0). So, if at first we don't succeed,
* try, try again...
*/
for (i = 0; i < MAXSYSLOGTRIES; i++)
if (syslog(pri, "%s", buf) == 0)
break;
#else
syslog(pri, "%s", buf);
#endif /* BROKEN_SYSLOG */
va_end(ap);
closelog();
}
#define FMT_FIRST "%8s : %s"
#define FMT_CONTD "%8s : (command continued) %s"
/*
* Log a message to syslog, pre-pending the username and splitting the
* message into parts if it is longer than MAXSYSLOGLEN.
*/
static void
do_syslog(pri, msg)
int pri;
char *msg;
{
size_t len, maxlen;
char *p, *tmp, save;
const char *fmt;
/*
* Log the full line, breaking into multiple syslog(3) calls if necessary
*/
fmt = FMT_FIRST;
maxlen = MAXSYSLOGLEN - (sizeof(FMT_FIRST) - 6 + strlen(user_name));
for (p = msg; *p != '\0'; ) {
len = strlen(p);
if (len > maxlen) {
/*
* Break up the line into what will fit on one syslog(3) line
* Try to avoid breaking words into several lines if possible.
*/
tmp = memrchr(p, ' ', maxlen);
if (tmp == NULL)
tmp = p + maxlen;
/* NULL terminate line, but save the char to restore later */
save = *tmp;
*tmp = '\0';
mysyslog(pri, fmt, user_name, p);
*tmp = save; /* restore saved character */
/* Advance p and eliminate leading whitespace */
for (p = tmp; *p == ' '; p++)
;
} else {
mysyslog(pri, fmt, user_name, p);
p += len;
}
fmt = FMT_CONTD;
maxlen = MAXSYSLOGLEN - (sizeof(FMT_CONTD) - 6 + strlen(user_name));
}
}
static void
do_logfile(msg)
char *msg;
{
char *full_line;
char *beg, *oldend, *end;
FILE *fp;
mode_t oldmask;
size_t maxlen;
oldmask = umask(077);
maxlen = def_loglinelen > 0 ? def_loglinelen : 0;
fp = fopen(def_logfile, "a");
(void) umask(oldmask);
if (fp == NULL) {
easprintf(&full_line, "Can't open log file: %s: %s",
def_logfile, strerror(errno));
send_mail(full_line);
efree(full_line);
} else if (!lock_file(fileno(fp), SUDO_LOCK)) {
easprintf(&full_line, "Can't lock log file: %s: %s",
def_logfile, strerror(errno));
send_mail(full_line);
efree(full_line);
} else {
time_t now;
now = time(NULL);
if (def_loglinelen == 0) {
/* Don't pretty-print long log file lines (hard to grep) */
if (def_log_host)
(void) fprintf(fp, "%s : %s : HOST=%s : %s\n",
get_timestr(now, def_log_year), user_name, user_shost, msg);
else
(void) fprintf(fp, "%s : %s : %s\n",
get_timestr(now, def_log_year), user_name, msg);
} else {
if (def_log_host)
easprintf(&full_line, "%s : %s : HOST=%s : %s",
get_timestr(now, def_log_year), user_name, user_shost, msg);
else
easprintf(&full_line, "%s : %s : %s",
get_timestr(now, def_log_year), user_name, msg);
/*
* Print out full_line with word wrap
*/
beg = end = full_line;
while (beg) {
oldend = end;
end = strchr(oldend, ' ');
if (maxlen > 0 && end) {
*end = '\0';
if (strlen(beg) > maxlen) {
/* too far, need to back up & print the line */
if (beg == (char *)full_line)
maxlen -= 4; /* don't indent first line */
*end = ' ';
if (oldend != beg) {
/* rewind & print */
end = oldend-1;
while (*end == ' ')
--end;
*(++end) = '\0';
(void) fprintf(fp, "%s\n ", beg);
*end = ' ';
} else {
(void) fprintf(fp, "%s\n ", beg);
}
/* reset beg to point to the start of the new substr */
beg = end;
while (*beg == ' ')
++beg;
} else {
/* we still have room */
*end = ' ';
}
/* remove leading whitespace */
while (*end == ' ')
++end;
} else {
/* final line */
(void) fprintf(fp, "%s\n", beg);
beg = NULL; /* exit condition */
}
}
efree(full_line);
}
(void) fflush(fp);
(void) lock_file(fileno(fp), SUDO_UNLOCK);
(void) fclose(fp);
}
}
/*
* Log and mail the denial message, optionally informing the user.
*/
void
log_denial(status, inform_user)
int status;
int inform_user;
{
char *message;
char *logline;
/* Set error message. */
if (ISSET(status, FLAG_NO_USER))
message = "user NOT in sudoers";
else if (ISSET(status, FLAG_NO_HOST))
message = "user NOT authorized on host";
else
message = "command not allowed";
logline = new_logline(message, 0);
if (should_mail(status))
send_mail(logline); /* send mail based on status */
/* Inform the user if they failed to authenticate. */
if (inform_user) {
if (ISSET(status, FLAG_NO_USER))
(void) fprintf(stderr, "%s is not in the sudoers file. %s",
user_name, "This incident will be reported.\n");
else if (ISSET(status, FLAG_NO_HOST))
(void) fprintf(stderr, "%s is not allowed to run sudo on %s. %s",
user_name, user_shost, "This incident will be reported.\n");
else if (ISSET(status, FLAG_NO_CHECK))
(void) fprintf(stderr, "Sorry, user %s may not run sudo on %s.\n",
user_name, user_shost);
else
(void) fprintf(stderr,
"Sorry, user %s is not allowed to execute '%s%s%s' as %s%s%s on %s.\n",
user_name, user_cmnd, user_args ? " " : "",
user_args ? user_args : "",
list_pw ? list_pw->pw_name : runas_pw ?
runas_pw->pw_name : user_name, runas_gr ? ":" : "",
runas_gr ? runas_gr->gr_name : "", user_host);
}
/*
* Log via syslog and/or a file.
*/
if (def_syslog)
do_syslog(def_syslog_badpri, logline);
if (def_logfile)
do_logfile(logline);
efree(logline);
}
/*
* Log and potentially mail the allowed command.
*/
void
log_allowed(status)
int status;
{
char *logline;
logline = new_logline(NULL, 0);
if (should_mail(status))
send_mail(logline); /* send mail based on status */
/*
* Log via syslog and/or a file.
*/
if (def_syslog)
do_syslog(def_syslog_goodpri, logline);
if (def_logfile)
do_logfile(logline);
efree(logline);
}
void
#ifdef __STDC__
log_error(int flags, const char *fmt, ...)
#else
log_error(flags, fmt, va_alist)
int flags;
const char *fmt;
va_dcl
#endif
{
int serrno = errno;
char *message;
char *logline;
va_list ap;
#ifdef __STDC__
va_start(ap, fmt);
#else
va_start(ap);
#endif
/* Become root if we are not already to avoid user interference */
set_perms(PERM_ROOT|PERM_NOEXIT);
/* Expand printf-style format + args. */
evasprintf(&message, fmt, ap);
va_end(ap);
if (ISSET(flags, MSG_ONLY))
logline = message;
else
logline = new_logline(message, ISSET(flags, USE_ERRNO) ? serrno : 0);
/*
* Tell the user.
*/
if (!ISSET(flags, NO_STDERR)) {
if (ISSET(flags, USE_ERRNO))
warning("%s", message);
else
warningx("%s", message);
}
if (logline != message)
efree(message);
/*
* Send a copy of the error via mail.
*/
if (!ISSET(flags, NO_MAIL))
send_mail(logline);
/*
* Log to syslog and/or a file.
*/
if (def_syslog)
do_syslog(def_syslog_badpri, logline);
if (def_logfile)
do_logfile(logline);
efree(logline);
if (!ISSET(flags, NO_EXIT)) {
cleanup(0);
exit(1);
}
}
#define MAX_MAILFLAGS 63
/*
* Send a message to MAILTO user
*/
static void
send_mail(line)
char *line;
{
FILE *mail;
char *p;
int fd, pfd[2], status;
pid_t pid, rv;
sigaction_t sa;
#ifndef NO_ROOT_MAILER
static char *root_envp[] = {
"HOME=/",
"PATH=/usr/bin:/bin",
"LOGNAME=root",
"USERNAME=root",
"USER=root",
NULL
};
#endif
/* Just return if mailer is disabled. */
if (!def_mailerpath || !def_mailto)
return;
/* Fork and return, child will daemonize. */
switch (pid = fork()) {
case -1:
/* Error. */
error(1, "cannot fork");
break;
case 0:
/* Child. */
switch (pid = fork()) {
case -1:
/* Error. */
mysyslog(LOG_ERR, "cannot fork: %m");
_exit(1);
case 0:
/* Grandchild continues below. */
break;
default:
/* Parent will wait for us. */
_exit(0);
}
break;
default:
/* Parent. */
do {
#ifdef HAVE_WAITPID
rv = waitpid(pid, &status, 0);
#else
rv = wait(&status);
#endif
} while (rv == -1 && errno == EINTR);
return;
}
/* Daemonize - disassociate from session/tty. */
#ifdef HAVE_SETSID
if (setsid() == -1)
warning("setsid");
#else
setpgrp(0, 0);
# ifdef TIOCNOTTY
if ((fd = open(_PATH_TTY, O_RDWR, 0644)) != -1) {
ioctl(fd, TIOCNOTTY, NULL);
close(fd);
}
# endif
#endif
chdir("/");
if ((fd = open(_PATH_DEVNULL, O_RDWR, 0644)) != -1) {
(void) dup2(fd, STDIN_FILENO);
(void) dup2(fd, STDOUT_FILENO);
(void) dup2(fd, STDERR_FILENO);
}
/* Close password, group and other fds so we don't leak. */
sudo_endpwent();
sudo_endgrent();
closefrom(STDERR_FILENO + 1);
/* Ignore SIGPIPE in case mailer exits prematurely (or is missing). */
zero_bytes(&sa, sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = SIG_IGN;
(void) sigaction(SIGPIPE, &sa, NULL);
if (pipe(pfd) == -1) {
mysyslog(LOG_ERR, "cannot open pipe: %m");
_exit(1);
}
switch (pid = fork()) {
case -1:
/* Error. */
mysyslog(LOG_ERR, "cannot fork: %m");
_exit(1);
break;
case 0:
{
char *argv[MAX_MAILFLAGS + 1];
char *mpath, *mflags;
int i;
/* Child, set stdin to output side of the pipe */
if (pfd[0] != STDIN_FILENO) {
(void) dup2(pfd[0], STDIN_FILENO);
(void) close(pfd[0]);
}
(void) close(pfd[1]);
/* Build up an argv based the mailer path and flags */
mflags = estrdup(def_mailerflags);
mpath = estrdup(def_mailerpath);
if ((argv[0] = strrchr(mpath, ' ')))
argv[0]++;
else
argv[0] = mpath;
i = 1;
if ((p = strtok(mflags, " \t"))) {
do {
argv[i] = p;
} while (++i < MAX_MAILFLAGS && (p = strtok(NULL, " \t")));
}
argv[i] = NULL;
/*
* Depending on the config, either run the mailer as root
* (so user cannot kill it) or as the user (for the paranoid).
*/
#ifndef NO_ROOT_MAILER
set_perms(PERM_ROOT|PERM_NOEXIT);
execve(mpath, argv, root_envp);
#else
set_perms(PERM_FULL_USER|PERM_NOEXIT);
execv(mpath, argv);
#endif /* NO_ROOT_MAILER */
mysyslog(LOG_ERR, "cannot execute %s: %m", mpath);
_exit(127);
}
break;
}
(void) close(pfd[0]);
mail = fdopen(pfd[1], "w");
/* Pipes are all setup, send message. */
(void) fprintf(mail, "To: %s\nFrom: %s\nAuto-Submitted: %s\nSubject: ",
def_mailto, def_mailfrom ? def_mailfrom : user_name, "auto-generated");
for (p = def_mailsub; *p; p++) {
/* Expand escapes in the subject */
if (*p == '%' && *(p+1) != '%') {
switch (*(++p)) {
case 'h':
(void) fputs(user_host, mail);
break;
case 'u':
(void) fputs(user_name, mail);
break;
default:
p--;
break;
}
} else
(void) fputc(*p, mail);
}
(void) fprintf(mail, "\n\n%s : %s : %s : %s\n\n", user_host,
get_timestr(time(NULL), def_log_year), user_name, line);
fclose(mail);
do {
#ifdef HAVE_WAITPID
rv = waitpid(pid, &status, 0);
#else
rv = wait(&status);
#endif
} while (rv == -1 && errno == EINTR);
_exit(0);
}
/*
* Determine whether we should send mail based on "status" and defaults options.
*/
static int
should_mail(status)
int status;
{
return(def_mail_always || ISSET(status, VALIDATE_ERROR) ||
(def_mail_no_user && ISSET(status, FLAG_NO_USER)) ||
(def_mail_no_host && ISSET(status, FLAG_NO_HOST)) ||
(def_mail_no_perms && !ISSET(status, VALIDATE_OK)));
}
#define LL_TTY_STR "TTY="
#define LL_CWD_STR "PWD=" /* XXX - should be CWD= */
#define LL_USER_STR "USER="
#define LL_GROUP_STR "GROUP="
#define LL_ENV_STR "ENV="
#define LL_CMND_STR "COMMAND="
#define LL_TSID_STR "TSID="
/*
* Allocate and fill in a new logline.
*/
static char *
new_logline(message, serrno)
const char *message;
int serrno;
{
size_t len = 0;
char *evstr = NULL;
char *errstr = NULL;
char *line;
/*
* Compute line length
*/
if (message != NULL)
len += strlen(message) + 3;
if (serrno) {
errstr = strerror(serrno);
len += strlen(errstr) + 3;
}
len += sizeof(LL_TTY_STR) + 2 + strlen(user_tty);
len += sizeof(LL_CWD_STR) + 2 + strlen(user_cwd);
if (runas_pw != NULL)
len += sizeof(LL_USER_STR) + 2 + strlen(runas_pw->pw_name);
if (runas_gr != NULL)
len += sizeof(LL_GROUP_STR) + 2 + strlen(runas_gr->gr_name);
if (sudo_user.sessid[0] != '\0')
len += sizeof(LL_TSID_STR) + 2 + strlen(sudo_user.sessid);
if (sudo_user.env_vars != NULL) {
size_t evlen = 0;
struct list_member *cur;
for (cur = sudo_user.env_vars; cur != NULL; cur = cur->next)
evlen += strlen(cur->value) + 1;
evstr = emalloc(evlen);
evstr[0] = '\0';
for (cur = sudo_user.env_vars; cur != NULL; cur = cur->next) {
strlcat(evstr, cur->value, evlen);
strlcat(evstr, " ", evlen); /* NOTE: last one will fail */
}
len += sizeof(LL_ENV_STR) + 2 + evlen;
}
len += sizeof(LL_CMND_STR) - 1 + strlen(user_cmnd);
if (user_args != NULL)
len += strlen(user_args) + 1;
/*
* Allocate and build up the line.
*/
line = emalloc(++len);
line[0] = '\0';
if (message != NULL) {
if (strlcat(line, message, len) >= len ||
strlcat(line, errstr ? " : " : " ; ", len) >= len)
goto toobig;
}
if (serrno) {
if (strlcat(line, errstr, len) >= len ||
strlcat(line, " ; ", len) >= len)
goto toobig;
}
if (strlcat(line, LL_TTY_STR, len) >= len ||
strlcat(line, user_tty, len) >= len ||
strlcat(line, " ; ", len) >= len)
goto toobig;
if (strlcat(line, LL_CWD_STR, len) >= len ||
strlcat(line, user_cwd, len) >= len ||
strlcat(line, " ; ", len) >= len)
goto toobig;
if (runas_pw != NULL) {
if (strlcat(line, LL_USER_STR, len) >= len ||
strlcat(line, runas_pw->pw_name, len) >= len ||
strlcat(line, " ; ", len) >= len)
goto toobig;
}
if (runas_gr != NULL) {
if (strlcat(line, LL_GROUP_STR, len) >= len ||
strlcat(line, runas_gr->gr_name, len) >= len ||
strlcat(line, " ; ", len) >= len)
goto toobig;
}
if (sudo_user.sessid[0] != '\0') {
if (strlcat(line, LL_TSID_STR, len) >= len ||
strlcat(line, sudo_user.sessid, len) >= len ||
strlcat(line, " ; ", len) >= len)
goto toobig;
}
if (evstr != NULL) {
if (strlcat(line, LL_ENV_STR, len) >= len ||
strlcat(line, evstr, len) >= len ||
strlcat(line, " ; ", len) >= len)
goto toobig;
efree(evstr);
}
if (strlcat(line, LL_CMND_STR, len) >= len ||
strlcat(line, user_cmnd, len) >= len)
goto toobig;
if (user_args != NULL) {
if (strlcat(line, " ", len) >= len ||
strlcat(line, user_args, len) >= len)
goto toobig;
}
return (line);
toobig:
errorx(1, "internal error: insufficient space for log line");
}

58
plugins/sudoers/logging.h Normal file
View File

@@ -0,0 +1,58 @@
/*
* Copyright (c) 1999-2005, 2009
* Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _LOGGING_H
#define _LOGGING_H
#include <syslog.h>
#ifdef __STDC__
# include <stdarg.h>
#else
# include <varargs.h>
#endif
/* Logging types */
#define SLOG_SYSLOG 0x01
#define SLOG_FILE 0x02
#define SLOG_BOTH 0x03
/* Flags for log_error() */
#define MSG_ONLY 0x01
#define USE_ERRNO 0x02
#define NO_MAIL 0x04
#define NO_EXIT 0x08
#define NO_STDERR 0x10
/*
* Maximum number of characters to log per entry. The syslogger
* will log this much, after that, it truncates the log line.
* We need this here to make sure that we continue with another
* syslog(3) call if the internal buffer is more than 1023 characters.
*/
#ifndef MAXSYSLOGLEN
# define MAXSYSLOGLEN 960
#endif
void audit_success __P((char **));
void audit_failure __P((char **, char const * const, ...));
void log_allowed __P((int));
void log_denial __P((int, int));
void log_error __P((int flags, const char *fmt, ...))
__printflike(2, 3);
RETSIGTYPE reapchild __P((int));
#endif /* _LOGGING_H */

887
plugins/sudoers/match.c Normal file
View File

@@ -0,0 +1,887 @@
/*
* Copyright (c) 1996, 1998-2005, 2007-2009
* Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#ifdef HAVE_FNMATCH
# include <fnmatch.h>
#endif /* HAVE_FNMATCH */
#ifdef HAVE_EXTENDED_GLOB
# include <glob.h>
#endif /* HAVE_EXTENDED_GLOB */
#ifdef HAVE_NETGROUP_H
# include <netgroup.h>
#endif /* HAVE_NETGROUP_H */
#include <ctype.h>
#include <pwd.h>
#include <grp.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#ifdef HAVE_DIRENT_H
# include <dirent.h>
# define NAMLEN(dirent) strlen((dirent)->d_name)
#else
# define dirent direct
# define NAMLEN(dirent) (dirent)->d_namlen
# ifdef HAVE_SYS_NDIR_H
# include <sys/ndir.h>
# endif
# ifdef HAVE_SYS_DIR_H
# include <sys/dir.h>
# endif
# ifdef HAVE_NDIR_H
# include <ndir.h>
# endif
#endif
#include "sudo.h"
#include "interfaces.h"
#include "parse.h"
#include <gram.h>
#ifndef HAVE_FNMATCH
# include "emul/fnmatch.h"
#endif /* HAVE_FNMATCH */
#ifndef HAVE_EXTENDED_GLOB
# include "emul/glob.h"
#endif /* HAVE_EXTENDED_GLOB */
#ifdef USING_NONUNIX_GROUPS
# include "nonunix.h"
#endif /* USING_NONUNIX_GROUPS */
static struct member_list empty;
static int command_matches_dir __P((char *, size_t));
static int command_matches_glob __P((char *, char *));
static int command_matches_fnmatch __P((char *, char *));
static int command_matches_normal __P((char *, char *));
/*
* Returns TRUE if string 's' contains meta characters.
*/
#define has_meta(s) (strpbrk(s, "\\?*[]") != NULL)
/*
* Check for user described by pw in a list of members.
* Returns ALLOW, DENY or UNSPEC.
*/
static int
_userlist_matches(pw, list)
struct passwd *pw;
struct member_list *list;
{
struct member *m;
struct alias *a;
int rval, matched = UNSPEC;
tq_foreach_rev(list, m) {
switch (m->type) {
case ALL:
matched = !m->negated;
break;
case NETGROUP:
if (netgr_matches(m->name, NULL, NULL, pw->pw_name))
matched = !m->negated;
break;
case USERGROUP:
if (usergr_matches(m->name, pw->pw_name, pw))
matched = !m->negated;
break;
case ALIAS:
if ((a = alias_find(m->name, USERALIAS)) != NULL) {
rval = _userlist_matches(pw, &a->members);
if (rval != UNSPEC)
matched = m->negated ? !rval : rval;
break;
}
/* FALLTHROUGH */
case WORD:
if (userpw_matches(m->name, pw->pw_name, pw))
matched = !m->negated;
break;
}
if (matched != UNSPEC)
break;
}
return(matched);
}
int
userlist_matches(pw, list)
struct passwd *pw;
struct member_list *list;
{
alias_seqno++;
return(_userlist_matches(pw, list));
}
/*
* Check for user described by pw in a list of members.
* If both lists are empty compare against def_runas_default.
* Returns ALLOW, DENY or UNSPEC.
*/
static int
_runaslist_matches(user_list, group_list)
struct member_list *user_list;
struct member_list *group_list;
{
struct member *m;
struct alias *a;
int rval, matched = UNSPEC;
if (runas_gr != NULL) {
if (tq_empty(group_list))
return(DENY); /* group was specified but none in sudoers */
if (runas_pw != NULL && strcmp(runas_pw->pw_name, user_name) &&
tq_empty(user_list))
return(DENY); /* user was specified but none in sudoers */
}
if (tq_empty(user_list) && tq_empty(group_list))
return(userpw_matches(def_runas_default, runas_pw->pw_name, runas_pw));
if (runas_pw != NULL) {
tq_foreach_rev(user_list, m) {
switch (m->type) {
case ALL:
matched = !m->negated;
break;
case NETGROUP:
if (netgr_matches(m->name, NULL, NULL, runas_pw->pw_name))
matched = !m->negated;
break;
case USERGROUP:
if (usergr_matches(m->name, runas_pw->pw_name, runas_pw))
matched = !m->negated;
break;
case ALIAS:
if ((a = alias_find(m->name, RUNASALIAS)) != NULL) {
rval = _runaslist_matches(&a->members, &empty);
if (rval != UNSPEC)
matched = m->negated ? !rval : rval;
break;
}
/* FALLTHROUGH */
case WORD:
if (userpw_matches(m->name, runas_pw->pw_name, runas_pw))
matched = !m->negated;
break;
}
if (matched != UNSPEC)
break;
}
}
if (runas_gr != NULL) {
tq_foreach_rev(group_list, m) {
switch (m->type) {
case ALL:
matched = !m->negated;
break;
case ALIAS:
if ((a = alias_find(m->name, RUNASALIAS)) != NULL) {
rval = _runaslist_matches(&a->members, &empty);
if (rval != UNSPEC)
matched = m->negated ? !rval : rval;
break;
}
/* FALLTHROUGH */
case WORD:
if (group_matches(m->name, runas_gr))
matched = !m->negated;
break;
}
if (matched != UNSPEC)
break;
}
}
return(matched);
}
int
runaslist_matches(user_list, group_list)
struct member_list *user_list;
struct member_list *group_list;
{
alias_seqno++;
return(_runaslist_matches(user_list ? user_list : &empty,
group_list ? group_list : &empty));
}
/*
* Check for host and shost in a list of members.
* Returns ALLOW, DENY or UNSPEC.
*/
static int
_hostlist_matches(list)
struct member_list *list;
{
struct member *m;
struct alias *a;
int rval, matched = UNSPEC;
tq_foreach_rev(list, m) {
switch (m->type) {
case ALL:
matched = !m->negated;
break;
case NETGROUP:
if (netgr_matches(m->name, user_host, user_shost, NULL))
matched = !m->negated;
break;
case NTWKADDR:
if (addr_matches(m->name))
matched = !m->negated;
break;
case ALIAS:
if ((a = alias_find(m->name, HOSTALIAS)) != NULL) {
rval = _hostlist_matches(&a->members);
if (rval != UNSPEC)
matched = m->negated ? !rval : rval;
break;
}
/* FALLTHROUGH */
case WORD:
if (hostname_matches(user_shost, user_host, m->name))
matched = !m->negated;
break;
}
if (matched != UNSPEC)
break;
}
return(matched);
}
int
hostlist_matches(list)
struct member_list *list;
{
alias_seqno++;
return(_hostlist_matches(list));
}
/*
* Check for cmnd and args in a list of members.
* Returns ALLOW, DENY or UNSPEC.
*/
static int
_cmndlist_matches(list)
struct member_list *list;
{
struct member *m;
int matched = UNSPEC;
tq_foreach_rev(list, m) {
matched = cmnd_matches(m);
if (matched != UNSPEC)
break;
}
return(matched);
}
int
cmndlist_matches(list)
struct member_list *list;
{
alias_seqno++;
return(_cmndlist_matches(list));
}
/*
* Check cmnd and args.
* Returns ALLOW, DENY or UNSPEC.
*/
int
cmnd_matches(m)
struct member *m;
{
struct alias *a;
struct sudo_command *c;
int rval, matched = UNSPEC;
switch (m->type) {
case ALL:
matched = !m->negated;
break;
case ALIAS:
alias_seqno++;
if ((a = alias_find(m->name, CMNDALIAS)) != NULL) {
rval = _cmndlist_matches(&a->members);
if (rval != UNSPEC)
matched = m->negated ? !rval : rval;
}
break;
case COMMAND:
c = (struct sudo_command *)m->name;
if (command_matches(c->cmnd, c->args))
matched = !m->negated;
break;
}
return(matched);
}
/*
* If path doesn't end in /, return TRUE iff cmnd & path name the same inode;
* otherwise, return TRUE if user_cmnd names one of the inodes in path.
*/
int
command_matches(sudoers_cmnd, sudoers_args)
char *sudoers_cmnd;
char *sudoers_args;
{
/* Check for pseudo-commands */
if (strchr(user_cmnd, '/') == NULL) {
/*
* Return true if both sudoers_cmnd and user_cmnd are "sudoedit" AND
* a) there are no args in sudoers OR
* b) there are no args on command line and none req by sudoers OR
* c) there are args in sudoers and on command line and they match
*/
if (strcmp(sudoers_cmnd, "sudoedit") != 0 ||
strcmp(user_cmnd, "sudoedit") != 0)
return(FALSE);
if (!sudoers_args ||
(!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) ||
(sudoers_args &&
fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) {
efree(safe_cmnd);
safe_cmnd = estrdup(sudoers_cmnd);
return(TRUE);
} else
return(FALSE);
}
if (has_meta(sudoers_cmnd)) {
/*
* If sudoers_cmnd has meta characters in it, we need to
* use glob(3) and/or fnmatch(3) to do the matching.
*/
if (def_fast_glob)
return(command_matches_fnmatch(sudoers_cmnd, sudoers_args));
return(command_matches_glob(sudoers_cmnd, sudoers_args));
}
return(command_matches_normal(sudoers_cmnd, sudoers_args));
}
static int
command_matches_fnmatch(sudoers_cmnd, sudoers_args)
char *sudoers_cmnd;
char *sudoers_args;
{
/*
* Return true if fnmatch(3) succeeds AND
* a) there are no args in sudoers OR
* b) there are no args on command line and none required by sudoers OR
* c) there are args in sudoers and on command line and they match
* else return false.
*/
if (fnmatch(sudoers_cmnd, user_cmnd, FNM_PATHNAME) != 0)
return(FALSE);
if (!sudoers_args ||
(!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) ||
(sudoers_args &&
fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) {
if (safe_cmnd)
free(safe_cmnd);
safe_cmnd = estrdup(user_cmnd);
return(TRUE);
} else
return(FALSE);
}
static int
command_matches_glob(sudoers_cmnd, sudoers_args)
char *sudoers_cmnd;
char *sudoers_args;
{
struct stat sudoers_stat;
size_t dlen;
char **ap, *base, *cp;
glob_t gl;
/*
* First check to see if we can avoid the call to glob(3).
* Short circuit if there are no meta chars in the command itself
* and user_base and basename(sudoers_cmnd) don't match.
*/
dlen = strlen(sudoers_cmnd);
if (sudoers_cmnd[dlen - 1] != '/') {
if ((base = strrchr(sudoers_cmnd, '/')) != NULL) {
base++;
if (!has_meta(base) && strcmp(user_base, base) != 0)
return(FALSE);
}
}
/*
* Return true if we find a match in the glob(3) results AND
* a) there are no args in sudoers OR
* b) there are no args on command line and none required by sudoers OR
* c) there are args in sudoers and on command line and they match
* else return false.
*/
#define GLOB_FLAGS (GLOB_NOSORT | GLOB_MARK | GLOB_BRACE | GLOB_TILDE)
if (glob(sudoers_cmnd, GLOB_FLAGS, NULL, &gl) != 0) {
globfree(&gl);
return(FALSE);
}
/* For each glob match, compare basename, st_dev and st_ino. */
for (ap = gl.gl_pathv; (cp = *ap) != NULL; ap++) {
/* If it ends in '/' it is a directory spec. */
dlen = strlen(cp);
if (cp[dlen - 1] == '/') {
if (command_matches_dir(cp, dlen))
return(TRUE);
continue;
}
/* Only proceed if user_base and basename(cp) match */
if ((base = strrchr(cp, '/')) != NULL)
base++;
else
base = cp;
if (strcmp(user_base, base) != 0 ||
stat(cp, &sudoers_stat) == -1)
continue;
if (user_stat == NULL ||
(user_stat->st_dev == sudoers_stat.st_dev &&
user_stat->st_ino == sudoers_stat.st_ino)) {
efree(safe_cmnd);
safe_cmnd = estrdup(cp);
break;
}
}
globfree(&gl);
if (cp == NULL)
return(FALSE);
if (!sudoers_args ||
(!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) ||
(sudoers_args &&
fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) {
efree(safe_cmnd);
safe_cmnd = estrdup(user_cmnd);
return(TRUE);
}
return(FALSE);
}
static int
command_matches_normal(sudoers_cmnd, sudoers_args)
char *sudoers_cmnd;
char *sudoers_args;
{
struct stat sudoers_stat;
char *base;
size_t dlen;
/* If it ends in '/' it is a directory spec. */
dlen = strlen(sudoers_cmnd);
if (sudoers_cmnd[dlen - 1] == '/')
return(command_matches_dir(sudoers_cmnd, dlen));
/* Only proceed if user_base and basename(sudoers_cmnd) match */
if ((base = strrchr(sudoers_cmnd, '/')) == NULL)
base = sudoers_cmnd;
else
base++;
if (strcmp(user_base, base) != 0 ||
stat(sudoers_cmnd, &sudoers_stat) == -1)
return(FALSE);
/*
* Return true if inode/device matches AND
* a) there are no args in sudoers OR
* b) there are no args on command line and none req by sudoers OR
* c) there are args in sudoers and on command line and they match
*/
if (user_stat != NULL &&
(user_stat->st_dev != sudoers_stat.st_dev ||
user_stat->st_ino != sudoers_stat.st_ino))
return(FALSE);
if (!sudoers_args ||
(!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) ||
(sudoers_args &&
fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) {
efree(safe_cmnd);
safe_cmnd = estrdup(sudoers_cmnd);
return(TRUE);
}
return(FALSE);
}
/*
* Return TRUE if user_cmnd names one of the inodes in dir, else FALSE.
*/
static int
command_matches_dir(sudoers_dir, dlen)
char *sudoers_dir;
size_t dlen;
{
struct stat sudoers_stat;
struct dirent *dent;
char buf[PATH_MAX];
DIR *dirp;
/*
* Grot through directory entries, looking for user_base.
*/
dirp = opendir(sudoers_dir);
if (dirp == NULL)
return(FALSE);
if (strlcpy(buf, sudoers_dir, sizeof(buf)) >= sizeof(buf)) {
closedir(dirp);
return(FALSE);
}
while ((dent = readdir(dirp)) != NULL) {
/* ignore paths > PATH_MAX (XXX - log) */
buf[dlen] = '\0';
if (strlcat(buf, dent->d_name, sizeof(buf)) >= sizeof(buf))
continue;
/* only stat if basenames are the same */
if (strcmp(user_base, dent->d_name) != 0 ||
stat(buf, &sudoers_stat) == -1)
continue;
if (user_stat->st_dev == sudoers_stat.st_dev &&
user_stat->st_ino == sudoers_stat.st_ino) {
efree(safe_cmnd);
safe_cmnd = estrdup(buf);
break;
}
}
closedir(dirp);
return(dent != NULL);
}
static int
addr_matches_if(n)
char *n;
{
int i;
struct in_addr addr;
struct interface *ifp;
#ifdef HAVE_IN6_ADDR
struct in6_addr addr6;
int j;
#endif
int family;
#ifdef HAVE_IN6_ADDR
if (inet_pton(AF_INET6, n, &addr6) > 0) {
family = AF_INET6;
} else
#endif
{
family = AF_INET;
addr.s_addr = inet_addr(n);
}
for (i = 0; i < num_interfaces; i++) {
ifp = &interfaces[i];
if (ifp->family != family)
continue;
switch(family) {
case AF_INET:
if (ifp->addr.ip4.s_addr == addr.s_addr ||
(ifp->addr.ip4.s_addr & ifp->netmask.ip4.s_addr)
== addr.s_addr)
return(TRUE);
break;
#ifdef HAVE_IN6_ADDR
case AF_INET6:
if (memcmp(ifp->addr.ip6.s6_addr, addr6.s6_addr,
sizeof(addr6.s6_addr)) == 0)
return(TRUE);
for (j = 0; j < sizeof(addr6.s6_addr); j++) {
if ((ifp->addr.ip6.s6_addr[j] & ifp->netmask.ip6.s6_addr[j]) != addr6.s6_addr[j])
break;
}
if (j == sizeof(addr6.s6_addr))
return(TRUE);
#endif
}
}
return(FALSE);
}
static int
addr_matches_if_netmask(n, m)
char *n;
char *m;
{
int i;
struct in_addr addr, mask;
struct interface *ifp;
#ifdef HAVE_IN6_ADDR
struct in6_addr addr6, mask6;
int j;
#endif
int family;
#ifdef HAVE_IN6_ADDR
if (inet_pton(AF_INET6, n, &addr6) > 0)
family = AF_INET6;
else
#endif
{
family = AF_INET;
addr.s_addr = inet_addr(n);
}
if (family == AF_INET) {
if (strchr(m, '.'))
mask.s_addr = inet_addr(m);
else {
i = 32 - atoi(m);
mask.s_addr = 0xffffffff;
mask.s_addr >>= i;
mask.s_addr <<= i;
mask.s_addr = htonl(mask.s_addr);
}
}
#ifdef HAVE_IN6_ADDR
else {
if (inet_pton(AF_INET6, m, &mask6) <= 0) {
j = atoi(m);
for (i = 0; i < 16; i++) {
if (j < i * 8)
mask6.s6_addr[i] = 0;
else if (i * 8 + 8 <= j)
mask6.s6_addr[i] = 0xff;
else
mask6.s6_addr[i] = 0xff00 >> (j - i * 8);
}
}
}
#endif /* HAVE_IN6_ADDR */
for (i = 0; i < num_interfaces; i++) {
ifp = &interfaces[i];
if (ifp->family != family)
continue;
switch(family) {
case AF_INET:
if ((ifp->addr.ip4.s_addr & mask.s_addr) == addr.s_addr)
return(TRUE);
#ifdef HAVE_IN6_ADDR
case AF_INET6:
for (j = 0; j < sizeof(addr6.s6_addr); j++) {
if ((ifp->addr.ip6.s6_addr[j] & mask6.s6_addr[j]) != addr6.s6_addr[j])
break;
}
if (j == sizeof(addr6.s6_addr))
return(TRUE);
#endif /* HAVE_IN6_ADDR */
}
}
return(FALSE);
}
/*
* Returns TRUE if "n" is one of our ip addresses or if
* "n" is a network that we are on, else returns FALSE.
*/
int
addr_matches(n)
char *n;
{
char *m;
int retval;
/* If there's an explicit netmask, use it. */
if ((m = strchr(n, '/'))) {
*m++ = '\0';
retval = addr_matches_if_netmask(n, m);
*(m - 1) = '/';
} else
retval = addr_matches_if(n);
return(retval);
}
/*
* Returns TRUE if the hostname matches the pattern, else FALSE
*/
int
hostname_matches(shost, lhost, pattern)
char *shost;
char *lhost;
char *pattern;
{
if (has_meta(pattern)) {
if (strchr(pattern, '.'))
return(!fnmatch(pattern, lhost, FNM_CASEFOLD));
else
return(!fnmatch(pattern, shost, FNM_CASEFOLD));
} else {
if (strchr(pattern, '.'))
return(!strcasecmp(lhost, pattern));
else
return(!strcasecmp(shost, pattern));
}
}
/*
* Returns TRUE if the user/uid from sudoers matches the specified user/uid,
* else returns FALSE.
*/
int
userpw_matches(sudoers_user, user, pw)
char *sudoers_user;
char *user;
struct passwd *pw;
{
if (pw != NULL && *sudoers_user == '#') {
uid_t uid = (uid_t) atoi(sudoers_user + 1);
if (uid == pw->pw_uid)
return(TRUE);
}
return(strcmp(sudoers_user, user) == 0);
}
/*
* Returns TRUE if the group/gid from sudoers matches the specified group/gid,
* else returns FALSE.
*/
int
group_matches(sudoers_group, gr)
char *sudoers_group;
struct group *gr;
{
if (*sudoers_group == '#') {
gid_t gid = (gid_t) atoi(sudoers_group + 1);
if (gid == gr->gr_gid)
return(TRUE);
}
return(strcmp(gr->gr_name, sudoers_group) == 0);
}
/*
* Returns TRUE if the given user belongs to the named group,
* else returns FALSE.
*/
int
usergr_matches(group, user, pw)
char *group;
char *user;
struct passwd *pw;
{
/* make sure we have a valid usergroup, sudo style */
if (*group++ != '%')
return(FALSE);
#ifdef USING_NONUNIX_GROUPS
if (*group == ':')
return(sudo_nonunix_groupcheck(++group, user, pw));
#endif /* USING_NONUNIX_GROUPS */
/* look up user's primary gid in the passwd file */
if (pw == NULL && (pw = sudo_getpwnam(user)) == NULL)
return(FALSE);
if (user_in_group(pw, group))
return(TRUE);
#ifdef USING_NONUNIX_GROUPS
/* not a Unix group, could be an AD group */
if (sudo_nonunix_groupcheck_available() &&
sudo_nonunix_groupcheck(group, user, pw))
return(TRUE);
#endif /* USING_NONUNIX_GROUPS */
return(FALSE);
}
/*
* Returns TRUE if "host" and "user" belong to the netgroup "netgr",
* else return FALSE. Either of "host", "shost" or "user" may be NULL
* in which case that argument is not checked...
*
* XXX - swap order of host & shost
*/
int
netgr_matches(netgr, lhost, shost, user)
char *netgr;
char *lhost;
char *shost;
char *user;
{
static char *domain;
#ifdef HAVE_GETDOMAINNAME
static int initialized;
#endif
/* make sure we have a valid netgroup, sudo style */
if (*netgr++ != '+')
return(FALSE);
#ifdef HAVE_GETDOMAINNAME
/* get the domain name (if any) */
if (!initialized) {
domain = (char *) emalloc(MAXHOSTNAMELEN + 1);
if (getdomainname(domain, MAXHOSTNAMELEN + 1) == -1 || *domain == '\0') {
efree(domain);
domain = NULL;
}
initialized = 1;
}
#endif /* HAVE_GETDOMAINNAME */
#ifdef HAVE_INNETGR
if (innetgr(netgr, lhost, user, domain))
return(TRUE);
else if (lhost != shost && innetgr(netgr, shost, user, domain))
return(TRUE);
#endif /* HAVE_INNETGR */
return(FALSE);
}

155
plugins/sudoers/mkdefaults Executable file
View File

@@ -0,0 +1,155 @@
#!/usr/bin/perl -w
#
# Generate sudo_defs_table and associated defines
#
# Input should be formatted thusly:
#
# var_name
# TYPE
# description (or NULL)
# array of struct def_values if TYPE == T_TUPLE
# Deal with optional -o (output) argument
if ($#ARGV > 0 && $ARGV[0] eq "-o") {
shift;
$header = $cfile = shift;
$header .= '.h';
$cfile .= '.c';
}
die "usage: $0 [input_file]\n" unless $#ARGV == -1 || $#ARGV == 0;
$infile = $ARGV[0] || "def_data.in";
if (!defined($header)) {
$header = $infile;
$header =~ s/(\.in)?$/.h/;
}
if (!defined($cfile)) {
$cfile = $infile;
$cfile =~ s/(\.in)?$/.c/;
}
open(IN, "<$infile") || die "$0: can't open $infile: $!\n";
open(HEADER, ">$header") || die "$0: can't open $header: $!\n";
open(CFILE, ">$cfile") || die "$0: can't open $cfile: $!\n";
$count = 0;
@tuple_values = ( "never" );
@records = ();
while(<IN>) {
chomp;
s/\s*#.*$//;
next if /^\s*$/;
if (/^\S/) {
# Store previous record and begin new one
$records[$count++] = [$var, $type, $desc, $values, $callback] if defined($var);
$var = $_;
$type = '';
$desc = undef;
$values = undef;
$callback = undef;
$field = 0;
} else {
$field++;
s/^\s+//;
s/\s+$//;
if ($field == 1) {
# type
$type = $_;
} elsif ($field == 2) {
# description
if ($_ eq "NULL") {
$desc = "NULL";
} else {
# Strip leading and trailing double quote and escape the rest
s/^"//;
s/"$//;
s/"/\\"/g;
$desc = "\"$_\"";
}
} elsif ($field == 3 || $field == 4) {
if (s/^\*//) {
$callback = $_;
} else {
die "$0: syntax error near line $.\n" if $type !~ /^T_TUPLE/;
$values = [ split ];
foreach $v (@$values) {
push(@tuple_values, $v) unless grep(/^$v$/, @tuple_values);
}
}
} else {
die "$0: syntax error near line $.\n";
}
}
}
$records[$count++] = [$var, $type, $desc, $values, $callback] if defined($var);
# Print out value arrays
for ($i = 0; $i < $count; $i++) {
if (defined($records[$i]->[3])) {
die "Values list specified for non-tupple\n" unless
$records[$i]->[1] =~ /^T_TUPLE/;
printf CFILE "static struct def_values def_data_%s[] = {\n", $records[$i]->[0];
foreach (@{$records[$i]->[3]}) {
print CFILE " { \"$_\", $_ },\n";
}
print CFILE " { NULL, 0 },\n";
print CFILE "};\n\n";
}
}
# Print each record
print CFILE "struct sudo_defs_types sudo_defs_table[] = {\n {\n";
for ($i = 0; $i < $count; $i++) {
&print_record($records[$i], $i);
}
print CFILE "\tNULL, 0, NULL\n }\n};\n";
# Print out def_tuple
if (@tuple_values) {
print HEADER "\nenum def_tupple {\n";
for ($i = 0; $i <= $#tuple_values; $i++) {
printf HEADER "\t%s%s\n", $tuple_values[$i],
$i != $#tuple_values ? "," : "";
}
print HEADER "};\n";
}
close(IN);
close(HEADER);
close(CFILE);
sub print_record {
my ($rec, $recnum) = @_;
my ($i, $v, $defname);
# each variable gets a macro to access its value
for ($rec->[1]) {
if (/^T_U?INT/) { $v = "ival"; }
elsif (/^T_STR/) { $v = "str"; }
elsif (/^T_FLAG/) { $v = "flag"; }
elsif (/^T_MODE/) { $v = "mode"; }
elsif (/^T_LIST/) { $v = "list"; }
elsif (/^T_LOGFAC/) { $v = "ival"; }
elsif (/^T_LOGPRI/) { $v = "ival"; }
elsif (/^T_TUPLE/) { $v = "tuple"; }
elsif (/^T_FLOAT/) { $v = "fval"; }
else { die "$0: unknown defaults type: $_\n"; }
}
printf HEADER "#define %-23s (sudo_defs_table[$recnum].sd_un.${v})\n",
"def_$rec->[0]";
$defname = "I_" . uc($rec->[0]);
printf HEADER "#define %-24s%d", $defname, $recnum;
#print HEADER "\t/* $rec->[2] */" if defined($rec->[2]);
print HEADER "\n";
print CFILE "\t\"$rec->[0]\", $rec->[1],\n\t$rec->[2],\n";
if (defined($rec->[3])) {
printf CFILE "\tdef_data_$rec->[0],\n";
} else {
printf CFILE "\tNULL,\n";
}
printf CFILE "\t$rec->[4],\n" if defined($rec->[4]);
print CFILE " }, {\n";
}

View File

@@ -0,0 +1,985 @@
/*
* Copyright (c) 2004-2005 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <config.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pwd.h>
#include <signal.h>
#include <fcntl.h>
#ifdef HAVE_DEV_SYSTRACE_H
# include <dev/systrace.h>
#else
# ifdef HAVE_SYS_SYSTRACE_H
# include <sys/systrace.h>
# else
# ifdef HAVE_LINUX_SYSTRACE_H
# include <linux/systrace.h>
# else
# include <systrace.h>
# endif
# endif
#endif
#include "sudo.h"
#include "mon_systrace.h"
/*
* Open the systrace device and return the fd or -1 on failure.
*/
static int
systrace_open()
{
int serrno, fd;
fd = open(_PATH_DEV_SYSTRACE, O_RDONLY, 0644);
if (fd == -1)
return(-1);
serrno = errno;
#ifdef SYSTR_CLONE
{
int tfd;
if (ioctl(fd, STRIOCCLONE, &tfd) == -1)
goto bad;
close(fd);
fd = tfd;
}
#endif
if (fcntl(fd, F_SETFD, 1) == -1) /* really needed? */
goto bad;
return(fd);
bad:
close(fd);
errno = serrno;
return(-1);
}
static void
catchsig(signo)
int signo;
{
dodetach = signo;
return;
}
/*
* Fork a process that monitors the command to be run and its descendents.
* The monitoring process will detach upon receipt of SIGHUP, SIGINT or SIGTERM.
*/
void
systrace_attach(pid)
pid_t pid;
{
struct syscallhandler *handler;
struct systrace_answer ans;
struct str_message msg;
sigaction_t sa, osa;
sigset_t set, oset;
ssize_t nread;
int fd, status;
if ((fd = systrace_open()) == -1)
error(1, "unable to open systrace");
fflush(stdout);
/*
* Do signal setup early so there is no race between when the tracer
* kill()s the tracee and when the tracee calls sigsuspend().
*/
sigfillset(&set);
if (sigprocmask(SIG_BLOCK, &set, &oset) != 0)
error(1, "sigprocmask");
zero_bytes(&sa, sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = catchsig;
if (sigaction(SIGUSR1, &sa, &osa) != 0)
error(1, "sigaction");
switch (fork()) {
case -1:
error(1, "can't fork");
case 0:
/* tracer, fork again to completely disassociate */
switch (fork()) {
case -1:
warning("can't fork");
kill(pid, SIGKILL);
_exit(1);
case 0:
break;
default:
/* the main sudo process will wait for us */
_exit(0);
}
break;
default:
/* tracee, sleep until the tracer process wakes us up. */
close(fd);
sigdelset(&set, SIGUSR1);
(void) sigsuspend(&set);
if (sigprocmask(SIG_SETMASK, &oset, NULL) != 0) {
warning("sigprocmask");
exit(1);
}
return;
}
/* set signal state for tracer */
dodetach = 0;
zero_bytes(&sa, sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = catchsig;
if (sigaction(SIGUSR1, &osa, NULL) != 0 ||
sigaction(SIGHUP, &sa, NULL) != 0 ||
sigaction(SIGINT, &sa, NULL) != 0 ||
sigaction(SIGTERM, &sa, NULL) != 0 ||
sigprocmask(SIG_SETMASK, &oset, NULL) != 0) {
warning("unable to setup signals for %s", user_cmnd);
goto fail;
}
/* become a daemon */
set_perms(PERM_ROOT);
if (setsid() == -1) {
warning("setsid");
kill(pid, SIGKILL);
_exit(1);
}
(void) chdir("/");
#ifdef HAVE_SETPROCTITLE
setproctitle("monitor %s%s%s", user_base, user_args ? " " : "",
user_args ? user_args : "");
#endif
if (ioctl(fd, STRIOCATTACH, &pid) == -1) {
if (errno == EBUSY) {
/* already being traced, nothing to do */
(void) kill(pid, SIGUSR1);
_exit(0);
}
warning("unable to systrace %s", user_cmnd);
goto fail;
}
new_child(-1, pid);
if (set_policy(fd, children.first) != 0) {
warning("failed to set policy for %s", user_cmnd);
goto fail;
}
if (kill(pid, SIGUSR1) != 0) {
warning("unable to wake up sleeping child");
_exit(1);
}
/* handle systrace events until the child finishes */
for (;;) {
if ((nread = read(fd, &msg, sizeof(msg))) != sizeof(msg)) {
if (dodetach) {
detachall(fd);
_exit(0);
}
if (nread == -1 && (errno == EINTR || errno == EAGAIN))
continue;
killall(SIGKILL);
_exit(nread != 0); /* shouldn't happen */
}
switch (msg.msg_type) {
case SYSTR_MSG_CHILD:
/* either a fork or an exit */
if (msg.msg_data.msg_child.new_pid != -1) {
new_child(msg.msg_pid, msg.msg_data.msg_child.new_pid);
} else {
rm_child(msg.msg_pid);
if (children.first == NULL)
_exit(0);
}
break;
case SYSTR_MSG_UGID:
/* uid/gid change */
memset(&ans, 0, sizeof(ans));
ans.stra_pid = msg.msg_pid;
ans.stra_seqnr = msg.msg_seqnr;
ans.stra_policy = SYSTR_POLICY_PERMIT;
if ((ioctl(fd, STRIOCANSWER, &ans)) == 0)
update_child(msg.msg_pid, msg.msg_data.msg_ugid.uid);
break;
case SYSTR_MSG_ASK:
memset(&ans, 0, sizeof(ans));
ans.stra_pid = msg.msg_pid;
ans.stra_seqnr = msg.msg_seqnr;
ans.stra_policy = SYSTR_POLICY_PERMIT;
handler = find_handler(msg.msg_pid, msg.msg_data.msg_ask.code);
if (handler != NULL && handler->checker != NULL) {
status = handler->checker(fd, msg.msg_pid, msg.msg_seqnr,
&msg.msg_data.msg_ask, &ans.stra_policy,
&ans.stra_error);
if (status >= 0 && ioctl(fd, STRIOCANSWER, &ans) == 0) {
if (handler->logger != NULL)
handler->logger(status);
}
} else
if (ioctl(fd, STRIOCANSWER, &ans) == -1)
warning("STRIOCANSWER");
break;
case SYSTR_MSG_EMUL:
/* Change in emulation. */
memset(&ans, 0, sizeof(ans));
ans.stra_pid = msg.msg_pid;
ans.stra_seqnr = msg.msg_seqnr;
if (switch_emulation(fd, &msg) == 0)
ans.stra_policy = SYSTR_POLICY_PERMIT;
else {
warningx("unsupported emulation \"%s\"",
msg.msg_data.msg_emul.emul);
ans.stra_policy = SYSTR_POLICY_NEVER;
}
if (ioctl(fd, STRIOCANSWER, &ans) == -1)
warning("STRIOCANSWER");
break;
#ifdef SYSTR_MSG_POLICYFREE
case SYSTR_MSG_POLICYFREE:
break;
#endif
default:
#ifdef SUDO_DEVEL
warningx("unexpected message type %d", msg.msg_type);
#endif
memset(&ans, 0, sizeof(ans));
ans.stra_pid = msg.msg_pid;
ans.stra_seqnr = msg.msg_seqnr;
ans.stra_policy = SYSTR_POLICY_PERMIT;
if (ioctl(fd, STRIOCANSWER, &ans) == -1)
warning("STRIOCANSWER");
break;
}
}
fail:
killall(SIGKILL);
_exit(1);
}
/*
* Push a new child to the head of the list, inheriting the struct pw
* of its parent.
*/
static void
new_child(ppid, pid)
pid_t ppid;
pid_t pid;
{
struct childinfo *entry;
struct passwd *pw;
struct syscallaction *action;
struct emulation *emul;
if (ppid != -1 && (entry = find_child(ppid)) != NULL) {
pw = entry->pw;
action = entry->action;
} else {
pw = runas_pw;
for (emul = emulations; emul != NULL; emul++)
if (strcmp(emul->name, "native") == 0) {
action = emul->action;
break;
}
if (emul == NULL)
errorx(1, "unable to find native emulation!");
}
entry = (struct childinfo *) emalloc(sizeof(*entry));
entry->pid = pid;
entry->pw = pw;
entry->action = action;
entry->next = children.first;
children.first = entry;
}
static int
switch_emulation(fd, msgp)
int fd;
struct str_message *msgp;
{
struct childinfo *entry;
struct emulation *emul;
if ((entry = find_child(msgp->msg_pid)) == NULL)
return(-1);
for (emul = emulations; emul != NULL; emul++)
if (strcmp(emul->name, msgp->msg_data.msg_emul.emul) == 0) {
entry->action = emul->action;
return(set_policy(fd, entry));
}
return(-1);
}
/*
* Remove the named pid from the list.
*/
static void
rm_child(pid)
pid_t pid;
{
struct childinfo *cur, *prev;
for (prev = NULL, cur = children.first; cur != NULL; cur = cur->next) {
if (cur->pid == pid) {
if (prev != NULL)
prev->next = cur->next;
else
children.first = cur->next;
efree(cur);
break;
}
prev = cur;
}
}
/*
* Find a child by pid.
*/
static struct childinfo *
find_child(pid)
pid_t pid;
{
struct childinfo *cur;
for (cur = children.first; cur != NULL; cur = cur->next) {
if (cur->pid == pid)
return(cur);
}
return(NULL);
}
/*
* Update the uid associated with a pid.
*/
static void
update_child(pid, uid)
pid_t pid;
uid_t uid;
{
struct childinfo *child;
if ((child = find_child(pid)) == NULL)
return; /* cannot happen */
if (child->pw->pw_uid != uid) {
/* look up uid in passwd db, using a stub on failure */
if ((child->pw = sudo_getpwuid(uid)) == NULL)
child->pw = sudo_fakepwuid(uid);
}
}
/*
* Create a policy that intercepts execve and lets all others go free.
*/
static int
set_policy(fd, child)
int fd;
struct childinfo *child;
{
struct syscallaction *sca;
struct systrace_policy pol;
int i;
pol.strp_op = SYSTR_POLICY_NEW;
pol.strp_num = -1;
pol.strp_maxents = SYSTRACE_MAXENTS;
if (ioctl(fd, STRIOCPOLICY, &pol) == -1)
return(-1);
pol.strp_op = SYSTR_POLICY_ASSIGN;
pol.strp_pid = child->pid;
if (ioctl(fd, STRIOCPOLICY, &pol) == -1)
return(-1);
for (i = 0; i < SYSTRACE_MAXENTS; i++) {
pol.strp_op = SYSTR_POLICY_MODIFY;
pol.strp_policy = SYSTR_POLICY_PERMIT;
pol.strp_code = i;
for (sca = child->action; sca->code != -1; sca++) {
if (sca->code == i) {
pol.strp_policy = sca->policy;
break;
}
}
if (ioctl(fd, STRIOCPOLICY, &pol) == -1)
return(-1);
}
return(0);
}
/*
* Read from an address and store in buf.
* XXX - should deal with EBUSY from STRIOCIO
*/
static ssize_t
systrace_read(fd, pid, addr, buf, bufsiz)
int fd;
pid_t pid;
void *addr;
void *buf;
size_t bufsiz;
{
struct systrace_io io;
int rval;
memset(&io, 0, sizeof(io));
io.strio_pid = pid;
io.strio_addr = buf;
io.strio_len = bufsiz;
io.strio_offs = addr;
io.strio_op = SYSTR_READ;
if ((rval = ioctl(fd, STRIOCIO, &io)) != 0)
warning("systrace_read: STRIOCIO");
return(rval ? -1 : (ssize_t)io.strio_len);
}
/*
* Read up to bufsiz bytes from addr into buf, stopping when we hit
* a NUL byte. Reads are done in chunks since STRIOCIO cannot
* handle a strio_len > the actual kernel buffer. It might be nice
* to pass a starting chunksize though.
*/
static ssize_t
read_string(fd, pid, addr, buf, bufsiz)
int fd;
pid_t pid;
void *addr;
char *buf;
size_t bufsiz;
{
size_t chunksiz = 32;
ssize_t nread;
char *cp = buf, *ep;
while (bufsiz >= chunksiz) {
if ((nread = systrace_read(fd, pid, addr, cp, chunksiz)) != -1) {
if ((ep = memchr(cp, '\0', nread)) != NULL) {
cp = ep; /* found NUL byte in chunk, done */
break;
}
cp += nread;
addr += nread;
bufsiz -= nread;
} else {
if (errno != EINVAL || chunksiz == 1)
return(-1);
chunksiz >>= 1; /* chunksiz too big, halve it */
}
}
#ifdef SUDO_DEVEL
if (cp == buf)
warningx("read empty string, chunksize == %d", chunksiz); /* XXX, should not happen but does */
#endif
return(bufsiz >= chunksiz ? cp - buf : -1);
}
static struct syscallhandler *
find_handler(pid, code)
pid_t pid;
int code;
{
struct syscallaction *sca;
struct childinfo *child;
if ((child = find_child(pid)) == NULL) {
warningx("unable to find child with pid %d", pid);
return(NULL);
}
for (sca = child->action; sca->code != -1; sca++) {
if (sca->code == code)
return(&sca->handler);
}
return(NULL);
}
#define SUDO_USER 0
#define SUDO_COMMAND 1
#define SUDO_UID 2
#define SUDO_GID 3
#ifdef STRIOCINJECT
/*
* Write buf to a kernel address.
* XXX - should deal with EBUSY from STRIOCIO
*/
static ssize_t
systrace_write(fd, pid, addr, buf, len)
int fd;
pid_t pid;
void *addr;
void *buf;
size_t len;
{
struct systrace_io io;
int rval;
memset(&io, 0, sizeof(io));
io.strio_pid = pid;
io.strio_addr = buf;
io.strio_len = len;
io.strio_offs = addr;
io.strio_op = SYSTR_WRITE;
if ((rval = ioctl(fd, STRIOCIO, &io)) != 0)
warning("systrace_write: STRIOCIO");
return(rval ? -1 : (ssize_t)io.strio_len);
}
/*
* Update SUDO_* variables in the process's environment.
*/
static int
update_env(fd, pid, seqnr, askp)
int fd;
pid_t pid;
u_int16_t seqnr;
struct str_msg_ask *askp;
{
struct systrace_replace repl;
ssize_t len;
char *envbuf[ARG_MAX / sizeof(char *)], **envp, **envep;
char buf[ARG_MAX], *ap, *cp, *off, *envptrs[4], *offsets[4], *replace[4];
int n;
/*
* Iterate through the environment, copying the data pointers and
* attempting to update the SUDO_* variables (space permitting).
*/
memset(offsets, 0, sizeof(offsets));
memset(replace, 1, sizeof(replace));
off = (char *)askp->args[2];
envep = envbuf + (sizeof(envbuf) / sizeof(char *));
for (envp = envbuf; envp < envep; envp++, off += sizeof(char *)) {
if (systrace_read(fd, pid, off, &ap, sizeof(ap)) == -1)
return(-1);
if ((*envp = ap) == NULL)
break;
memset(buf, 0, sizeof(buf));
if ((len = read_string(fd, pid, ap, buf, sizeof(buf))) == -1)
return(-1);
if (buf[0] == 'S') {
if (strncmp(buf, "SUDO_USER=", 10) == 0) {
offsets[SUDO_USER] = off;
envptrs[SUDO_USER] = ap;
if (strcmp(&buf[10], user_name) == 0)
replace[SUDO_USER] = NULL;
else {
len = strlen(buf);
n = snprintf(buf, len + 1, "SUDO_USER=%s", user_name);
if (n > 0 && n <= len &&
systrace_write(fd, pid, ap, buf, len + 1) != -1)
replace[SUDO_USER] = NULL;
}
} else if (strncmp(buf, "SUDO_COMMAND=", 13) == 0) {
offsets[SUDO_COMMAND] = off;
envptrs[SUDO_COMMAND] = ap;
len = strlen(user_cmnd);
if (strncmp(&buf[13], user_cmnd, len) == 0) {
if (user_args == NULL) {
if (buf[13 + len] == '\0')
replace[SUDO_COMMAND] = NULL;
} else if (buf[13 + len] == ' ') {
if (strcmp(&buf[14 + len], user_args) == 0)
replace[SUDO_COMMAND] = NULL;
}
}
if (replace[SUDO_COMMAND] != NULL) {
len = strlen(buf);
n = snprintf(buf, len + 1, "SUDO_COMMAND=%s%s%s",
user_cmnd, user_args ? " " : "",
user_args ? user_args : "");
if (n > 0 && n <= len &&
systrace_write(fd, pid, ap, buf, len + 1) != -1)
replace[SUDO_COMMAND] = NULL;
}
} else if (strncmp(buf, "SUDO_UID=", 9) == 0) {
offsets[SUDO_UID] = off;
envptrs[SUDO_UID] = ap;
if ((uid_t) atoi(&buf[9]) == user_uid)
replace[SUDO_UID] = NULL;
else {
len = strlen(buf);
n = snprintf(buf, len + 1,
"SUDO_UID=%lu", (unsigned long) user_uid);
if (n > 0 && n <= len &&
systrace_write(fd, pid, ap, buf, len + 1) != -1)
replace[SUDO_UID] = NULL;
}
} else if (strncmp(buf, "SUDO_GID=", 9) == 0) {
offsets[SUDO_GID] = off;
envptrs[SUDO_GID] = ap;
if ((gid_t) atoi(&buf[9]) == user_gid)
replace[SUDO_GID] = NULL;
else {
len = strlen(buf);
n = snprintf(buf, len + 1,
"SUDO_GID=%lu", (unsigned long) user_gid);
if (n > 0 && n <= len &&
systrace_write(fd, pid, ap, buf, len + 1) != -1)
replace[SUDO_GID] = NULL;
}
}
}
}
/*
* Allocate space for any SUDO_* variables we didn't have room for
* or that weren't present.
*/
cp = buf;
if (replace[SUDO_USER]) {
n = snprintf(cp, sizeof(buf) - (cp - buf), "SUDO_USER=%s", user_name);
if (n < 0 || n >= sizeof(buf) - (cp - buf))
return(-1);
replace[SUDO_USER] = cp;
cp += n + 1;
}
if (replace[SUDO_COMMAND]) {
n = snprintf(cp, sizeof(buf) - (cp - buf), "SUDO_COMMAND=%s%s%s",
user_cmnd, user_args ? " " : "", user_args ? user_args : "");
if (n < 0 || n >= sizeof(buf) - (cp - buf))
return(-1);
replace[SUDO_COMMAND] = cp;
cp += n + 1;
}
if (replace[SUDO_UID]) {
n = snprintf(cp, sizeof(buf) - (cp - buf), "SUDO_UID=%lu",
(unsigned long) user_uid);
if (n < 0 || n >= sizeof(buf) - (cp - buf))
return(-1);
replace[SUDO_UID] = cp;
cp += n + 1;
}
if (replace[SUDO_GID]) {
n = snprintf(cp, sizeof(buf) - (cp - buf), "SUDO_GID=%lu",
(unsigned long) user_gid);
if (n < 0 || n >= sizeof(buf) - (cp - buf))
return(-1);
replace[SUDO_GID] = cp;
cp += n + 1;
}
if (cp != buf) {
struct systrace_inject inject;
memset(&inject, 0, sizeof(inject));
inject.stri_pid = pid;
inject.stri_addr = buf;
inject.stri_len = cp - buf;
if (ioctl(fd, STRIOCINJECT, &inject) != 0)
return(-1);
n = (offsets[SUDO_USER] == NULL) + (offsets[SUDO_COMMAND] == NULL) +
(offsets[SUDO_UID] == NULL) + (offsets[SUDO_GID] == NULL);
/*
* If there were SUDO_* variables missing in the environment we
* need to add them to our copy of envp and replace the envp in
* the user process. If no, we just update the addresses
* of the modified environment variables in the process's envp.
*/
if (n == 0) {
/* No missing variables, just update addresses in user process. */
for (n = 0; n < 4; n++) {
if (replace[n] == NULL)
continue;
ap = inject.stri_addr + (replace[n] - buf);
if (systrace_write(fd, pid, offsets[n], &ap, sizeof(ap)) == -1)
return(-1);
}
} else {
/*
* There were missing variables; make existing variables
* relative to inject.stri_addr and add missing one.
*/
for (envp = envbuf; *envp != NULL; envp++) {
if (replace[SUDO_USER] != NULL && *envp == envptrs[SUDO_USER])
*envp = inject.stri_addr + (replace[SUDO_USER] - buf);
else if (replace[SUDO_COMMAND] != NULL && *envp == envptrs[SUDO_COMMAND])
*envp = inject.stri_addr + (replace[SUDO_COMMAND] - buf);
else if (replace[SUDO_UID] != NULL && *envp == envptrs[SUDO_UID])
*envp = inject.stri_addr + (replace[SUDO_UID] - buf);
else if (replace[SUDO_GID] != NULL && *envp == envptrs[SUDO_GID])
*envp = inject.stri_addr + (replace[SUDO_GID] - buf);
}
if (envp + n >= envep)
return(-1);
if (offsets[SUDO_USER] == NULL)
*envp++ = inject.stri_addr + (replace[SUDO_USER] - buf);
if (offsets[SUDO_COMMAND] == NULL)
*envp++ = inject.stri_addr + (replace[SUDO_COMMAND] - buf);
if (offsets[SUDO_UID] == NULL)
*envp++ = inject.stri_addr + (replace[SUDO_UID] - buf);
if (offsets[SUDO_GID] == NULL)
*envp++ = inject.stri_addr + (replace[SUDO_GID] - buf);
*envp++ = NULL;
/* Replace existing envp with our new one. */
memset(&repl, 0, sizeof(repl));
repl.strr_pid = pid;
repl.strr_seqnr = seqnr;
repl.strr_nrepl = 1;
repl.strr_base = (char *)envbuf;
repl.strr_len = (char *)envp - (char *)envbuf;
repl.strr_argind[0] = 2;
repl.strr_off[0] = 0;
repl.strr_offlen[0] = (char *)envp - (char *)envbuf;
if (ioctl(fd, STRIOCREPLACE, &repl) != 0)
return(-1);
}
}
return(0);
}
#endif /* STRIOCINJECT */
/*
* Decode path and argv from systrace and fill in user_cmnd,
* user_base and user_args.
*/
static int
decode_args(fd, pid, askp)
int fd;
pid_t pid;
struct str_msg_ask *askp;
{
ssize_t len;
int i, argc, argc_max;
char *off, *ap, *cp, *ep, **argv;
static char pbuf[PATH_MAX], abuf[ARG_MAX];
/*
* Fill in user_cmnd and user_base from the 1st arg to execve().
* Note that this is the path the kernel will execute, which
* may be different from argv[0].
*/
memset(pbuf, 0, sizeof(pbuf));
if (read_string(fd, pid, (void *)askp->args[0], pbuf, sizeof(pbuf)) == -1)
return(-1);
if ((user_base = strrchr(user_cmnd = pbuf, '/')) != NULL)
user_base++;
else
user_base = user_cmnd;
user_args = NULL;
/* XXX - write exec path back to stack gap */
/*
* Make a local copy of argv, looping until we hit the
* terminating NULL pointer.
*/
argc = 0;
argc_max = 16;
argv = emalloc2(argc_max, sizeof(char *));
memset(abuf, 0, sizeof(abuf));
off = (char *)askp->args[1];
for (cp = abuf, ep = abuf + sizeof(abuf); cp < ep; off += sizeof(char *)) {
if (systrace_read(fd, pid, off, &ap, sizeof(ap)) == -1)
return(-1);
if (ap == NULL)
break; /* end of args */
if (argc + 1 >= argc_max) {
argc_max *= 2;
argv = erealloc3(argv, argc_max, sizeof(char *));
}
if ((len = read_string(fd, pid, ap, cp, ep - cp)) == -1)
return(-1);
argv[argc++] = cp;
cp += len;
}
ep = cp;
argv[argc] = NULL;
/* XXX - now write argv back into stack gap. */
/*
* Collapse argv into user_args, skipping argv[0].
* Since argv strings are contiguous (in abuf) we can
* just replace the previous char in each string with a
* space.
*/
if (argc > 1) {
user_args = argv[1];
for (i = 2; i < argc; i++)
argv[i][-1] = ' '; /* replace NUL with a space */
}
efree(argv);
return(0);
}
static void
log_exec(status)
int status;
{
if (status > 0)
log_auth(status, TRUE);
}
/*
* Decode the args to exec and check the command in sudoers.
*/
static int
check_execv(fd, pid, seqnr, askp, policyp, errorp)
int fd;
pid_t pid;
u_int16_t seqnr;
struct str_msg_ask *askp;
int *policyp;
int *errorp;
{
int rval, validated;
struct childinfo *info;
#ifdef HAVE_LDAP
void *ld;
#endif
/* We're not really initialized until the first exec finishes. */
if (initialized == 0) {
initialized = 1;
*policyp = SYSTR_POLICY_PERMIT;
return(0);
}
/* Failure should not be possible. */
if ((info = find_child(pid)) == NULL) {
*policyp = SYSTR_POLICY_NEVER;
*errorp = ECHILD;
return(0);
}
/* Fill in user_cmnd, user_base, user_args and user_stat. */
if (decode_args(fd, pid, askp) != 0) {
if (errno == EBUSY)
return(-1);
*policyp = SYSTR_POLICY_NEVER;
*errorp = errno;
return(0);
}
/* Get process cwd. */
rval = ioctl(fd, STRIOCGETCWD, &pid);
if (rval == -1 || getcwd(user_cwd, sizeof(user_cwd)) == NULL) {
if (rval == -1 && errno == EBUSY)
return(-1);
warningx("cannot get working directory");
(void) strlcpy(user_cwd, "unknown", sizeof(user_cwd));
}
/*
* Stat user_cmnd and restore cwd
*/
if (sudo_goodpath(user_cmnd, user_stat) == NULL) {
if (rval != -1 && ioctl(fd, STRIOCRESCWD, 0) != 0)
warning("can't restore cwd");
*policyp = SYSTR_POLICY_NEVER;
*errorp = EACCES;
return(0);
}
if (rval != -1 && ioctl(fd, STRIOCRESCWD, 0) != 0)
warning("can't restore cwd");
/* Check sudoers and log the result. */
init_defaults();
def_authenticate = FALSE;
runas_pw = info->pw;
validated = VALIDATE_NOT_OK;
#ifdef HAVE_LDAP
if ((ld = sudo_ldap_open()) != NULL) {
sudo_ldap_update_defaults(ld);
validated = sudo_ldap_check(ld, 0);
sudo_ldap_close(ld);
}
if (!def_ignore_local_sudoers && !ISSET(validated, VALIDATE_OK))
#endif
{
(void) update_defaults(SET_ALL);
validated = sudoers_lookup(0);
}
if (ISSET(validated, VALIDATE_OK)) {
*policyp = SYSTR_POLICY_PERMIT;
} else {
*policyp = SYSTR_POLICY_NEVER;
*errorp = EACCES;
}
return(validated);
}
/*
* Call check_execv() and, if the command it permitted, set
* the SUDO_* environment variables.
*/
static int
check_execve(fd, pid, seqnr, askp, policyp, errorp)
int fd;
u_int16_t seqnr;
pid_t pid;
struct str_msg_ask *askp;
int *policyp;
int *errorp;
{
int rval;
rval = check_execv(fd, pid, seqnr, askp, policyp, errorp);
#ifdef STRIOCINJECT
if (rval > 0 && *policyp == SYSTR_POLICY_PERMIT) {
/* read environment into buf, munge, and bung it back */
if (update_env(fd, pid, seqnr, askp) != 0)
rval = -1;
}
#endif
return(rval);
}
/*
* Kill all pids in the list
*/
static void
killall(sig)
int sig;
{
struct childinfo *child;
for (child = children.first; child != NULL; child = child->next)
(void) kill(child->pid, sig);
}
/*
* Detach all traced processes.
*/
static void
detachall(fd)
int fd;
{
struct childinfo *child;
for (child = children.first; child != NULL; child = child->next)
(void) ioctl(fd, STRIOCDETACH, &child->pid);
}

View File

@@ -0,0 +1,236 @@
/*
* Copyright (c) 2004 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#define SYSTRACE_MAXENTS 1024
struct childinfo;
struct listhead;
struct syscallhandler;
static int check_execv __P((int, pid_t, u_int16_t,
struct str_msg_ask *, int *, int *));
static int check_execve __P((int, pid_t, u_int16_t,
struct str_msg_ask *, int *, int *));
static void log_exec __P((int));
static int decode_args __P((int, pid_t, struct str_msg_ask *));
static int set_policy __P((int, struct childinfo *));
static int switch_emulation __P((int, struct str_message *));
static int systrace_open __P((void));
static ssize_t systrace_read __P((int, pid_t, void *, void *, size_t));
#ifdef STRIOCINJECT
static ssize_t systrace_write __P((int, pid_t, void *, void *, size_t));
static int update_env __P((int, pid_t, u_int16_t, struct str_msg_ask *));
#endif
static struct syscallhandler *find_handler __P((pid_t, int));
static ssize_t read_string __P((int, pid_t, void *, char *, size_t));
static struct childinfo *find_child __P((pid_t));
static void catchsig __P((int));
static void detachall __P((int));
static void killall __P((int));
static void new_child __P((pid_t, pid_t));
static void rm_child __P((pid_t));
static void update_child __P((pid_t, uid_t));
static struct listhead children; /* list of children being traced */
static int initialized; /* set to true when we are inited */
static volatile sig_atomic_t dodetach; /* caught signal */
struct listhead {
void *first;
};
struct childinfo {
pid_t pid;
struct passwd *pw;
struct syscallaction *action;
struct childinfo *next;
};
struct syscallhandler {
int (*checker)
__P((int, pid_t, u_int16_t, struct str_msg_ask *, int *, int *));
void (*logger)
__P((int));
};
/*
* Each emulation has a list of actionable syscalls.
*/
struct syscallaction {
int code;
int policy;
struct syscallhandler handler;
};
static struct syscallaction syscalls_openbsd[] = {
{ 23, SYSTR_POLICY_ASK }, /* OPENBSD_SYS_setuid */
{ 59, SYSTR_POLICY_ASK, { check_execve, log_exec } }, /* OPENBSD_SYS_execve */
{ 126, SYSTR_POLICY_ASK }, /* OPENBSD_SYS_setreuid */
{ 183, SYSTR_POLICY_ASK }, /* OPENBSD_SYS_seteuid */
{ 282, SYSTR_POLICY_ASK }, /* OPENBSD_SYS_setresuid */
{ -1, -1 }
};
static struct syscallaction syscalls_bsdos[] = {
{ 23, SYSTR_POLICY_ASK }, /* BSDOS_SYS_setuid */
{ 59, SYSTR_POLICY_ASK, { check_execve, log_exec } }, /* BSDOS_SYS_execve */
{ 126, SYSTR_POLICY_ASK }, /* BSDOS_SYS_setreuid */
{ 183, SYSTR_POLICY_ASK }, /* BSDOS_SYS_seteuid */
{ -1, -1 }
};
static struct syscallaction syscalls_freebsd[] = {
{ 23, SYSTR_POLICY_ASK }, /* FREEBSD_SYS_setuid */
{ 59, SYSTR_POLICY_ASK, { check_execve, log_exec } }, /* FREEBSD_SYS_execve */
{ 126, SYSTR_POLICY_ASK }, /* FREEBSD_SYS_setreuid */
{ 183, SYSTR_POLICY_ASK }, /* FREEBSD_SYS_seteuid */
{ 311, SYSTR_POLICY_ASK }, /* FREEBSD_SYS_setresuid */
{ -1, -1 }
};
static struct syscallaction syscalls_netbsd[] = {
{ 23, SYSTR_POLICY_ASK }, /* NETBSD_SYS_setuid */
{ 59, SYSTR_POLICY_ASK, { check_execve, log_exec } }, /* NETBSD_SYS_execve */
{ 126, SYSTR_POLICY_ASK }, /* NETBSD_SYS_setreuid */
{ 183, SYSTR_POLICY_ASK }, /* NETBSD_SYS_seteuid */
{ -1, -1 }
};
static struct syscallaction syscalls_hpux[] = {
{ 11, SYSTR_POLICY_ASK, { check_execv, log_exec } }, /* HPUX_SYS_execv */
{ 23, SYSTR_POLICY_ASK }, /* HPUX_SYS_setuid */
{ 59, SYSTR_POLICY_ASK, { check_execve, log_exec } }, /* HPUX_SYS_execve */
{ 126, SYSTR_POLICY_ASK }, /* HPUX_SYS_setresuid */
{ -1, -1 }
};
static struct syscallaction syscalls_ibsc2[] = {
{ 11, SYSTR_POLICY_ASK, { check_execv, log_exec } }, /* ISCS2_SYS_execv */
{ 23, SYSTR_POLICY_ASK }, /* ISCS2_SYS_setuid */
{ 59, SYSTR_POLICY_ASK, { check_execve, log_exec } }, /* ISCS2_SYS_execve */
{ -1, -1 }
};
static struct syscallaction syscalls_linux[] = {
{ 11, SYSTR_POLICY_ASK, { check_execve, log_exec } }, /* LINUX_SYS_execve */
{ 23, SYSTR_POLICY_ASK }, /* LINUX_SYS_setuid16 */
{ 70, SYSTR_POLICY_ASK }, /* LINUX_SYS_setreuid16 */
{ 138, SYSTR_POLICY_ASK }, /* LINUX_SYS_setfsuid16 */
{ 164, SYSTR_POLICY_ASK }, /* LINUX_SYS_setresuid16 */
{ 203, SYSTR_POLICY_ASK }, /* LINUX_SYS_setreuid */
{ 208, SYSTR_POLICY_ASK }, /* LINUX_SYS_setresuid */
{ 213, SYSTR_POLICY_ASK }, /* LINUX_SYS_setuid */
{ 215, SYSTR_POLICY_ASK }, /* LINUX_SYS_setfsuid */
{ -1, -1 }
};
static struct syscallaction syscalls_osf1[] = {
{ 23, SYSTR_POLICY_ASK }, /* OSF1_SYS_setuid */
{ 59, SYSTR_POLICY_ASK, { check_execve, log_exec } }, /* OSF1_SYS_execve */
{ 126, SYSTR_POLICY_ASK }, /* OSF1_SYS_setreuid */
{ -1, -1 }
};
static struct syscallaction syscalls_sunos[] = {
{ 11, SYSTR_POLICY_ASK, { check_execv, log_exec } }, /* SUNOS_SYS_execv */
{ 23, SYSTR_POLICY_ASK }, /* SUNOS_SYS_setuid */
{ 59, SYSTR_POLICY_ASK, { check_execve, log_exec } }, /* SUNOS_SYS_execve */
{ 126, SYSTR_POLICY_ASK }, /* SUNOS_SYS_setreuid */
{ -1, -1 }
};
static struct syscallaction syscalls_svr4[] = {
{ 11, SYSTR_POLICY_ASK, { check_execv, log_exec } }, /* SVR4_SYS_execv */
{ 23, SYSTR_POLICY_ASK }, /* SVR4_SYS_setuid */
{ 59, SYSTR_POLICY_ASK, { check_execve, log_exec } }, /* SVR4_SYS_execve */
{ 141, SYSTR_POLICY_ASK }, /* SVR4_SYS_seteuid */
{ 202, SYSTR_POLICY_ASK }, /* SVR4_SYS_setreuid */
{ -1, -1 }
};
static struct syscallaction syscalls_ultrix[] = {
{ 11, SYSTR_POLICY_ASK, { check_execv, log_exec } }, /* ULTRIX_SYS_execv */
{ 23, SYSTR_POLICY_ASK }, /* ULTRIX_SYS_setuid */
{ 59, SYSTR_POLICY_ASK, { check_execve, log_exec } }, /* ULTRIX_SYS_execve */
{ 126, SYSTR_POLICY_ASK }, /* ULTRIX_SYS_setreuid */
{ -1, -1 }
};
static struct syscallaction syscalls_irix[] = {
{ 11, SYSTR_POLICY_ASK, { check_execv, log_exec } }, /* IRIX_SYS_execv */
{ 23, SYSTR_POLICY_ASK }, /* IRIX_SYS_setuid */
{ 59, SYSTR_POLICY_ASK, { check_execve, log_exec } }, /* IRIX_SYS_execve */
{ 124, SYSTR_POLICY_ASK }, /* IRIX_SYS_setreuid */
{ -1, -1 }
};
static struct syscallaction syscalls_darwin[] = {
{ 23, SYSTR_POLICY_ASK }, /* DARWIN_SYS_setuid */
{ 59, SYSTR_POLICY_ASK, { check_execve, log_exec } }, /* DARWIN_SYS_execve */
{ 126, SYSTR_POLICY_ASK }, /* DARWIN_SYS_setreuid */
{ 183, SYSTR_POLICY_ASK }, /* DARWIN_SYS_seteuid */
{ -1, -1 }
};
/*
* List of emulations we support. Not all OSes support all emulations but
* they are all listed here to make things simpler.
* Attempts to run programs with unknown emulations will be rejected.
*/
static struct emulation {
const char *name;
struct syscallaction *action;
} emulations[] = {
{ "bsdos", syscalls_bsdos },
#if defined(__darwin__) || defined(__APPLE__)
{ "native", syscalls_darwin },
#else
{ "darwin", syscalls_darwin },
#endif
#ifdef __FreeBSD__
{ "native", syscalls_freebsd },
#else
{ "freebsd", syscalls_freebsd },
#endif
{ "hpux", syscalls_hpux },
{ "ibsc2", syscalls_ibsc2 },
{ "irix", syscalls_irix },
#if defined(__linux__)
{ "native", syscalls_linux },
#else
{ "linux", syscalls_linux },
#endif
#ifdef __NetBSD__
{ "native", syscalls_netbsd },
#else
{ "netbsd", syscalls_netbsd },
#endif
{ "netbsd32", syscalls_netbsd },
#ifdef __OpenBSD__
{ "native", syscalls_openbsd },
#else
{ "openbsd", syscalls_openbsd },
#endif
{ "osf1", syscalls_osf1 },
{ "pecoff", syscalls_netbsd },
{ "sunos", syscalls_sunos },
{ "sunos32", syscalls_sunos },
{ "svr4", syscalls_svr4 },
{ "svr4_32", syscalls_svr4 },
{ "ultrix", syscalls_ultrix },
{ NULL, NULL }
};

46
plugins/sudoers/nonunix.h Normal file
View File

@@ -0,0 +1,46 @@
/*
* (c) 2006 Quest Software, Inc. 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 Quest Software, Inc. 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 THE COPYRIGHT HOLDERS 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 THE COPYRIGHT OWNER 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.
*/
#ifndef _NONUNIX_H
#define _NONUNIX_H
void
sudo_nonunix_groupcheck_init(void);
void
sudo_nonunix_groupcheck_cleanup(void);
int
sudo_nonunix_groupcheck( const char* group, const char* user, const struct passwd* pwd );
int
sudo_nonunix_groupcheck_available(void);
#endif /* _NONUNIX_H */

679
plugins/sudoers/parse.c Normal file
View File

@@ -0,0 +1,679 @@
/*
* Copyright (c) 2004-2005, 2007-2009 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
* 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 <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <ctype.h>
#include <pwd.h>
#include <grp.h>
#include "sudo.h"
#include "parse.h"
#include "lbuf.h"
#include <gram.h>
/* Characters that must be quoted in sudoers */
#define SUDOERS_QUOTED ":\\,=#\""
/* sudoers nsswitch routines */
struct sudo_nss sudo_nss_file = {
&sudo_nss_file,
NULL,
sudo_file_open,
sudo_file_close,
sudo_file_parse,
sudo_file_setdefs,
sudo_file_lookup,
sudo_file_display_cmnd,
sudo_file_display_defaults,
sudo_file_display_bound_defaults,
sudo_file_display_privs
};
/*
* Parser externs.
*/
extern FILE *yyin;
extern char *errorfile;
extern int errorlineno, parse_error;
/*
* Local prototypes.
*/
static void print_member __P((struct lbuf *, char *, int, int, int));
static int display_bound_defaults __P((int, struct lbuf *));
int
sudo_file_open(nss)
struct sudo_nss *nss;
{
if (def_ignore_local_sudoers)
return(-1);
nss->handle = open_sudoers(_PATH_SUDOERS, FALSE, NULL);
return(nss->handle ? 0 : -1);
}
int
sudo_file_close(nss)
struct sudo_nss *nss;
{
/* Free parser data structures and close sudoers file. */
init_parser(NULL, 0);
if (nss->handle != NULL) {
fclose(nss->handle);
nss->handle = NULL;
yyin = NULL;
}
return(0);
}
/*
* Parse the specified sudoers file.
*/
int
sudo_file_parse(nss)
struct sudo_nss *nss;
{
if (nss->handle == NULL)
return(-1);
init_parser(_PATH_SUDOERS, 0);
yyin = nss->handle;
if (yyparse() != 0 || parse_error) {
log_error(NO_EXIT, "parse error in %s near line %d",
errorfile, errorlineno);
return(-1);
}
return(0);
}
/*
* Wrapper around update_defaults() for nsswitch code.
*/
int
sudo_file_setdefs(nss)
struct sudo_nss *nss;
{
if (nss->handle == NULL)
return(-1);
if (!update_defaults(SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER))
return(-1);
return(0);
}
/*
* Look up the user in the parsed sudoers file and check to see if they are
* allowed to run the specified command on this host as the target user.
*/
int
sudo_file_lookup(nss, validated, pwflag)
struct sudo_nss *nss;
int validated;
int pwflag;
{
int match, host_match, runas_match, cmnd_match;
struct cmndspec *cs;
struct cmndtag *tags = NULL;
struct privilege *priv;
struct userspec *us;
if (nss->handle == NULL)
return(validated);
/*
* Only check the actual command if pwflag is not set.
* It is set for the "validate", "list" and "kill" pseudo-commands.
* Always check the host and user.
*/
if (pwflag) {
int nopass;
enum def_tupple pwcheck;
pwcheck = (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple;
nopass = (pwcheck == all) ? TRUE : FALSE;
if (list_pw == NULL)
SET(validated, FLAG_NO_CHECK);
CLR(validated, FLAG_NO_USER);
CLR(validated, FLAG_NO_HOST);
match = DENY;
tq_foreach_fwd(&userspecs, us) {
if (userlist_matches(sudo_user.pw, &us->users) != ALLOW)
continue;
tq_foreach_fwd(&us->privileges, priv) {
if (hostlist_matches(&priv->hostlist) != ALLOW)
continue;
tq_foreach_fwd(&priv->cmndlist, cs) {
/* Only check the command when listing another user. */
if (user_uid == 0 || list_pw == NULL ||
user_uid == list_pw->pw_uid ||
cmnd_matches(cs->cmnd) == ALLOW)
match = ALLOW;
if ((pwcheck == any && cs->tags.nopasswd == TRUE) ||
(pwcheck == all && cs->tags.nopasswd != TRUE))
nopass = cs->tags.nopasswd;
}
}
}
if (match == ALLOW || user_uid == 0) {
/* User has an entry for this host. */
SET(validated, VALIDATE_OK);
} else if (match == DENY)
SET(validated, VALIDATE_NOT_OK);
if (pwcheck == always && def_authenticate)
SET(validated, FLAG_CHECK_USER);
else if (pwcheck == never || nopass == TRUE)
def_authenticate = FALSE;
return(validated);
}
/* Need to be runas user while stat'ing things. */
set_perms(PERM_RUNAS);
match = UNSPEC;
tq_foreach_rev(&userspecs, us) {
if (userlist_matches(sudo_user.pw, &us->users) != ALLOW)
continue;
CLR(validated, FLAG_NO_USER);
tq_foreach_rev(&us->privileges, priv) {
host_match = hostlist_matches(&priv->hostlist);
if (host_match == ALLOW)
CLR(validated, FLAG_NO_HOST);
else
continue;
tq_foreach_rev(&priv->cmndlist, cs) {
runas_match = runaslist_matches(&cs->runasuserlist,
&cs->runasgrouplist);
if (runas_match == ALLOW) {
cmnd_match = cmnd_matches(cs->cmnd);
if (cmnd_match != UNSPEC) {
match = cmnd_match;
tags = &cs->tags;
#ifdef HAVE_SELINUX
/* Set role and type if not specified on command line. */
if (user_role == NULL)
user_role = cs->role ? estrdup(cs->role) : def_role;
if (user_type == NULL)
user_type = cs->type ? estrdup(cs->type) : def_type;
#endif /* HAVE_SELINUX */
goto matched2;
}
}
}
}
}
matched2:
if (match == ALLOW) {
SET(validated, VALIDATE_OK);
CLR(validated, VALIDATE_NOT_OK);
if (tags != NULL) {
if (tags->nopasswd != UNSPEC)
def_authenticate = !tags->nopasswd;
if (tags->noexec != UNSPEC)
def_noexec = tags->noexec;
if (tags->setenv != UNSPEC)
def_setenv = tags->setenv;
if (tags->transcript != UNSPEC)
def_transcript = tags->transcript;
}
} else if (match == DENY) {
SET(validated, VALIDATE_NOT_OK);
CLR(validated, VALIDATE_OK);
}
set_perms(PERM_ROOT);
return(validated);
}
#define TAG_CHANGED(t) \
(cs->tags.t != UNSPEC && cs->tags.t != IMPLIED && cs->tags.t != tags->t)
static void
sudo_file_append_cmnd(cs, tags, lbuf)
struct cmndspec *cs;
struct cmndtag *tags;
struct lbuf *lbuf;
{
struct member *m;
#ifdef HAVE_SELINUX
if (cs->role)
lbuf_append(lbuf, "ROLE=", cs->role, " ", NULL);
if (cs->type)
lbuf_append(lbuf, "TYPE=", cs->type, " ", NULL);
#endif /* HAVE_SELINUX */
if (TAG_CHANGED(setenv)) {
lbuf_append(lbuf, cs->tags.setenv ? "SETENV: " :
"NOSETENV: ", NULL);
tags->setenv = cs->tags.setenv;
}
if (TAG_CHANGED(noexec)) {
lbuf_append(lbuf, cs->tags.noexec ? "NOEXEC: " :
"EXEC: ", NULL);
tags->noexec = cs->tags.noexec;
}
if (TAG_CHANGED(nopasswd)) {
lbuf_append(lbuf, cs->tags.nopasswd ? "NOPASSWD: " :
"PASSWD: ", NULL);
tags->nopasswd = cs->tags.nopasswd;
}
if (TAG_CHANGED(transcript)) {
lbuf_append(lbuf, cs->tags.transcript ? "SCRIPT: " :
"NOSCRIPT: ", NULL);
tags->transcript = cs->tags.transcript;
}
m = cs->cmnd;
print_member(lbuf, m->name, m->type, m->negated,
CMNDALIAS);
}
static int
sudo_file_display_priv_short(pw, us, lbuf)
struct passwd *pw;
struct userspec *us;
struct lbuf *lbuf;
{
struct cmndspec *cs;
struct member *m;
struct privilege *priv;
struct cmndtag tags;
int nfound = 0;
tq_foreach_fwd(&us->privileges, priv) {
if (hostlist_matches(&priv->hostlist) != ALLOW)
continue;
tags.noexec = UNSPEC;
tags.setenv = UNSPEC;
tags.nopasswd = UNSPEC;
tags.transcript = UNSPEC;
lbuf_append(lbuf, " ", NULL);
tq_foreach_fwd(&priv->cmndlist, cs) {
if (cs != tq_first(&priv->cmndlist))
lbuf_append(lbuf, ", ", NULL);
lbuf_append(lbuf, "(", NULL);
if (!tq_empty(&cs->runasuserlist)) {
tq_foreach_fwd(&cs->runasuserlist, m) {
if (m != tq_first(&cs->runasuserlist))
lbuf_append(lbuf, ", ", NULL);
print_member(lbuf, m->name, m->type, m->negated,
RUNASALIAS);
}
} else if (tq_empty(&cs->runasgrouplist)) {
lbuf_append(lbuf, def_runas_default, NULL);
} else {
lbuf_append(lbuf, pw->pw_name, NULL);
}
if (!tq_empty(&cs->runasgrouplist)) {
lbuf_append(lbuf, " : ", NULL);
tq_foreach_fwd(&cs->runasgrouplist, m) {
if (m != tq_first(&cs->runasgrouplist))
lbuf_append(lbuf, ", ", NULL);
print_member(lbuf, m->name, m->type, m->negated,
RUNASALIAS);
}
}
lbuf_append(lbuf, ") ", NULL);
sudo_file_append_cmnd(cs, &tags, lbuf);
nfound++;
}
lbuf_print(lbuf); /* forces a newline */
}
return(nfound);
}
static int
sudo_file_display_priv_long(pw, us, lbuf)
struct passwd *pw;
struct userspec *us;
struct lbuf *lbuf;
{
struct cmndspec *cs;
struct member *m;
struct privilege *priv;
struct cmndtag tags;
int nfound = 0;
tq_foreach_fwd(&us->privileges, priv) {
if (hostlist_matches(&priv->hostlist) != ALLOW)
continue;
tags.noexec = UNSPEC;
tags.setenv = UNSPEC;
tags.nopasswd = UNSPEC;
tags.transcript = UNSPEC;
lbuf_print(lbuf); /* force a newline */
lbuf_append(lbuf, "Sudoers entry:", NULL);
lbuf_print(lbuf);
tq_foreach_fwd(&priv->cmndlist, cs) {
lbuf_append(lbuf, " RunAsUsers: ", NULL);
if (!tq_empty(&cs->runasuserlist)) {
tq_foreach_fwd(&cs->runasuserlist, m) {
if (m != tq_first(&cs->runasuserlist))
lbuf_append(lbuf, ", ", NULL);
print_member(lbuf, m->name, m->type, m->negated,
RUNASALIAS);
}
} else if (tq_empty(&cs->runasgrouplist)) {
lbuf_append(lbuf, def_runas_default, NULL);
} else {
lbuf_append(lbuf, pw->pw_name, NULL);
}
lbuf_print(lbuf);
if (!tq_empty(&cs->runasgrouplist)) {
lbuf_append(lbuf, " RunAsGroups: ", NULL);
tq_foreach_fwd(&cs->runasgrouplist, m) {
if (m != tq_first(&cs->runasgrouplist))
lbuf_append(lbuf, ", ", NULL);
print_member(lbuf, m->name, m->type, m->negated,
RUNASALIAS);
}
lbuf_print(lbuf);
}
lbuf_append(lbuf, " Commands: ", NULL);
lbuf_print(lbuf);
lbuf_append(lbuf, "\t", NULL);
sudo_file_append_cmnd(cs, &tags, lbuf);
lbuf_print(lbuf);
nfound++;
}
}
return(nfound);
}
int
sudo_file_display_privs(nss, pw, lbuf)
struct sudo_nss *nss;
struct passwd *pw;
struct lbuf *lbuf;
{
struct userspec *us;
int nfound = 0;
if (nss->handle == NULL)
return(-1);
tq_foreach_fwd(&userspecs, us) {
if (userlist_matches(pw, &us->users) != ALLOW)
continue;
if (long_list)
nfound += sudo_file_display_priv_long(pw, us, lbuf);
else
nfound += sudo_file_display_priv_short(pw, us, lbuf);
}
return(nfound);
}
/*
* Display matching Defaults entries for the given user on this host.
*/
int
sudo_file_display_defaults(nss, pw, lbuf)
struct sudo_nss *nss;
struct passwd *pw;
struct lbuf *lbuf;
{
struct defaults *d;
char *prefix = NULL;
int nfound = 0;
if (nss->handle == NULL)
return(-1);
if (lbuf->len == 0)
prefix = " ";
else
prefix = ", ";
tq_foreach_fwd(&defaults, d) {
switch (d->type) {
case DEFAULTS_HOST:
if (hostlist_matches(&d->binding) != ALLOW)
continue;
break;
case DEFAULTS_USER:
if (userlist_matches(pw, &d->binding) != ALLOW)
continue;
break;
case DEFAULTS_RUNAS:
case DEFAULTS_CMND:
continue;
}
lbuf_append(lbuf, prefix, NULL);
if (d->val != NULL) {
lbuf_append(lbuf, d->var, d->op == '+' ? "+=" :
d->op == '-' ? "-=" : "=", NULL);
if (strpbrk(d->val, " \t") != NULL) {
lbuf_append(lbuf, "\"", NULL);
lbuf_append_quoted(lbuf, "\"", d->val, NULL);
lbuf_append(lbuf, "\"", NULL);
} else
lbuf_append_quoted(lbuf, SUDOERS_QUOTED, d->val, NULL);
} else
lbuf_append(lbuf, d->op == FALSE ? "!" : "", d->var, NULL);
prefix = ", ";
nfound++;
}
return(nfound);
}
/*
* Display Defaults entries that are per-runas or per-command
*/
int
sudo_file_display_bound_defaults(nss, pw, lbuf)
struct sudo_nss *nss;
struct passwd *pw;
struct lbuf *lbuf;
{
int nfound = 0;
/* XXX - should only print ones that match what the user can do. */
nfound += display_bound_defaults(DEFAULTS_RUNAS, lbuf);
nfound += display_bound_defaults(DEFAULTS_CMND, lbuf);
return(nfound);
}
/*
* Display Defaults entries of the given type.
*/
static int
display_bound_defaults(dtype, lbuf)
int dtype;
struct lbuf *lbuf;
{
struct defaults *d;
struct member *m, *binding = NULL;
char *dname, *dsep;
int atype, nfound = 0;
switch (dtype) {
case DEFAULTS_HOST:
atype = HOSTALIAS;
dname = "host";
dsep = "@";
break;
case DEFAULTS_USER:
atype = USERALIAS;
dname = "user";
dsep = ":";
break;
case DEFAULTS_RUNAS:
atype = RUNASALIAS;
dname = "runas";
dsep = ">";
break;
case DEFAULTS_CMND:
atype = CMNDALIAS;
dname = "cmnd";
dsep = "!";
break;
default:
return(-1);
}
/* printf("Per-%s Defaults entries:\n", dname); */
tq_foreach_fwd(&defaults, d) {
if (d->type != dtype)
continue;
nfound++;
if (binding != tq_first(&d->binding)) {
binding = tq_first(&d->binding);
if (nfound != 1)
lbuf_append(lbuf, "\n", NULL);
lbuf_append(lbuf, " Defaults", dsep, NULL);
for (m = binding; m != NULL; m = m->next) {
if (m != binding)
lbuf_append(lbuf, ",", NULL);
print_member(lbuf, m->name, m->type, m->negated, atype);
lbuf_append(lbuf, " ", NULL);
}
} else
lbuf_append(lbuf, ", ", NULL);
if (d->val != NULL) {
lbuf_append(lbuf, d->var, d->op == '+' ? "+=" :
d->op == '-' ? "-=" : "=", d->val, NULL);
} else
lbuf_append(lbuf, d->op == FALSE ? "!" : "", d->var, NULL);
}
return(nfound);
}
int
sudo_file_display_cmnd(nss, pw)
struct sudo_nss *nss;
struct passwd *pw;
{
struct cmndspec *cs;
struct member *match;
struct privilege *priv;
struct userspec *us;
int rval = 1;
int host_match, runas_match, cmnd_match;
if (nss->handle == NULL)
return(rval);
match = NULL;
tq_foreach_rev(&userspecs, us) {
if (userlist_matches(pw, &us->users) != ALLOW)
continue;
tq_foreach_rev(&us->privileges, priv) {
host_match = hostlist_matches(&priv->hostlist);
if (host_match != ALLOW)
continue;
tq_foreach_rev(&priv->cmndlist, cs) {
runas_match = runaslist_matches(&cs->runasuserlist,
&cs->runasgrouplist);
if (runas_match == ALLOW) {
cmnd_match = cmnd_matches(cs->cmnd);
if (cmnd_match != UNSPEC) {
match = host_match && runas_match ?
cs->cmnd : NULL;
goto matched;
}
}
}
}
}
matched:
if (match != NULL && !match->negated) {
printf("%s%s%s\n", safe_cmnd, user_args ? " " : "",
user_args ? user_args : "");
rval = 0;
}
return(rval);
}
/*
* Print the contents of a struct member to stdout
*/
static void
_print_member(lbuf, name, type, negated, alias_type)
struct lbuf *lbuf;
char *name;
int type, negated, alias_type;
{
struct alias *a;
struct member *m;
struct sudo_command *c;
switch (type) {
case ALL:
lbuf_append(lbuf, negated ? "!ALL" : "ALL", NULL);
break;
case COMMAND:
c = (struct sudo_command *) name;
if (negated)
lbuf_append(lbuf, "!", NULL);
lbuf_append_quoted(lbuf, SUDOERS_QUOTED, c->cmnd, NULL);
if (c->args) {
lbuf_append(lbuf, " ", NULL);
lbuf_append_quoted(lbuf, SUDOERS_QUOTED, c->args, NULL);
}
break;
case ALIAS:
if ((a = alias_find(name, alias_type)) != NULL) {
tq_foreach_fwd(&a->members, m) {
if (m != tq_first(&a->members))
lbuf_append(lbuf, ", ", NULL);
_print_member(lbuf, m->name, m->type,
negated ? !m->negated : m->negated, alias_type);
}
break;
}
/* FALLTHROUGH */
default:
lbuf_append(lbuf, negated ? "!" : "", name, NULL);
break;
}
}
static void
print_member(lbuf, name, type, negated, alias_type)
struct lbuf *lbuf;
char *name;
int type, negated, alias_type;
{
alias_seqno++;
_print_member(lbuf, name, type, negated, alias_type);
}

190
plugins/sudoers/parse.h Normal file
View File

@@ -0,0 +1,190 @@
/*
* Copyright (c) 1996, 1998-2000, 2004, 2007-2009
* Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _SUDO_PARSE_H
#define _SUDO_PARSE_H
#undef UNSPEC
#define UNSPEC -1
#undef DENY
#define DENY 0
#undef ALLOW
#define ALLOW 1
#undef IMPLIED
#define IMPLIED 2
/*
* A command with args. XXX - merge into struct member.
*/
struct sudo_command {
char *cmnd;
char *args;
};
/*
* Tags associated with a command.
* Possible valus: TRUE, FALSE, UNSPEC.
*/
struct cmndtag {
__signed char nopasswd;
__signed char noexec;
__signed char setenv;
__signed char transcript;
};
/*
* SELinux-specific container struct.
* Currently just contains a role and type.
*/
struct selinux_info {
char *role;
char *type;
};
/*
* The parses sudoers file is stored as a collection of linked lists,
* modelled after the yacc grammar.
*
* Other than the alias struct, which is stored in a red-black tree,
* the data structure used is basically a doubly-linked tail queue without
* a separate head struct--the first entry acts as the head where the prev
* pointer does double duty as the tail pointer. This makes it possible
* to trivally append sub-lists. In addition, the prev pointer is always
* valid (even if it points to itself). Unlike a circle queue, the next
* pointer of the last entry is NULL and does not point back to the head.
*
* Note that each list struct must contain a "prev" and "next" pointer as
* the first two members of the struct (in that order).
*/
/*
* Tail queue list head structure.
*/
TQ_DECLARE(defaults)
TQ_DECLARE(userspec)
TQ_DECLARE(member)
TQ_DECLARE(privilege)
TQ_DECLARE(cmndspec)
/*
* Structure describing a user specification and list thereof.
*/
struct userspec {
struct userspec *prev, *next;
struct member_list users; /* list of users */
struct privilege_list privileges; /* list of privileges */
};
/*
* Structure describing a privilege specification.
*/
struct privilege {
struct privilege *prev, *next;
struct member_list hostlist; /* list of hosts */
struct cmndspec_list cmndlist; /* list of Cmnd_Specs */
};
/*
* Structure describing a linked list of Cmnd_Specs.
*/
struct cmndspec {
struct cmndspec *prev, *next;
struct member_list runasuserlist; /* list of runas users */
struct member_list runasgrouplist; /* list of runas groups */
struct member *cmnd; /* command to allow/deny */
struct cmndtag tags; /* tag specificaion */
#ifdef HAVE_SELINUX
char *role, *type; /* SELinux role and type */
#endif
};
/*
* Generic structure to hold users, hosts, commands.
*/
struct member {
struct member *prev, *next;
char *name; /* member name */
short type; /* type (see gram.h) */
short negated; /* negated via '!'? */
};
struct runascontainer {
struct member *runasusers;
struct member *runasgroups;
};
/*
* Generic structure to hold {User,Host,Runas,Cmnd}_Alias
* Aliases are stored in a red-black tree, sorted by name and type.
*/
struct alias {
char *name; /* alias name */
unsigned short type; /* {USER,HOST,RUNAS,CMND}ALIAS */
unsigned short seqno; /* sequence number */
struct member_list members; /* list of alias members */
};
/*
* Structure describing a Defaults entry and a list thereof.
*/
struct defaults {
struct defaults *prev, *next;
char *var; /* variable name */
char *val; /* variable value */
struct member_list binding; /* user/host/runas binding */
int type; /* DEFAULTS{,_USER,_RUNAS,_HOST} */
int op; /* TRUE, FALSE, '+', '-' */
};
/*
* Parsed sudoers info.
*/
extern struct userspec_list userspecs;
extern struct defaults_list defaults;
/*
* Alias sequence number to avoid loops.
*/
extern unsigned int alias_seqno;
/*
* Prototypes
*/
char *alias_add __P((char *, int, struct member *));
int addr_matches __P((char *));
int cmnd_matches __P((struct member *));
int cmndlist_matches __P((struct member_list *));
int command_matches __P((char *, char *));
int hostlist_matches __P((struct member_list *));
int hostname_matches __P((char *, char *, char *));
int netgr_matches __P((char *, char *, char *, char *));
int no_aliases __P((void));
int runaslist_matches __P((struct member_list *, struct member_list *));
int userlist_matches __P((struct passwd *, struct member_list *));
int usergr_matches __P((char *, char *, struct passwd *));
int userpw_matches __P((char *, char *, struct passwd *));
int group_matches __P((char *, struct group *));
struct alias *alias_find __P((char *, int));
struct alias *alias_remove __P((char *, int));
void alias_free __P((void *));
void alias_apply __P((int (*)(void *, void *), void *));
void init_aliases __P((void));
void init_lexer __P((void));
void init_parser __P((char *, int));
int alias_compare __P((const void *, const void *));
#endif /* _SUDO_PARSE_H */

607
plugins/sudoers/pwutil.c Normal file
View File

@@ -0,0 +1,607 @@
/*
* Copyright (c) 1996, 1998-2005, 2007-2009
* Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
# include <memory.h>
# endif
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <pwd.h>
#include <grp.h>
#include "sudo.h"
#include "redblack.h"
/*
* The passwd and group caches.
*/
static struct rbtree *pwcache_byuid, *pwcache_byname;
static struct rbtree *grcache_bygid, *grcache_byname;
static int cmp_pwuid __P((const void *, const void *));
static int cmp_pwnam __P((const void *, const void *));
static int cmp_grgid __P((const void *, const void *));
static int cmp_grnam __P((const void *, const void *));
/*
* Compare by uid.
*/
static int
cmp_pwuid(v1, v2)
const void *v1;
const void *v2;
{
const struct passwd *pw1 = (const struct passwd *) v1;
const struct passwd *pw2 = (const struct passwd *) v2;
return(pw1->pw_uid - pw2->pw_uid);
}
/*
* Compare by user name.
*/
static int
cmp_pwnam(v1, v2)
const void *v1;
const void *v2;
{
const struct passwd *pw1 = (const struct passwd *) v1;
const struct passwd *pw2 = (const struct passwd *) v2;
return(strcmp(pw1->pw_name, pw2->pw_name));
}
#define FIELD_SIZE(src, name, size) \
do { \
if (src->name) { \
size = strlen(src->name) + 1; \
total += size; \
} \
} while (0)
#define FIELD_COPY(src, dst, name, size) \
do { \
if (src->name) { \
memcpy(cp, src->name, size); \
dst->name = cp; \
cp += size; \
} \
} while (0)
/*
* Dynamically allocate space for a struct password and the constituent parts
* that we care about. Fills in pw_passwd from shadow file.
*/
static struct passwd *
sudo_pwdup(pw)
const struct passwd *pw;
{
char *cp;
const char *pw_shell;
size_t nsize, psize, csize, gsize, dsize, ssize, total;
struct passwd *newpw;
/* If shell field is empty, expand to _PATH_BSHELL. */
pw_shell = (pw->pw_shell == NULL || pw->pw_shell[0] == '\0')
? _PATH_BSHELL : pw->pw_shell;
/* Allocate in one big chunk for easy freeing. */
nsize = psize = csize = gsize = dsize = ssize = 0;
total = sizeof(struct passwd);
FIELD_SIZE(pw, pw_name, nsize);
FIELD_SIZE(pw, pw_passwd, psize);
#ifdef HAVE_LOGIN_CAP_H
FIELD_SIZE(pw, pw_class, csize);
#endif
FIELD_SIZE(pw, pw_gecos, gsize);
FIELD_SIZE(pw, pw_dir, dsize);
FIELD_SIZE(pw, pw_shell, ssize);
if ((cp = malloc(total)) == NULL)
return(NULL);
newpw = (struct passwd *) cp;
/*
* Copy in passwd contents and make strings relative to space
* at the end of the buffer.
*/
memcpy(newpw, pw, sizeof(struct passwd));
cp += sizeof(struct passwd);
FIELD_COPY(pw, newpw, pw_name, nsize);
FIELD_COPY(pw, newpw, pw_passwd, psize);
#ifdef HAVE_LOGIN_CAP_H
FIELD_COPY(pw, newpw, pw_class, csize);
#endif
FIELD_COPY(pw, newpw, pw_gecos, gsize);
FIELD_COPY(pw, newpw, pw_dir, dsize);
FIELD_COPY(pw, newpw, pw_shell, ssize);
return(newpw);
}
/*
* Get a password entry by uid and allocate space for it.
* Fills in pw_passwd from shadow file if necessary.
*/
struct passwd *
sudo_getpwuid(uid)
uid_t uid;
{
struct passwd key, *pw;
struct rbnode *node;
char *cp;
key.pw_uid = uid;
if ((node = rbfind(pwcache_byuid, &key)) != NULL) {
pw = (struct passwd *) node->data;
return(pw->pw_name != NULL ? pw : NULL);
}
/*
* Cache passwd db entry if it exists or a negative response if not.
*/
if ((pw = getpwuid(uid)) != NULL) {
pw = sudo_pwdup(pw);
cp = sudo_getepw(pw); /* get shadow password */
if (pw->pw_passwd != NULL)
zero_bytes(pw->pw_passwd, strlen(pw->pw_passwd));
pw->pw_passwd = cp;
if (rbinsert(pwcache_byuid, (void *) pw) != NULL)
errorx(1, "unable to cache uid %lu (%s), already exists",
uid, pw->pw_name);
return(pw);
} else {
pw = emalloc(sizeof(*pw));
zero_bytes(pw, sizeof(*pw));
pw->pw_uid = uid;
if (rbinsert(pwcache_byuid, (void *) pw) != NULL)
errorx(1, "unable to cache uid %lu, already exists", uid);
return(NULL);
}
}
/*
* Get a password entry by name and allocate space for it.
* Fills in pw_passwd from shadow file if necessary.
*/
struct passwd *
sudo_getpwnam(name)
const char *name;
{
struct passwd key, *pw;
struct rbnode *node;
size_t len;
char *cp;
key.pw_name = (char *) name;
if ((node = rbfind(pwcache_byname, &key)) != NULL) {
pw = (struct passwd *) node->data;
return(pw->pw_uid != (uid_t) -1 ? pw : NULL);
}
/*
* Cache passwd db entry if it exists or a negative response if not.
*/
if ((pw = getpwnam(name)) != NULL) {
pw = sudo_pwdup(pw);
cp = sudo_getepw(pw); /* get shadow password */
if (pw->pw_passwd != NULL)
zero_bytes(pw->pw_passwd, strlen(pw->pw_passwd));
pw->pw_passwd = cp;
if (rbinsert(pwcache_byname, (void *) pw) != NULL)
errorx(1, "unable to cache user %s, already exists", name);
return(pw);
} else {
len = strlen(name) + 1;
cp = emalloc(sizeof(*pw) + len);
zero_bytes(cp, sizeof(*pw));
pw = (struct passwd *) cp;
cp += sizeof(*pw);
memcpy(cp, name, len);
pw->pw_name = cp;
pw->pw_uid = (uid_t) -1;
if (rbinsert(pwcache_byname, (void *) pw) != NULL)
errorx(1, "unable to cache user %s, already exists", name);
return(NULL);
}
}
/*
* Take a uid in string form "#123" and return a faked up passwd struct.
*/
struct passwd *
sudo_fakepwnam(user, gid)
const char *user;
gid_t gid;
{
struct passwd *pw;
struct rbnode *node;
size_t len;
len = strlen(user);
pw = emalloc(sizeof(struct passwd) + len + 1 /* pw_name */ +
sizeof("*") /* pw_passwd */ + sizeof("") /* pw_gecos */ +
sizeof("/") /* pw_dir */ + sizeof(_PATH_BSHELL));
zero_bytes(pw, sizeof(struct passwd));
pw->pw_uid = (uid_t) atoi(user + 1);
pw->pw_gid = gid;
pw->pw_name = (char *)pw + sizeof(struct passwd);
memcpy(pw->pw_name, user, len + 1);
pw->pw_passwd = pw->pw_name + len + 1;
memcpy(pw->pw_passwd, "*", 2);
pw->pw_gecos = pw->pw_passwd + 2;
pw->pw_gecos[0] = '\0';
pw->pw_dir = pw->pw_gecos + 1;
memcpy(pw->pw_dir, "/", 2);
pw->pw_shell = pw->pw_dir + 2;
memcpy(pw->pw_shell, _PATH_BSHELL, sizeof(_PATH_BSHELL));
/* Store by uid and by name, overwriting cached version. */
if ((node = rbinsert(pwcache_byuid, pw)) != NULL) {
efree(node->data);
node->data = (void *) pw;
}
if ((node = rbinsert(pwcache_byname, pw)) != NULL) {
efree(node->data);
node->data = (void *) pw;
}
return(pw);
}
/*
* Take a gid in string form "#123" and return a faked up group struct.
*/
struct group *
sudo_fakegrnam(group)
const char *group;
{
struct group *gr;
struct rbnode *node;
size_t len;
len = strlen(group);
gr = emalloc(sizeof(struct group) + len + 1);
zero_bytes(gr, sizeof(struct group));
gr->gr_gid = (gid_t) atoi(group + 1);
gr->gr_name = (char *)gr + sizeof(struct group);
strlcpy(gr->gr_name, group, len + 1);
/* Store by gid and by name, overwriting cached version. */
if ((node = rbinsert(grcache_bygid, gr)) != NULL) {
efree(node->data);
node->data = (void *) gr;
}
if ((node = rbinsert(grcache_byname, gr)) != NULL) {
efree(node->data);
node->data = (void *) gr;
}
return(gr);
}
void
sudo_setpwent()
{
setpwent();
sudo_setspent();
if (pwcache_byuid == NULL)
pwcache_byuid = rbcreate(cmp_pwuid);
if (pwcache_byname == NULL)
pwcache_byname = rbcreate(cmp_pwnam);
}
#ifdef PURIFY
static void pw_free __P((void *));
void
sudo_freepwcache()
{
if (pwcache_byuid != NULL) {
rbdestroy(pwcache_byuid, pw_free);
pwcache_byuid = NULL;
}
if (pwcache_byname != NULL) {
rbdestroy(pwcache_byname, NULL);
pwcache_byname = NULL;
}
}
static void
pw_free(v)
void *v;
{
struct passwd *pw = (struct passwd *) v;
if (pw->pw_passwd != NULL) {
zero_bytes(pw->pw_passwd, strlen(pw->pw_passwd));
efree(pw->pw_passwd);
}
efree(pw);
}
#endif /* PURIFY */
void
sudo_endpwent()
{
endpwent();
sudo_endspent();
#ifdef PURIFY
sudo_freepwcache();
#endif
}
/*
* Compare by gid.
*/
static int
cmp_grgid(v1, v2)
const void *v1;
const void *v2;
{
const struct group *grp1 = (const struct group *) v1;
const struct group *grp2 = (const struct group *) v2;
return(grp1->gr_gid - grp2->gr_gid);
}
/*
* Compare by group name.
*/
static int
cmp_grnam(v1, v2)
const void *v1;
const void *v2;
{
const struct group *grp1 = (const struct group *) v1;
const struct group *grp2 = (const struct group *) v2;
return(strcmp(grp1->gr_name, grp2->gr_name));
}
struct group *
sudo_grdup(gr)
const struct group *gr;
{
char *cp;
size_t nsize, psize, nmem, total, len;
struct group *newgr;
/* Allocate in one big chunk for easy freeing. */
nsize = psize = nmem = 0;
total = sizeof(struct group);
FIELD_SIZE(gr, gr_name, nsize);
FIELD_SIZE(gr, gr_passwd, psize);
if (gr->gr_mem) {
for (nmem = 0; gr->gr_mem[nmem] != NULL; nmem++)
total += strlen(gr->gr_mem[nmem]) + 1;
nmem++;
total += sizeof(char *) * nmem;
}
if ((cp = malloc(total)) == NULL)
return(NULL);
newgr = (struct group *)cp;
/*
* Copy in group contents and make strings relative to space
* at the end of the buffer. Note that gr_mem must come
* immediately after struct group to guarantee proper alignment.
*/
(void)memcpy(newgr, gr, sizeof(struct group));
cp += sizeof(struct group);
if (gr->gr_mem) {
newgr->gr_mem = (char **)cp;
cp += sizeof(char *) * nmem;
for (nmem = 0; gr->gr_mem[nmem] != NULL; nmem++) {
len = strlen(gr->gr_mem[nmem]) + 1;
memcpy(cp, gr->gr_mem[nmem], len);
newgr->gr_mem[nmem] = cp;
cp += len;
}
newgr->gr_mem[nmem] = NULL;
}
FIELD_COPY(gr, newgr, gr_passwd, psize);
FIELD_COPY(gr, newgr, gr_name, nsize);
return(newgr);
}
/*
* Get a group entry by gid and allocate space for it.
*/
struct group *
sudo_getgrgid(gid)
gid_t gid;
{
struct group key, *gr;
struct rbnode *node;
key.gr_gid = gid;
if ((node = rbfind(grcache_bygid, &key)) != NULL) {
gr = (struct group *) node->data;
return(gr->gr_name != NULL ? gr : NULL);
}
/*
* Cache group db entry if it exists or a negative response if not.
*/
if ((gr = getgrgid(gid)) != NULL) {
gr = sudo_grdup(gr);
if (rbinsert(grcache_bygid, (void *) gr) != NULL)
errorx(1, "unable to cache gid %lu (%s), already exists",
gid, gr->gr_name);
return(gr);
} else {
gr = emalloc(sizeof(*gr));
zero_bytes(gr, sizeof(*gr));
gr->gr_gid = gid;
if (rbinsert(grcache_bygid, (void *) gr) != NULL)
errorx(1, "unable to cache gid %lu, already exists, gid");
return(NULL);
}
}
/*
* Get a group entry by name and allocate space for it.
*/
struct group *
sudo_getgrnam(name)
const char *name;
{
struct group key, *gr;
struct rbnode *node;
size_t len;
char *cp;
key.gr_name = (char *) name;
if ((node = rbfind(grcache_byname, &key)) != NULL) {
gr = (struct group *) node->data;
return(gr->gr_gid != (gid_t) -1 ? gr : NULL);
}
/*
* Cache group db entry if it exists or a negative response if not.
*/
if ((gr = getgrnam(name)) != NULL) {
gr = sudo_grdup(gr);
if (rbinsert(grcache_byname, (void *) gr) != NULL)
errorx(1, "unable to cache group %s, already exists", name);
return(gr);
} else {
len = strlen(name) + 1;
cp = emalloc(sizeof(*gr) + len);
zero_bytes(cp, sizeof(*gr));
gr = (struct group *) cp;
cp += sizeof(*gr);
memcpy(cp, name, len);
gr->gr_name = cp;
gr->gr_gid = (gid_t) -1;
if (rbinsert(grcache_byname, (void *) gr) != NULL)
errorx(1, "unable to cache group %s, already exists", name);
return(NULL);
}
}
void
sudo_setgrent()
{
setgrent();
if (grcache_bygid == NULL)
grcache_bygid = rbcreate(cmp_grgid);
if (grcache_byname == NULL)
grcache_byname = rbcreate(cmp_grnam);
}
#ifdef PURIFY
void
sudo_freegrcache()
{
if (grcache_bygid != NULL) {
rbdestroy(grcache_bygid, free);
grcache_bygid = NULL;
}
if (grcache_byname != NULL) {
rbdestroy(grcache_byname, NULL);
grcache_byname = NULL;
}
}
#endif /* PURIFY */
void
sudo_endgrent()
{
endgrent();
#ifdef PURIFY
sudo_freegrcache();
#endif
}
int
user_in_group(pw, group)
struct passwd *pw;
const char *group;
{
#ifdef HAVE_MBR_CHECK_MEMBERSHIP
uuid_t gu, uu;
int ismember;
#else
char **gr_mem;
int i;
#endif
struct group *grp;
if ((grp = sudo_getgrnam(group)) == NULL)
return(FALSE);
/* check against user's primary (passwd file) gid */
if (grp->gr_gid == pw->pw_gid)
return(TRUE);
#ifdef HAVE_MBR_CHECK_MEMBERSHIP
/* If we are matching the invoking user use the stashed uuid. */
if (strcmp(pw->pw_name, user_name) == 0) {
if (mbr_gid_to_uuid(grp->gr_gid, gu) == 0 &&
mbr_check_membership(user_uuid, gu, &ismember) == 0 && ismember)
return(TRUE);
} else {
if (mbr_uid_to_uuid(pw->pw_uid, uu) == 0 &&
mbr_gid_to_uuid(grp->gr_gid, gu) == 0 &&
mbr_check_membership(uu, gu, &ismember) == 0 && ismember)
return(TRUE);
}
#else /* HAVE_MBR_CHECK_MEMBERSHIP */
# ifdef HAVE_GETGROUPS
/*
* If we are matching the invoking or list user and that user has a
* supplementary group vector, check it.
*/
if (user_ngroups >= 0 &&
strcmp(pw->pw_name, list_pw ? list_pw->pw_name : user_name) == 0) {
for (i = 0; i < user_ngroups; i++) {
if (grp->gr_gid == user_groups[i])
return(TRUE);
}
} else
# endif /* HAVE_GETGROUPS */
{
if (grp != NULL && grp->gr_mem != NULL) {
for (gr_mem = grp->gr_mem; *gr_mem; gr_mem++) {
if (strcmp(*gr_mem, pw->pw_name) == 0)
return(TRUE);
}
}
}
#endif /* HAVE_MBR_CHECK_MEMBERSHIP */
return(FALSE);
}

473
plugins/sudoers/redblack.c Normal file
View File

@@ -0,0 +1,473 @@
/*
* Copyright (c) 2004-2005, 2007,2009 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* Adapted from the following code written by Emin Martinian:
* http://web.mit.edu/~emin/www/source_code/red_black_tree/index.html
*
* Copyright (c) 2001 Emin Martinian
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that neither the name of Emin
* Martinian nor the names of any contributors are be used to endorse or
* promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
* OWNER 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 <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#include "sudo.h"
#include "redblack.h"
static void rbrepair __P((struct rbtree *, struct rbnode *));
static void rotate_left __P((struct rbtree *, struct rbnode *));
static void rotate_right __P((struct rbtree *, struct rbnode *));
static void _rbdestroy __P((struct rbtree *, struct rbnode *,
void (*)(void *)));
/*
* Red-Black tree, see http://en.wikipedia.org/wiki/Red-black_tree
*
* A red-black tree is a binary search tree where each node has a color
* attribute, the value of which is either red or black. Essentially, it
* is just a convenient way to express a 2-3-4 binary search tree where
* the color indicates whether the node is part of a 3-node or a 4-node.
* In addition to the ordinary requirements imposed on binary search
* trees, we make the following additional requirements of any valid
* red-black tree:
* 1) Every node is either red or black.
* 2) The root is black.
* 3) All leaves are black.
* 4) Both children of each red node are black.
* 5) The paths from each leaf up to the root each contain the same
* number of black nodes.
*/
/*
* Create a red black tree struct using the specified compare routine.
* Allocates and returns the initialized (empty) tree.
*/
struct rbtree *
rbcreate(compar)
int (*compar)__P((const void *, const void*));
{
struct rbtree *tree;
tree = (struct rbtree *) emalloc(sizeof(*tree));
tree->compar = compar;
/*
* We use a self-referencing sentinel node called nil to simplify the
* code by avoiding the need to check for NULL pointers.
*/
tree->nil.left = tree->nil.right = tree->nil.parent = &tree->nil;
tree->nil.color = black;
tree->nil.data = NULL;
/*
* Similarly, the fake root node keeps us from having to worry
* about splitting the root.
*/
tree->root.left = tree->root.right = tree->root.parent = &tree->nil;
tree->root.color = black;
tree->root.data = NULL;
return(tree);
}
/*
* Perform a left rotation starting at node.
*/
static void
rotate_left(tree, node)
struct rbtree *tree;
struct rbnode *node;
{
struct rbnode *child;
child = node->right;
node->right = child->left;
if (child->left != rbnil(tree))
child->left->parent = node;
child->parent = node->parent;
if (node == node->parent->left)
node->parent->left = child;
else
node->parent->right = child;
child->left = node;
node->parent = child;
}
/*
* Perform a right rotation starting at node.
*/
static void
rotate_right(tree, node)
struct rbtree *tree;
struct rbnode *node;
{
struct rbnode *child;
child = node->left;
node->left = child->right;
if (child->right != rbnil(tree))
child->right->parent = node;
child->parent = node->parent;
if (node == node->parent->left)
node->parent->left = child;
else
node->parent->right = child;
child->right = node;
node->parent = child;
}
/*
* Insert data pointer into a redblack tree.
* Returns a NULL pointer on success. If a node matching "data"
* already exists, a pointer to the existant node is returned.
*/
struct rbnode *
rbinsert(tree, data)
struct rbtree *tree;
void *data;
{
struct rbnode *node = rbfirst(tree);
struct rbnode *parent = rbroot(tree);
int res;
/* Find correct insertion point. */
while (node != rbnil(tree)) {
parent = node;
if ((res = tree->compar(data, node->data)) == 0)
return(node);
node = res < 0 ? node->left : node->right;
}
node = (struct rbnode *) emalloc(sizeof(*node));
node->data = data;
node->left = node->right = rbnil(tree);
node->parent = parent;
if (parent == rbroot(tree) || tree->compar(data, parent->data) < 0)
parent->left = node;
else
parent->right = node;
node->color = red;
/*
* If the parent node is black we are all set, if it is red we have
* the following possible cases to deal with. We iterate through
* the rest of the tree to make sure none of the required properties
* is violated.
*
* 1) The uncle is red. We repaint both the parent and uncle black
* and repaint the grandparent node red.
*
* 2) The uncle is black and the new node is the right child of its
* parent, and the parent in turn is the left child of its parent.
* We do a left rotation to switch the roles of the parent and
* child, relying on further iterations to fixup the old parent.
*
* 3) The uncle is black and the new node is the left child of its
* parent, and the parent in turn is the left child of its parent.
* We switch the colors of the parent and grandparent and perform
* a right rotation around the grandparent. This makes the former
* parent the parent of the new node and the former grandparent.
*
* Note that because we use a sentinel for the root node we never
* need to worry about replacing the root.
*/
while (node->parent->color == red) {
struct rbnode *uncle;
if (node->parent == node->parent->parent->left) {
uncle = node->parent->parent->right;
if (uncle->color == red) {
node->parent->color = black;
uncle->color = black;
node->parent->parent->color = red;
node = node->parent->parent;
} else /* if (uncle->color == black) */ {
if (node == node->parent->right) {
node = node->parent;
rotate_left(tree, node);
}
node->parent->color = black;
node->parent->parent->color = red;
rotate_right(tree, node->parent->parent);
}
} else { /* if (node->parent == node->parent->parent->right) */
uncle = node->parent->parent->left;
if (uncle->color == red) {
node->parent->color = black;
uncle->color = black;
node->parent->parent->color = red;
node = node->parent->parent;
} else /* if (uncle->color == black) */ {
if (node == node->parent->left) {
node = node->parent;
rotate_right(tree, node);
}
node->parent->color = black;
node->parent->parent->color = red;
rotate_left(tree, node->parent->parent);
}
}
}
rbfirst(tree)->color = black; /* first node is always black */
return(NULL);
}
/*
* Look for a node matching key in tree.
* Returns a pointer to the node if found, else NULL.
*/
struct rbnode *
rbfind(tree, key)
struct rbtree *tree;
void *key;
{
struct rbnode *node = rbfirst(tree);
int res;
while (node != rbnil(tree)) {
if ((res = tree->compar(key, node->data)) == 0)
return(node);
node = res < 0 ? node->left : node->right;
}
return(NULL);
}
/*
* Call func() for each node, passing it the node data and a cookie;
* If func() returns non-zero for a node, the traversal stops and the
* error value is returned. Returns 0 on successful traversal.
*/
int
rbapply_node(tree, node, func, cookie, order)
struct rbtree *tree;
struct rbnode *node;
int (*func)__P((void *, void *));
void *cookie;
enum rbtraversal order;
{
int error;
if (node != rbnil(tree)) {
if (order == preorder)
if ((error = func(node->data, cookie)) != 0)
return(error);
if ((error = rbapply_node(tree, node->left, func, cookie, order)) != 0)
return(error);
if (order == inorder)
if ((error = func(node->data, cookie)) != 0)
return(error);
if ((error = rbapply_node(tree, node->right, func, cookie, order)) != 0)
return(error);
if (order == postorder)
if ((error = func(node->data, cookie)) != 0)
return(error);
}
return (0);
}
/*
* Returns the successor of node, or nil if there is none.
*/
static struct rbnode *
rbsuccessor(tree, node)
struct rbtree *tree;
struct rbnode *node;
{
struct rbnode *succ;
if ((succ = node->right) != rbnil(tree)) {
while (succ->left != rbnil(tree))
succ = succ->left;
} else {
/* No right child, move up until we find it or hit the root */
for (succ = node->parent; node == succ->right; succ = succ->parent)
node = succ;
if (succ == rbroot(tree))
succ = rbnil(tree);
}
return(succ);
}
/*
* Recursive portion of rbdestroy().
*/
static void
_rbdestroy(tree, node, destroy)
struct rbtree *tree;
struct rbnode *node;
void (*destroy)__P((void *));
{
if (node != rbnil(tree)) {
_rbdestroy(tree, node->left, destroy);
_rbdestroy(tree, node->right, destroy);
if (destroy != NULL)
destroy(node->data);
efree(node);
}
}
/*
* Destroy the specified tree, calling the destructor destroy
* for each node and then freeing the tree itself.
*/
void
rbdestroy(tree, destroy)
struct rbtree *tree;
void (*destroy)__P((void *));
{
_rbdestroy(tree, rbfirst(tree), destroy);
efree(tree);
}
/*
* Delete node 'z' from the tree and return its data pointer.
*/
void *rbdelete(tree, z)
struct rbtree *tree;
struct rbnode *z;
{
struct rbnode *x, *y;
void *data = z->data;
if (z->left == rbnil(tree) || z->right == rbnil(tree))
y = z;
else
y = rbsuccessor(tree, z);
x = (y->left == rbnil(tree)) ? y->right : y->left;
if ((x->parent = y->parent) == rbroot(tree)) {
rbfirst(tree) = x;
} else {
if (y == y->parent->left)
y->parent->left = x;
else
y->parent->right = x;
}
if (y->color == black)
rbrepair(tree, x);
if (y != z) {
y->left = z->left;
y->right = z->right;
y->parent = z->parent;
y->color = z->color;
z->left->parent = z->right->parent = y;
if (z == z->parent->left)
z->parent->left = y;
else
z->parent->right = y;
}
free(z);
return (data);
}
/*
* Repair the tree after a node has been deleted by rotating and repainting
* colors to restore the 4 properties inherent in red-black trees.
*/
static void
rbrepair(tree, node)
struct rbtree *tree;
struct rbnode *node;
{
struct rbnode *sibling;
while (node->color == black && node != rbroot(tree)) {
if (node == node->parent->left) {
sibling = node->parent->right;
if (sibling->color == red) {
sibling->color = black;
node->parent->color = red;
rotate_left(tree, node->parent);
sibling = node->parent->right;
}
if (sibling->right->color == black && sibling->left->color == black) {
sibling->color = red;
node = node->parent;
} else {
if (sibling->right->color == black) {
sibling->left->color = black;
sibling->color = red;
rotate_right(tree, sibling);
sibling = node->parent->right;
}
sibling->color = node->parent->color;
node->parent->color = black;
sibling->right->color = black;
rotate_left(tree, node->parent);
node = rbroot(tree); /* exit loop */
}
} else { /* if (node == node->parent->right) */
sibling = node->parent->left;
if (sibling->color == red) {
sibling->color = black;
node->parent->color = red;
rotate_right(tree, node->parent);
sibling = node->parent->left;
}
if (sibling->right->color == black && sibling->left->color == black) {
sibling->color = red;
node = node->parent;
} else {
if (sibling->left->color == black) {
sibling->right->color = black;
sibling->color = red;
rotate_left(tree, sibling);
sibling = node->parent->left;
}
sibling->color = node->parent->color;
node->parent->color = black;
sibling->left->color = black;
rotate_right(tree, node->parent);
node = rbroot(tree); /* exit loop */
}
}
}
node->color = black;
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (c) 2004, 2007 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _SUDO_REDBLACK_H
#define _SUDO_REDBLACK_H
enum rbcolor {
red,
black
};
enum rbtraversal {
preorder,
inorder,
postorder
};
struct rbnode {
struct rbnode *left, *right, *parent;
void *data;
enum rbcolor color;
};
struct rbtree {
int (*compar) __P((const void *, const void *));
struct rbnode root;
struct rbnode nil;
};
#define rbapply(t, f, c, o) rbapply_node((t), (t)->root.left, (f), (c), (o))
#define rbisempty(t) ((t)->root.left == &(t)->nil && (t)->root.right == &(t)->nil)
#define rbfirst(t) ((t)->root.left)
#define rbroot(t) (&(t)->root)
#define rbnil(t) (&(t)->nil)
void *rbdelete __P((struct rbtree *, struct rbnode *));
int rbapply_node __P((struct rbtree *, struct rbnode *,
int (*)(void *, void *), void *,
enum rbtraversal));
struct rbnode *rbfind __P((struct rbtree *, void *));
struct rbnode *rbinsert __P((struct rbtree *, void *));
struct rbtree *rbcreate __P((int (*)(const void *, const void *)));
void rbdestroy __P((struct rbtree *, void (*)(void *)));
#endif /* _SUDO_REDBLACK_H */

583
plugins/sudoers/set_perms.c Normal file
View File

@@ -0,0 +1,583 @@
/*
* Copyright (c) 1994-1996,1998-2009 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <pwd.h>
#include <errno.h>
#include <grp.h>
#ifdef HAVE_LOGIN_CAP_H
# include <login_cap.h>
#endif
#include "sudo.h"
#ifdef __TANDEM
# define ROOT_UID 65535
#else
# define ROOT_UID 0
#endif
/*
* Prototypes
*/
static void runas_setup __P((void));
static void runas_setgroups __P((void));
static void restore_groups __P((void));
static int current_perm = -1;
#ifdef HAVE_SETRESUID
/*
* Set real and effective and saved uids and gids based on perm.
* We always retain a saved uid of 0 unless we are headed for an exec().
* We only flip the effective gid since it only changes for PERM_SUDOERS.
* This version of set_perms() works fine with the "stay_setuid" option.
*/
int
set_perms(perm)
int perm;
{
const char *errstr;
int noexit;
noexit = ISSET(perm, PERM_NOEXIT);
CLR(perm, PERM_MASK);
if (perm == current_perm)
return(1);
switch (perm) {
case PERM_ROOT:
if (setresuid(ROOT_UID, ROOT_UID, ROOT_UID)) {
errstr = "setresuid(ROOT_UID, ROOT_UID, ROOT_UID)";
goto bad;
}
(void) setresgid(-1, user_gid, -1);
if (current_perm == PERM_RUNAS)
restore_groups();
break;
case PERM_USER:
(void) setresgid(-1, user_gid, -1);
if (setresuid(user_uid, user_uid, ROOT_UID)) {
errstr = "setresuid(user_uid, user_uid, ROOT_UID)";
goto bad;
}
break;
case PERM_FULL_USER:
/* headed for exec() */
(void) setgid(user_gid);
if (setresuid(user_uid, user_uid, user_uid)) {
errstr = "setresuid(user_uid, user_uid, user_uid)";
goto bad;
}
break;
case PERM_RUNAS:
runas_setgroups();
(void) setresgid(-1, runas_gr ?
runas_gr->gr_gid : runas_pw->pw_gid, -1);
if (setresuid(-1, runas_pw ? runas_pw->pw_uid :
user_uid, -1)) {
errstr = "unable to change to runas uid";
goto bad;
}
break;
case PERM_FULL_RUNAS:
/* headed for exec(), assume euid == ROOT_UID */
runas_setup();
if (setresuid(def_stay_setuid ?
user_uid : runas_pw->pw_uid,
runas_pw->pw_uid, runas_pw->pw_uid)) {
errstr = "unable to change to runas uid";
goto bad;
}
break;
case PERM_SUDOERS:
/* assume euid == ROOT_UID, ruid == user */
if (setresgid(-1, SUDOERS_GID, -1))
error(1, "unable to change to sudoers gid");
/*
* If SUDOERS_UID == ROOT_UID and SUDOERS_MODE
* is group readable we use a non-zero
* uid in order to avoid NFS lossage.
* Using uid 1 is a bit bogus but should
* work on all OS's.
*/
if (SUDOERS_UID == ROOT_UID) {
if ((SUDOERS_MODE & 040) && setresuid(ROOT_UID, 1, ROOT_UID)) {
errstr = "setresuid(ROOT_UID, 1, ROOT_UID)";
goto bad;
}
} else {
if (setresuid(ROOT_UID, SUDOERS_UID, ROOT_UID)) {
errstr = "setresuid(ROOT_UID, SUDOERS_UID, ROOT_UID)";
goto bad;
}
}
break;
case PERM_TIMESTAMP:
if (setresuid(ROOT_UID, timestamp_uid, ROOT_UID)) {
errstr = "setresuid(ROOT_UID, timestamp_uid, ROOT_UID)";
goto bad;
}
break;
}
current_perm = perm;
return(1);
bad:
warningx("%s: %s", errstr,
errno == EAGAIN ? "too many processes" : strerror(errno));
if (noexit)
return(0);
exit(1);
}
#else
# ifdef HAVE_SETREUID
/*
* Set real and effective uids and gids based on perm.
* We always retain a real or effective uid of ROOT_UID unless
* we are headed for an exec().
* This version of set_perms() works fine with the "stay_setuid" option.
*/
int
set_perms(perm)
int perm;
{
const char *errstr;
int noexit;
noexit = ISSET(perm, PERM_NOEXIT);
CLR(perm, PERM_MASK);
if (perm == current_perm)
return(1);
switch (perm) {
case PERM_ROOT:
if (setreuid(-1, ROOT_UID)) {
errstr = "setreuid(-1, ROOT_UID)";
goto bad;
}
if (setuid(ROOT_UID)) {
errstr = "setuid(ROOT_UID)";
goto bad;
}
(void) setregid(-1, user_gid);
if (current_perm == PERM_RUNAS)
restore_groups();
break;
case PERM_USER:
(void) setregid(-1, user_gid);
if (setreuid(ROOT_UID, user_uid)) {
errstr = "setreuid(ROOT_UID, user_uid)";
goto bad;
}
break;
case PERM_FULL_USER:
/* headed for exec() */
(void) setgid(user_gid);
if (setreuid(user_uid, user_uid)) {
errstr = "setreuid(user_uid, user_uid)";
goto bad;
}
break;
case PERM_RUNAS:
runas_setgroups();
(void) setregid(-1, runas_gr ?
runas_gr->gr_gid : runas_pw->pw_gid);
if (setreuid(-1,
runas_pw ? runas_pw->pw_uid : user_uid)) {
errstr = "unable to change to runas uid";
goto bad;
}
break;
case PERM_FULL_RUNAS:
/* headed for exec(), assume euid == ROOT_UID */
runas_setup();
if (setreuid(def_stay_setuid ? user_uid :
runas_pw->pw_uid, runas_pw->pw_uid)) {
errstr = "unable to change to runas uid";
goto bad;
}
break;
case PERM_SUDOERS:
/* assume euid == ROOT_UID, ruid == user */
if (setregid(-1, SUDOERS_GID))
error(1, "unable to change to sudoers gid");
/*
* If SUDOERS_UID == ROOT_UID and SUDOERS_MODE
* is group readable we use a non-zero
* uid in order to avoid NFS lossage.
* Using uid 1 is a bit bogus but should
* work on all OS's.
*/
if (SUDOERS_UID == ROOT_UID) {
if ((SUDOERS_MODE & 040) && setreuid(ROOT_UID, 1)) {
errstr = "setreuid(ROOT_UID, 1)";
goto bad;
}
} else {
if (setreuid(ROOT_UID, SUDOERS_UID)) {
errstr = "setreuid(ROOT_UID, SUDOERS_UID)";
goto bad;
}
}
break;
case PERM_TIMESTAMP:
if (setreuid(ROOT_UID, timestamp_uid)) {
errstr = "setreuid(ROOT_UID, timestamp_uid)";
goto bad;
}
break;
}
current_perm = perm;
return(1);
bad:
warningx("%s: %s", errstr,
errno == EAGAIN ? "too many processes" : strerror(errno));
if (noexit)
return(0);
exit(1);
}
# else /* !HAVE_SETRESUID && !HAVE_SETREUID */
# ifdef HAVE_SETEUID
/*
* Set real and effective uids and gids based on perm.
* NOTE: does not support the "stay_setuid" option.
*/
int
set_perms(perm)
int perm;
{
const char *errstr;
int noexit;
noexit = ISSET(perm, PERM_NOEXIT);
CLR(perm, PERM_MASK);
if (perm == current_perm)
return(1);
/*
* Since we only have setuid() and seteuid() and semantics
* for these calls differ on various systems, we set
* real and effective uids to ROOT_UID initially to be safe.
*/
if (seteuid(ROOT_UID)) {
errstr = "seteuid(ROOT_UID)";
goto bad;
}
if (setuid(ROOT_UID)) {
errstr = "setuid(ROOT_UID)";
goto bad;
}
switch (perm) {
case PERM_ROOT:
/* uid set above */
(void) setegid(user_gid);
if (current_perm == PERM_RUNAS)
restore_groups();
break;
case PERM_USER:
(void) setegid(user_gid);
if (seteuid(user_uid)) {
errstr = "seteuid(user_uid)";
goto bad;
}
break;
case PERM_FULL_USER:
/* headed for exec() */
(void) setgid(user_gid);
if (setuid(user_uid)) {
errstr = "setuid(user_uid)";
goto bad;
}
break;
case PERM_RUNAS:
runas_setgroups();
(void) setegid(runas_gr ?
runas_gr->gr_gid : runas_pw->pw_gid);
if (seteuid(runas_pw ? runas_pw->pw_uid : user_uid)) {
errstr = "unable to change to runas uid";
goto bad;
}
break;
case PERM_FULL_RUNAS:
/* headed for exec() */
runas_setup();
if (setuid(runas_pw->pw_uid)) {
errstr = "unable to change to runas uid";
goto bad;
}
break;
case PERM_SUDOERS:
if (setegid(SUDOERS_GID))
error(1, "unable to change to sudoers gid");
/*
* If SUDOERS_UID == ROOT_UID and SUDOERS_MODE
* is group readable we use a non-zero
* uid in order to avoid NFS lossage.
* Using uid 1 is a bit bogus but should
* work on all OS's.
*/
if (SUDOERS_UID == ROOT_UID) {
if ((SUDOERS_MODE & 040) && seteuid(1)) {
errstr = "seteuid(1)";
goto bad;
}
} else {
if (seteuid(SUDOERS_UID)) {
errstr = "seteuid(SUDOERS_UID)";
goto bad;
}
}
break;
case PERM_TIMESTAMP:
if (seteuid(timestamp_uid)) {
errstr = "seteuid(timestamp_uid)";
goto bad;
}
break;
}
current_perm = perm;
return(1);
bad:
warningx("%s: %s", errstr,
errno == EAGAIN ? "too many processes" : strerror(errno));
if (noexit)
return(0);
exit(1);
}
# else /* !HAVE_SETRESUID && !HAVE_SETREUID && !HAVE_SETEUID */
/*
* Set uids and gids based on perm via setuid() and setgid().
* NOTE: does not support the "stay_setuid" or timestampowner options.
* Also, SUDOERS_UID and SUDOERS_GID are not used.
*/
int
set_perms(perm)
int perm;
{
const char *errstr;
int noexit;
noexit = ISSET(perm, PERM_NOEXIT);
CLR(perm, PERM_MASK);
if (perm == current_perm)
return(1);
switch (perm) {
case PERM_ROOT:
if (setuid(ROOT_UID)) {
errstr = "setuid(ROOT_UID)";
goto bad;
}
if (current_perm == PERM_RUNAS)
restore_groups();
break;
case PERM_FULL_USER:
(void) setgid(user_gid);
if (setuid(user_uid)) {
errstr = "setuid(user_uid)";
goto bad;
}
break;
case PERM_FULL_RUNAS:
runas_setup();
if (setuid(runas_pw->pw_uid)) {
errstr = "unable to change to runas uid";
goto bad;
}
break;
case PERM_USER:
case PERM_SUDOERS:
case PERM_RUNAS:
case PERM_TIMESTAMP:
/* Unsupported since we can't set euid. */
break;
}
current_perm = perm;
return(1);
bad:
warningx("%s: %s", errstr,
errno == EAGAIN ? "too many processes" : strerror(errno));
if (noexit)
return(0);
exit(1);
}
# endif /* HAVE_SETEUID */
# endif /* HAVE_SETREUID */
#endif /* HAVE_SETRESUID */
#ifdef HAVE_INITGROUPS
static void
runas_setgroups()
{
static int ngroups = -1;
#ifdef HAVE_GETGROUPS
static GETGROUPS_T *groups;
#endif
struct passwd *pw;
if (def_preserve_groups)
return;
/*
* Use stashed copy of runas groups if available, else initgroups and stash.
*/
if (ngroups == -1) {
pw = runas_pw ? runas_pw : sudo_user.pw;
if (initgroups(pw->pw_name, pw->pw_gid) < 0)
log_error(USE_ERRNO|MSG_ONLY, "can't set runas group vector");
#ifdef HAVE_GETGROUPS
if ((ngroups = getgroups(0, NULL)) > 0) {
groups = emalloc2(ngroups, sizeof(GETGROUPS_T));
if (getgroups(ngroups, groups) < 0)
log_error(USE_ERRNO|MSG_ONLY, "can't get runas group vector");
}
} else {
if (setgroups(ngroups, groups) < 0)
log_error(USE_ERRNO|MSG_ONLY, "can't set runas group vector");
#endif /* HAVE_GETGROUPS */
}
}
static void
restore_groups()
{
if (user_groups >= 0 && setgroups(user_ngroups, user_groups) < 0)
log_error(USE_ERRNO|MSG_ONLY, "can't reset user group vector");
}
#else
static void
runas_setgroups()
{
/* STUB */
}
static void
restore_groups()
{
/* STUB */
}
#endif /* HAVE_INITGROUPS */
static void
runas_setup()
{
gid_t gid;
#ifdef HAVE_LOGIN_CAP_H
int flags;
extern login_cap_t *lc;
#endif
if (runas_pw->pw_name != NULL) {
gid = runas_gr ? runas_gr->gr_gid : runas_pw->pw_gid;
#ifdef HAVE_GETUSERATTR
aix_setlimits(runas_pw->pw_name);
#endif
#ifdef HAVE_PAM
pam_prep_user(runas_pw);
#endif /* HAVE_PAM */
#ifdef HAVE_LOGIN_CAP_H
if (def_use_loginclass) {
/*
* We only use setusercontext() to set the nice value and rlimits.
*/
flags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY;
if (setusercontext(lc, runas_pw, runas_pw->pw_uid, flags)) {
if (runas_pw->pw_uid != ROOT_UID)
error(1, "unable to set user context");
else
warning("unable to set user context");
}
}
#endif /* HAVE_LOGIN_CAP_H */
/*
* Initialize group vector
*/
runas_setgroups();
#ifdef HAVE_SETEUID
if (setegid(gid))
warning("cannot set egid to runas gid");
#endif
if (setgid(gid))
warning("cannot set gid to runas gid");
}
}

294
plugins/sudoers/sudo_nss.c Normal file
View File

@@ -0,0 +1,294 @@
/*
* Copyright (c) 2007-2009 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <pwd.h>
#include <grp.h>
#include <ctype.h>
#include "sudo.h"
#include "lbuf.h"
extern struct sudo_nss sudo_nss_file;
#ifdef HAVE_LDAP
extern struct sudo_nss sudo_nss_ldap;
#endif
#if defined(HAVE_LDAP) && defined(_PATH_NSSWITCH_CONF)
/*
* Read in /etc/nsswitch.conf
* Returns a tail queue of matches.
*/
struct sudo_nss_list *
sudo_read_nss()
{
FILE *fp;
char *cp;
int saw_files = FALSE;
int saw_ldap = FALSE;
int got_match = FALSE;
static struct sudo_nss_list snl;
if ((fp = fopen(_PATH_NSSWITCH_CONF, "r")) == NULL)
goto nomatch;
while ((cp = sudo_parseln(fp)) != NULL) {
/* Skip blank or comment lines */
if (*cp == '\0')
continue;
/* Look for a line starting with "sudoers:" */
if (strncasecmp(cp, "sudoers:", 8) != 0)
continue;
/* Parse line */
for ((cp = strtok(cp + 8, " \t")); cp != NULL; (cp = strtok(NULL, " \t"))) {
if (strcasecmp(cp, "files") == 0 && !saw_files) {
tq_append(&snl, &sudo_nss_file);
got_match = TRUE;
} else if (strcasecmp(cp, "ldap") == 0 && !saw_ldap) {
tq_append(&snl, &sudo_nss_ldap);
got_match = TRUE;
} else if (strcasecmp(cp, "[NOTFOUND=return]") == 0 && got_match) {
/* NOTFOUND affects the most recent entry */
tq_last(&snl)->ret_if_notfound = TRUE;
got_match = FALSE;
} else
got_match = FALSE;
}
/* Only parse the first "sudoers:" line */
break;
}
fclose(fp);
nomatch:
/* Default to files only if no matches */
if (tq_empty(&snl))
tq_append(&snl, &sudo_nss_file);
return(&snl);
}
#else /* HAVE_LDAP && _PATH_NSSWITCH_CONF */
# if defined(HAVE_LDAP) && defined(_PATH_NETSVC_CONF)
/*
* Read in /etc/netsvc.conf (like nsswitch.conf on AIX)
* Returns a tail queue of matches.
*/
struct sudo_nss_list *
sudo_read_nss()
{
FILE *fp;
char *cp, *ep;
int saw_files = FALSE;
int saw_ldap = FALSE;
int got_match = FALSE;
static struct sudo_nss_list snl;
if ((fp = fopen(_PATH_NETSVC_CONF, "r")) == NULL)
goto nomatch;
while ((cp = sudo_parseln(fp)) != NULL) {
/* Skip blank or comment lines */
if (*cp == '\0')
continue;
/* Look for a line starting with "sudoers = " */
if (strncasecmp(cp, "sudoers", 7) != 0)
continue;
cp += 7;
while (isspace((unsigned char)*cp))
cp++;
if (*cp++ != '=')
continue;
/* Parse line */
for ((cp = strtok(cp, ",")); cp != NULL; (cp = strtok(NULL, ","))) {
/* Trim leading whitespace. */
while (isspace((unsigned char)*cp))
cp++;
if (!saw_files && strncasecmp(cp, "files", 5) == 0 &&
(isspace((unsigned char)cp[5]) || cp[5] == '\0')) {
tq_append(&snl, &sudo_nss_file);
got_match = TRUE;
ep = &cp[5];
} else if (!saw_ldap && strncasecmp(cp, "ldap", 4) == 0 &&
(isspace((unsigned char)cp[4]) || cp[4] == '\0')) {
tq_append(&snl, &sudo_nss_ldap);
got_match = TRUE;
ep = &cp[4];
} else {
got_match = FALSE;
}
/* check for = auth qualifier */
if (got_match && *ep) {
cp = ep;
while (isspace((unsigned char)*cp) || *cp == '=')
cp++;
if (strncasecmp(cp, "auth", 4) == 0 &&
(isspace((unsigned char)cp[4]) || cp[4] == '\0')) {
tq_last(&snl)->ret_if_found = TRUE;
}
}
}
/* Only parse the first "sudoers" line */
break;
}
fclose(fp);
nomatch:
/* Default to files only if no matches */
if (tq_empty(&snl))
tq_append(&snl, &sudo_nss_file);
return(&snl);
}
# else /* !_PATH_NETSVC_CONF && !_PATH_NSSWITCH_CONF */
/*
* Non-nsswitch.conf version with hard-coded order.
*/
struct sudo_nss_list *
sudo_read_nss()
{
static struct sudo_nss_list snl;
# ifdef HAVE_LDAP
tq_append(&snl, &sudo_nss_ldap);
# endif
tq_append(&snl, &sudo_nss_file);
return(&snl);
}
# endif /* !HAVE_LDAP || !_PATH_NETSVC_CONF */
#endif /* HAVE_LDAP && _PATH_NSSWITCH_CONF */
/* Reset user_groups based on passwd entry. */
static void
reset_groups(pw)
struct passwd *pw;
{
#if defined(HAVE_INITGROUPS) && defined(HAVE_GETGROUPS)
if (pw != sudo_user.pw) {
(void) initgroups(pw->pw_name, pw->pw_gid);
efree(user_groups);
user_groups = NULL;
if ((user_ngroups = getgroups(0, NULL)) > 0) {
user_groups = emalloc2(user_ngroups, sizeof(GETGROUPS_T));
if (getgroups(user_ngroups, user_groups) < 0)
log_error(USE_ERRNO|MSG_ONLY, "can't get group vector");
}
}
#endif
}
/*
* Print out privileges for the specified user.
* We only get here if the user is allowed to run something on this host.
*/
void
display_privs(snl, pw)
struct sudo_nss_list *snl;
struct passwd *pw;
{
struct sudo_nss *nss;
struct lbuf lbuf;
int count;
/* Reset group vector so group matching works correctly. */
reset_groups(pw);
lbuf_init(&lbuf, NULL, 4, 0);
/* Display defaults from all sources. */
count = 0;
tq_foreach_fwd(snl, nss)
count += nss->display_defaults(nss, pw, &lbuf);
if (count) {
printf("Matching Defaults entries for %s on this host:\n", pw->pw_name);
lbuf_print(&lbuf);
putchar('\n');
}
/* Display Runas and Cmnd-specific defaults from all sources. */
count = 0;
tq_foreach_fwd(snl, nss)
count += nss->display_bound_defaults(nss, pw, &lbuf);
if (count) {
printf("Runas and Command-specific defaults for %s:\n", pw->pw_name);
lbuf_print(&lbuf);
putchar('\n');
}
/* Display privileges from all sources. */
printf("User %s may run the following commands on this host:\n",
pw->pw_name);
tq_foreach_fwd(snl, nss)
(void) nss->display_privs(nss, pw, &lbuf);
if (lbuf.len != 0)
lbuf_print(&lbuf); /* print remainder, if any */
lbuf_destroy(&lbuf);
}
/*
* Check user_cmnd against sudoers and print the matching entry if the
* command is allowed.
*/
int
display_cmnd(snl, pw)
struct sudo_nss_list *snl;
struct passwd *pw;
{
struct sudo_nss *nss;
/* Reset group vector so group matching works correctly. */
reset_groups(pw);
tq_foreach_fwd(snl, nss) {
if (nss->display_cmnd(nss, pw) == 0)
return(0);
}
return(1);
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2007-2009 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
struct lbuf;
struct passwd;
struct sudo_nss {
struct sudo_nss *prev;
struct sudo_nss *next;
int (*open) __P((struct sudo_nss *nss));
int (*close) __P((struct sudo_nss *nss));
int (*parse) __P((struct sudo_nss *nss));
int (*setdefs) __P((struct sudo_nss *nss));
int (*lookup) __P((struct sudo_nss *nss, int, int));
int (*display_cmnd) __P((struct sudo_nss *nss, struct passwd *));
int (*display_defaults) __P((struct sudo_nss *nss, struct passwd *, struct lbuf *));
int (*display_bound_defaults) __P((struct sudo_nss *nss, struct passwd *, struct lbuf *));
int (*display_privs) __P((struct sudo_nss *nss, struct passwd *, struct lbuf *));
void *handle;
short ret_if_found;
short ret_if_notfound;
};
TQ_DECLARE(sudo_nss)
struct sudo_nss_list *sudo_read_nss __P((void));

31
plugins/sudoers/sudoers Normal file
View File

@@ -0,0 +1,31 @@
# sudoers file.
#
# This file MUST be edited with the 'visudo' command as root.
# Failure to use 'visudo' may result in syntax or file permission errors
# that prevent sudo from running.
#
# See the sudoers man page for the details on how to write a sudoers file.
#
# Host alias specification
# User alias specification
# Cmnd alias specification
# Defaults specification
# Runas alias specification
# User privilege specification
root ALL=(ALL) ALL
# Uncomment to allow people in group wheel to run all commands
# %wheel ALL=(ALL) ALL
# Same thing without a password
# %wheel ALL=(ALL) NOPASSWD: ALL
# Samples
# %users ALL=/sbin/mount /cdrom,/sbin/umount /cdrom
# %users localhost=/sbin/shutdown -h now

1557
plugins/sudoers/sudoers.c Normal file

File diff suppressed because it is too large Load Diff

304
plugins/sudoers/sudoers.h Normal file
View File

@@ -0,0 +1,304 @@
/*
* Copyright (c) 1993-1996, 1998-2005, 2007-2009
* Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#ifndef _SUDO_SUDO_H
#define _SUDO_SUDO_H
#include <pathnames.h>
#include <limits.h>
#include "compat.h"
#include "alloc.h"
#include "defaults.h"
#include "error.h"
#include "list.h"
#include "logging.h"
#include "missing.h"
#include "sudo_nss.h"
#ifdef HAVE_MBR_CHECK_MEMBERSHIP
# include <membership.h>
#endif
/*
* Info pertaining to the invoking user.
*/
struct sudo_user {
struct passwd *pw;
struct passwd *_runas_pw;
struct group *_runas_gr;
struct stat *cmnd_stat;
char *path;
char *shell;
char *tty;
char *ttypath;
char *host;
char *shost;
char *prompt;
char *cmnd;
char *cmnd_args;
char *cmnd_base;
char *cmnd_safe;
char *class_name;
char *krb5_ccname;
char *display;
char *askpass;
int ngroups;
GETGROUPS_T *groups;
struct list_member *env_vars;
#ifdef HAVE_SELINUX
char *role;
char *type;
#endif
char cwd[PATH_MAX];
char sessid[7];
#ifdef HAVE_MBR_CHECK_MEMBERSHIP
uuid_t uuid;
#endif
};
/*
* Return values for sudoers_lookup(), also used as arguments for log_auth()
* Note: cannot use '0' as a value here.
*/
/* XXX - VALIDATE_SUCCESS and VALIDATE_FAILURE instead? */
#define VALIDATE_ERROR 0x001
#define VALIDATE_OK 0x002
#define VALIDATE_NOT_OK 0x004
#define FLAG_CHECK_USER 0x010
#define FLAG_NO_USER 0x020
#define FLAG_NO_HOST 0x040
#define FLAG_NO_CHECK 0x080
/*
* Pseudo-boolean values
*/
#undef TRUE
#define TRUE 1
#undef FALSE
#define FALSE 0
/*
* find_path()/load_cmnd() return values
*/
#define FOUND 1
#define NOT_FOUND 0
#define NOT_FOUND_DOT -1
/*
* Various modes sudo can be in (based on arguments) in hex
*/
#define MODE_RUN 0x00000001
#define MODE_EDIT 0x00000002
#define MODE_VALIDATE 0x00000004
#define MODE_INVALIDATE 0x00000008
#define MODE_KILL 0x00000010
#define MODE_VERSION 0x00000020
#define MODE_HELP 0x00000040
#define MODE_LIST 0x00000080
#define MODE_CHECK 0x00000100
#define MODE_LISTDEFS 0x00000200
#define MODE_MASK 0x0000ffff
/* Mode flags */
#define MODE_BACKGROUND 0x00010000
#define MODE_SHELL 0x00020000
#define MODE_LOGIN_SHELL 0x00040000
#define MODE_IMPLIED_SHELL 0x00080000
#define MODE_RESET_HOME 0x00100000
#define MODE_PRESERVE_GROUPS 0x00200000
#define MODE_PRESERVE_ENV 0x00400000
#define MODE_NONINTERACTIVE 0x00800000
/*
* Used with set_perms()
*/
#define PERM_ROOT 0x00
#define PERM_USER 0x01
#define PERM_FULL_USER 0x02
#define PERM_SUDOERS 0x03
#define PERM_RUNAS 0x04
#define PERM_FULL_RUNAS 0x05
#define PERM_TIMESTAMP 0x06
#define PERM_NOEXIT 0x10 /* flag */
#define PERM_MASK 0xf0
/*
* Shortcuts for sudo_user contents.
*/
#define user_name (sudo_user.pw->pw_name)
#define user_passwd (sudo_user.pw->pw_passwd)
#define user_uid (sudo_user.pw->pw_uid)
#define user_uuid (sudo_user.uuid)
#define user_gid (sudo_user.pw->pw_gid)
#define user_dir (sudo_user.pw->pw_dir)
#define user_shell (sudo_user.shell)
#define user_ngroups (sudo_user.ngroups)
#define user_groups (sudo_user.groups)
#define user_tty (sudo_user.tty)
#define user_ttypath (sudo_user.ttypath)
#define user_cwd (sudo_user.cwd)
#define user_cmnd (sudo_user.cmnd)
#define user_args (sudo_user.cmnd_args)
#define user_base (sudo_user.cmnd_base)
#define user_stat (sudo_user.cmnd_stat)
#define user_path (sudo_user.path)
#define user_prompt (sudo_user.prompt)
#define user_host (sudo_user.host)
#define user_shost (sudo_user.shost)
#define user_ccname (sudo_user.krb5_ccname)
#define user_display (sudo_user.display)
#define user_askpass (sudo_user.askpass)
#define safe_cmnd (sudo_user.cmnd_safe)
#define login_class (sudo_user.class_name)
#define runas_pw (sudo_user._runas_pw)
#define runas_gr (sudo_user._runas_gr)
#define user_role (sudo_user.role)
#define user_type (sudo_user.type)
/*
* We used to use the system definition of PASS_MAX or _PASSWD_LEN,
* but that caused problems with various alternate authentication
* methods. So, we just define our own and assume that it is >= the
* system max.
*/
#define SUDO_PASS_MAX 256
/*
* Flags for lock_file()
*/
#define SUDO_LOCK 1 /* lock a file */
#define SUDO_TLOCK 2 /* test & lock a file (non-blocking) */
#define SUDO_UNLOCK 4 /* unlock a file */
/*
* Flags for tgetpass()
*/
#define TGP_ECHO 0x01 /* leave echo on when reading passwd */
#define TGP_STDIN 0x02 /* read from stdin, not /dev/tty */
#define TGP_ASKPASS 0x04 /* read from askpass helper program */
struct lbuf;
struct passwd;
/*
* Function prototypes
*/
#define YY_DECL int yylex __P((void))
char *sudo_goodpath __P((const char *, struct stat *));
char *tgetpass __P((const char *, int, int));
int find_path __P((char *, char **, struct stat *, char *));
int tty_present __P((void));
void check_user __P((int, int));
void verify_user __P((struct passwd *, char *));
#ifdef HAVE_LDAP
int sudo_ldap_open __P((struct sudo_nss *));
int sudo_ldap_close __P((struct sudo_nss *));
int sudo_ldap_setdefs __P((struct sudo_nss *));
int sudo_ldap_lookup __P((struct sudo_nss *, int, int));
int sudo_ldap_parse __P((struct sudo_nss *));
int sudo_ldap_display_cmnd __P((struct sudo_nss *, struct passwd *));
int sudo_ldap_display_defaults __P((struct sudo_nss *, struct passwd *, struct lbuf *));
int sudo_ldap_display_bound_defaults __P((struct sudo_nss *, struct passwd *, struct lbuf *));
int sudo_ldap_display_privs __P((struct sudo_nss *, struct passwd *, struct lbuf *));
#endif
int sudo_file_open __P((struct sudo_nss *));
int sudo_file_close __P((struct sudo_nss *));
int sudo_file_setdefs __P((struct sudo_nss *));
int sudo_file_lookup __P((struct sudo_nss *, int, int));
int sudo_file_parse __P((struct sudo_nss *));
int sudo_file_display_cmnd __P((struct sudo_nss *, struct passwd *));
int sudo_file_display_defaults __P((struct sudo_nss *, struct passwd *, struct lbuf *));
int sudo_file_display_bound_defaults __P((struct sudo_nss *, struct passwd *, struct lbuf *));
int sudo_file_display_privs __P((struct sudo_nss *, struct passwd *, struct lbuf *));
int set_perms __P((int));
void remove_timestamp __P((int));
int check_secureware __P((char *));
void sia_attempt_auth __P((void));
void pam_attempt_auth __P((void));
int yyparse __P((void));
void pass_warn __P((FILE *));
void dump_defaults __P((void));
void dump_auth_methods __P((void));
void init_envtables __P((void));
void read_env_file __P((const char *, int));
int lock_file __P((int, int));
int touch __P((int, char *, struct timespec *));
int user_is_exempt __P((void));
void set_fqdn __P((void));
char *sudo_getepw __P((const struct passwd *));
int pam_prep_user __P((struct passwd *));
void zero_bytes __P((volatile void *, size_t));
int gettime __P((struct timespec *));
FILE *open_sudoers __P((const char *, int, int *));
void display_privs __P((struct sudo_nss_list *, struct passwd *));
int display_cmnd __P((struct sudo_nss_list *, struct passwd *));
int get_ttycols __P((void));
char *sudo_parseln __P((FILE *));
void sudo_setgrent __P((void));
void sudo_endgrent __P((void));
void sudo_setpwent __P((void));
void sudo_endpwent __P((void));
void sudo_setspent __P((void));
void sudo_endspent __P((void));
void cleanup __P((int));
struct passwd *sudo_getpwnam __P((const char *));
struct passwd *sudo_fakepwnam __P((const char *, gid_t));
struct passwd *sudo_getpwuid __P((uid_t));
struct group *sudo_getgrnam __P((const char *));
struct group *sudo_fakegrnam __P((const char *));
struct group *sudo_getgrgid __P((gid_t));
#ifdef HAVE_SELINUX
void selinux_exec __P((char *, char *, char **));
void selinux_execv __P((char *, char **));
void selinux_prefork __P((char *, char *, int));
#endif
#ifdef HAVE_GETUSERATTR
void aix_setlimits __P((char *));
#endif
int script_duplow __P((int));
int script_execv __P((char *, char **));
void script_nextid __P((void));
void script_setup __P((void));
int term_cbreak __P((int));
int term_copy __P((int, int, int));
int term_noecho __P((int));
int term_raw __P((int, int, int));
int term_restore __P((int, int));
char *get_timestr __P((time_t, int));
time_t get_boottime __P((void));
int user_in_group __P((struct passwd *, const char *));
YY_DECL;
/* Only provide extern declarations outside of sudo.c. */
#ifndef _SUDO_MAIN
extern struct sudo_user sudo_user;
extern struct passwd *auth_pw, *list_pw;
extern int tgetpass_flags;
extern int long_list;
extern uid_t timestamp_uid;
#endif
#ifndef errno
extern int errno;
#endif
#endif /* _SUDO_SUDO_H */

View File

@@ -0,0 +1,132 @@
#!/usr/bin/env perl
use strict;
#
# Converts a sudoers file to LDIF format in prepration for loading into
# the LDAP server.
#
# BUGS:
# Does not yet handle multiple lines with : in them
# Does not yet remove quotation marks from options
# Does not yet escape + at the beginning of a dn
# Does not yet handle line wraps correctly
# Does not yet handle multiple roles with same name (needs tiebreaker)
#
# CAVEATS:
# Sudoers entries can have multiple RunAs entries that override former ones,
# with LDAP sudoRunAs{Group,User} applies to all commands in a sudoRole
my %RA;
my %UA;
my %HA;
my %CA;
my $base=$ENV{SUDOERS_BASE} or die "$0: Container SUDOERS_BASE undefined\n";
my @options=();
my $did_defaults=0;
# parse sudoers one line at a time
while (<>){
# remove comment
s/#.*//;
# line continuation
$_.=<> while s/\\\s*$//s;
# cleanup newline
chomp;
# ignore blank lines
next if /^\s*$/;
if (/^Defaults\s+/i) {
my $opt=$';
$opt=~s/\s+$//; # remove trailing whitespace
push @options,$opt;
} elsif (/^(\S+)\s+(.+)=\s*(.*)/) {
# Aliases or Definitions
my ($p1,$p2,$p3)=($1,$2,$3);
$p2=~s/\s+$//; # remove trailing whitespace
$p3=~s/\s+$//; # remove trailing whitespace
if ($p1 eq "User_Alias") {
$UA{$p2}=$p3;
} elsif ($p1 eq "Runas_Alias") {
$RA{$p2}=$p3;
} elsif ($p1 eq "Host_Alias") {
$HA{$p2}=$p3;
} elsif ($p1 eq "Cmnd_Alias") {
$CA{$p2}=$p3;
} else {
if (!$did_defaults++){
# do this once
print "dn: cn=defaults,$base\n";
print "objectClass: top\n";
print "objectClass: sudoRole\n";
print "cn: defaults\n";
print "description: Default sudoOption's go here\n";
print "sudoOption: $_\n" foreach @options;
print "\n";
}
# Definition
my @users=split /\s*,\s*/,$p1;
my @hosts=split /\s*,\s*/,$p2;
my @cmds= split /\s*,\s*/,$p3;
@options=();
print "dn: cn=$users[0],$base\n";
print "objectClass: top\n";
print "objectClass: sudoRole\n";
print "cn: $users[0]\n";
# will clobber options
print "sudoUser: $_\n" foreach expand(\%UA,@users);
print "sudoHost: $_\n" foreach expand(\%HA,@hosts);
foreach (@cmds) {
if (s/^\(([^\)]+)\)\s*//) {
my @runas = split(/:\s*/, $1);
if (defined($runas[0])) {
print "sudoRunAsUser: $_\n" foreach expand(\%RA, split(/,\s*/, $runas[0]));
}
if (defined($runas[1])) {
print "sudoRunAsGroup: $_\n" foreach expand(\%RA, split(/,\s*/, $runas[1]));
}
}
}
print "sudoCommand: $_\n" foreach expand(\%CA,@cmds);
print "sudoOption: $_\n" foreach @options;
print "\n";
}
} else {
print "parse error: $_\n";
}
}
#
# recursively expand hash elements
sub expand{
my $ref=shift;
my @a=();
# preen the line a little
foreach (@_){
# if NOPASSWD: directive found, mark entire entry as not requiring
s/NOPASSWD:\s*// && push @options,"!authenticate";
s/PASSWD:\s*// && push @options,"authenticate";
s/NOEXEC:\s*// && push @options,"noexec";
s/EXEC:\s*// && push @options,"!noexec";
s/SETENV:\s*// && push @options,"setenv";
s/NOSETENV:\s*// && push @options,"!setenv";
s/\w+://; # silently remove other directives
s/\s+$//; # right trim
}
# do the expanding
push @a,$ref->{$_} ? expand($ref,split /\s*,\s*/,$ref->{$_}):$_ foreach @_;
@a;
}

View File

@@ -0,0 +1,827 @@
/*
* Copyright (c) 2009 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif /* HAVE_SYS_SELECT_H */
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
# include <memory.h>
# endif
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#if TIME_WITH_SYS_TIME
# include <time.h>
#endif
#ifndef HAVE_TIMESPEC
# include <emul/timespec.h>
#endif
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <fcntl.h>
#ifdef HAVE_DIRENT_H
# include <dirent.h>
# define NAMLEN(dirent) strlen((dirent)->d_name)
#else
# define dirent direct
# define NAMLEN(dirent) (dirent)->d_namlen
# ifdef HAVE_SYS_NDIR_H
# include <sys/ndir.h>
# endif
# ifdef HAVE_SYS_DIR_H
# include <sys/dir.h>
# endif
# ifdef HAVE_NDIR_H
# include <ndir.h>
# endif
#endif
#ifdef HAVE_REGCOMP
# include <regex.h>
#endif
#ifdef HAVE_ZLIB
# include <zlib.h>
#endif
#include <signal.h>
#include <pathnames.h>
#include "compat.h"
#include "alloc.h"
#include "error.h"
#include "missing.h"
/* For getopt(3) */
extern char *optarg;
extern int optind;
int Argc;
char **Argv;
const char *session_dir = _PATH_SUDO_TRANSCRIPT;
/*
* Info present in the transcript log file
*/
struct log_info {
char *cwd;
char *user;
char *runas_user;
char *runas_group;
char *tty;
char *cmd;
time_t tstamp;
};
/*
* Handle expressions like:
* ( user millert or user root ) and tty console and command /bin/sh
*/
struct search_node {
struct search_node *next;
#define ST_EXPR 1
#define ST_TTY 2
#define ST_USER 3
#define ST_PATTERN 4
#define ST_RUNASUSER 5
#define ST_RUNASGROUP 6
#define ST_FROMDATE 7
#define ST_TODATE 8
#define ST_CWD 9
char type;
char negated;
char or;
char pad;
union {
#ifdef HAVE_REGCOMP
regex_t cmdre;
#endif
time_t tstamp;
char *cwd;
char *tty;
char *user;
char *pattern;
char *runas_group;
char *runas_user;
struct search_node *expr;
void *ptr;
} u;
} *search_expr;
#define STACK_NODE_SIZE 32
static struct search_node *node_stack[32];
static int stack_top;
extern time_t get_date __P((char *));
extern char *get_timestr __P((time_t, int));
extern int term_raw __P((int, int, int));
extern int term_restore __P((int, int));
extern void zero_bytes __P((volatile void *, size_t));
void cleanup __P((int));
static int list_sessions __P((int, char **, const char *, const char *, const char *));
static int parse_expr __P((struct search_node **, char **));
static void check_input __P((int, double *));
static void delay __P((double));
static void usage __P((void));
#ifdef HAVE_REGCOMP
# define REGEX_T regex_t
#else
# define REGEX_T char
#endif
#define VALID_ID(s) (isalnum((s)[0]) && isalnum((s)[1]) && isalnum((s)[2]) && \
isalnum((s)[3]) && isalnum((s)[4]) && isalnum((s)[5]) && (s)[6] == '\0')
int
main(argc, argv)
int argc;
char **argv;
{
int ch, plen, nready, interactive = 0, listonly = 0;
const char *id, *user = NULL, *pattern = NULL, *tty = NULL;
char path[PATH_MAX], buf[LINE_MAX], *cp, *ep;
FILE *lfile;
#ifdef HAVE_ZLIB
gzFile tfile, sfile;
#else
FILE *tfile, *sfile;
#endif
fd_set *fdsr;
sigaction_t sa;
unsigned long nbytes;
size_t len, nread;
ssize_t nwritten;
double seconds;
double speed = 1.0;
double max_wait = 0;
double to_wait;
Argc = argc;
Argv = argv;
while ((ch = getopt(argc, argv, "d:lm:s:V")) != -1) {
switch(ch) {
case 'd':
session_dir = optarg;
break;
case 'l':
listonly = 1;
break;
case 'm':
errno = 0;
max_wait = strtod(optarg, &ep);
if (*ep != '\0' || errno != 0)
error(1, "invalid max wait: %s", optarg);
break;
case 's':
errno = 0;
speed = strtod(optarg, &ep);
if (*ep != '\0' || errno != 0)
error(1, "invalid speed factor: %s", optarg);
break;
case 'V':
(void) printf("%s version %s\n", getprogname(), PACKAGE_VERSION);
exit(0);
default:
usage();
/* NOTREACHED */
}
}
argc -= optind;
argv += optind;
if (listonly)
exit(list_sessions(argc, argv, pattern, user, tty));
if (argc != 1)
usage();
/* 6 digit ID in base 36, e.g. 01G712AB */
id = argv[0];
if (!VALID_ID(id))
errorx(1, "invalid ID %s", id);
plen = snprintf(path, sizeof(path), "%s/%.2s/%.2s/%.2s.tim",
session_dir, id, &id[2], &id[4]);
if (plen <= 0 || plen >= sizeof(path))
errorx(1, "%s/%.2s/%.2s/%.2s/%.2s.tim: %s", session_dir,
id, &id[2], &id[4], strerror(ENAMETOOLONG));
/* timing file */
#ifdef HAVE_ZLIB
tfile = gzopen(path, "r");
#else
tfile = fopen(path, "r");
#endif
if (tfile == NULL)
error(1, "unable to open %s", path);
/* script file */
memcpy(&path[plen - 3], "scr", 3);
#ifdef HAVE_ZLIB
sfile = gzopen(path, "r");
#else
sfile = fopen(path, "r");
#endif
if (sfile == NULL)
error(1, "unable to open %s", path);
/* log file */
path[plen - 4] = '\0';
lfile = fopen(path, "r");
if (lfile == NULL)
error(1, "unable to open %s", path);
cp = NULL;
getline(&cp, &len, lfile); /* log */
getline(&cp, &len, lfile); /* cwd */
getline(&cp, &len, lfile); /* command */
printf("Replaying sudo session: %s", cp);
free(cp);
fclose(lfile);
fflush(stdout);
zero_bytes(&sa, sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESETHAND;
sa.sa_handler = cleanup;
(void) sigaction(SIGINT, &sa, NULL);
(void) sigaction(SIGKILL, &sa, NULL);
(void) sigaction(SIGTERM, &sa, NULL);
(void) sigaction(SIGHUP, &sa, NULL);
sa.sa_flags = SA_RESTART;
sa.sa_handler = SIG_IGN;
(void) sigaction(SIGTSTP, &sa, NULL);
(void) sigaction(SIGQUIT, &sa, NULL);
/* Set stdin to raw mode if it is a tty */
interactive = isatty(STDIN_FILENO);
if (interactive) {
ch = fcntl(STDIN_FILENO, F_GETFL, 0);
if (ch != -1)
(void) fcntl(STDIN_FILENO, F_SETFL, ch | O_NONBLOCK);
if (!term_raw(STDIN_FILENO, 0, 1))
error(1, "cannot set tty to raw mode");
}
fdsr = (fd_set *)emalloc2(howmany(STDOUT_FILENO + 1, NFDBITS),
sizeof(fd_mask));
/*
* Timing file consists of line of the format: "%f %d\n"
*/
#ifdef HAVE_ZLIB
while (gzgets(tfile, buf, sizeof(buf)) != NULL) {
#else
while (fgets(buf, sizeof(buf), tfile) != NULL) {
#endif
errno = 0;
seconds = strtod(buf, &ep);
if (errno != 0 || !isspace((unsigned char) *ep))
error(1, "invalid timing file line: %s", buf);
for (cp = ep + 1; isspace((unsigned char) *cp); cp++)
continue;
errno = 0;
nbytes = strtoul(cp, &ep, 10);
if (errno == ERANGE && nbytes == ULONG_MAX)
error(1, "invalid timing file byte count: %s", cp);
if (interactive)
check_input(STDIN_FILENO, &speed);
/* Adjust delay using speed factor and clamp to max_wait */
to_wait = seconds / speed;
if (max_wait && to_wait > max_wait)
to_wait = max_wait;
delay(to_wait);
while (nbytes != 0) {
if (nbytes > sizeof(buf))
len = sizeof(buf);
else
len = nbytes;
#ifdef HAVE_ZLIB
nread = gzread(sfile, buf, len);
#else
nread = fread(buf, 1, len, sfile);
#endif
nbytes -= nread;
do {
/* no stdio, must be unbuffered */
nwritten = write(STDOUT_FILENO, buf, nread);
if (nwritten == -1) {
if (errno == EINTR)
continue;
if (errno == EAGAIN) {
FD_SET(STDOUT_FILENO, fdsr);
do {
nready = select(STDOUT_FILENO + 1, fdsr, NULL, NULL, NULL);
} while (nready == -1 && errno == EINTR);
if (nready == 1)
continue;
}
error(1, "writing to standard output");
}
nread -= nwritten;
} while (nread);
}
}
term_restore(STDOUT_FILENO, 0);
exit(0);
}
static void
delay(secs)
double secs;
{
struct timespec ts, rts;
int rval;
/*
* Typical max resolution is 1/HZ but we can't portably check that.
* If the interval is small enough, just ignore it.
*/
if (secs < 0.0001)
return;
rts.tv_sec = secs;
rts.tv_nsec = (secs - (double) rts.tv_sec) * 1000000000.0;
do {
memcpy(&ts, &rts, sizeof(ts));
rval = nanosleep(&ts, &rts);
} while (rval == -1 && errno == EINTR);
if (rval == -1)
error(1, "nanosleep: tv_sec %ld, tv_nsec %ld", ts.tv_sec, ts.tv_nsec);
}
/*
* Build expression list from search args
*/
static int
parse_expr(headp, argv)
struct search_node **headp;
char **argv;
{
struct search_node *sn, *newsn;
char or = 0, not = 0, type, **av;
sn = *headp;
for (av = argv; *av; av++) {
switch (av[0][0]) {
case 'a': /* and (ignore) */
if (strncmp(*av, "and", strlen(*av)) != 0)
goto bad;
continue;
case 'o': /* or */
if (strncmp(*av, "or", strlen(*av)) != 0)
goto bad;
or = 1;
continue;
case '!': /* negate */
if (av[0][1] != '\0')
goto bad;
not = 1;
continue;
case 'c': /* command */
if (av[0][1] == '\0')
errorx(1, "ambiguous expression \"%s\"", *av);
if (strncmp(*av, "cwd", strlen(*av)) == 0)
type = ST_CWD;
else if (strncmp(*av, "command", strlen(*av)) == 0)
type = ST_PATTERN;
else
goto bad;
break;
case 'f': /* from date */
if (strncmp(*av, "fromdate", strlen(*av)) != 0)
goto bad;
type = ST_FROMDATE;
break;
case 'g': /* runas group */
if (strncmp(*av, "group", strlen(*av)) != 0)
goto bad;
type = ST_RUNASGROUP;
break;
case 'r': /* runas user */
if (strncmp(*av, "runas", strlen(*av)) != 0)
goto bad;
type = ST_RUNASUSER;
break;
case 't': /* tty or to date */
if (av[0][1] == '\0')
errorx(1, "ambiguous expression \"%s\"", *av);
if (strncmp(*av, "todate", strlen(*av)) == 0)
type = ST_TODATE;
else if (strncmp(*av, "tty", strlen(*av)) == 0)
type = ST_TTY;
else
goto bad;
break;
case 'u': /* user */
if (strncmp(*av, "user", strlen(*av)) != 0)
goto bad;
type = ST_USER;
break;
case '(': /* start sub-expression */
if (av[0][1] != '\0')
goto bad;
if (stack_top + 1 == STACK_NODE_SIZE) {
errorx(1, "too many parenthesized expressions, max %d",
STACK_NODE_SIZE);
}
node_stack[stack_top++] = sn;
type = ST_EXPR;
break;
case ')': /* end sub-expression */
if (av[0][1] != '\0')
goto bad;
/* pop */
if (--stack_top < 0)
errorx(1, "unmatched ')' in expression");
if (node_stack[stack_top])
sn->next = node_stack[stack_top]->next;
return(av - argv + 1);
bad:
default:
errorx(1, "unknown search term \"%s\"", *av);
/* NOTREACHED */
}
/* Allocate new search node */
newsn = emalloc(sizeof(*newsn));
newsn->next = NULL;
newsn->type = type;
newsn->or = or;
newsn->negated = not;
if (type == ST_EXPR) {
av += parse_expr(&newsn->u.expr, av + 1);
} else {
if (*(++av) == NULL)
errorx(1, "%s requires an argument", av[-1]);
#ifdef HAVE_REGCOMP
if (type == ST_PATTERN) {
if (regcomp(&newsn->u.cmdre, *av, REG_EXTENDED|REG_NOSUB) != 0)
errorx(1, "invalid regex: %s", *av);
} else
#endif
if (type == ST_TODATE || type == ST_FROMDATE) {
newsn->u.tstamp = get_date(*av);
if (newsn->u.tstamp == -1)
errorx(1, "could not parse date \"%s\"", *av);
} else {
newsn->u.ptr = *av;
}
}
not = or = 0; /* reset state */
if (sn)
sn->next = newsn;
else
*headp = newsn;
sn = newsn;
}
if (stack_top)
errorx(1, "unmatched '(' in expression");
if (or)
errorx(1, "illegal trailing \"or\"");
if (not)
errorx(1, "illegal trailing \"!\"");
return(av - argv);
}
static int
match_expr(head, log)
struct search_node *head;
struct log_info *log;
{
struct search_node *sn;
int matched = 1, rc;
for (sn = head; sn; sn = sn->next) {
/* If we have no match, skip ahead to the next OR entry. */
if (!matched && !sn->or)
continue;
switch (sn->type) {
case ST_EXPR:
matched = match_expr(sn->u.expr, log);
break;
case ST_CWD:
matched = strcmp(sn->u.cwd, log->cwd) == 0;
break;
case ST_TTY:
matched = strcmp(sn->u.tty, log->tty) == 0;
break;
case ST_RUNASGROUP:
matched = strcmp(sn->u.runas_group, log->runas_group) == 0;
break;
case ST_RUNASUSER:
matched = strcmp(sn->u.runas_user, log->runas_user) == 0;
break;
case ST_USER:
matched = strcmp(sn->u.user, log->user) == 0;
break;
case ST_PATTERN:
#ifdef HAVE_REGCOMP
rc = regexec(&sn->u.cmdre, log->cmd, 0, NULL, 0);
if (rc && rc != REG_NOMATCH) {
char buf[BUFSIZ];
regerror(rc, &sn->u.cmdre, buf, sizeof(buf));
errorx(1, "%s", buf);
}
matched = rc == REG_NOMATCH ? 0 : 1;
#else
matched = strstr(log.cmd, sn->u.pattern) != NULL;
#endif
break;
case ST_FROMDATE:
matched = log->tstamp >= sn->u.tstamp;
break;
case ST_TODATE:
matched = log->tstamp <= sn->u.tstamp;
break;
}
if (sn->negated)
matched = !matched;
}
return(matched);
}
static int
list_session_dir(pathbuf, re, user, tty)
char *pathbuf;
REGEX_T *re;
const char *user;
const char *tty;
{
FILE *fp;
DIR *d;
struct dirent *dp;
char *buf = NULL, *cmd = NULL, *cwd = NULL, idstr[7], *cp;
struct log_info li;
size_t bufsize = 0, cwdsize = 0, cmdsize = 0, plen;
plen = strlen(pathbuf);
d = opendir(pathbuf);
if (d == NULL && errno != ENOTDIR) {
warning("cannot opendir %s", pathbuf);
return(-1);
}
while ((dp = readdir(d)) != NULL) {
if (NAMLEN(dp) != 2 || !isalnum(dp->d_name[0]) ||
!isalnum(dp->d_name[1]))
continue;
/* open log file, print id and command */
pathbuf[plen + 0] = '/';
pathbuf[plen + 1] = dp->d_name[0];
pathbuf[plen + 2] = dp->d_name[1];
pathbuf[plen + 3] = '\0';
fp = fopen(pathbuf, "r");
if (fp == NULL) {
warning("unable to open %s", pathbuf);
continue;
}
/*
* ID file has three lines:
* 1) a log info line
* 2) cwd
* 3) command with args
*/
if (getline(&buf, &bufsize, fp) == -1 ||
getline(&cwd, &cwdsize, fp) == -1 ||
getline(&cmd, &cmdsize, fp) == -1) {
fclose(fp);
continue;
}
fclose(fp);
/* crack the log line: timestamp:user:runas_user:runas_group:tty */
buf[strcspn(buf, "\n")] = '\0';
if ((li.tstamp = atoi(buf)) == 0)
continue;
if ((cp = strchr(buf, ':')) == NULL)
continue;
*cp++ = '\0';
li.user = cp;
if ((cp = strchr(cp, ':')) == NULL)
continue;
*cp++ = '\0';
li.runas_user = cp;
if ((cp = strchr(cp, ':')) == NULL)
continue;
*cp++ = '\0';
li.runas_group = cp;
if ((cp = strchr(cp, ':')) == NULL)
continue;
*cp++ = '\0';
li.tty = cp;
cwd[strcspn(cwd, "\n")] = '\0';
li.cwd = cwd;
cmd[strcspn(cmd, "\n")] = '\0';
li.cmd = cmd;
/* Match on search expression if there is one. */
if (search_expr && !match_expr(search_expr, &li))
continue;
/* Convert from /var/log/sudo-sessions/00/00/01 to 000001 */
idstr[0] = pathbuf[plen - 5];
idstr[1] = pathbuf[plen - 4];
idstr[2] = pathbuf[plen - 2];
idstr[3] = pathbuf[plen - 1];
idstr[4] = pathbuf[plen + 1];
idstr[5] = pathbuf[plen + 2];
idstr[6] = '\0';
printf("%s : %s : TTY=%s ; CWD=%s ; USER=%s ; ",
get_timestr(li.tstamp, 1), li.user, li.tty, li.cwd, li.runas_user);
if (*li.runas_group)
printf("GROUP=%s ; ", li.runas_group);
printf("TSID=%s ; COMMAND=%s\n", idstr, li.cmd);
}
return(0);
}
static int
list_sessions(argc, argv, pattern, user, tty)
int argc;
char **argv;
const char *pattern;
const char *user;
const char *tty;
{
DIR *d1, *d2;
struct dirent *dp1, *dp2;
REGEX_T rebuf, *re = NULL;
size_t sdlen;
char pathbuf[PATH_MAX];
/* Parse search expression if present */
parse_expr(&search_expr, argv);
d1 = opendir(session_dir);
if (d1 == NULL)
error(1, "unable to open %s", session_dir);
#ifdef HAVE_REGCOMP
/* optional regex */
if (pattern) {
re = &rebuf;
if (regcomp(re, pattern, REG_EXTENDED|REG_NOSUB) != 0)
errorx(1, "invalid regex: %s", pattern);
}
#else
re = (char *) pattern;
#endif /* HAVE_REGCOMP */
sdlen = strlcpy(pathbuf, session_dir, sizeof(pathbuf));
/*
* Three levels of directory, e.g. 00/00/00 .. ZZ/ZZ/ZZ
* We do a depth-first traversal.
*/
while ((dp1 = readdir(d1)) != NULL) {
if (NAMLEN(dp1) != 2 || !isalnum(dp1->d_name[0]) ||
!isalnum(dp1->d_name[1]))
continue;
pathbuf[sdlen + 0] = '/';
pathbuf[sdlen + 1] = dp1->d_name[0];
pathbuf[sdlen + 2] = dp1->d_name[1];
pathbuf[sdlen + 3] = '\0';
d2 = opendir(pathbuf);
if (d2 == NULL)
continue;
while ((dp2 = readdir(d2)) != NULL) {
if (NAMLEN(dp2) != 2 || !isalnum(dp2->d_name[0]) ||
!isalnum(dp2->d_name[1]))
continue;
pathbuf[sdlen + 3] = '/';
pathbuf[sdlen + 4] = dp2->d_name[0];
pathbuf[sdlen + 5] = dp2->d_name[1];
pathbuf[sdlen + 6] = '\0';
list_session_dir(pathbuf, re, user, tty);
}
closedir(d2);
}
closedir(d1);
return(0);
}
/*
* Check input for ' ', '<', '>'
* pause, slow, fast
*/
static void
check_input(ttyfd, speed)
int ttyfd;
double *speed;
{
fd_set *fdsr;
int nready, paused = 0;
struct timeval tv;
char ch;
ssize_t n;
fdsr = (fd_set *)emalloc2(howmany(ttyfd + 1, NFDBITS), sizeof(fd_mask));
for (;;) {
FD_SET(ttyfd, fdsr);
tv.tv_sec = 0;
tv.tv_usec = 0;
nready = select(ttyfd + 1, fdsr, NULL, NULL, paused ? NULL : &tv);
if (nready != 1)
break;
n = read(ttyfd, &ch, 1);
if (n == 1) {
if (paused) {
paused = 0;
continue;
}
switch (ch) {
case ' ':
paused = 1;
break;
case '<':
*speed /= 2;
break;
case '>':
*speed *= 2;
break;
}
}
}
free(fdsr);
}
static void
usage()
{
fprintf(stderr,
"usage: %s [-d directory] [-m max_wait] [-s speed_factor] ID\n",
getprogname());
fprintf(stderr,
"usage: %s [-d directory] -l [search expression]\n",
getprogname());
exit(1);
}
/*
* Cleanup hook for error()/errorx()
*/
void
cleanup(signo)
int signo;
{
term_restore(STDOUT_FILENO, 0);
if (signo)
kill(getpid(), signo);
}

View File

@@ -0,0 +1,555 @@
/*
* Copyright (c) 1996, 1998-2005, 2007-2009
* Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#define _SUDO_MAIN
#include <config.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#ifdef HAVE_FNMATCH
# include <fnmatch.h>
#endif /* HAVE_FNMATCH */
#ifdef HAVE_NETGROUP_H
# include <netgroup.h>
#endif /* HAVE_NETGROUP_H */
#include <ctype.h>
#include <pwd.h>
#include <grp.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include "sudo.h"
#include "interfaces.h"
#include "parse.h"
#include <gram.h>
#ifndef HAVE_FNMATCH
# include "emul/fnmatch.h"
#endif /* HAVE_FNMATCH */
/*
* Globals
*/
int Argc, NewArgc;
char **Argv, **NewArgv;
int num_interfaces;
struct interface *interfaces;
struct sudo_user sudo_user;
struct passwd *list_pw;
extern int parse_error;
/* For getopt(3) */
extern char *optarg;
extern int optind;
#if defined(SUDO_DEVEL) && defined(__OpenBSD__)
extern char *malloc_options;
#endif
#ifdef YYDEBUG
extern int yydebug;
#endif
int print_alias __P((void *, void *));
void dump_sudoers __P((void));
void print_defaults __P((void));
void print_privilege __P((struct privilege *));
void print_userspecs __P((void));
void usage __P((void)) __attribute__((__noreturn__));
void set_runasgr __P((char *));
void set_runaspw __P((char *));
extern void setgrfile __P((const char *));
extern void setgrent __P((void));
extern void endgrent __P((void));
extern struct group *getgrent __P((void));
extern struct group *getgrnam __P((const char *));
extern struct group *getgrgid __P((gid_t));
extern void setpwfile __P((const char *));
extern void setpwent __P((void));
extern void endpwent __P((void));
extern struct passwd *getpwent __P((void));
extern struct passwd *getpwnam __P((const char *));
extern struct passwd *getpwuid __P((uid_t));
int
main(argc, argv)
int argc;
char **argv;
{
struct cmndspec *cs;
struct privilege *priv;
struct userspec *us;
char *p, *grfile, *pwfile, *runas_group, *runas_user;
char hbuf[MAXHOSTNAMELEN + 1];
int ch, dflag, rval, matched;
#if defined(SUDO_DEVEL) && defined(__OpenBSD__)
malloc_options = "AFGJPR";
#endif
#ifdef YYDEBUG
yydebug = 1;
#endif
Argv = argv;
Argc = argc;
dflag = 0;
grfile = pwfile = runas_group = runas_user = NULL;
while ((ch = getopt(argc, argv, "dg:G:h:p:u:")) != -1) {
switch (ch) {
case 'd':
dflag = 1;
break;
case 'h':
user_host = optarg;
break;
case 'G':
grfile = optarg;
break;
case 'g':
runas_group = optarg;
break;
case 'p':
pwfile = optarg;
break;
case 'u':
runas_user = optarg;
break;
default:
usage();
break;
}
}
argc -= optind;
argv += optind;
NewArgc = argc;
NewArgv = argv;
/* Set group/passwd file and init the cache. */
if (grfile)
setgrfile(grfile);
if (pwfile)
setpwfile(pwfile);
sudo_setpwent();
sudo_setgrent();
if (argc < 2) {
if (!dflag)
usage();
if ((sudo_user.pw = sudo_getpwnam("nobody")) == NULL)
errorx(1, "no passwd entry for nobody!");
user_cmnd = user_base = "true";
} else {
if ((sudo_user.pw = sudo_getpwnam(*argv)) == NULL)
errorx(1, "no passwd entry for %s!", *argv);
user_cmnd = *++argv;
if ((p = strrchr(user_cmnd, '/')) != NULL)
user_base = p + 1;
else
user_base = user_cmnd;
NewArgc -= 2;
}
if (user_host == NULL) {
if (gethostname(hbuf, sizeof(hbuf)) != 0)
error(1, "gethostname");
hbuf[sizeof(hbuf) - 1] = '\0';
user_host = hbuf;
}
if ((p = strchr(user_host, '.'))) {
*p = '\0';
user_shost = estrdup(user_host);
*p = '.';
} else {
user_shost = user_host;
}
/* Fill in user_args from NewArgv. */
if (NewArgc > 1) {
char *to, **from;
size_t size, n;
for (size = 0, from = NewArgv + 1; *from; from++)
size += strlen(*from) + 1;
user_args = (char *) emalloc(size);
for (to = user_args, from = NewArgv + 1; *from; from++) {
n = strlcpy(to, *from, size - (to - user_args));
if (n >= size - (to - user_args))
errorx(1, "internal error, init_vars() overflow");
to += n;
*to++ = ' ';
}
*--to = '\0';
}
/* Initialize default values. */
init_defaults();
/* Load ip addr/mask for each interface. */
load_interfaces();
/* Allocate space for data structures in the parser. */
init_parser("sudoers", 0);
if (yyparse() != 0 || parse_error)
(void) fputs("Does not parse", stdout);
else
(void) fputs("Parses OK", stdout);
if (!update_defaults(SETDEF_ALL))
(void) fputs(" (problem with defaults entries)", stdout);
puts(".");
/*
* Set runas passwd/group entries based on command line or sudoers.
* Note that if runas_group was specified without runas_user we
* defer setting runas_pw so the match routines know to ignore it.
*/
if (runas_group != NULL) {
set_runasgr(runas_group);
if (runas_user != NULL)
set_runaspw(runas_user);
} else
set_runaspw(runas_user ? runas_user : def_runas_default);
if (dflag) {
(void) putchar('\n');
dump_sudoers();
if (argc < 2)
exit(0);
}
/* This loop must match the one in sudoers_lookup() */
printf("\nEntries for user %s:\n", user_name);
matched = UNSPEC;
tq_foreach_rev(&userspecs, us) {
if (userlist_matches(sudo_user.pw, &us->users) != ALLOW)
continue;
tq_foreach_rev(&us->privileges, priv) {
putchar('\n');
print_privilege(priv); /* XXX */
putchar('\n');
if (hostlist_matches(&priv->hostlist) == ALLOW) {
puts("\thost matched");
tq_foreach_rev(&priv->cmndlist, cs) {
if (runaslist_matches(&cs->runasuserlist,
&cs->runasgrouplist) == ALLOW) {
puts("\trunas matched");
rval = cmnd_matches(cs->cmnd);
if (rval != UNSPEC)
matched = rval;
printf("\tcmnd %s\n", rval == ALLOW ? "allowed" :
rval == DENY ? "denied" : "unmatched");
}
}
} else
puts("\thost unmatched");
}
}
printf("\nCommand %s\n", matched == ALLOW ? "allowed" :
matched == DENY ? "denied" : "unmatched");
exit(0);
}
void
set_runaspw(user)
char *user;
{
if (*user == '#') {
if ((runas_pw = sudo_getpwuid(atoi(user + 1))) == NULL)
runas_pw = sudo_fakepwnam(user, runas_gr ? runas_gr->gr_gid : 0);
} else {
if ((runas_pw = sudo_getpwnam(user)) == NULL)
errorx(1, "unknown user: %s", user);
}
}
void
set_runasgr(group)
char *group;
{
if (*group == '#') {
if ((runas_gr = sudo_getgrgid(atoi(group + 1))) == NULL)
runas_gr = sudo_fakegrnam(group);
} else {
if ((runas_gr = sudo_getgrnam(group)) == NULL)
errorx(1, "unknown group: %s", group);
}
}
void
sudo_setspent()
{
return;
}
void
sudo_endspent()
{
return;
}
char *
sudo_getepw(pw)
const struct passwd *pw;
{
return (pw->pw_passwd);
}
void
set_fqdn()
{
return;
}
FILE *
open_sudoers(path, isdir, keepopen)
const char *path;
int isdir;
int *keepopen;
{
return(fopen(path, "r"));
}
void
init_envtables()
{
return;
}
int
set_perms(perm)
int perm;
{
return(1);
}
void
cleanup(gotsignal)
int gotsignal;
{
if (!gotsignal) {
sudo_endpwent();
sudo_endgrent();
}
}
void
print_member(m)
struct member *m;
{
struct sudo_command *c;
if (m->negated)
putchar('!');
if (m->name == NULL)
fputs("ALL", stdout);
else if (m->type != COMMAND)
fputs(m->name, stdout);
else {
c = (struct sudo_command *) m->name;
printf("%s%s%s", c->cmnd, c->args ? " " : "",
c->args ? c->args : "");
}
}
void
print_defaults()
{
struct defaults *d;
struct member *m;
tq_foreach_fwd(&defaults, d) {
(void) fputs("Defaults", stdout);
switch (d->type) {
case DEFAULTS_HOST:
putchar('@');
break;
case DEFAULTS_USER:
putchar(':');
break;
case DEFAULTS_RUNAS:
putchar('>');
break;
case DEFAULTS_CMND:
putchar('!');
break;
}
tq_foreach_fwd(&d->binding, m) {
if (m != tq_first(&d->binding))
putchar(',');
print_member(m);
}
printf("\t%s%s", d->op == FALSE ? "!" : "", d->var);
if (d->val != NULL) {
printf("%c%s", d->op == TRUE ? '=' : d->op, d->val);
}
putchar('\n');
}
}
int
print_alias(v1, v2)
void *v1, *v2;
{
struct alias *a = (struct alias *)v1;
struct member *m;
struct sudo_command *c;
switch (a->type) {
case HOSTALIAS:
(void) printf("Host_Alias\t%s = ", a->name);
break;
case CMNDALIAS:
(void) printf("Cmnd_Alias\t%s = ", a->name);
break;
case USERALIAS:
(void) printf("User_Alias\t%s = ", a->name);
break;
case RUNASALIAS:
(void) printf("Runas_Alias\t%s = ", a->name);
break;
}
tq_foreach_fwd(&a->members, m) {
if (m != tq_first(&a->members))
fputs(", ", stdout);
if (m->type == COMMAND) {
c = (struct sudo_command *) m->name;
printf("%s%s%s", c->cmnd, c->args ? " " : "",
c->args ? c->args : "");
} else
fputs(m->name, stdout);
}
putchar('\n');
return(0);
}
void
print_privilege(priv)
struct privilege *priv;
{
struct cmndspec *cs;
struct member *m;
struct privilege *p;
struct cmndtag tags;
for (p = priv; p != NULL; p = p->next) {
if (p != priv)
fputs(" : ", stdout);
tq_foreach_fwd(&p->hostlist, m) {
if (m != tq_first(&p->hostlist))
fputs(", ", stdout);
print_member(m);
}
fputs(" = ", stdout);
tags.nopasswd = tags.noexec = UNSPEC;
tq_foreach_fwd(&p->cmndlist, cs) {
if (cs != tq_first(&p->cmndlist))
fputs(", ", stdout);
/* XXX - runasgrouplist too */
if (!tq_empty(&cs->runasuserlist)) {
fputs("(", stdout);
tq_foreach_fwd(&cs->runasuserlist, m) {
if (m != tq_first(&cs->runasuserlist))
fputs(", ", stdout);
print_member(m);
}
fputs(") ", stdout);
}
#ifdef HAVE_SELINUX
if (cs->role)
printf("ROLE=%s ", cs->role);
if (cs->type)
printf("TYPE=%s ", cs->type);
#endif /* HAVE_SELINUX */
if (cs->tags.nopasswd != UNSPEC && cs->tags.nopasswd != tags.nopasswd)
printf("%sPASSWD: ", cs->tags.nopasswd ? "NO" : "");
if (cs->tags.noexec != UNSPEC && cs->tags.noexec != tags.noexec)
printf("%sEXEC: ", cs->tags.noexec ? "NO" : "");
print_member(cs->cmnd);
memcpy(&tags, &cs->tags, sizeof(tags));
}
}
}
void
print_userspecs()
{
struct member *m;
struct userspec *us;
tq_foreach_fwd(&userspecs, us) {
tq_foreach_fwd(&us->users, m) {
if (m != tq_first(&us->users))
fputs(", ", stdout);
print_member(m);
}
putchar('\t');
print_privilege(us->privileges.first); /* XXX */
putchar('\n');
}
}
void
dump_sudoers()
{
print_defaults();
putchar('\n');
alias_apply(print_alias, NULL);
putchar('\n');
print_userspecs();
}
void
usage()
{
(void) fprintf(stderr, "usage: %s [-d] [-G grfile] [-g group] [-h host] [-p pwfile] [-u user] <user> <command> [args]\n", getprogname());
exit(1);
}

69
plugins/sudoers/timestr.c Normal file
View File

@@ -0,0 +1,69 @@
/*
* Copyright (c) 1999, 2009 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <config.h>
#include <sys/types.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#include <time.h>
#include "compat.h"
char *get_timestr __P((time_t, int));
/*
* Return an ascii string with the current date + time
* Uses strftime() if available, else falls back to ctime().
*/
char *
get_timestr(tstamp, log_year)
time_t tstamp;
int log_year;
{
char *s;
#ifdef HAVE_STRFTIME
static char buf[128];
struct tm *timeptr;
timeptr = localtime(&tstamp);
if (log_year)
s = "%h %e %T %Y";
else
s = "%h %e %T";
/* strftime() does not guarantee to NUL-terminate so we must check. */
buf[sizeof(buf) - 1] = '\0';
if (strftime(buf, sizeof(buf), s, timeptr) && buf[sizeof(buf) - 1] == '\0')
return(buf);
#endif /* HAVE_STRFTIME */
s = ctime(&tstamp) + 4; /* skip day of the week */
if (log_year)
s[20] = '\0'; /* avoid the newline */
else
s[15] = '\0'; /* don't care about year */
return(s);
}

3633
plugins/sudoers/toke.c Normal file

File diff suppressed because it is too large Load Diff

1012
plugins/sudoers/toke.l Normal file

File diff suppressed because it is too large Load Diff

305
plugins/sudoers/tsgetgrpw.c Normal file
View File

@@ -0,0 +1,305 @@
/*
* Copyright (c) 2005,2008 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* Trivial replacements for the libc get{gr,pw}{uid,nam}() routines
* for use by testsudoers in the sudo test harness.
* We need our own since many platforms don't provide set{pw,gr}file().
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
# include <memory.h>
# endif
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#include <limits.h>
#include <pwd.h>
#include <grp.h>
#include "sudo.h"
#ifndef LINE_MAX
# define LINE_MAX 2048
#endif
#undef GRMEM_MAX
#define GRMEM_MAX 200
static FILE *pwf;
static const char *pwfile = "/etc/passwd";
static int pw_stayopen;
static FILE *grf;
static const char *grfile = "/etc/group";
static int gr_stayopen;
void setgrfile __P((const char *));
void setgrent __P((void));
void endgrent __P((void));
struct group *getgrent __P((void));
struct group *getgrnam __P((const char *));
struct group *getgrgid __P((gid_t));
void setpwfile __P((const char *));
void setpwent __P((void));
void endpwent __P((void));
struct passwd *getpwent __P((void));
struct passwd *getpwnam __P((const char *));
struct passwd *getpwuid __P((uid_t));
void
setpwfile(file)
const char *file;
{
pwfile = file;
if (pwf != NULL)
endpwent();
}
void
setpwent()
{
if (pwf == NULL)
pwf = fopen(pwfile, "r");
else
rewind(pwf);
pw_stayopen = 1;
}
void
endpwent()
{
if (pwf != NULL) {
fclose(pwf);
pwf = NULL;
}
pw_stayopen = 0;
}
struct passwd *
getpwent()
{
static struct passwd pw;
static char pwbuf[LINE_MAX];
size_t len;
char *cp, *colon;
if ((colon = fgets(pwbuf, sizeof(pwbuf), pwf)) == NULL)
return(NULL);
zero_bytes(&pw, sizeof(pw));
if ((colon = strchr(cp = colon, ':')) == NULL)
return(NULL);
*colon++ = '\0';
pw.pw_name = cp;
if ((colon = strchr(cp = colon, ':')) == NULL)
return(NULL);
*colon++ = '\0';
pw.pw_passwd = cp;
if ((colon = strchr(cp = colon, ':')) == NULL)
return(NULL);
*colon++ = '\0';
pw.pw_uid = atoi(cp);
if ((colon = strchr(cp = colon, ':')) == NULL)
return(NULL);
*colon++ = '\0';
pw.pw_gid = atoi(cp);
if ((colon = strchr(cp = colon, ':')) == NULL)
return(NULL);
*colon++ = '\0';
pw.pw_gecos = cp;
if ((colon = strchr(cp = colon, ':')) == NULL)
return(NULL);
*colon++ = '\0';
pw.pw_dir = cp;
pw.pw_shell = colon;
len = strlen(colon);
if (len > 0 && colon[len - 1] == '\n')
colon[len - 1] = '\0';
return(&pw);
}
struct passwd *
getpwnam(name)
const char *name;
{
struct passwd *pw;
if (pwf != NULL)
rewind(pwf);
else if ((pwf = fopen(pwfile, "r")) == NULL)
return(NULL);
while ((pw = getpwent()) != NULL) {
if (strcmp(pw->pw_name, name) == 0)
break;
}
if (!pw_stayopen) {
fclose(pwf);
pwf = NULL;
}
return(pw);
}
struct passwd *
getpwuid(uid)
uid_t uid;
{
struct passwd *pw;
if (pwf != NULL)
rewind(pwf);
else if ((pwf = fopen(pwfile, "r")) == NULL)
return(NULL);
while ((pw = getpwent()) != NULL) {
if (pw->pw_uid == uid)
break;
}
if (!pw_stayopen) {
fclose(pwf);
pwf = NULL;
}
return(pw);
}
void
setgrfile(file)
const char *file;
{
grfile = file;
if (grf != NULL)
endgrent();
}
void
setgrent()
{
if (grf == NULL)
grf = fopen(grfile, "r");
else
rewind(grf);
gr_stayopen = 1;
}
void
endgrent()
{
if (grf != NULL) {
fclose(grf);
grf = NULL;
}
gr_stayopen = 0;
}
struct group *
getgrent()
{
static struct group gr;
static char grbuf[LINE_MAX], *gr_mem[GRMEM_MAX+1];
size_t len;
char *cp, *colon;
int n;
if ((colon = fgets(grbuf, sizeof(grbuf), grf)) == NULL)
return(NULL);
zero_bytes(&gr, sizeof(gr));
if ((colon = strchr(cp = colon, ':')) == NULL)
return(NULL);
*colon++ = '\0';
gr.gr_name = cp;
if ((colon = strchr(cp = colon, ':')) == NULL)
return(NULL);
*colon++ = '\0';
gr.gr_passwd = cp;
if ((colon = strchr(cp = colon, ':')) == NULL)
return(NULL);
*colon++ = '\0';
gr.gr_gid = atoi(cp);
len = strlen(colon);
if (len > 0 && colon[len - 1] == '\n')
colon[len - 1] = '\0';
if (*colon != '\0') {
gr.gr_mem = gr_mem;
cp = strtok(colon, ",");
for (n = 0; cp != NULL && n < GRMEM_MAX; n++) {
gr.gr_mem[n] = cp;
cp = strtok(NULL, ",");
}
gr.gr_mem[n++] = NULL;
} else
gr.gr_mem = NULL;
return(&gr);
}
struct group *
getgrnam(name)
const char *name;
{
struct group *gr;
if (grf != NULL)
rewind(grf);
else if ((grf = fopen(grfile, "r")) == NULL)
return(NULL);
while ((gr = getgrent()) != NULL) {
if (strcmp(gr->gr_name, name) == 0)
break;
}
if (!gr_stayopen) {
fclose(grf);
grf = NULL;
}
return(gr);
}
struct group *
getgrgid(gid)
gid_t gid;
{
struct group *gr;
if (grf != NULL)
rewind(grf);
else if ((grf = fopen(grfile, "r")) == NULL)
return(NULL);
while ((gr = getgrent()) != NULL) {
if (gr->gr_gid == gid)
break;
}
if (!gr_stayopen) {
fclose(grf);
grf = NULL;
}
return(gr);
}

323
plugins/sudoers/vasgroups.c Normal file
View File

@@ -0,0 +1,323 @@
/*
* (c) 2006 Quest Software, Inc. 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 Quest Software, Inc. 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 THE COPYRIGHT HOLDERS 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 THE COPYRIGHT OWNER 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 <stdlib.h>
#include <sys/types.h>
#include <pwd.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <dlfcn.h>
#include <vas.h>
#include "compat.h"
#include "logging.h"
#include "nonunix.h"
#include "parse.h"
#include "sudo.h"
/* Pseudo-boolean types */
#undef TRUE
#undef FALSE
#define FALSE 0
#define TRUE 1
static vas_ctx_t *sudo_vas_ctx;
static vas_id_t *sudo_vas_id;
/* Don't use VAS_NAME_FLAG_NO_CACHE or lookups just won't work.
* -tedp, 2006-08-29 */
static const int update_flags = 0;
static int sudo_vas_available = 0;
static char *err_msg = NULL;
static void *libvas_handle = NULL;
/* libvas functions */
static vas_err_t (*v_ctx_alloc) (vas_ctx_t **ctx);
static void (*v_ctx_free) (vas_ctx_t *ctx);
static vas_err_t (*v_id_alloc) (vas_ctx_t *ctx, const char *name, vas_id_t **id);
static void (*v_id_free) (vas_ctx_t *ctx, vas_id_t *id);
static vas_err_t (*v_id_establish_cred_keytab) (vas_ctx_t *ctx, vas_id_t *id, int credflags, const char *keytab);
static vas_err_t (*v_user_init) (vas_ctx_t *ctx, vas_id_t *id, const char *name, int flags, vas_user_t **user);
static void (*v_user_free) (vas_ctx_t *ctx, vas_user_t *user);
static vas_err_t (*v_group_init) (vas_ctx_t *ctx, vas_id_t *id, const char *name, int flags, vas_group_t **group);
static void (*v_group_free) (vas_ctx_t *ctx, vas_group_t *group);
static vas_err_t (*v_user_is_member) (vas_ctx_t *ctx, vas_id_t *id, vas_user_t *user, vas_group_t *group);
static const char* (*v_err_get_string) (vas_ctx_t *ctx, int with_cause);
static int resolve_vas_funcs(void);
/**
* Whether nonunix group lookups are available.
* @return 1 if available, 0 if not.
*/
int
sudo_nonunix_groupcheck_available(void)
{
return sudo_vas_available;
}
/**
* Check if the user is in the group
* @param group group name which can be in DOMAIN\sam format or just the group
* name
* @param user user name
* @param pwd (unused)
* @return 1 if user is a member of the group, 0 if not (or error occurred)
*/
int
sudo_nonunix_groupcheck( const char* group, const char* user, const struct passwd* pwd )
{
static int error_cause_shown = FALSE;
int rval = FALSE;
vas_err_t vaserr;
vas_user_t* vas_user = NULL;
vas_group_t* vas_group = NULL;
if (!sudo_vas_available) {
if (error_cause_shown == FALSE) {
/* Produce the saved error reason */
log_error(NO_MAIL|NO_EXIT, "Non-unix group checking unavailable: %s",
err_msg ? err_msg
: "(unknown cause)");
error_cause_shown = TRUE;
}
return 0;
}
/* resolve the user and group. The user will be a real Unix account name,
* while the group may be a unix name, or any group name accepted by
* vas_name_to_dn, which means any of:
* - Group Name
* - Group Name@FULLY.QUALIFIED.DOMAIN
* - CN=sudoers,CN=Users,DC=rcdev,DC=vintela,DC=com
* - S-1-2-34-5678901234-5678901234-5678901234-567
*
* XXX - we may get non-VAS user accounts here. You can add local users to an
* Active Directory group through override files. Should we handle that case?
* */
if( (vaserr = v_user_init( sudo_vas_ctx, sudo_vas_id, user, update_flags, &vas_user )) != VAS_ERR_SUCCESS ) {
if (vaserr == VAS_ERR_NOT_FOUND) {
/* No such user in AD. Probably a local user. */
vaserr = VAS_ERR_SUCCESS;
}
goto FINISHED;
}
if( (vaserr = v_group_init( sudo_vas_ctx, sudo_vas_id, group, update_flags, &vas_group )) != VAS_ERR_SUCCESS ) {
goto FINISHED;
}
/* do the membership check */
if( (vaserr = v_user_is_member( sudo_vas_ctx, sudo_vas_id, vas_user, vas_group )) == VAS_ERR_SUCCESS ) {
rval = TRUE;
}
else if (vaserr == VAS_ERR_NOT_FOUND) {
/* fake the vaserr code so no error is triggered */
vaserr = VAS_ERR_SUCCESS;
}
FINISHED: /* cleanups */
if (vaserr != VAS_ERR_SUCCESS) {
int error_flags = NO_MAIL | MSG_ONLY | (uses_inversion ? 0 : NO_EXIT);
log_error(error_flags, "Error while checking group membership "
"for user \"%s\", group \"%s\", error: %s%s.", user, group,
v_err_get_string(sudo_vas_ctx, 1),
/* A helpful hint if there seems to be a non-FQDN as the domain */
(strchr(group, '@') && !strchr(group, '.'))
? "\nMake sure the fully qualified domain name is specified"
: "");
}
if( vas_group ) v_group_free( sudo_vas_ctx, vas_group );
if( vas_user ) v_user_free( sudo_vas_ctx, vas_user );
return(rval);
}
static void
set_err_msg(const char *msg, ...) {
va_list ap;
if (!msg) /* assert */
return;
if (err_msg)
free(err_msg);
va_start(ap, msg);
if (vasprintf(&err_msg, msg, ap) == -1)
err_msg = NULL;
va_end(ap);
}
/**
* Initialise nonunix_groupcheck state.
*/
void
sudo_nonunix_groupcheck_init(void)
{
vas_err_t vaserr;
void *libvas;
if (err_msg) {
free(err_msg);
err_msg = NULL;
}
libvas = dlopen(LIBVAS_SO, RTLD_LAZY);
if (!libvas) {
set_err_msg("dlopen() failed: %s", dlerror());
return;
}
libvas_handle = libvas;
if (resolve_vas_funcs() != 0)
return;
if (VAS_ERR_SUCCESS == (vaserr = v_ctx_alloc(&sudo_vas_ctx))) {
if (VAS_ERR_SUCCESS == (vaserr = v_id_alloc(sudo_vas_ctx, "host/", &sudo_vas_id))) {
if (update_flags & VAS_NAME_FLAG_NO_LDAP) {
sudo_vas_available = 1;
return; /* OK */
} else { /* Get a keytab */
if ((vaserr = v_id_establish_cred_keytab( sudo_vas_ctx,
sudo_vas_id,
VAS_ID_FLAG_USE_MEMORY_CCACHE
| VAS_ID_FLAG_KEEP_COPY_OF_CRED
| VAS_ID_FLAG_NO_INITIAL_TGT,
NULL )) == VAS_ERR_SUCCESS) {
sudo_vas_available = 1;
return; /* OK */
}
if (!err_msg)
set_err_msg("unable to establish creds: %s",
v_err_get_string(sudo_vas_ctx, 1));
}
v_id_free(sudo_vas_ctx, sudo_vas_id);
sudo_vas_id = NULL;
}
/* This is the last opportunity to get an error message from libvas */
if (!err_msg)
set_err_msg("Error initializing non-unix group checking: %s",
v_err_get_string(sudo_vas_ctx, 1));
v_ctx_free(sudo_vas_ctx);
sudo_vas_ctx = NULL;
}
if (!err_msg)
set_err_msg("Failed to get a libvas handle for non-unix group checking (unknown cause)");
sudo_vas_available = 0;
}
/**
* Clean up nonunix_groupcheck state.
*/
void
sudo_nonunix_groupcheck_cleanup()
{
if (err_msg) {
free(err_msg);
err_msg = NULL;
}
if (sudo_vas_available) {
v_id_free(sudo_vas_ctx, sudo_vas_id);
sudo_vas_id = NULL;
v_ctx_free(sudo_vas_ctx);
sudo_vas_ctx = NULL;
sudo_vas_available = FALSE;
}
if (libvas_handle) {
if (dlclose(libvas_handle) != 0)
log_error(NO_MAIL|NO_EXIT, "dlclose() failed: %s", dlerror());
libvas_handle = NULL;
}
}
#define RESOLVE_OR_ERR(fptr, sym) \
do { \
void *_fptr = dlsym(libvas_handle, (sym)); \
if (!_fptr) { \
set_err_msg("dlsym() failed: %s", dlerror()); \
return -1; \
} \
fptr = _fptr; \
} while (0)
/**
* Resolve all the libvas functions.
* Returns -1 and sets err_msg if something went wrong, or 0 on success.
*/
int
resolve_vas_funcs(void)
{
if (!libvas_handle) /* assert */
return -1;
RESOLVE_OR_ERR(v_ctx_alloc, "vas_ctx_alloc");
RESOLVE_OR_ERR(v_ctx_free, "vas_ctx_free");
RESOLVE_OR_ERR(v_id_alloc, "vas_id_alloc");
RESOLVE_OR_ERR(v_id_free, "vas_id_free");
RESOLVE_OR_ERR(v_id_establish_cred_keytab, "vas_id_establish_cred_keytab");
RESOLVE_OR_ERR(v_user_init, "vas_user_init");
RESOLVE_OR_ERR(v_user_free, "vas_user_free");
RESOLVE_OR_ERR(v_group_init, "vas_group_init");
RESOLVE_OR_ERR(v_group_free, "vas_group_free");
RESOLVE_OR_ERR(v_user_is_member, "vas_user_is_member");
RESOLVE_OR_ERR(v_err_get_string, "vas_err_get_string");
return 0;
}

1156
plugins/sudoers/visudo.c Normal file

File diff suppressed because it is too large Load Diff