Top level directory reorg

Move src/po -> po
Combine common and compat -> lib/util
Move zlib -> lib/zlib
This commit is contained in:
Todd C. Miller
2014-06-26 15:51:02 -06:00
parent d41276f171
commit 4d37a4a162
187 changed files with 1083 additions and 1217 deletions

433
lib/util/Makefile.in Normal file
View File

@@ -0,0 +1,433 @@
#
# Copyright (c) 2011-2014 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.
#
# @configure_input@
#
#### Start of system configuration section. ####
srcdir = @srcdir@
devdir = @devdir@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
incdir = $(top_srcdir)/include
cross_compiling = @CROSS_COMPILING@
# Where to install things...
prefix = @prefix@
exec_prefix = @exec_prefix@
bindir = @bindir@
sbindir = @sbindir@
sysconfdir = @sysconfdir@
libexecdir = @libexecdir@
datarootdir = @datarootdir@
localstatedir = @localstatedir@
# Compiler & tools to use
CC = @CC@
LIBTOOL = @LIBTOOL@
# C preprocessor flags
CPPFLAGS = -I$(incdir) -I$(top_builddir) -I$(top_srcdir) @CPPFLAGS@
# Usually -O and/or -g
CFLAGS = @CFLAGS@
# PIE flags
PIE_CFLAGS = @PIE_CFLAGS@
PIE_LDFLAGS = @PIE_LDFLAGS@
# Stack smashing protection flags
SSP_CFLAGS = @SSP_CFLAGS@
SSP_LDFLAGS = @SSP_LDFLAGS@
# cppcheck options, usually set in the top-level Makefile
CPPCHECK_OPTS = -q --force --enable=warning,performance,portability --suppress=constStatement --error-exitcode=1 --inline-suppr -U__cplusplus -UQUAD_MAX -UQUAD_MIN -UUQUAD_MAX -U_POSIX_HOST_NAME_MAX -U_POSIX_PATH_MAX
# Regression tests
TEST_PROGS = atofoo_test conf_test hltq_test parseln_test @COMPAT_TEST_PROGS@
TEST_LIBS = @LIBS@ @LIBINTL@
TEST_LDFLAGS = @LDFLAGS@
# OS dependent defines
DEFS = @OSDEFS@ -D_PATH_SUDO_CONF=\"$(sysconfdir)/sudo.conf\"
# Set to non-empty for development mode
DEVEL = @DEVEL@
#### End of system configuration section. ####
SHELL = @SHELL@
LTOBJS = alloc.lo atobool.lo atoid.lo atomode.lo event.lo fatal.lo fileops.lo \
fmt_string.lo gidlist.lo lbuf.lo progname.lo secure_path.lo \
setgroups.lo sudo_conf.lo sudo_debug.lo sudo_dso.lo sudo_printf.lo \
term.lo ttysize.lo @COMMON_OBJS@ @LTLIBOBJS@
ATOFOO_TEST_OBJS = atofoo_test.lo locale_stub.lo
PARSELN_TEST_OBJS = parseln_test.lo locale_stub.lo
CONF_TEST_OBJS = conf_test.lo locale_stub.lo
HLTQ_TEST_OBJS = hltq_test.lo locale_stub.lo
all: libsudo_util.la
Makefile: $(srcdir)/Makefile.in
(cd $(top_builddir) && ./config.status --file lib/util/Makefile)
.SUFFIXES: .o .c .h .lo
.c.o:
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $<
.c.lo:
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $<
libsudo_util.la: $(LTOBJS)
$(LIBTOOL) --mode=link $(CC) -o $@ $(LTOBJS) -no-install
siglist.c: mksiglist
./mksiglist > $@
signame.c: mksigname
./mksigname > $@
mksiglist: $(srcdir)/mksiglist.c $(srcdir)/mksiglist.h $(incdir)/missing.h $(top_builddir)/config.h
$(CC) $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/mksiglist.c -o $@
mksigname: $(srcdir)/mksigname.c $(srcdir)/mksigname.h $(incdir)/missing.h $(top_builddir)/config.h
$(CC) $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/mksigname.c -o $@
fnm_test: fnm_test.o libsudo_util.la
$(LIBTOOL) --mode=link $(CC) -o $@ fnm_test.o libsudo_util.la $(PIE_LDFLAGS) $(SSP_LDFLAGS)
globtest: globtest.o libsudo_util.la
$(LIBTOOL) --mode=link $(CC) -o $@ globtest.o libsudo_util.la $(PIE_LDFLAGS) $(SSP_LDFLAGS)
$(srcdir)/mksiglist.h: $(srcdir)/siglist.in
@if [ -n "$(DEVEL)" ]; then \
awk 'BEGIN {print "/* public domain */\n"} /^ [A-Z]/ {printf("#ifdef SIG%s\n if (sudo_sys_siglist[SIG%s] == NULL)\n\tsudo_sys_siglist[SIG%s] = \"%s\";\n#endif\n", $$1, $$1, $$1, substr($$0, 13))}' < $(srcdir)/siglist.in > $@; \
fi
$(srcdir)/mksigname.h: $(srcdir)/siglist.in
@if [ -n "$(DEVEL)" ]; then \
awk 'BEGIN {print "/* public domain */\n"} /^ [A-Z]/ {printf("#ifdef SIG%s\n if (sudo_sys_signame[SIG%s] == NULL)\n\tsudo_sys_signame[SIG%s] = \"%s\";\n#endif\n", $$1, $$1, $$1, $$1)}' < $(srcdir)/siglist.in > $@; \
fi
atofoo_test: $(ATOFOO_TEST_OBJS) libsudo_util.la
$(LIBTOOL) --mode=link $(CC) -o $@ $(ATOFOO_TEST_OBJS) libsudo_util.la $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(TEST_LDFLAGS) $(TEST_LIBS)
conf_test: $(CONF_TEST_OBJS) libsudo_util.la
$(LIBTOOL) --mode=link $(CC) -o $@ $(CONF_TEST_OBJS) libsudo_util.la $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(TEST_LDFLAGS) $(TEST_LIBS)
parseln_test: $(PARSELN_TEST_OBJS) libsudo_util.la
$(LIBTOOL) --mode=link $(CC) -o $@ $(PARSELN_TEST_OBJS) libsudo_util.la $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(TEST_LDFLAGS) $(TEST_LIBS)
hltq_test: $(HLTQ_TEST_OBJS) libsudo_util.la
$(LIBTOOL) --mode=link $(CC) -o $@ $(HLTQ_TEST_OBJS) libsudo_util.la $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(TEST_LDFLAGS) $(TEST_LIBS)
pre-install:
install:
install-dirs:
install-binaries:
install-includes:
install-doc:
install-plugin:
uninstall:
cppcheck:
cppcheck $(CPPCHECK_OPTS) -I$(incdir) -I$(top_builddir) -I$(top_srcdir) $(srcdir)/*.c
# Note: some regress checks are run from srcdir for consistent error messages
check: $(TEST_PROGS)
@if test X"$(cross_compiling)" != X"yes"; then \
rval=0; \
if test -f fnm_test; then \
./fnm_test $(srcdir)/regress/fnmatch/fnm_test.in || rval=`expr $$rval + $$?`; \
fi; \
if test -f globtest; then \
mkdir -p `sed 's@/[^/]*$$@@' $(srcdir)/regress/glob/files | sort -u`; \
touch `cat $(srcdir)/regress/glob/files`; \
chmod 0755 `grep '/r[^/]*$$' $(srcdir)/regress/glob/files`; \
chmod 0444 `grep '/s[^/]*$$' $(srcdir)/regress/glob/files`; \
chmod 0711 `grep '/t[^/]*$$' $(srcdir)/regress/glob/files`; \
./globtest $(srcdir)/regress/glob/globtest.in || rval=`expr $$rval + $$?`; \
rm -rf fake; \
fi; \
./atofoo_test || rval=`expr $$rval + $$?`; \
./hltq_test || rval=`expr $$rval + $$?`; \
build_dir=`pwd`; \
cd $(srcdir); \
for dir in sudo_conf sudo_parseln; do \
passed=0; failed=0; total=0; \
mkdir -p $$build_dir/regress/$$dir; \
for t in regress/$$dir/*.in; do \
base=`basename $$t .in`; \
out="$$build_dir/regress/$$dir/$${base}.out"; \
out_ok="regress/$$dir/$${base}.out.ok"; \
err="$$build_dir/regress/$$dir/$${base}.err"; \
err_ok="regress/$$dir/$${base}.err.ok"; \
if test "$$dir" = "sudo_conf"; then \
$$build_dir/conf_test $$t >$$out 2>$$err; \
else \
$$build_dir/parseln_test <$$t >$$out 2>$$err; \
fi; \
if cmp $$out $$out_ok >/dev/null; then \
passed=`expr $$passed + 1`; \
echo "$$dir/$$base: OK"; \
else \
failed=`expr $$failed + 1`; \
echo "$$dir/$$base: FAIL"; \
diff $$out $$out_ok || true; \
fi; \
total=`expr $$total + 1`; \
if test -s $$err_ok; then \
if cmp $$err $$err_ok >/dev/null; then \
passed=`expr $$passed + 1`; \
echo "$$dir/$$base (stderr): OK"; \
else \
failed=`expr $$failed + 1`; \
echo "$$dir/$$base (stderr): FAIL"; \
diff $$err $$err_ok || true; \
fi; \
total=`expr $$total + 1`; \
elif test -s $$err; then \
failed=`expr $$failed + 1`; \
echo "$$dir/$$base (stderr): FAIL"; \
cat $$err 1>&2; \
fi; \
done; \
if test $$failed -ne 0; then \
rval=`expr $$rval + $$failed`; \
fi; \
echo "$$dir: $$passed/$$total tests passed; $$failed/$$total tests failed"; \
done; \
exit $$rval; \
fi
clean:
-$(LIBTOOL) --mode=clean rm -f $(TEST_PROGS) *.lo *.o *.la *.a stamp-* core *.core core.* regress/*/*.out
mostlyclean: clean
distclean: clean
-rm -rf Makefile .libs
clobber: distclean
realclean: distclean
rm -f TAGS tags
cleandir: realclean
# Autogenerated dependencies, do not modify
aix.lo: $(srcdir)/aix.c $(incdir)/alloc.h $(incdir)/compat/stdbool.h \
$(incdir)/fatal.h $(incdir)/gettext.h $(incdir)/missing.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_util.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/aix.c
alloc.lo: $(srcdir)/alloc.c $(incdir)/alloc.h $(incdir)/fatal.h \
$(incdir)/gettext.h $(incdir)/missing.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/alloc.c
atobool.lo: $(srcdir)/atobool.c $(incdir)/compat/stdbool.h $(incdir)/missing.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_util.h \
$(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/atobool.c
atofoo_test.lo: $(srcdir)/regress/atofoo/atofoo_test.c \
$(incdir)/compat/stdbool.h $(incdir)/fatal.h \
$(incdir)/missing.h $(incdir)/sudo_util.h \
$(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/regress/atofoo/atofoo_test.c
atoid.lo: $(srcdir)/atoid.c $(incdir)/compat/stdbool.h $(incdir)/gettext.h \
$(incdir)/missing.h $(incdir)/sudo_debug.h $(incdir)/sudo_util.h \
$(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/atoid.c
atomode.lo: $(srcdir)/atomode.c $(incdir)/compat/stdbool.h $(incdir)/gettext.h \
$(incdir)/missing.h $(incdir)/sudo_debug.h $(incdir)/sudo_util.h \
$(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/atomode.c
clock_gettime.lo: $(srcdir)/clock_gettime.c $(incdir)/compat/timespec.h \
$(incdir)/missing.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/clock_gettime.c
closefrom.lo: $(srcdir)/closefrom.c $(incdir)/missing.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/closefrom.c
conf_test.lo: $(srcdir)/regress/sudo_conf/conf_test.c \
$(incdir)/compat/stdbool.h $(incdir)/missing.h $(incdir)/queue.h \
$(incdir)/sudo_conf.h $(incdir)/sudo_util.h \
$(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/regress/sudo_conf/conf_test.c
event.lo: $(srcdir)/event.c $(incdir)/alloc.h $(incdir)/compat/stdbool.h \
$(incdir)/fatal.h $(incdir)/missing.h $(incdir)/queue.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_event.h $(incdir)/sudo_util.h \
$(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/event.c
event_poll.lo: $(srcdir)/event_poll.c $(incdir)/alloc.h \
$(incdir)/compat/stdbool.h $(incdir)/fatal.h \
$(incdir)/missing.h $(incdir)/queue.h $(incdir)/sudo_debug.h \
$(incdir)/sudo_event.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/event_poll.c
event_select.lo: $(srcdir)/event_select.c $(incdir)/alloc.h \
$(incdir)/compat/stdbool.h $(incdir)/fatal.h \
$(incdir)/missing.h $(incdir)/queue.h $(incdir)/sudo_debug.h \
$(incdir)/sudo_event.h $(incdir)/sudo_util.h \
$(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/event_select.c
fatal.lo: $(srcdir)/fatal.c $(incdir)/alloc.h $(incdir)/compat/stdbool.h \
$(incdir)/fatal.h $(incdir)/gettext.h $(incdir)/missing.h \
$(incdir)/queue.h $(incdir)/sudo_plugin.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/fatal.c
fileops.lo: $(srcdir)/fileops.c $(incdir)/compat/stdbool.h \
$(incdir)/compat/timespec.h $(incdir)/fileops.h \
$(incdir)/missing.h $(incdir)/sudo_debug.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/fileops.c
fmt_string.lo: $(srcdir)/fmt_string.c $(incdir)/compat/stdbool.h \
$(incdir)/missing.h $(incdir)/sudo_debug.h \
$(incdir)/sudo_util.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/fmt_string.c
fnm_test.o: $(srcdir)/regress/fnmatch/fnm_test.c $(incdir)/compat/fnmatch.h \
$(incdir)/missing.h $(top_builddir)/config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/regress/fnmatch/fnm_test.c
fnmatch.lo: $(srcdir)/fnmatch.c $(incdir)/compat/charclass.h \
$(incdir)/compat/fnmatch.h $(incdir)/missing.h \
$(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/fnmatch.c
getaddrinfo.lo: $(srcdir)/getaddrinfo.c $(incdir)/compat/getaddrinfo.h \
$(incdir)/missing.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/getaddrinfo.c
getcwd.lo: $(srcdir)/getcwd.c $(incdir)/missing.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/getcwd.c
getgrouplist.lo: $(srcdir)/getgrouplist.c $(incdir)/compat/nss_dbdefs.h \
$(incdir)/compat/stdbool.h $(incdir)/missing.h \
$(incdir)/sudo_util.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/getgrouplist.c
getline.lo: $(srcdir)/getline.c $(incdir)/missing.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/getline.c
getopt_long.lo: $(srcdir)/getopt_long.c $(incdir)/compat/getopt.h \
$(incdir)/fatal.h $(incdir)/missing.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/getopt_long.c
gidlist.lo: $(srcdir)/gidlist.c $(incdir)/alloc.h $(incdir)/compat/stdbool.h \
$(incdir)/fatal.h $(incdir)/gettext.h $(incdir)/missing.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_util.h \
$(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/gidlist.c
glob.lo: $(srcdir)/glob.c $(incdir)/compat/charclass.h $(incdir)/compat/glob.h \
$(incdir)/missing.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/glob.c
globtest.o: $(srcdir)/regress/glob/globtest.c $(incdir)/compat/glob.h \
$(incdir)/missing.h $(top_builddir)/config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/regress/glob/globtest.c
hltq_test.lo: $(srcdir)/regress/tailq/hltq_test.c $(incdir)/compat/stdbool.h \
$(incdir)/fatal.h $(incdir)/missing.h $(incdir)/queue.h \
$(incdir)/sudo_util.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/regress/tailq/hltq_test.c
isblank.lo: $(srcdir)/isblank.c $(incdir)/missing.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/isblank.c
lbuf.lo: $(srcdir)/lbuf.c $(incdir)/alloc.h $(incdir)/fatal.h $(incdir)/lbuf.h \
$(incdir)/missing.h $(incdir)/sudo_debug.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/lbuf.c
locale_stub.lo: $(top_srcdir)/src/locale_stub.c $(incdir)/fatal.h \
$(incdir)/gettext.h $(incdir)/missing.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(top_srcdir)/src/locale_stub.c
memrchr.lo: $(srcdir)/memrchr.c $(incdir)/missing.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/memrchr.c
memset_s.lo: $(srcdir)/memset_s.c $(incdir)/missing.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/memset_s.c
mksiglist.lo: $(srcdir)/mksiglist.c $(incdir)/compat/mksiglist.h \
$(incdir)/missing.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/mksiglist.c
mksigname.lo: $(srcdir)/mksigname.c $(incdir)/compat/mksigname.h \
$(incdir)/missing.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/mksigname.c
mktemp.lo: $(srcdir)/mktemp.c $(incdir)/missing.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/mktemp.c
parseln_test.lo: $(srcdir)/regress/sudo_parseln/parseln_test.c \
$(incdir)/compat/stdbool.h $(incdir)/fileops.h \
$(incdir)/missing.h $(incdir)/sudo_util.h \
$(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/regress/sudo_parseln/parseln_test.c
progname.lo: $(srcdir)/progname.c $(incdir)/compat/stdbool.h \
$(incdir)/missing.h $(incdir)/sudo_util.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/progname.c
pw_dup.lo: $(srcdir)/pw_dup.c $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/pw_dup.c
secure_path.lo: $(srcdir)/secure_path.c $(incdir)/missing.h \
$(incdir)/secure_path.h $(incdir)/sudo_debug.h \
$(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/secure_path.c
setgroups.lo: $(srcdir)/setgroups.c $(incdir)/compat/stdbool.h \
$(incdir)/missing.h $(incdir)/sudo_debug.h $(incdir)/sudo_util.h \
$(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/setgroups.c
sha2.lo: $(srcdir)/sha2.c $(incdir)/compat/endian.h $(incdir)/compat/sha2.h \
$(incdir)/missing.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/sha2.c
sig2str.lo: $(srcdir)/sig2str.c $(incdir)/missing.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/sig2str.c
siglist.lo: siglist.c $(incdir)/missing.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) siglist.c
signame.lo: signame.c $(incdir)/missing.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) signame.c
snprintf.lo: $(srcdir)/snprintf.c $(incdir)/missing.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/snprintf.c
strlcat.lo: $(srcdir)/strlcat.c $(incdir)/missing.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/strlcat.c
strlcpy.lo: $(srcdir)/strlcpy.c $(incdir)/missing.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/strlcpy.c
strsignal.lo: $(srcdir)/strsignal.c $(incdir)/gettext.h $(incdir)/missing.h \
$(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/strsignal.c
strtonum.lo: $(srcdir)/strtonum.c $(incdir)/gettext.h $(incdir)/missing.h \
$(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/strtonum.c
sudo_conf.lo: $(srcdir)/sudo_conf.c $(incdir)/alloc.h \
$(incdir)/compat/stdbool.h $(incdir)/fatal.h $(incdir)/fileops.h \
$(incdir)/gettext.h $(incdir)/missing.h $(incdir)/queue.h \
$(incdir)/secure_path.h $(incdir)/sudo_conf.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_plugin.h \
$(incdir)/sudo_util.h $(top_builddir)/config.h \
$(top_builddir)/pathnames.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/sudo_conf.c
sudo_debug.lo: $(srcdir)/sudo_debug.c $(incdir)/alloc.h \
$(incdir)/compat/stdbool.h $(incdir)/fatal.h \
$(incdir)/gettext.h $(incdir)/missing.h $(incdir)/sudo_debug.h \
$(incdir)/sudo_plugin.h $(incdir)/sudo_util.h \
$(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/sudo_debug.c
sudo_dso.lo: $(srcdir)/sudo_dso.c $(incdir)/missing.h $(incdir)/sudo_dso.h \
$(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/sudo_dso.c
sudo_printf.lo: $(srcdir)/sudo_printf.c $(incdir)/missing.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_plugin.h \
$(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/sudo_printf.c
term.lo: $(srcdir)/term.c $(incdir)/compat/stdbool.h $(incdir)/missing.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_util.h $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/term.c
ttysize.lo: $(srcdir)/ttysize.c $(incdir)/compat/stdbool.h $(incdir)/missing.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_util.h \
$(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/ttysize.c
utimes.lo: $(srcdir)/utimes.c $(incdir)/compat/utime.h $(incdir)/missing.h \
$(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/utimes.c

207
lib/util/aix.c Normal file
View File

@@ -0,0 +1,207 @@
/*
* Copyright (c) 2008, 2010-2014 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/resource.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 <usersec.h>
#include <uinfo.h>
#define DEFAULT_TEXT_DOMAIN "sudo"
#include "gettext.h" /* must be included before missing.h */
#include "missing.h"
#include "alloc.h"
#include "fatal.h"
#include "sudo_debug.h"
#include "sudo_util.h"
#ifdef HAVE_GETUSERATTR
#ifndef HAVE_SETRLIMIT64
# define setrlimit64(a, b) setrlimit(a, b)
# define rlimit64 rlimit
# define rlim64_t rlim_t
# define RLIM64_INFINITY RLIM_INFINITY
#endif /* HAVE_SETRLIMIT64 */
#ifndef RLIM_SAVED_MAX
# define RLIM_SAVED_MAX RLIM64_INFINITY
#endif
struct aix_limit {
int resource;
char *soft;
char *hard;
int factor;
};
static struct aix_limit aix_limits[] = {
{ RLIMIT_FSIZE, S_UFSIZE, S_UFSIZE_HARD, 512 },
{ RLIMIT_CPU, S_UCPU, S_UCPU_HARD, 1 },
{ RLIMIT_DATA, S_UDATA, S_UDATA_HARD, 512 },
{ RLIMIT_STACK, S_USTACK, S_USTACK_HARD, 512 },
{ RLIMIT_RSS, S_URSS, S_URSS_HARD, 512 },
{ RLIMIT_CORE, S_UCORE, S_UCORE_HARD, 512 },
{ RLIMIT_NOFILE, S_UNOFILE, S_UNOFILE_HARD, 1 }
};
static int
aix_getlimit(char *user, char *lim, int *valp)
{
debug_decl(aix_getlimit, SUDO_DEBUG_UTIL)
if (getuserattr(user, lim, valp, SEC_INT) != 0)
debug_return_int(-1);
debug_return_int(0);
}
static int
aix_setlimits(char *user)
{
struct rlimit64 rlim;
int val;
size_t n;
debug_decl(aix_setlimits, SUDO_DEBUG_UTIL)
if (setuserdb(S_READ) != 0) {
warning(U_("unable to open userdb"));
debug_return_int(-1);
}
/*
* For each resource limit, get the soft/hard values for the user
* and set those values via setrlimit64(). Must be run as euid 0.
*/
for (n = 0; n < sizeof(aix_limits) / sizeof(aix_limits[0]); n++) {
/*
* We have two strategies, depending on whether or not the
* hard limit has been defined.
*/
if (aix_getlimit(user, aix_limits[n].hard, &val) == 0) {
rlim.rlim_max = val == -1 ? RLIM64_INFINITY : (rlim64_t)val * aix_limits[n].factor;
if (aix_getlimit(user, aix_limits[n].soft, &val) == 0)
rlim.rlim_cur = val == -1 ? RLIM64_INFINITY : (rlim64_t)val * aix_limits[n].factor;
else
rlim.rlim_cur = rlim.rlim_max; /* soft not specd, use hard */
} else {
/* No hard limit set, try soft limit, if it exists. */
if (aix_getlimit(user, aix_limits[n].soft, &val) == -1)
continue;
rlim.rlim_cur = val == -1 ? RLIM64_INFINITY : (rlim64_t)val * aix_limits[n].factor;
/* Set hard limit per AIX /etc/security/limits documentation. */
switch (aix_limits[n].resource) {
case RLIMIT_CPU:
case RLIMIT_FSIZE:
rlim.rlim_max = rlim.rlim_cur;
break;
case RLIMIT_STACK:
rlim.rlim_max = RLIM_SAVED_MAX;
break;
default:
rlim.rlim_max = RLIM64_INFINITY;
break;
}
}
(void)setrlimit64(aix_limits[n].resource, &rlim);
}
enduserdb();
debug_return_int(0);
}
#ifdef HAVE_SETAUTHDB
/*
* Look up administrative domain for user (SYSTEM in /etc/security/user) and
* set it as the default for the process. This ensures that password and
* group lookups are made against the correct source (files, NIS, LDAP, etc).
*/
int
aix_setauthdb(char *user)
{
char *registry;
debug_decl(aix_setauthdb, SUDO_DEBUG_UTIL)
if (user != NULL) {
if (setuserdb(S_READ) != 0) {
warning(U_("unable to open userdb"));
debug_return_int(-1);
}
if (getuserattr(user, S_REGISTRY, &registry, SEC_CHAR) == 0) {
if (setauthdb(registry, NULL) != 0) {
warning(U_("unable to switch to registry \"%s\" for %s"),
registry, user);
debug_return_int(-1);
}
}
enduserdb();
}
debug_return_int(0);
}
/*
* Restore the saved administrative domain, if any.
*/
int
aix_restoreauthdb(void)
{
debug_decl(aix_setauthdb, SUDO_DEBUG_UTIL)
if (setauthdb(NULL, NULL) != 0) {
warning(U_("unable to restore registry"));
debug_return_int(-1);
}
debug_return_int(0);
}
#endif
int
aix_prep_user(char *user, const char *tty)
{
char *info;
int len;
debug_decl(aix_setauthdb, SUDO_DEBUG_UTIL)
/* set usrinfo, like login(1) does */
len = easprintf(&info, "NAME=%s%cLOGIN=%s%cLOGNAME=%s%cTTY=%s%c",
user, '\0', user, '\0', user, '\0', tty ? tty : "", '\0');
(void)usrinfo(SETUINFO, info, len);
efree(info);
#ifdef HAVE_SETAUTHDB
/* set administrative domain */
if (aix_setauthdb(user) != 0)
debug_return_int(-1);
#endif
/* set resource limits */
if (aix_setlimits(user) != 0)
debug_return_int(-1);
debug_return_int(0);
}
#endif /* HAVE_GETUSERATTR */

271
lib/util/alloc.c Normal file
View File

@@ -0,0 +1,271 @@
/*
* Copyright (c) 1999-2005, 2007, 2010-2014
* 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 <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>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRING_H */
#if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS)
# include <malloc.h>
#endif /* HAVE_MALLOC_H && !STDC_HEADERS */
#ifdef HAVE_INTTYPES_H
# include <inttypes.h>
#endif
#include <limits.h>
#define DEFAULT_TEXT_DOMAIN "sudo"
#include "gettext.h" /* must be included before missing.h */
#include "missing.h"
#include "alloc.h"
#include "fatal.h"
/*
* 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). This just means that
* emallocarray() and ereallocarray() cannot allocate huge amounts on such a
* platform but that is OK since sudo doesn't need to do so anyway.
*/
#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 */
/*
* emalloc() calls the system malloc(3) and exits with an error if
* malloc(3) fails.
*/
void *
emalloc(size_t size)
{
void *ptr;
if (size == 0)
fatalx_nodebug(_("internal error, tried to emalloc(0)"));
if ((ptr = malloc(size)) == NULL)
fatal_nodebug(NULL);
return ptr;
}
/*
* emallocarray() allocates nmemb * size bytes and exits with an error
* if overflow would occur or if the system malloc(3) fails.
*/
void *
emallocarray(size_t nmemb, size_t size)
{
void *ptr;
if (nmemb == 0 || size == 0)
fatalx_nodebug(_("internal error, tried to emallocarray(0)"));
if (nmemb > SIZE_MAX / size)
fatalx_nodebug(_("internal error, %s overflow"), "emallocarray");
size *= nmemb;
if ((ptr = malloc(size)) == NULL)
fatal_nodebug(NULL);
return ptr;
}
/*
* ecalloc() allocates nmemb * size bytes and exits with an error
* if overflow would occur or if the system malloc(3) fails.
* On success, the allocated space is zero-filled.
*/
void *
ecalloc(size_t nmemb, size_t size)
{
void *ptr;
if (nmemb == 0 || size == 0)
fatalx_nodebug(_("internal error, tried to ecalloc(0)"));
if (nmemb != 1) {
if (nmemb > SIZE_MAX / size)
fatalx_nodebug(_("internal error, %s overflow"), "ecalloc");
size *= nmemb;
}
if ((ptr = malloc(size)) == NULL)
fatal_nodebug(NULL);
memset(ptr, 0, size);
return ptr;
}
/*
* erealloc() calls the system realloc(3) and exits with an error if
* realloc(3) fails. You can call erealloc() with a NULL pointer even
* if the system realloc(3) does not support this.
*/
void *
erealloc(void *ptr, size_t size)
{
if (size == 0)
fatalx_nodebug(_("internal error, tried to erealloc(0)"));
ptr = ptr ? realloc(ptr, size) : malloc(size);
if (ptr == NULL)
fatal_nodebug(NULL);
return ptr;
}
/*
* ereallocarray() realloc(3)s nmemb * size bytes and exits with an error
* if overflow would occur or if the system malloc(3)/realloc(3) fails.
* You can call erealloc() with a NULL pointer even if the system realloc(3)
* does not support this.
*/
void *
ereallocarray(void *ptr, size_t nmemb, size_t size)
{
if (nmemb == 0 || size == 0)
fatalx_nodebug(_("internal error, tried to ereallocarray(0)"));
if (nmemb > SIZE_MAX / size)
fatalx_nodebug(_("internal error, %s overflow"), "ereallocarray");
size *= nmemb;
ptr = ptr ? realloc(ptr, size) : malloc(size);
if (ptr == NULL)
fatal_nodebug(NULL);
return ptr;
}
/*
* erecalloc() realloc(3)s nmemb * msize bytes and exits with an error
* if overflow would occur or if the system malloc(3)/realloc(3) fails.
* On success, the new space is zero-filled. You can call erealloc()
* with a NULL pointer even if the system realloc(3) does not support this.
*/
void *
erecalloc(void *ptr, size_t onmemb, size_t nmemb, size_t msize)
{
size_t size;
if (nmemb == 0 || msize == 0)
fatalx_nodebug(_("internal error, tried to erecalloc(0)"));
if (nmemb > SIZE_MAX / msize)
fatalx_nodebug(_("internal error, %s overflow"), "erecalloc");
size = nmemb * msize;
ptr = ptr ? realloc(ptr, size) : malloc(size);
if (ptr == NULL)
fatal_nodebug(NULL);
if (nmemb > onmemb) {
size = (nmemb - onmemb) * msize;
memset((char *)ptr + (onmemb * msize), 0, size);
}
return ptr;
}
/*
* estrdup() is like strdup(3) except that it exits with an error if
* malloc(3) fails. NOTE: unlike strdup(3), estrdup(NULL) is legal.
*/
char *
estrdup(const char *src)
{
char *dst = NULL;
size_t len;
if (src != NULL) {
len = strlen(src);
dst = (char *) emalloc(len + 1);
(void) memcpy(dst, src, len);
dst[len] = '\0';
}
return dst;
}
/*
* estrdup() is like strndup(3) except that it exits with an error if
* malloc(3) fails. NOTE: unlike strdup(3), estrdup(NULL) is legal.
*/
char *
estrndup(const char *src, size_t maxlen)
{
char *dst = NULL;
size_t len = 0;
if (src != NULL) {
while (maxlen != 0 && src[len] != '\0') {
len++;
maxlen--;
}
dst = (char *) emalloc(len + 1);
(void) memcpy(dst, src, len);
dst[len] = '\0';
}
return dst;
}
/*
* easprintf() calls vasprintf() and exits with an error if vasprintf()
* returns -1 (out of memory).
*/
int
easprintf(char **ret, const char *fmt, ...)
{
int len;
va_list ap;
va_start(ap, fmt);
len = vasprintf(ret, fmt, ap);
va_end(ap);
if (len == -1)
fatal_nodebug(NULL);
return len;
}
/*
* evasprintf() calls vasprintf() and exits with an error if vasprintf()
* returns -1 (out of memory).
*/
int
evasprintf(char **ret, const char *format, va_list args)
{
int len;
if ((len = vasprintf(ret, format, args)) == -1)
fatal_nodebug(NULL);
return len;
}

84
lib/util/atobool.c Normal file
View File

@@ -0,0 +1,84 @@
/*
* Copyright (c) 2010-2014 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 */
#ifdef HAVE_STRING_H
# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
# include <memory.h>
# endif
# include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#include "missing.h"
#include "sudo_debug.h"
#include "sudo_util.h"
int
atobool(const char *str)
{
debug_decl(atobool, SUDO_DEBUG_UTIL)
switch (*str) {
case '0':
case '1':
if (str[1] == '\0')
debug_return_int(*str - '0');
break;
case 'y':
case 'Y':
if (strcasecmp(str, "yes") == 0)
debug_return_int(1);
break;
case 't':
case 'T':
if (strcasecmp(str, "true") == 0)
debug_return_int(1);
break;
case 'o':
case 'O':
if (strcasecmp(str, "on") == 0)
debug_return_int(1);
if (strcasecmp(str, "off") == 0)
debug_return_int(0);
break;
case 'n':
case 'N':
if (strcasecmp(str, "no") == 0)
debug_return_int(0);
break;
case 'f':
case 'F':
if (strcasecmp(str, "false") == 0)
debug_return_int(0);
break;
}
debug_return_int(-1);
}

125
lib/util/atoid.c Normal file
View File

@@ -0,0 +1,125 @@
/*
* Copyright (c) 2013 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 */
#if defined(HAVE_STDINT_H)
# include <stdint.h>
#elif defined(HAVE_INTTYPES_H)
# include <inttypes.h>
#endif
#ifdef HAVE_STDBOOL_H
# include <stdbool.h>
#else
# include "compat/stdbool.h"
#endif
#include <errno.h>
#include <limits.h>
#define DEFAULT_TEXT_DOMAIN "sudo"
#include "gettext.h" /* must be included before missing.h */
#include "missing.h"
#include "sudo_debug.h"
#include "sudo_util.h"
/*
* Parse a uid/gid in string form.
* If sep is non-NULL, it contains valid separator characters (e.g. comma, space)
* If endp is non-NULL it is set to the next char after the ID.
* On success, returns the parsed ID and clears errstr.
* On error, returns 0 and sets errstr.
*/
id_t
atoid(const char *p, const char *sep, char **endp, const char **errstr)
{
char *ep;
id_t rval = 0;
bool valid = false;
debug_decl(atoid, SUDO_DEBUG_UTIL)
if (sep == NULL)
sep = "";
errno = 0;
if (*p == '-') {
long lval = strtol(p, &ep, 10);
if (ep != p) {
/* check for valid separator (including '\0') */
do {
if (*ep == *sep)
valid = true;
} while (*sep++ != '\0');
}
if (!valid) {
if (errstr != NULL)
*errstr = N_("invalid value");
errno = EINVAL;
goto done;
}
if ((errno == ERANGE && lval == LONG_MAX) || lval > INT_MAX) {
errno = ERANGE;
if (errstr != NULL)
*errstr = N_("value too large");
goto done;
}
if ((errno == ERANGE && lval == LONG_MIN) || lval < INT_MIN) {
errno = ERANGE;
if (errstr != NULL)
*errstr = N_("value too small");
goto done;
}
rval = (id_t)lval;
} else {
unsigned long ulval = strtoul(p, &ep, 10);
if (ep != p) {
/* check for valid separator (including '\0') */
do {
if (*ep == *sep)
valid = true;
} while (*sep++ != '\0');
}
if (!valid) {
if (errstr != NULL)
*errstr = N_("invalid value");
errno = EINVAL;
goto done;
}
if ((errno == ERANGE && ulval == ULONG_MAX) || ulval > UINT_MAX) {
errno = ERANGE;
if (errstr != NULL)
*errstr = N_("value too large");
goto done;
}
rval = (id_t)ulval;
}
if (errstr != NULL)
*errstr = NULL;
if (endp != NULL)
*endp = ep;
done:
debug_return_int(rval);
}

68
lib/util/atomode.c Normal file
View File

@@ -0,0 +1,68 @@
/*
* Copyright (c) 2013-2014 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 <errno.h>
#define DEFAULT_TEXT_DOMAIN "sudo"
#include "gettext.h" /* must be included before missing.h */
#include "missing.h"
#include "sudo_debug.h"
#include "sudo_util.h"
/*
* Parse an octal file mode in the range [0, 0777].
* On success, returns the parsed mode and clears errstr.
* On error, returns 0 and sets errstr.
*/
int
atomode(const char *cp, const char **errstr)
{
char *ep;
long lval;
debug_decl(atomode, SUDO_DEBUG_UTIL)
errno = 0;
lval = strtol(cp, &ep, 8);
if (ep == cp || *ep != '\0') {
if (errstr != NULL)
*errstr = N_("invalid value");
errno = EINVAL;
debug_return_int(0);
}
if (lval < 0 || lval > 0777) {
if (errstr != NULL)
*errstr = lval < 0 ? N_("value too small") : N_("value too large");
errno = ERANGE;
debug_return_int(0);
}
if (errstr != NULL)
*errstr = NULL;
debug_return_int((int)lval);
}

80
lib/util/clock_gettime.c Normal file
View File

@@ -0,0 +1,80 @@
/*
* Copyright (c) 2014 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>
#if !defined(HAVE_CLOCK_GETTIME)
#include <sys/types.h>
#include <sys/time.h>
#include <errno.h>
#include <stdio.h>
#ifdef TIME_WITH_SYS_TIME
# include <time.h>
#endif
#ifndef HAVE_STRUCT_TIMESPEC
# include "compat/timespec.h"
#endif
#include "missing.h"
#ifdef __MACH__
# include <mach/mach.h>
# include <mach/mach_time.h>
# include <mach/clock.h>
#endif
/*
* Trivial clock_gettime() that supports CLOCK_REALTIME
* (and CLOCK_MONOTONIC on Mach).
*/
int
clock_gettime(clockid_t clock_id, struct timespec *ts)
{
switch (clock_id) {
#ifdef __MACH__
case CLOCK_MONOTONIC:
{
uint64_t abstime, nsec;
static mach_timebase_info_data_t timebase_info;
if (timebase_info.denom == 0)
(void) mach_timebase_info(&timebase_info);
abstime = mach_absolute_time();
nsec = abstime * timebase_info.numer / timebase_info.denom;
ts->tv_sec = nsec / 1000000000;
ts->tv_nsec = nsec % 1000000000;
return 0;
}
#endif
case CLOCK_REALTIME:
{
struct timeval tv;
gettimeofday(&tv, NULL);
ts->tv_sec = tv.tv_sec;
ts->tv_nsec = tv.tv_usec * 1000;
return 0;
}
default:
errno = EINVAL;
return -1;
}
}
#endif /* !HAVE_CLOCK_GETTIME */

152
lib/util/closefrom.c Normal file
View File

@@ -0,0 +1,152 @@
/*
* Copyright (c) 2004-2005, 2007, 2010, 2012-2013
* 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>
#ifndef HAVE_CLOSEFROM
#include <sys/types.h>
#include <unistd.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 <fcntl.h>
#include <limits.h>
#ifdef HAVE_PSTAT_GETPROC
# include <sys/param.h>
# include <sys/pstat.h>
#else
# 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
#endif
#include "missing.h"
#if defined(HAVE_FCNTL_CLOSEM) && !defined(HAVE_DIRFD)
# define closefrom closefrom_fallback
#endif
/*
* Close all file descriptors greater than or equal to lowfd.
* This is the expensive (fallback) method.
*/
void
closefrom_fallback(int lowfd)
{
long fd, maxfd;
/*
* Fall back on sysconf() or getdtablesize(). We avoid checking
* resource limits since it is possible to open a file descriptor
* and then drop the rlimit such that it is below the open fd.
*/
#ifdef HAVE_SYSCONF
maxfd = sysconf(_SC_OPEN_MAX);
#else
maxfd = getdtablesize();
#endif /* HAVE_SYSCONF */
if (maxfd < 0)
maxfd = OPEN_MAX;
for (fd = lowfd; fd < maxfd; fd++) {
#ifdef __APPLE__
/* Avoid potential libdispatch crash when we close its fds. */
(void) fcntl((int) fd, F_SETFD, FD_CLOEXEC);
#else
(void) close((int) fd);
#endif
}
}
/*
* Close all file descriptors greater than or equal to lowfd.
* We try the fast way first, falling back on the slow method.
*/
#if defined(HAVE_FCNTL_CLOSEM)
void
closefrom(int lowfd)
{
if (fcntl(lowfd, F_CLOSEM, 0) == -1)
closefrom_fallback(lowfd);
}
#elif defined(HAVE_PSTAT_GETPROC)
void
closefrom(int lowfd)
{
struct pst_status pstat;
int fd;
if (pstat_getproc(&pstat, sizeof(pstat), 0, getpid()) != -1) {
for (fd = lowfd; fd <= pstat.pst_highestfd; fd++)
(void) close(fd);
} else {
closefrom_fallback(lowfd);
}
}
#elif defined(HAVE_DIRFD)
void
closefrom(int lowfd)
{
const char *path;
DIR *dirp;
/* Use /proc/self/fd (or /dev/fd on FreeBSD) if it exists. */
# if defined(__FreeBSD__) || defined(__APPLE__)
path = "/dev/fd";
# else
path = "/proc/self/fd";
# endif
if ((dirp = opendir(path)) != NULL) {
struct dirent *dent;
while ((dent = readdir(dirp)) != NULL) {
const char *errstr;
int fd = strtonum(dent->d_name, lowfd, INT_MAX, &errstr);
if (errstr == NULL && fd != dirfd(dirp)) {
# ifdef __APPLE__
/* Avoid potential libdispatch crash when we close its fds. */
(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
# else
(void) close(fd);
# endif
}
}
(void) closedir(dirp);
} else
closefrom_fallback(lowfd);
}
#endif /* HAVE_FCNTL_CLOSEM */
#endif /* HAVE_CLOSEFROM */

403
lib/util/event.c Normal file
View File

@@ -0,0 +1,403 @@
/*
* Copyright (c) 2013-2014 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>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STDBOOL_H
# include <stdbool.h>
#else
# include "compat/stdbool.h"
#endif /* HAVE_STDBOOL_H */
#ifdef HAVE_STRING_H
# include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <errno.h>
#include "missing.h"
#include "alloc.h"
#include "fatal.h"
#include "sudo_debug.h"
#include "sudo_event.h"
#include "sudo_util.h"
/* XXX - use non-exiting allocators? */
struct sudo_event_base *
sudo_ev_base_alloc(void)
{
struct sudo_event_base *base;
debug_decl(sudo_ev_base_alloc, SUDO_DEBUG_EVENT)
base = ecalloc(1, sizeof(*base));
TAILQ_INIT(&base->events);
TAILQ_INIT(&base->timeouts);
if (sudo_ev_base_alloc_impl(base) != 0) {
efree(base);
base = NULL;
}
debug_return_ptr(base);
}
void
sudo_ev_base_free(struct sudo_event_base *base)
{
struct sudo_event *ev, *next;
debug_decl(sudo_ev_base_free, SUDO_DEBUG_EVENT)
/* Remove any existing events before freeing the base. */
TAILQ_FOREACH_SAFE(ev, &base->events, entries, next) {
sudo_ev_del(base, ev);
}
sudo_ev_base_free_impl(base);
efree(base);
debug_return;
}
struct sudo_event *
sudo_ev_alloc(int fd, short events, sudo_ev_callback_t callback, void *closure)
{
struct sudo_event *ev;
debug_decl(sudo_ev_alloc, SUDO_DEBUG_EVENT)
/* XXX - sanity check events value */
ev = ecalloc(1, sizeof(*ev));
ev->fd = fd;
ev->events = events;
ev->pfd_idx = -1;
ev->callback = callback;
ev->closure = closure;
debug_return_ptr(ev);
}
void
sudo_ev_free(struct sudo_event *ev)
{
debug_decl(sudo_ev_free, SUDO_DEBUG_EVENT)
/* Make sure ev is not in use before freeing it. */
if (ISSET(ev->flags, SUDO_EVQ_INSERTED))
(void)sudo_ev_del(NULL, ev);
free(ev);
debug_return;
}
int
sudo_ev_add(struct sudo_event_base *base, struct sudo_event *ev,
struct timeval *timo, bool tohead)
{
debug_decl(sudo_ev_add, SUDO_DEBUG_EVENT)
/* If no base specified, use existing one. */
if (base == NULL) {
if (ev->base == NULL) {
sudo_debug_printf(SUDO_DEBUG_ERROR, "%s: no base specified",
__func__);
debug_return_int(-1);
}
base = ev->base;
}
/* Only add new events to the events list. */
if (ISSET(ev->flags, SUDO_EVQ_INSERTED)) {
/* If event no longer has a timeout, remove from timeouts queue. */
if (timo == NULL && ISSET(ev->flags, SUDO_EVQ_TIMEOUTS)) {
sudo_debug_printf(SUDO_DEBUG_INFO,
"%s: removing event %p from timeouts queue", __func__, ev);
CLR(ev->flags, SUDO_EVQ_TIMEOUTS);
TAILQ_REMOVE(&base->timeouts, ev, timeouts_entries);
}
} else {
/* Add event to the base. */
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: adding event %p to base %p",
__func__, ev, base);
if (ev->events & (SUDO_EV_READ|SUDO_EV_WRITE)) {
if (sudo_ev_add_impl(base, ev) != 0)
debug_return_int(-1);
}
ev->base = base;
if (tohead) {
TAILQ_INSERT_HEAD(&base->events, ev, entries);
} else {
TAILQ_INSERT_TAIL(&base->events, ev, entries);
}
SET(ev->flags, SUDO_EVQ_INSERTED);
}
/* Timeouts can be changed for existing events. */
if (timo != NULL) {
struct sudo_event *evtmp;
if (ISSET(ev->flags, SUDO_EVQ_TIMEOUTS)) {
/* Remove from timeouts list, then add back. */
TAILQ_REMOVE(&base->timeouts, ev, timeouts_entries);
}
/* Convert to absolute time and insert in sorted order; O(n). */
gettimeofday(&ev->timeout, NULL);
ev->timeout.tv_sec += timo->tv_sec;
ev->timeout.tv_usec += timo->tv_usec;
TAILQ_FOREACH(evtmp, &base->timeouts, timeouts_entries) {
if (sudo_timevalcmp(timo, &evtmp->timeout, <))
break;
}
if (evtmp != NULL) {
TAILQ_INSERT_BEFORE(evtmp, ev, timeouts_entries);
} else {
TAILQ_INSERT_TAIL(&base->timeouts, ev, timeouts_entries);
}
SET(ev->flags, SUDO_EVQ_TIMEOUTS);
}
debug_return_int(0);
}
int
sudo_ev_del(struct sudo_event_base *base, struct sudo_event *ev)
{
debug_decl(sudo_ev_del, SUDO_DEBUG_EVENT)
/* Make sure event is really in the queue. */
if (!ISSET(ev->flags, SUDO_EVQ_INSERTED)) {
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: event %p not in queue",
__func__, ev);
debug_return_int(0);
}
/* Check for event base mismatch, if one is specified. */
if (base == NULL) {
if (ev->base == NULL) {
sudo_debug_printf(SUDO_DEBUG_ERROR, "%s: no base specified",
__func__);
debug_return_int(-1);
}
base = ev->base;
} else if (base != ev->base) {
sudo_debug_printf(SUDO_DEBUG_ERROR, "%s: mismatch base %p, ev->base %p",
__func__, base, ev->base);
debug_return_int(-1);
}
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: removing event %p from base %p",
__func__, ev, base);
/* Call backend. */
if (ev->events & (SUDO_EV_READ|SUDO_EV_WRITE)) {
if (sudo_ev_del_impl(base, ev) != 0)
debug_return_int(-1);
}
/* Unlink from event list. */
TAILQ_REMOVE(&base->events, ev, entries);
/* Unlink from timeouts list. */
if (ISSET(ev->flags, SUDO_EVQ_TIMEOUTS))
TAILQ_REMOVE(&base->timeouts, ev, timeouts_entries);
/* Unlink from active list and update base pointers as needed. */
if (ISSET(ev->flags, SUDO_EVQ_ACTIVE))
TAILQ_REMOVE(&base->active, ev, active_entries);
/* Mark event unused. */
ev->flags = 0;
ev->pfd_idx = -1;
debug_return_int(0);
}
/*
* Run main event loop.
* Returns 0 on success, 1 if no events registered and -1 on error
*/
int
sudo_ev_loop(struct sudo_event_base *base, int flags)
{
struct timeval now;
struct sudo_event *ev;
int nready, rc = 0;
debug_decl(sudo_ev_loop, SUDO_DEBUG_EVENT)
/*
* If sudo_ev_loopexit() was called when events were not running
* the next invocation of sudo_ev_loop() only runs once.
* All other base flags are ignored unless we are running events.
*/
if (ISSET(base->flags, SUDO_EVBASE_LOOPEXIT))
SET(flags, SUDO_EVLOOP_ONCE);
base->flags = 0;
for (;;) {
rescan:
/* Make sure we have some events. */
if (TAILQ_EMPTY(&base->events)) {
rc = 1;
break;
}
/* Call backend to scan for I/O events. */
TAILQ_INIT(&base->active);
nready = sudo_ev_scan_impl(base, flags);
switch (nready) {
case -1:
if (errno == EINTR || errno == ENOMEM)
continue;
rc = -1;
goto done;
case 0:
/* Timed out, activate timeout events. */
gettimeofday(&now, NULL);
while ((ev = TAILQ_FIRST(&base->timeouts)) != NULL) {
if (sudo_timevalcmp(&ev->timeout, &now, >))
break;
/* Remove from timeouts list. */
CLR(ev->flags, SUDO_EVQ_TIMEOUTS);
TAILQ_REMOVE(&base->timeouts, ev, timeouts_entries);
/* Make event active. */
ev->revents = SUDO_EV_TIMEOUT;
TAILQ_INSERT_TAIL(&base->active, ev, active_entries);
SET(ev->flags, SUDO_EVQ_ACTIVE);
}
if (ISSET(flags, SUDO_EVLOOP_NONBLOCK)) {
/* If nonblocking, return immediately if no active events. */
if (TAILQ_EMPTY(&base->active))
goto done;
}
break;
default:
/* I/O events active, sudo_ev_scan_impl() already added them. */
break;
}
/*
* Service each event in the active queue.
* We store the current event pointer in the base so that
* it can be cleared by sudo_ev_del(). This prevents a use
* after free if the callback frees its own event.
*/
while ((ev = TAILQ_FIRST(&base->active)) != NULL) {
/* Pop first event off the active queue. */
CLR(ev->flags, SUDO_EVQ_ACTIVE);
TAILQ_REMOVE(&base->active, ev, active_entries);
/* Remove from base unless persistent. */
if (!ISSET(ev->events, SUDO_EV_PERSIST))
sudo_ev_del(base, ev);
ev->callback(ev->fd, ev->revents,
ev->closure == sudo_ev_self_cbarg() ? ev : ev->closure);
if (ISSET(base->flags, SUDO_EVBASE_LOOPBREAK)) {
/* Stop processing events immediately. */
SET(base->flags, SUDO_EVBASE_GOT_BREAK);
while ((ev = TAILQ_FIRST(&base->active)) != NULL) {
CLR(ev->flags, SUDO_EVQ_ACTIVE);
TAILQ_REMOVE(&base->active, ev, active_entries);
}
goto done;
}
if (ISSET(base->flags, SUDO_EVBASE_LOOPCONT)) {
/* Rescan events and start polling again. */
CLR(base->flags, SUDO_EVBASE_LOOPCONT);
if (!ISSET(flags, SUDO_EVLOOP_ONCE)) {
while ((ev = TAILQ_FIRST(&base->active)) != NULL) {
CLR(ev->flags, SUDO_EVQ_ACTIVE);
TAILQ_REMOVE(&base->active, ev, active_entries);
}
goto rescan;
}
}
}
if (ISSET(base->flags, SUDO_EVBASE_LOOPEXIT)) {
/* exit loop after once through */
SET(base->flags, SUDO_EVBASE_GOT_EXIT);
goto done;
}
if (ISSET(flags, SUDO_EVLOOP_ONCE))
break;
}
done:
base->flags &= SUDO_EVBASE_GOT_MASK;
debug_return_int(rc);
}
void
sudo_ev_loopexit(struct sudo_event_base *base)
{
debug_decl(sudo_ev_loopexit, SUDO_DEBUG_EVENT)
SET(base->flags, SUDO_EVBASE_LOOPEXIT);
debug_return;
}
void
sudo_ev_loopbreak(struct sudo_event_base *base)
{
debug_decl(sudo_ev_loopbreak, SUDO_DEBUG_EVENT)
SET(base->flags, SUDO_EVBASE_LOOPBREAK);
debug_return;
}
void
sudo_ev_loopcontinue(struct sudo_event_base *base)
{
debug_decl(sudo_ev_loopcontinue, SUDO_DEBUG_EVENT)
SET(base->flags, SUDO_EVBASE_LOOPCONT);
debug_return;
}
bool
sudo_ev_got_exit(struct sudo_event_base *base)
{
debug_decl(sudo_ev_got_exit, SUDO_DEBUG_EVENT)
debug_return_bool(ISSET(base->flags, SUDO_EVBASE_GOT_EXIT));
}
bool
sudo_ev_got_break(struct sudo_event_base *base)
{
debug_decl(sudo_ev_got_break, SUDO_DEBUG_EVENT)
debug_return_bool(ISSET(base->flags, SUDO_EVBASE_GOT_BREAK));
}
int
sudo_ev_get_timeleft(struct sudo_event *ev, struct timeval *tv)
{
struct timeval now;
debug_decl(sudo_ev_get_timeleft, SUDO_DEBUG_EVENT)
if (!ISSET(ev->flags, SUDO_EVQ_TIMEOUTS)) {
sudo_timevalclear(tv);
debug_return_int(-1);
}
gettimeofday(&now, NULL);
sudo_timevalsub(&ev->timeout, &now, tv);
if (tv->tv_sec < 0 || (tv->tv_sec == 0 && tv->tv_usec < 0))
sudo_timevalclear(tv);
debug_return_int(0);
}

183
lib/util/event_poll.c Normal file
View File

@@ -0,0 +1,183 @@
/*
* Copyright (c) 2013-2014 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>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STDBOOL_H
# include <stdbool.h>
#else
# include "compat/stdbool.h"
#endif /* HAVE_STDBOOL_H */
#ifdef HAVE_STRING_H
# include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <errno.h>
#include <poll.h>
#include "missing.h"
#include "alloc.h"
#include "fatal.h"
#include "sudo_debug.h"
#include "sudo_event.h"
/* XXX - use non-exiting allocators? */
int
sudo_ev_base_alloc_impl(struct sudo_event_base *base)
{
int i;
debug_decl(sudo_ev_base_alloc_impl, SUDO_DEBUG_EVENT)
base->pfd_high = -1;
base->pfd_max = 32;
base->pfds = ereallocarray(NULL, base->pfd_max, sizeof(struct pollfd));
for (i = 0; i < base->pfd_max; i++) {
base->pfds[i].fd = -1;
}
debug_return_int(0);
}
void
sudo_ev_base_free_impl(struct sudo_event_base *base)
{
debug_decl(sudo_ev_base_free_impl, SUDO_DEBUG_EVENT)
efree(base->pfds);
debug_return;
}
int
sudo_ev_add_impl(struct sudo_event_base *base, struct sudo_event *ev)
{
struct pollfd *pfd;
debug_decl(sudo_ev_add_impl, SUDO_DEBUG_EVENT)
/* If out of space in pfds array, realloc. */
if (base->pfd_free == base->pfd_max) {
int i;
base->pfd_max <<= 1;
base->pfds =
ereallocarray(base->pfds, base->pfd_max, sizeof(struct pollfd));
for (i = base->pfd_free; i < base->pfd_max; i++) {
base->pfds[i].fd = -1;
}
}
/* Fill in pfd entry. */
ev->pfd_idx = base->pfd_free;
pfd = &base->pfds[ev->pfd_idx];
pfd->fd = ev->fd;
pfd->events = 0;
if (ISSET(ev->events, SUDO_EV_READ))
pfd->events |= POLLIN;
if (ISSET(ev->events, SUDO_EV_WRITE))
pfd->events |= POLLOUT;
/* Update pfd_high and pfd_free. */
if (ev->pfd_idx > base->pfd_high)
base->pfd_high = ev->pfd_idx;
for (;;) {
if (++base->pfd_free == base->pfd_max)
break;
if (base->pfds[base->pfd_free].fd == -1)
break;
}
debug_return_int(0);
}
int
sudo_ev_del_impl(struct sudo_event_base *base, struct sudo_event *ev)
{
debug_decl(sudo_ev_del_impl, SUDO_DEBUG_EVENT)
/* Mark pfd entry unused, add to free list and adjust high slot. */
base->pfds[ev->pfd_idx].fd = -1;
if (ev->pfd_idx < base->pfd_free)
base->pfd_free = ev->pfd_idx;
while (base->pfd_high >= 0 && base->pfds[base->pfd_high].fd == -1)
base->pfd_high--;
debug_return_int(0);
}
int
sudo_ev_scan_impl(struct sudo_event_base *base, int flags)
{
struct sudo_event *ev;
int nready, timeout;
struct timeval now;
debug_decl(sudo_ev_scan_impl, SUDO_DEBUG_EVENT)
if ((ev = TAILQ_FIRST(&base->timeouts)) != NULL) {
struct timeval *timo = &ev->timeout;
gettimeofday(&now, NULL);
timeout = ((timo->tv_sec - now.tv_sec) * 1000) +
((timo->tv_usec - now.tv_usec) / 1000);
if (timeout <= 0)
timeout = 0;
} else {
timeout = (flags & SUDO_EVLOOP_NONBLOCK) ? 0 : -1;
}
nready = poll(base->pfds, base->pfd_high + 1, timeout);
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: %d fds ready", __func__, nready);
switch (nready) {
case -1:
/* Error or interrupted by signal. */
debug_return_int(-1);
case 0:
/* Front end will activate timeout events. */
break;
default:
/* Activate each I/O event that fired. */
TAILQ_FOREACH(ev, &base->events, entries) {
if (ev->pfd_idx != -1 && base->pfds[ev->pfd_idx].revents) {
int what = 0;
if (base->pfds[ev->pfd_idx].revents & (POLLIN|POLLHUP|POLLNVAL|POLLERR))
what |= (ev->events & SUDO_EV_READ);
if (base->pfds[ev->pfd_idx].revents & (POLLOUT|POLLHUP|POLLNVAL|POLLERR))
what |= (ev->events & SUDO_EV_WRITE);
/* Make event active. */
sudo_debug_printf(SUDO_DEBUG_DEBUG,
"%s: polled fd %d, events %d, activating %p",
__func__, ev->fd, what, ev);
ev->revents = what;
TAILQ_INSERT_TAIL(&base->active, ev, active_entries);
SET(ev->flags, SUDO_EVQ_ACTIVE);
}
}
break;
}
debug_return_int(nready);
}

212
lib/util/event_select.c Normal file
View File

@@ -0,0 +1,212 @@
/*
* Copyright (c) 2013 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> /* for howmany() on Linux */
#include <sys/time.h>
#ifdef HAVE_SYS_SYSMACROS_H
# include <sys/sysmacros.h> /* for howmany() on Solaris */
#endif
#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_STDBOOL_H
# include <stdbool.h>
#else
# include "compat/stdbool.h"
#endif /* HAVE_STDBOOL_H */
#ifdef HAVE_STRING_H
# include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <errno.h>
#include "missing.h"
#include "alloc.h"
#include "fatal.h"
#include "sudo_debug.h"
#include "sudo_event.h"
#include "sudo_util.h"
/* XXX - use non-exiting allocators? */
int
sudo_ev_base_alloc_impl(struct sudo_event_base *base)
{
debug_decl(sudo_ev_base_alloc_impl, SUDO_DEBUG_EVENT)
base->maxfd = NFDBITS - 1;
base->readfds_in = ecalloc(1, sizeof(fd_mask));
base->writefds_in = ecalloc(1, sizeof(fd_mask));
base->readfds_out = ecalloc(1, sizeof(fd_mask));
base->writefds_out = ecalloc(1, sizeof(fd_mask));
debug_return_int(0);
}
void
sudo_ev_base_free_impl(struct sudo_event_base *base)
{
debug_decl(sudo_ev_base_free_impl, SUDO_DEBUG_EVENT)
efree(base->readfds_in);
efree(base->writefds_in);
efree(base->readfds_out);
efree(base->writefds_out);
debug_return;
}
int
sudo_ev_add_impl(struct sudo_event_base *base, struct sudo_event *ev)
{
debug_decl(sudo_ev_add_impl, SUDO_DEBUG_EVENT)
/* If out of space in fd sets, realloc. */
if (ev->fd > base->maxfd) {
const int o = (base->maxfd + 1) / NFDBITS;
const int n = howmany(ev->fd + 1, NFDBITS);
base->readfds_in = erecalloc(base->readfds_in, o, n, sizeof(fd_mask));
base->writefds_in = erecalloc(base->writefds_in, o, n, sizeof(fd_mask));
base->readfds_out = erecalloc(base->readfds_out, o, n, sizeof(fd_mask));
base->writefds_out = erecalloc(base->writefds_out, o, n, sizeof(fd_mask));
base->maxfd = (n * NFDBITS) - 1;
}
/* Set events and adjust high fd as needed. */
if (ISSET(ev->events, SUDO_EV_READ)) {
sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: added fd %d to readfs",
__func__, ev->fd);
FD_SET(ev->fd, base->readfds_in);
}
if (ISSET(ev->events, SUDO_EV_WRITE)) {
sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: added fd %d to writefds",
__func__, ev->fd);
FD_SET(ev->fd, base->writefds_in);
}
if (ev->fd > base->highfd)
base->highfd = ev->fd;
debug_return_int(0);
}
int
sudo_ev_del_impl(struct sudo_event_base *base, struct sudo_event *ev)
{
debug_decl(sudo_ev_del_impl, SUDO_DEBUG_EVENT)
/* Remove from readfds and writefds and adjust high fd. */
if (ISSET(ev->events, SUDO_EV_READ)) {
sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: removed fd %d from readfds",
__func__, ev->fd);
FD_CLR(ev->fd, base->readfds_in);
}
if (ISSET(ev->events, SUDO_EV_WRITE)) {
sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: removed fd %d from writefds",
__func__, ev->fd);
FD_CLR(ev->fd, base->writefds_in);
}
if (base->highfd == ev->fd) {
for (;;) {
if (FD_ISSET(base->highfd, base->readfds_in) ||
FD_ISSET(base->highfd, base->writefds_in))
break;
if (--base->highfd < 0)
break;
}
}
debug_return_int(0);
}
int
sudo_ev_scan_impl(struct sudo_event_base *base, int flags)
{
struct timeval now, tv, *timeout;
struct sudo_event *ev;
size_t setsize;
int nready;
debug_decl(sudo_ev_loop, SUDO_DEBUG_EVENT)
if ((ev = TAILQ_FIRST(&base->timeouts)) != NULL) {
gettimeofday(&now, NULL);
sudo_timevalsub(&ev->timeout, &now, &tv);
if (tv.tv_sec < 0 || (tv.tv_sec == 0 && tv.tv_usec < 0))
sudo_timevalclear(&tv);
timeout = &tv;
} else {
if (ISSET(flags, SUDO_EVLOOP_NONBLOCK)) {
sudo_timevalclear(&tv);
timeout = &tv;
} else {
timeout = NULL;
}
}
/* select() overwrites readfds/writefds so make a copy. */
setsize = howmany(base->highfd + 1, NFDBITS) * sizeof(fd_mask);
memcpy(base->readfds_out, base->readfds_in, setsize);
memcpy(base->writefds_out, base->writefds_in, setsize);
sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: select high fd %d",
__func__, base->highfd);
nready = select(base->highfd + 1, base->readfds_out, base->writefds_out,
NULL, timeout);
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: %d fds ready", __func__, nready);
switch (nready) {
case -1:
/* Error or interrupted by signal. */
debug_return_int(-1);
case 0:
/* Front end will activate timeout events. */
break;
default:
/* Activate each I/O event that fired. */
TAILQ_FOREACH(ev, &base->events, entries) {
if (ev->fd >= 0) {
int what = 0;
if (FD_ISSET(ev->fd, base->readfds_out))
what |= (ev->events & SUDO_EV_READ);
if (FD_ISSET(ev->fd, base->writefds_out))
what |= (ev->events & SUDO_EV_WRITE);
if (what != 0) {
/* Make event active. */
sudo_debug_printf(SUDO_DEBUG_DEBUG,
"%s: selected fd %d, events %d, activating %p",
__func__, ev->fd, what, ev);
ev->revents = what;
TAILQ_INSERT_TAIL(&base->active, ev, active_entries);
SET(ev->flags, SUDO_EVQ_ACTIVE);
}
}
}
break;
}
debug_return_int(nready);
}

228
lib/util/fatal.c Normal file
View File

@@ -0,0 +1,228 @@
/*
* Copyright (c) 2004-2005, 2010-2013 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 <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_STDBOOL_H
# include <stdbool.h>
#else
# include "compat/stdbool.h"
#endif /* HAVE_STDBOOL_H */
#define DEFAULT_TEXT_DOMAIN "sudo"
#include "gettext.h" /* must be included before missing.h */
#include "missing.h"
#include "alloc.h"
#include "fatal.h"
#include "queue.h"
#include "sudo_plugin.h"
struct sudo_fatal_callback {
SLIST_ENTRY(sudo_fatal_callback) entries;
void (*func)(void);
};
SLIST_HEAD(sudo_fatal_callback_list, sudo_fatal_callback);
sigjmp_buf fatal_jmp;
static bool setjmp_enabled = false;
static struct sudo_fatal_callback_list callbacks;
static void _warning(int, const char *, va_list);
static void
do_cleanup(void)
{
struct sudo_fatal_callback *cb;
/* Run callbacks, removing them from the list as we go. */
while ((cb = SLIST_FIRST(&callbacks)) != NULL) {
SLIST_REMOVE_HEAD(&callbacks, entries);
cb->func();
free(cb);
}
}
void
fatal_nodebug(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
_warning(1, fmt, ap);
va_end(ap);
do_cleanup();
if (setjmp_enabled)
siglongjmp(fatal_jmp, 1);
else
exit(EXIT_FAILURE);
}
void
fatalx_nodebug(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
_warning(0, fmt, ap);
va_end(ap);
do_cleanup();
if (setjmp_enabled)
siglongjmp(fatal_jmp, 1);
else
exit(EXIT_FAILURE);
}
void
vfatal_nodebug(const char *fmt, va_list ap)
{
_warning(1, fmt, ap);
do_cleanup();
if (setjmp_enabled)
siglongjmp(fatal_jmp, 1);
else
exit(EXIT_FAILURE);
}
void
vfatalx_nodebug(const char *fmt, va_list ap)
{
_warning(0, fmt, ap);
do_cleanup();
if (setjmp_enabled)
siglongjmp(fatal_jmp, 1);
else
exit(EXIT_FAILURE);
}
void
warning_nodebug(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
_warning(1, fmt, ap);
va_end(ap);
}
void
warningx_nodebug(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
_warning(0, fmt, ap);
va_end(ap);
}
void
vwarning_nodebug(const char *fmt, va_list ap)
{
_warning(1, fmt, ap);
}
void
vwarningx_nodebug(const char *fmt, va_list ap)
{
_warning(0, fmt, ap);
}
static void
_warning(int use_errno, const char *fmt, va_list ap)
{
int serrno = errno;
char *str;
evasprintf(&str, fmt, ap);
if (use_errno) {
if (fmt != NULL) {
sudo_printf(SUDO_CONV_ERROR_MSG,
_("%s: %s: %s\n"), getprogname(), str, strerror(serrno));
} else {
sudo_printf(SUDO_CONV_ERROR_MSG,
_("%s: %s\n"), getprogname(), strerror(serrno));
}
} else {
sudo_printf(SUDO_CONV_ERROR_MSG,
_("%s: %s\n"), getprogname(), str ? str : "(null)");
}
efree(str);
errno = serrno;
}
/*
* Register a callback to be run when fatal()/fatalx() is called.
*/
int
fatal_callback_register(void (*func)(void))
{
struct sudo_fatal_callback *cb;
/* Do not register the same callback twice. */
SLIST_FOREACH(cb, &callbacks, entries) {
if (func == cb->func)
return -1; /* dupe! */
}
/* Allocate and insert new callback. */
cb = malloc(sizeof(*cb));
if (cb == NULL)
return -1;
cb->func = func;
SLIST_INSERT_HEAD(&callbacks, cb, entries);
return 0;
}
/*
* Deregister a fatal()/fatalx() callback.
*/
int
fatal_callback_deregister(void (*func)(void))
{
struct sudo_fatal_callback *cb, **prev;
/* Search for callback and remove if found, dupes are not allowed. */
SLIST_FOREACH_PREVPTR(cb, prev, &callbacks, entries) {
if (cb->func == func) {
if (cb == SLIST_FIRST(&callbacks))
SLIST_REMOVE_HEAD(&callbacks, entries);
else
SLIST_REMOVE_AFTER(*prev, entries);
free(cb);
return 0;
}
}
return -1;
}
void
fatal_disable_setjmp(void)
{
setjmp_enabled = false;
}
void
fatal_enable_setjmp(void)
{
setjmp_enabled = true;
}

234
lib/util/fileops.c Normal file
View File

@@ -0,0 +1,234 @@
/*
* Copyright (c) 1999-2005, 2007, 2009-2013
* 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/time.h>
#ifdef HAVE_FLOCK
# include <sys/file.h>
#endif /* HAVE_FLOCK */
#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>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRING_H */
#if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS)
# include <malloc.h>
#endif /* HAVE_MALLOC_H && !STDC_HEADERS */
#include <ctype.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <fcntl.h>
#ifdef HAVE_STDBOOL_H
# include <stdbool.h>
#else
# include "compat/stdbool.h"
#endif
#ifdef TIME_WITH_SYS_TIME
# include <time.h>
#endif
#ifndef HAVE_STRUCT_TIMESPEC
# include "compat/timespec.h"
#endif
#include "missing.h"
#include "fileops.h"
#include "sudo_debug.h"
/*
* Update the access and modify times on an fd or file.
*/
int
touch(int fd, char *path, struct timeval *tvp)
{
struct timeval times[2];
int rval = -1;
debug_decl(touch, SUDO_DEBUG_UTIL)
if (tvp != NULL) {
times[0].tv_sec = times[1].tv_sec = tvp->tv_sec;
times[0].tv_usec = times[1].tv_usec = tvp->tv_usec;
}
#if defined(HAVE_FUTIME) || defined(HAVE_FUTIMES)
if (fd != -1)
rval = futimes(fd, tvp ? times : NULL);
else
#endif
if (path != NULL)
rval = utimes(path, tvp ? times : NULL);
debug_return_int(rval);
}
/*
* Lock/unlock a file.
*/
#ifdef HAVE_LOCKF
bool
lock_file(int fd, int lockit)
{
int op = 0;
debug_decl(lock_file, SUDO_DEBUG_UTIL)
switch (lockit) {
case SUDO_LOCK:
op = F_LOCK;
break;
case SUDO_TLOCK:
op = F_TLOCK;
break;
case SUDO_UNLOCK:
op = F_ULOCK;
break;
}
debug_return_bool(lockf(fd, op, 0) == 0);
}
#elif defined(HAVE_FLOCK)
bool
lock_file(int fd, int lockit)
{
int op = 0;
debug_decl(lock_file, SUDO_DEBUG_UTIL)
switch (lockit) {
case SUDO_LOCK:
op = LOCK_EX;
break;
case SUDO_TLOCK:
op = LOCK_EX | LOCK_NB;
break;
case SUDO_UNLOCK:
op = LOCK_UN;
break;
}
debug_return_bool(flock(fd, op) == 0);
}
#else
bool
lock_file(int fd, int lockit)
{
#ifdef F_SETLK
int func;
struct flock lock;
debug_decl(lock_file, SUDO_DEBUG_UTIL)
lock.l_start = 0;
lock.l_len = 0;
lock.l_pid = getpid();
lock.l_type = (lockit == SUDO_UNLOCK) ? F_UNLCK : F_WRLCK;
lock.l_whence = SEEK_SET;
func = (lockit == SUDO_LOCK) ? F_SETLKW : F_SETLK;
debug_return_bool(fcntl(fd, func, &lock) == 0);
#else
return true;
#endif
}
#endif
/*
* Read a line of input, honoring line continuation chars.
* Remove comments and strips off leading and trailing spaces.
* Returns the line length and updates the buf and bufsize pointers.
* XXX - just use a struct w/ state, including getline buffer?
* could also make comment char and line continuation configurable
*/
ssize_t
sudo_parseln(char **bufp, size_t *bufsizep, unsigned int *lineno, FILE *fp)
{
size_t linesize = 0, total = 0;
ssize_t len;
char *cp, *line = NULL;
bool continued;
debug_decl(sudo_parseln, SUDO_DEBUG_UTIL)
do {
continued = false;
len = getline(&line, &linesize, fp);
if (len == -1)
break;
if (lineno != NULL)
(*lineno)++;
/* Remove trailing newline(s) if present. */
while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r'))
line[--len] = '\0';
/* Remove comments or check for line continuation (but not both) */
if ((cp = strchr(line, '#')) != NULL) {
*cp = '\0';
len = (size_t)(cp - line);
} else if (len > 0 && line[len - 1] == '\\' && (len == 1 || line[len - 2] != '\\')) {
line[--len] = '\0';
continued = true;
}
/* Trim leading and trailing whitespace */
if (!continued) {
while (len > 0 && isblank((unsigned char)line[len - 1]))
line[--len] = '\0';
}
for (cp = line; isblank((unsigned char)*cp); cp++)
len--;
if (*bufp == NULL || total + len >= *bufsizep) {
void *tmp;
size_t size = total + len + 1;
if (size < 64) {
size = 64;
} else if (size <= 0x80000000) {
/* Round up to next highest power of two. */
size--;
size |= size >> 1;
size |= size >> 2;
size |= size >> 4;
size |= size >> 8;
size |= size >> 16;
size++;
}
if ((tmp = realloc(*bufp, size)) == NULL)
break;
*bufp = tmp;
*bufsizep = size;
}
memcpy(*bufp + total, cp, len + 1);
total += len;
} while (continued);
free(line);
if (len == -1 && total == 0)
debug_return_size_t((size_t)-1);
debug_return_size_t(total);
}

66
lib/util/fmt_string.c Normal file
View File

@@ -0,0 +1,66 @@
/*
* Copyright (c) 2010-2012 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 */
#ifdef HAVE_STRING_H
# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
# include <memory.h>
# endif
# include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#include "missing.h"
#include "sudo_debug.h"
#include "sudo_util.h"
/*
* Allocate storage for a name=value string and return it.
*/
char *
fmt_string(const char *var, const char *val)
{
size_t var_len = strlen(var);
size_t val_len = strlen(val);
char *cp, *str;
debug_decl(fmt_string, SUDO_DEBUG_UTIL)
cp = str = malloc(var_len + 1 + val_len + 1);
if (str != NULL) {
memcpy(cp, var, var_len);
cp += var_len;
*cp++ = '=';
memcpy(cp, val, val_len);
cp += val_len;
*cp = '\0';
}
debug_return_str(str);
}

477
lib/util/fnmatch.c Normal file
View File

@@ -0,0 +1,477 @@
/* $OpenBSD: fnmatch.c,v 1.15 2011/02/10 21:31:59 stsp Exp $ */
/* Copyright (c) 2011, VMware, 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:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of the VMware, 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 VMWARE, INC. 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.
*/
/* Authored by William A. Rowe Jr. <wrowe; apache.org, vmware.com>, April 2011
*
* Derived from The Open Group Base Specifications Issue 7, IEEE Std 1003.1-2008
* as described in;
* http://pubs.opengroup.org/onlinepubs/9699919799/functions/fnmatch.html
*
* Filename pattern matches defined in section 2.13, "Pattern Matching Notation"
* from chapter 2. "Shell Command Language"
* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13
* where; 1. A bracket expression starting with an unquoted <circumflex> '^'
* character CONTINUES to specify a non-matching list; 2. an explicit <period> '.'
* in a bracket expression matching list, e.g. "[.abc]" does NOT match a leading
* <period> in a filename; 3. a <left-square-bracket> '[' which does not introduce
* a valid bracket expression is treated as an ordinary character; 4. a differing
* number of consecutive slashes within pattern and string will NOT match;
* 5. a trailing '\' in FNM_ESCAPE mode is treated as an ordinary '\' character.
*
* Bracket expansion defined in section 9.3.5, "RE Bracket Expression",
* from chapter 9, "Regular Expressions"
* http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_03_05
* with no support for collating symbols, equivalence class expressions or
* character class expressions. A partial range expression with a leading
* hyphen following a valid range expression will match only the ordinary
* <hyphen> and the ending character (e.g. "[a-m-z]" will match characters
* 'a' through 'm', a <hyphen> '-', or a 'z').
*
* Supports BSD extensions FNM_LEADING_DIR to match pattern to the end of one
* path segment of string, and FNM_CASEFOLD to ignore alpha case.
*
* NOTE: Only POSIX/C single byte locales are correctly supported at this time.
* Notably, non-POSIX locales with FNM_CASEFOLD produce undefined results,
* particularly in ranges of mixed case (e.g. "[A-z]") or spanning alpha and
* nonalpha characters within a range.
*
* XXX comments below indicate porting required for multi-byte character sets
* and non-POSIX locale collation orders; requires mbr* APIs to track shift
* state of pattern and string (rewinding pattern and string repeatedly).
*
* Certain parts of the code assume 0x00-0x3F are unique with any MBCS (e.g.
* UTF-8, SHIFT-JIS, etc). Any implementation allowing '\' as an alternate
* path delimiter must be aware that 0x5C is NOT unique within SHIFT-JIS.
*/
#include <config.h>
#ifndef HAVE_FNMATCH
#include <sys/types.h>
#include <stdio.h>
#include <ctype.h>
#ifdef HAVE_STRING_H
# include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#include <limits.h>
#include "missing.h"
#include "compat/charclass.h"
#include "compat/fnmatch.h"
#define RANGE_MATCH 1
#define RANGE_NOMATCH 0
#define RANGE_ERROR (-1)
static int
classmatch(const char *pattern, char test, int foldcase, const char **ep)
{
const char * const mismatch = pattern;
const char *colon;
struct cclass *cc;
int rval = RANGE_NOMATCH;
size_t len;
if (pattern[0] != '[' || pattern[1] != ':') {
*ep = mismatch;
return RANGE_ERROR;
}
pattern += 2;
if ((colon = strchr(pattern, ':')) == NULL || colon[1] != ']') {
*ep = mismatch;
return RANGE_ERROR;
}
*ep = colon + 2;
len = (size_t)(colon - pattern);
if (foldcase && strncmp(pattern, "upper:]", 7) == 0)
pattern = "lower:]";
for (cc = cclasses; cc->name != NULL; cc++) {
if (!strncmp(pattern, cc->name, len) && cc->name[len] == '\0') {
if (cc->isctype((unsigned char)test))
rval = RANGE_MATCH;
break;
}
}
if (cc->name == NULL) {
/* invalid character class, treat as normal text */
*ep = mismatch;
rval = RANGE_ERROR;
}
return rval;
}
/* Most MBCS/collation/case issues handled here. Wildcard '*' is not handled.
* EOS '\0' and the FNM_PATHNAME '/' delimiters are not advanced over,
* however the "\/" sequence is advanced to '/'.
*
* Both pattern and string are **char to support pointer increment of arbitrary
* multibyte characters for the given locale, in a later iteration of this code
*/
static int fnmatch_ch(const char **pattern, const char **string, int flags)
{
const char * const mismatch = *pattern;
const int nocase = !!(flags & FNM_CASEFOLD);
const int escape = !(flags & FNM_NOESCAPE);
const int slash = !!(flags & FNM_PATHNAME);
int result = FNM_NOMATCH;
const char *startch;
int negate;
if (**pattern == '[')
{
++*pattern;
/* Handle negation, either leading ! or ^ operators (never both) */
negate = ((**pattern == '!') || (**pattern == '^'));
if (negate)
++*pattern;
/* ']' is an ordinary character at the start of the range pattern */
if (**pattern == ']')
goto leadingclosebrace;
while (**pattern)
{
if (**pattern == ']') {
++*pattern;
/* XXX: Fix for MBCS character width */
++*string;
return (result ^ negate);
}
if (escape && (**pattern == '\\')) {
++*pattern;
/* Patterns must be terminated with ']', not EOS */
if (!**pattern)
break;
}
/* Patterns must be terminated with ']' not '/' */
if (slash && (**pattern == '/'))
break;
/* Match character classes. */
if (classmatch(*pattern, **string, nocase, pattern)
== RANGE_MATCH) {
result = 0;
continue;
}
leadingclosebrace:
/* Look at only well-formed range patterns;
* "x-]" is not allowed unless escaped ("x-\]")
* XXX: Fix for locale/MBCS character width
*/
if (((*pattern)[1] == '-') && ((*pattern)[2] != ']'))
{
startch = *pattern;
*pattern += (escape && ((*pattern)[2] == '\\')) ? 3 : 2;
/* NOT a properly balanced [expr] pattern, EOS terminated
* or ranges containing a slash in FNM_PATHNAME mode pattern
* fall out to to the rewind and test '[' literal code path
*/
if (!**pattern || (slash && (**pattern == '/')))
break;
/* XXX: handle locale/MBCS comparison, advance by MBCS char width */
if ((**string >= *startch) && (**string <= **pattern))
result = 0;
else if (nocase && (isupper((unsigned char)**string) ||
isupper((unsigned char)*startch) ||
isupper((unsigned char)**pattern))
&& (tolower((unsigned char)**string) >= tolower((unsigned char)*startch))
&& (tolower((unsigned char)**string) <= tolower((unsigned char)**pattern)))
result = 0;
++*pattern;
continue;
}
/* XXX: handle locale/MBCS comparison, advance by MBCS char width */
if ((**string == **pattern))
result = 0;
else if (nocase && (isupper((unsigned char)**string) ||
isupper((unsigned char)**pattern))
&& (tolower((unsigned char)**string) == tolower((unsigned char)**pattern)))
result = 0;
++*pattern;
}
/* NOT a properly balanced [expr] pattern; Rewind
* and reset result to test '[' literal
*/
*pattern = mismatch;
result = FNM_NOMATCH;
}
else if (**pattern == '?') {
/* Optimize '?' match before unescaping **pattern */
if (!**string || (slash && (**string == '/')))
return FNM_NOMATCH;
result = 0;
goto fnmatch_ch_success;
}
else if (escape && (**pattern == '\\') && (*pattern)[1]) {
++*pattern;
}
/* XXX: handle locale/MBCS comparison, advance by the MBCS char width */
if (**string == **pattern)
result = 0;
else if (nocase && (isupper((unsigned char)**string) || isupper((unsigned char)**pattern))
&& (tolower((unsigned char)**string) == tolower((unsigned char)**pattern)))
result = 0;
/* Refuse to advance over trailing slash or nulls
*/
if (!**string || !**pattern || (slash && ((**string == '/') || (**pattern == '/'))))
return result;
fnmatch_ch_success:
++*pattern;
++*string;
return result;
}
int rpl_fnmatch(const char *pattern, const char *string, int flags)
{
static const char dummystring[2] = {' ', 0};
const int escape = !(flags & FNM_NOESCAPE);
const int slash = !!(flags & FNM_PATHNAME);
const int leading_dir = !!(flags & FNM_LEADING_DIR);
const char *strendseg;
const char *dummyptr;
const char *matchptr;
int wild;
/* For '*' wild processing only; surpress 'used before initialization'
* warnings with dummy initialization values;
*/
const char *strstartseg = NULL;
const char *mismatch = NULL;
int matchlen = 0;
if (strlen(pattern) > PATH_MAX || strlen(string) > PATH_MAX)
return FNM_NOMATCH;
if (*pattern == '*')
goto firstsegment;
while (*pattern && *string)
{
/* Pre-decode "\/" which has no special significance, and
* match balanced slashes, starting a new segment pattern
*/
if (slash && escape && (*pattern == '\\') && (pattern[1] == '/'))
++pattern;
if (slash && (*pattern == '/') && (*string == '/')) {
++pattern;
++string;
}
firstsegment:
/* At the beginning of each segment, validate leading period behavior.
*/
if ((flags & FNM_PERIOD) && (*string == '.'))
{
if (*pattern == '.')
++pattern;
else if (escape && (*pattern == '\\') && (pattern[1] == '.'))
pattern += 2;
else
return FNM_NOMATCH;
++string;
}
/* Determine the end of string segment
*
* Presumes '/' character is unique, not composite in any MBCS encoding
*/
if (slash) {
strendseg = strchr(string, '/');
if (!strendseg)
strendseg = strchr(string, '\0');
}
else {
strendseg = strchr(string, '\0');
}
/* Allow pattern '*' to be consumed even with no remaining string to match
*/
while (*pattern)
{
if ((string > strendseg)
|| ((string == strendseg) && (*pattern != '*')))
break;
if (slash && ((*pattern == '/')
|| (escape && (*pattern == '\\')
&& (pattern[1] == '/'))))
break;
/* Reduce groups of '*' and '?' to n '?' matches
* followed by one '*' test for simplicity
*/
for (wild = 0; ((*pattern == '*') || (*pattern == '?')); ++pattern)
{
if (*pattern == '*') {
wild = 1;
}
else if (string < strendseg) { /* && (*pattern == '?') */
/* XXX: Advance 1 char for MBCS locale */
++string;
}
else { /* (string >= strendseg) && (*pattern == '?') */
return FNM_NOMATCH;
}
}
if (wild)
{
strstartseg = string;
mismatch = pattern;
/* Count fixed (non '*') char matches remaining in pattern
* excluding '/' (or "\/") and '*'
*/
for (matchptr = pattern, matchlen = 0; 1; ++matchlen)
{
if ((*matchptr == '\0')
|| (slash && ((*matchptr == '/')
|| (escape && (*matchptr == '\\')
&& (matchptr[1] == '/')))))
{
/* Compare precisely this many trailing string chars,
* the resulting match needs no wildcard loop
*/
/* XXX: Adjust for MBCS */
if (string + matchlen > strendseg)
return FNM_NOMATCH;
string = strendseg - matchlen;
wild = 0;
break;
}
if (*matchptr == '*')
{
/* Ensure at least this many trailing string chars remain
* for the first comparison
*/
/* XXX: Adjust for MBCS */
if (string + matchlen > strendseg)
return FNM_NOMATCH;
/* Begin first wild comparison at the current position */
break;
}
/* Skip forward in pattern by a single character match
* Use a dummy fnmatch_ch() test to count one "[range]" escape
*/
/* XXX: Adjust for MBCS */
if (escape && (*matchptr == '\\') && matchptr[1]) {
matchptr += 2;
}
else if (*matchptr == '[') {
dummyptr = dummystring;
fnmatch_ch(&matchptr, &dummyptr, flags);
}
else {
++matchptr;
}
}
}
/* Incrementally match string against the pattern
*/
while (*pattern && (string < strendseg))
{
/* Success; begin a new wild pattern search
*/
if (*pattern == '*')
break;
if (slash && ((*string == '/')
|| (*pattern == '/')
|| (escape && (*pattern == '\\')
&& (pattern[1] == '/'))))
break;
/* Compare ch's (the pattern is advanced over "\/" to the '/',
* but slashes will mismatch, and are not consumed)
*/
if (!fnmatch_ch(&pattern, &string, flags))
continue;
/* Failed to match, loop against next char offset of string segment
* until not enough string chars remain to match the fixed pattern
*/
if (wild) {
/* XXX: Advance 1 char for MBCS locale */
string = ++strstartseg;
if (string + matchlen > strendseg)
return FNM_NOMATCH;
pattern = mismatch;
continue;
}
else
return FNM_NOMATCH;
}
}
if (*string && !((slash || leading_dir) && (*string == '/')))
return FNM_NOMATCH;
if (*pattern && !(slash && ((*pattern == '/')
|| (escape && (*pattern == '\\')
&& (pattern[1] == '/')))))
return FNM_NOMATCH;
if (leading_dir && !*pattern && *string == '/')
return 0;
}
/* Where both pattern and string are at EOS, declare success
*/
if (!*string && !*pattern)
return 0;
/* pattern didn't match to the end of string */
return FNM_NOMATCH;
}
#endif /* HAVE_FNMATCH */

422
lib/util/getaddrinfo.c Normal file
View File

@@ -0,0 +1,422 @@
/*
* Replacement for a missing getaddrinfo.
*
* This is an implementation of getaddrinfo for systems that don't have one so
* that networking code can use a consistant interface without #ifdef. It is
* a fairly minimal implementation, with the following limitations:
*
* - IPv4 support only. IPv6 is not supported.
* - AI_ADDRCONFIG is ignored.
* - Not thread-safe due to gethostbyname and getservbyname.
* - SOCK_DGRAM and SOCK_STREAM only.
* - Multiple possible socket types only generate one addrinfo struct.
* - Protocol hints aren't used correctly.
*
* The last four issues could probably be easily remedied, but haven't been
* needed to date. Adding IPv6 support isn't worth it; systems with IPv6
* support should already support getaddrinfo natively.
*
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
*
* Written by Russ Allbery <rra@stanford.edu>
*
* The authors hereby relinquish any claim to any copyright that they may have
* in this work, whether granted under contract or by operation of law or
* international treaty, and hereby commit to the public, at large, that they
* shall not, at any time in the future, seek to enforce any copyright in this
* work against any person or entity, or prevent any person or entity from
* copying, publishing, distributing or creating derivative works of this
* work.
*/
#include <config.h>
#ifndef HAVE_GETADDRINFO
#include <sys/types.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>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#include <limits.h>
#include <netdb.h>
#include <errno.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "compat/getaddrinfo.h"
#include "missing.h"
/* We need access to h_errno to map errors from gethostbyname. */
#ifndef HAVE_DECL_H_ERRNO
extern int h_errno;
#endif
/*
* The netdb constants, which aren't always defined (particularly if h_errno
* isn't declared). We also make sure that a few of the less-used ones are
* defined so that we can deal with them in case statements.
*/
#ifndef HOST_NOT_FOUND
# define HOST_NOT_FOUND 1
# define TRY_AGAIN 2
# define NO_RECOVERY 3
# define NO_DATA 4
#endif
#ifndef NETDB_INTERNAL
# define NETDB_INTERNAL -1
#endif
/*
* If we're running the test suite, rename the functions to avoid conflicts
* with the system version. Note that we don't rename the structures and
* constants, but that should be okay (except possibly for gai_strerror).
*/
#ifdef TESTING
# define gai_strerror test_gai_strerror
# define freeaddrinfo test_freeaddrinfo
# define getaddrinfo test_getaddrinfo
const char *test_gai_strerror(int);
void test_freeaddrinfo(struct addrinfo *);
int test_getaddrinfo(const char *, const char *, const struct addrinfo *,
struct addrinfo **);
#endif
/*
* If the native platform doesn't support AI_NUMERICSERV or AI_NUMERICHOST,
* pick some other values for them.
*/
#ifdef TESTING
# if AI_NUMERICSERV == 0
# undef AI_NUMERICSERV
# define AI_NUMERICSERV 0x0080
# endif
# if AI_NUMERICHOST == 0
# undef AI_NUMERICHOST
# define AI_NUMERICHOST 0x0100
# endif
#endif
/*
* Value representing all of the hint flags set. Linux uses flags up to
* 0x0400, so be sure not to break when testing on that platform.
*/
#ifdef TESTING
# ifdef HAVE_GETADDRINFO
# define AI_INTERNAL_ALL 0x04ff
# else
# define AI_INTERNAL_ALL 0x01ff
# endif
#else
# define AI_INTERNAL_ALL 0x007f
#endif
/* Table of strings corresponding to the EAI_* error codes. */
static const char * const gai_errors[] = {
"Host name lookup failure", /* 1 EAI_AGAIN */
"Invalid flag value", /* 2 EAI_BADFLAGS */
"Unknown server error", /* 3 EAI_FAIL */
"Unsupported address family", /* 4 EAI_FAMILY */
"Memory allocation failure", /* 5 EAI_MEMORY */
"Host unknown or not given", /* 6 EAI_NONAME */
"Service not supported for socket", /* 7 EAI_SERVICE */
"Unsupported socket type", /* 8 EAI_SOCKTYPE */
"System error", /* 9 EAI_SYSTEM */
"Supplied buffer too small", /* 10 EAI_OVERFLOW */
};
/* Macro to set the len attribute of sockaddr_in. */
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
# define sin_set_length(s) ((s)->sin_len = sizeof(struct sockaddr_in))
#else
# define sin_set_length(s) /* empty */
#endif
/*
* Used for iterating through arrays. ARRAY_SIZE returns the number of
* elements in the array (useful for a < upper bound in a for loop).
*/
#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
/*
* Return a constant string for a given EAI_* error code or a string
* indicating an unknown error.
*/
const char *
gai_strerror(int ecode)
{
if (ecode < 1 || (size_t) ecode > ARRAY_SIZE(gai_errors))
return "Unknown error";
else
return gai_errors[ecode - 1];
}
/*
* Free a linked list of addrinfo structs.
*/
void
freeaddrinfo(struct addrinfo *ai)
{
struct addrinfo *next;
while (ai != NULL) {
next = ai->ai_next;
if (ai->ai_addr != NULL)
free(ai->ai_addr);
if (ai->ai_canonname != NULL)
free(ai->ai_canonname);
free(ai);
ai = next;
}
}
/*
* Allocate a new addrinfo struct, setting some defaults given that this
* implementation is IPv4 only. Also allocates an attached sockaddr_in and
* zeroes it, per the requirement for getaddrinfo. Takes the socktype,
* canonical name (which is copied if not NULL), address, and port. Returns
* NULL on a memory allocation failure.
*/
static struct addrinfo *
gai_addrinfo_new(int socktype, const char *canonical, struct in_addr addr,
unsigned short port)
{
struct addrinfo *ai;
ai = malloc(sizeof(*ai));
if (ai == NULL)
return NULL;
ai->ai_addr = malloc(sizeof(struct sockaddr_in));
if (ai->ai_addr == NULL) {
free(ai);
return NULL;
}
ai->ai_next = NULL;
if (canonical == NULL)
ai->ai_canonname = NULL;
else {
ai->ai_canonname = strdup(canonical);
if (ai->ai_canonname == NULL) {
freeaddrinfo(ai);
return NULL;
}
}
memset(ai->ai_addr, 0, sizeof(struct sockaddr_in));
ai->ai_flags = 0;
ai->ai_family = AF_INET;
ai->ai_socktype = socktype;
ai->ai_protocol = (socktype == SOCK_DGRAM) ? IPPROTO_UDP : IPPROTO_TCP;
ai->ai_addrlen = sizeof(struct sockaddr_in);
((struct sockaddr_in *) ai->ai_addr)->sin_family = AF_INET;
((struct sockaddr_in *) ai->ai_addr)->sin_addr = addr;
((struct sockaddr_in *) ai->ai_addr)->sin_port = htons(port);
sin_set_length((struct sockaddr_in *) ai->ai_addr);
return ai;
}
/*
* Look up a service. Takes the service name (which may be numeric), the hint
* flags, a pointer to the socket type (used to determine whether TCP or UDP
* services are of interest and, if 0, is filled in with the result of
* getservbyname if the service was not numeric), and a pointer to the
* addrinfo struct to fill in. Returns 0 on success or an EAI_* error on
* failure.
*/
static int
gai_service(const char *servname, int flags, int *type, unsigned short *port)
{
struct servent *servent;
const char *protocol;
const char *errstr;
unsigned short value;
value = strtonum(servname, 0, USHRT_MAX, &errstr);
if (errstr == NULL) {
*port = value;
} else if (errno == ERANGE) {
return EAI_SERVICE;
} else {
if (flags & AI_NUMERICSERV)
return EAI_NONAME;
if (*type != 0)
protocol = (*type == SOCK_DGRAM) ? "udp" : "tcp";
else
protocol = NULL;
/*
* We really technically should be generating an addrinfo struct for
* each possible protocol unless type is set, but this works well
* enough for what I need this for.
*/
servent = getservbyname(servname, protocol);
if (servent == NULL)
return EAI_NONAME;
if (strcmp(servent->s_proto, "udp") == 0)
*type = SOCK_DGRAM;
else if (strcmp(servent->s_proto, "tcp") == 0)
*type = SOCK_STREAM;
else
return EAI_SERVICE;
*port = htons(servent->s_port);
}
return 0;
}
/*
* Look up a host and fill in a linked list of addrinfo structs with the
* results, one per IP address of the returned host. Takes the name or IP
* address of the host as a string, the lookup flags, the type of socket (to
* fill into the addrinfo structs), the port (likewise), and a pointer to
* where the head of the linked list should be put. Returns 0 on success or
* the appropriate EAI_* error.
*/
static int
gai_lookup(const char *nodename, int flags, int socktype, unsigned short port,
struct addrinfo **res)
{
struct addrinfo *ai, *first, *prev;
struct in_addr addr;
struct hostent *host;
const char *canonical;
int i;
if (inet_pton(AF_INET, nodename, &addr)) {
canonical = (flags & AI_CANONNAME) ? nodename : NULL;
ai = gai_addrinfo_new(socktype, canonical, addr, port);
if (ai == NULL)
return EAI_MEMORY;
*res = ai;
return 0;
} else {
if (flags & AI_NUMERICHOST)
return EAI_NONAME;
host = gethostbyname(nodename);
if (host == NULL)
switch (h_errno) {
case HOST_NOT_FOUND:
return EAI_NONAME;
case TRY_AGAIN:
case NO_DATA:
return EAI_AGAIN;
case NO_RECOVERY:
return EAI_FAIL;
case NETDB_INTERNAL:
default:
return EAI_SYSTEM;
}
if (host->h_addr_list[0] == NULL)
return EAI_FAIL;
canonical = (flags & AI_CANONNAME)
? ((host->h_name != NULL) ? host->h_name : nodename)
: NULL;
first = NULL;
prev = NULL;
for (i = 0; host->h_addr_list[i] != NULL; i++) {
if (host->h_length != sizeof(addr)) {
freeaddrinfo(first);
return EAI_FAIL;
}
memcpy(&addr, host->h_addr_list[i], sizeof(addr));
ai = gai_addrinfo_new(socktype, canonical, addr, port);
if (ai == NULL) {
freeaddrinfo(first);
return EAI_MEMORY;
}
if (first == NULL) {
first = ai;
prev = ai;
} else {
prev->ai_next = ai;
prev = ai;
}
}
*res = first;
return 0;
}
}
/*
* The actual getaddrinfo implementation.
*/
int
getaddrinfo(const char *nodename, const char *servname,
const struct addrinfo *hints, struct addrinfo **res)
{
struct addrinfo *ai;
struct in_addr addr;
int flags, socktype, status;
unsigned short port;
/* Take the hints into account and check them for validity. */
if (hints != NULL) {
flags = hints->ai_flags;
socktype = hints->ai_socktype;
if ((flags & AI_INTERNAL_ALL) != flags)
return EAI_BADFLAGS;
if (hints->ai_family != AF_UNSPEC && hints->ai_family != AF_INET)
return EAI_FAMILY;
if (socktype != 0 && socktype != SOCK_STREAM && socktype != SOCK_DGRAM)
return EAI_SOCKTYPE;
/* EAI_SOCKTYPE isn't quite right, but there isn't anything better. */
if (hints->ai_protocol != 0) {
int protocol = hints->ai_protocol;
if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP)
return EAI_SOCKTYPE;
}
} else {
flags = 0;
socktype = 0;
}
/*
* See what we're doing. If nodename is null, either AI_PASSIVE is set or
* we're getting information for connecting to a service on the loopback
* address. Otherwise, we're getting information for connecting to a
* remote system.
*/
if (servname == NULL)
port = 0;
else {
status = gai_service(servname, flags, &socktype, &port);
if (status != 0)
return status;
}
if (nodename != NULL)
return gai_lookup(nodename, flags, socktype, port, res);
else {
if (servname == NULL)
return EAI_NONAME;
if ((flags & AI_PASSIVE) == AI_PASSIVE)
addr.s_addr = INADDR_ANY;
else
addr.s_addr = htonl(0x7f000001UL);
ai = gai_addrinfo_new(socktype, NULL, addr, port);
if (ai == NULL)
return EAI_MEMORY;
*res = ai;
return 0;
}
}
#endif /* HAVE_GETADDRINFO */

267
lib/util/getcwd.c Normal file
View File

@@ -0,0 +1,267 @@
/*
* Copyright (c) 1989, 1991, 1993
* The Regents of the University of California. 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 the University 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 REGENTS 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 REGENTS 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>
#ifndef HAVE_GETCWD
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.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>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS)
# include <malloc.h>
#endif /* HAVE_MALLOC_H && !STDC_HEADERS */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_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 "missing.h"
#define ISDOT(dp) \
(dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \
(dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
char *
getcwd(char *pt, size_t size)
{
struct dirent *dp;
DIR *dir = NULL;
dev_t dev;
ino_t ino;
int first;
char *bpt, *bup;
struct stat s;
dev_t root_dev;
ino_t root_ino;
size_t ptsize, upsize;
int save_errno;
char *ept, *eup, *up;
/*
* If no buffer specified by the user, allocate one as necessary.
* If a buffer is specified, the size has to be non-zero. The path
* is built from the end of the buffer backwards.
*/
if (pt) {
ptsize = 0;
if (!size) {
errno = EINVAL;
return NULL;
}
ept = pt + size;
} else {
if ((pt = malloc(ptsize = 1024 - 4)) == NULL)
return NULL;
ept = pt + ptsize;
}
bpt = ept - 1;
*bpt = '\0';
/*
* Allocate bytes (1024 - malloc space) for the string of "../"'s.
* Should always be enough (it's 340 levels). If it's not, allocate
* as necessary. Special * case the first stat, it's ".", not "..".
*/
if ((up = malloc(upsize = 1024 - 4)) == NULL)
goto err;
eup = up + PATH_MAX;
bup = up;
up[0] = '.';
up[1] = '\0';
/* Save root values, so know when to stop. */
if (stat("/", &s))
goto err;
root_dev = s.st_dev;
root_ino = s.st_ino;
errno = 0; /* XXX readdir has no error return. */
for (first = 1;; first = 0) {
/* Stat the current level. */
if (lstat(up, &s))
goto err;
/* Save current node values. */
ino = s.st_ino;
dev = s.st_dev;
/* Check for reaching root. */
if (root_dev == dev && root_ino == ino) {
*--bpt = '/';
/*
* It's unclear that it's a requirement to copy the
* path to the beginning of the buffer, but it's always
* been that way and stuff would probably break.
*/
bcopy(bpt, pt, ept - bpt);
free(up);
return pt;
}
/*
* Build pointer to the parent directory, allocating memory
* as necessary. Max length is 3 for "../", the largest
* possible component name, plus a trailing NULL.
*/
if (bup + 3 + MAXNAMLEN + 1 >= eup) {
char *nup;
if ((nup = realloc(up, upsize *= 2)) == NULL)
goto err;
up = nup;
bup = up;
eup = up + upsize;
}
*bup++ = '.';
*bup++ = '.';
*bup = '\0';
/* Open and stat parent directory. */
if (!(dir = opendir(up)) || fstat(dirfd(dir), &s))
goto err;
/* Add trailing slash for next directory. */
*bup++ = '/';
/*
* If it's a mount point, have to stat each element because
* the inode number in the directory is for the entry in the
* parent directory, not the inode number of the mounted file.
*/
save_errno = 0;
if (s.st_dev == dev) {
for (;;) {
if (!(dp = readdir(dir)))
goto notfound;
if (dp->d_fileno == ino)
break;
}
} else
for (;;) {
if (!(dp = readdir(dir)))
goto notfound;
if (ISDOT(dp))
continue;
bcopy(dp->d_name, bup, NAMLEN(dp) + 1);
/* Save the first error for later. */
if (lstat(up, &s)) {
if (!save_errno)
save_errno = errno;
errno = 0;
continue;
}
if (s.st_dev == dev && s.st_ino == ino)
break;
}
/*
* Check for length of the current name, preceding slash,
* leading slash.
*/
if (bpt - pt <= NAMLEN(dp) + (first ? 1 : 2)) {
size_t len, off;
char *npt;
if (!ptsize) {
errno = ERANGE;
goto err;
}
off = bpt - pt;
len = ept - bpt;
if ((npt = realloc(pt, ptsize *= 2)) == NULL)
goto err;
pt = npt;
bpt = pt + off;
ept = pt + ptsize;
bcopy(bpt, ept - len, len);
bpt = ept - len;
}
if (!first)
*--bpt = '/';
bpt -= NAMLEN(dp);
bcopy(dp->d_name, bpt, NAMLEN(dp));
(void)closedir(dir);
/* Truncate any file name. */
*bup = '\0';
}
notfound:
/*
* If readdir set errno, use it, not any saved error; otherwise,
* didn't find the current directory in its parent directory, set
* errno to ENOENT.
*/
if (!errno)
errno = save_errno ? save_errno : ENOENT;
/* FALLTHROUGH */
err:
if (ptsize)
free(pt);
if (up)
free(up);
if (dir)
(void)closedir(dir);
return NULL;
}
#endif /* HAVE_GETCWD */

362
lib/util/getgrouplist.c Normal file
View File

@@ -0,0 +1,362 @@
/*
* Copyright (c) 2010, 2011, 2013, 2014
* 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>
#ifndef HAVE_GETGROUPLIST
#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>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#include <grp.h>
#ifdef HAVE_NSS_SEARCH
# include <limits.h>
# include <nsswitch.h>
# ifdef HAVE_NSS_DBDEFS_H
# include <nss_dbdefs.h>
# else
# include "compat/nss_dbdefs.h"
# endif
#endif
#include "missing.h"
#include "sudo_util.h"
#if defined(HAVE_GETGRSET)
/*
* BSD-compatible getgrouplist(3) using AIX getgrset(3)
*/
int
getgrouplist(const char *name, gid_t basegid, gid_t *groups, int *ngroupsp)
{
char *cp, *grset = NULL;
int ngroups = 1;
int grpsize = *ngroupsp;
int rval = -1;
gid_t gid;
/* We support BSD semantics where the first element is the base gid */
if (grpsize <= 0)
return -1;
groups[0] = basegid;
#ifdef HAVE_SETAUTHDB
aix_setauthdb((char *) name);
#endif
if ((grset = getgrset(name)) != NULL) {
const char *errstr;
for (cp = strtok(grset, ","); cp != NULL; cp = strtok(NULL, ",")) {
gid = atoid(cp, NULL, NULL, &errstr);
if (errstr == NULL && gid != basegid) {
if (ngroups == grpsize)
goto done;
groups[ngroups++] = gid;
}
}
}
rval = 0;
done:
free(grset);
#ifdef HAVE_SETAUTHDB
aix_restoreauthdb();
#endif
*ngroupsp = ngroups;
return rval;
}
#elif defined(HAVE_NSS_SEARCH)
#ifndef ALIGNBYTES
# define ALIGNBYTES (sizeof(long) - 1L)
#endif
#ifndef ALIGN
# define ALIGN(p) (((unsigned long)(p) + ALIGNBYTES) & ~ALIGNBYTES)
#endif
#if defined(HAVE__NSS_INITF_GROUP) || defined(HAVE___NSS_INITF_GROUP)
extern void _nss_initf_group(nss_db_params_t *params);
#else
static void
_nss_initf_group(nss_db_params_t *params)
{
params->name = NSS_DBNAM_GROUP;
params->default_config = NSS_DEFCONF_GROUP;
}
#endif
static id_t
strtoid(const char *p, int *errval)
{
char *ep;
id_t rval = 0;
errno = 0;
if (*p == '-') {
long lval = strtol(p, &ep, 10);
if (ep == p || *ep != '\0') {
*errval = EINVAL;
goto done;
}
if ((errno == ERANGE && (lval == LONG_MAX || lval == LONG_MIN)) ||
(lval > INT_MAX || lval < INT_MIN)) {
*errval = ERANGE;
goto done;
}
rval = (id_t)lval;
*errval = 0;
} else {
unsigned long ulval = strtoul(p, &ep, 10);
if (ep == p || *ep != '\0') {
*errval = EINVAL;
goto done;
}
if ((errno == ERANGE && ulval == ULONG_MAX) || ulval > UINT_MAX) {
*errval = ERANGE;
goto done;
}
rval = (id_t)ulval;
*errval = 0;
}
done:
return rval;
}
/*
* Convert a groups file string (instr) to a struct group (ent) using
* buf for storage.
*/
static int
str2grp(const char *instr, int inlen, void *ent, char *buf, int buflen)
{
struct group *grp = ent;
char *cp, *fieldsep = buf;
char **gr_mem, **gr_end;
int errval, yp = 0;
id_t id;
/* Must at least have space to copy instr -> buf. */
if (inlen >= buflen)
return NSS_STR_PARSE_ERANGE;
/* Paranoia: buf and instr should be distinct. */
if (buf != instr) {
memmove(buf, instr, inlen);
buf[inlen] = '\0';
}
if ((fieldsep = strchr(cp = fieldsep, ':')) == NULL)
return NSS_STR_PARSE_PARSE;
*fieldsep++ = '\0';
grp->gr_name = cp;
/* Check for YP inclusion/exclusion entries. */
if (*cp == '+' || *cp == '-') {
/* Only the name is required for YP inclusion/exclusion entries. */
grp->gr_passwd = "";
grp->gr_gid = 0;
grp->gr_mem = NULL;
yp = 1;
}
if ((fieldsep = strchr(cp = fieldsep, ':')) == NULL)
return yp ? NSS_STR_PARSE_SUCCESS : NSS_STR_PARSE_PARSE;
*fieldsep++ = '\0';
grp->gr_passwd = cp;
if ((fieldsep = strchr(cp = fieldsep, ':')) == NULL)
return yp ? NSS_STR_PARSE_SUCCESS : NSS_STR_PARSE_PARSE;
*fieldsep++ = '\0';
id = strtoid(cp, &errval);
if (errval != 0) {
/*
* A range error is always a fatal error, but ignore garbage
* at the end of YP entries since it has no meaning.
*/
if (errval == ERANGE)
return NSS_STR_PARSE_ERANGE;
return yp ? NSS_STR_PARSE_SUCCESS : NSS_STR_PARSE_PARSE;
}
#ifdef GID_NOBODY
/* Negative gids get mapped to nobody on Solaris. */
if (*cp == '-' && id != 0)
grp->gr_gid = GID_NOBODY;
else
#endif
grp->gr_gid = (gid_t)id;
/* Store group members, taking care to use proper alignment. */
grp->gr_mem = NULL;
if (*fieldsep != '\0') {
grp->gr_mem = gr_mem = (char **)ALIGN(buf + inlen + 1);
gr_end = (char **)((unsigned long)(buf + buflen) & ~ALIGNBYTES);
for (;;) {
if (gr_mem == gr_end)
return NSS_STR_PARSE_ERANGE; /* out of space! */
*gr_mem++ = cp;
if (fieldsep == NULL)
break;
if ((fieldsep = strchr(cp = fieldsep, ',')) != NULL)
*fieldsep++ = '\0';
}
*gr_mem = NULL;
}
return NSS_STR_PARSE_SUCCESS;
}
static nss_status_t
process_cstr(const char *instr, int inlen, struct nss_groupsbymem *gbm)
{
const char *user = gbm->username;
nss_status_t rval = NSS_NOTFOUND;
nss_XbyY_buf_t *buf;
struct group *grp;
char **gr_mem;
int error, i;
buf = _nss_XbyY_buf_alloc(sizeof(struct group), NSS_BUFLEN_GROUP);
if (buf == NULL)
return NSS_UNAVAIL;
/* Parse groups file string -> struct group. */
grp = buf->result;
error = (*gbm->str2ent)(instr, inlen, grp, buf->buffer, buf->buflen);
if (error || grp->gr_mem == NULL)
goto done;
for (gr_mem = grp->gr_mem; *gr_mem != NULL; gr_mem++) {
if (strcmp(*gr_mem, user) == 0) {
/* Append to gid_array unless gr_gid is a dupe. */
for (i = 0; i < gbm->numgids; i++) {
if (gbm->gid_array[i] == grp->gr_gid)
goto done; /* already present */
}
/* Store gid if there is space. */
if (i < gbm->maxgids)
gbm->gid_array[i] = grp->gr_gid;
/* Always increment numgids so we can detect when out of space. */
gbm->numgids++;
goto done;
}
}
done:
_nss_XbyY_buf_free(buf);
return rval;
}
/*
* BSD-compatible getgrouplist(3) using nss_search(3)
*/
int
getgrouplist(const char *name, gid_t basegid, gid_t *groups, int *ngroupsp)
{
struct nss_groupsbymem gbm;
static DEFINE_NSS_DB_ROOT(db_root);
/* We support BSD semantics where the first element is the base gid */
if (*ngroupsp <= 0)
return -1;
groups[0] = basegid;
memset(&gbm, 0, sizeof(gbm));
gbm.username = name;
gbm.gid_array = groups;
gbm.maxgids = *ngroupsp;
gbm.numgids = 1; /* for basegid */
gbm.force_slow_way = 1;
gbm.str2ent = str2grp;
gbm.process_cstr = process_cstr;
/*
* Can't use nss_search return value since it may return NSS_UNAVAIL
* when no nsswitch.conf entry (e.g. compat mode).
*/
(void)nss_search(&db_root, _nss_initf_group, NSS_DBOP_GROUP_BYMEMBER, &gbm);
if (gbm.numgids <= gbm.maxgids) {
*ngroupsp = gbm.numgids;
return 0;
}
*ngroupsp = gbm.maxgids;
return -1;
}
#else /* !HAVE_GETGRSET && !HAVE__GETGROUPSBYMEMBER */
/*
* BSD-compatible getgrouplist(3) using getgrent(3)
*/
int
getgrouplist(const char *name, gid_t basegid, gid_t *groups, int *ngroupsp)
{
int i, ngroups = 1;
int grpsize = *ngroupsp;
int rval = -1;
struct group *grp;
/* We support BSD semantics where the first element is the base gid */
if (grpsize <= 0)
return -1;
groups[0] = basegid;
setgrent();
while ((grp = getgrent()) != NULL) {
if (grp->gr_gid == basegid || grp->gr_mem == NULL)
continue;
for (i = 0; grp->gr_mem[i] != NULL; i++) {
if (strcmp(name, grp->gr_mem[i]) == 0)
break;
}
if (grp->gr_mem[i] == NULL)
continue; /* user not found */
/* Only add if it is not the same as an existing gid */
for (i = 0; i < ngroups; i++) {
if (grp->gr_gid == groups[i])
break;
}
if (i == ngroups) {
if (ngroups == grpsize)
goto done;
groups[ngroups++] = grp->gr_gid;
}
}
rval = 0;
done:
endgrent();
*ngroupsp = ngroups;
return rval;
}
#endif /* !HAVE_GETGRSET && !HAVE__GETGROUPSBYMEMBER */
#endif /* HAVE_GETGROUPLIST */

107
lib/util/getline.c Normal file
View File

@@ -0,0 +1,107 @@
/*
* Copyright (c) 2009-2010, 2012-2013 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>
#ifndef HAVE_GETLINE
#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>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#include <limits.h>
#include "missing.h"
#ifndef LINE_MAX
# define LINE_MAX 2048
#endif
#ifdef HAVE_FGETLN
ssize_t
getline(char **bufp, size_t *bufsizep, FILE *fp)
{
char *buf, *cp;
size_t bufsize;
size_t len;
buf = fgetln(fp, &len);
if (buf) {
bufsize = *bufp ? *bufsizep : 0;
if (bufsize == 0 || bufsize - 1 < len) {
bufsize = len + 1;
cp = *bufp ? realloc(*bufp, bufsize) : malloc(bufsize);
if (cp == NULL)
return -1;
*bufp = cp;
*bufsizep = bufsize;
}
memcpy(*bufp, buf, len);
(*bufp)[len] = '\0';
}
return buf ? len : -1;
}
#else
ssize_t
getline(char **bufp, size_t *bufsizep, FILE *fp)
{
char *buf, *cp;
size_t bufsize;
ssize_t len = 0;
buf = *bufp;
bufsize = *bufsizep;
if (buf == NULL || bufsize == 0) {
bufsize = LINE_MAX;
cp = buf ? realloc(buf, bufsize) : malloc(bufsize);
if (cp == NULL)
return -1;
buf = cp;
}
for (;;) {
if (fgets(buf + len, bufsize - len, fp) == NULL) {
len = -1;
break;
}
len = strlen(buf);
if (!len || buf[len - 1] == '\n' || feof(fp))
break;
bufsize *= 2;
cp = realloc(buf, bufsize);
if (cp == NULL)
return -1;
buf = cp;
}
*bufp = buf;
*bufsizep = bufsize;
return len;
}
#endif /* HAVE_FGETLN */
#endif /* HAVE_GETLINE */

630
lib/util/getopt_long.c Normal file
View File

@@ -0,0 +1,630 @@
/* $OpenBSD: getopt_long.c,v 1.26 2013/06/08 22:47:56 millert Exp $ */
/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */
/* $FreeBSD: head/lib/libc/stdlib/getopt_long.c 236936 2012-06-11 22:25:20Z delphij $ */
/*
* Copyright (c) 2002 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.
*/
/*-
* Copyright (c) 2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Dieter Baron and Thomas Klausner.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <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>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#define SUDO_ERROR_WRAP 0
#include "missing.h"
#include "fatal.h"
#include "compat/getopt.h"
#define GNU_COMPATIBLE /* Be more compatible with GNU getopt. */
#ifdef REPLACE_GETOPT
int opterr = 1; /* if error message should be printed */
int optind = 1; /* index into parent argv vector */
int optopt = '?'; /* character checked for validity */
char *optarg; /* argument associated with option */
#else
extern int opterr; /* if error message should be printed */
extern int optind; /* index into parent argv vector */
extern int optopt; /* character checked for validity */
extern char *optarg; /* argument associated with option */
#endif
#if !defined(REPLACE_GETOPT) && !defined(HAVE_OPTRESET)
int optreset; /* reset getopt */
#endif
#define PRINT_ERROR ((opterr) && (*options != ':'))
#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
/* return values */
#define BADCH (int)'?'
#define BADARG ((*options == ':') ? (int)':' : (int)'?')
#define INORDER (int)1
#define EMSG ""
#ifdef GNU_COMPATIBLE
#define NO_PREFIX (-1)
#define D_PREFIX 0
#define DD_PREFIX 1
#define W_PREFIX 2
#endif
static int getopt_internal(int, char * const *, const char *,
const struct option *, int *, int);
static int parse_long_options(char * const *, const char *,
const struct option *, int *, int, int);
static int gcd(int, int);
static void permute_args(int, int, int, char * const *);
static char *place = EMSG; /* option letter processing */
/* XXX: set optreset to 1 rather than these two */
static int nonopt_start = -1; /* first non option argument (for permute) */
static int nonopt_end = -1; /* first option after non options (for permute) */
/* Error messages */
static const char recargchar[] = "option requires an argument -- %c";
static const char illoptchar[] = "illegal option -- %c"; /* From P1003.2 */
#ifdef GNU_COMPATIBLE
static int dash_prefix = NO_PREFIX;
static const char gnuoptchar[] = "invalid option -- %c";
static const char recargstring[] = "option `%s%s' requires an argument";
static const char ambig[] = "option `%s%.*s' is ambiguous";
static const char noarg[] = "option `%s%.*s' doesn't allow an argument";
static const char illoptstring[] = "unrecognized option `%s%s'";
#else
static const char recargstring[] = "option requires an argument -- %s";
static const char ambig[] = "ambiguous option -- %.*s";
static const char noarg[] = "option doesn't take an argument -- %.*s";
static const char illoptstring[] = "unknown option -- %s";
#endif
/*
* Compute the greatest common divisor of a and b.
*/
static int
gcd(int a, int b)
{
int c;
c = a % b;
while (c != 0) {
a = b;
b = c;
c = a % b;
}
return (b);
}
/*
* Exchange the block from nonopt_start to nonopt_end with the block
* from nonopt_end to opt_end (keeping the same order of arguments
* in each block).
*/
static void
permute_args(int panonopt_start, int panonopt_end, int opt_end,
char * const *nargv)
{
int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
char *swap;
/*
* compute lengths of blocks and number and size of cycles
*/
nnonopts = panonopt_end - panonopt_start;
nopts = opt_end - panonopt_end;
ncycle = gcd(nnonopts, nopts);
cyclelen = (opt_end - panonopt_start) / ncycle;
for (i = 0; i < ncycle; i++) {
cstart = panonopt_end+i;
pos = cstart;
for (j = 0; j < cyclelen; j++) {
if (pos >= panonopt_end)
pos -= nnonopts;
else
pos += nopts;
swap = nargv[pos];
/* LINTED const cast */
((char **) nargv)[pos] = nargv[cstart];
/* LINTED const cast */
((char **)nargv)[cstart] = swap;
}
}
}
/*
* parse_long_options --
* Parse long options in argc/argv argument vector.
* Returns -1 if short_too is set and the option does not match long_options.
*/
static int
parse_long_options(char * const *nargv, const char *options,
const struct option *long_options, int *idx, int short_too, int flags)
{
char *current_argv, *has_equal;
#ifdef GNU_COMPATIBLE
char *current_dash;
#endif
size_t current_argv_len;
int i, match, exact_match, second_partial_match;
current_argv = place;
#ifdef GNU_COMPATIBLE
switch (dash_prefix) {
case D_PREFIX:
current_dash = "-";
break;
case DD_PREFIX:
current_dash = "--";
break;
case W_PREFIX:
current_dash = "-W ";
break;
default:
current_dash = "";
break;
}
#endif
match = -1;
exact_match = 0;
second_partial_match = 0;
optind++;
if ((has_equal = strchr(current_argv, '=')) != NULL) {
/* argument found (--option=arg) */
current_argv_len = has_equal - current_argv;
has_equal++;
} else
current_argv_len = strlen(current_argv);
for (i = 0; long_options[i].name; i++) {
/* find matching long option */
if (strncmp(current_argv, long_options[i].name,
current_argv_len))
continue;
if (strlen(long_options[i].name) == current_argv_len) {
/* exact match */
match = i;
exact_match = 1;
break;
}
/*
* If this is a known short option, don't allow
* a partial match of a single character.
*/
if (short_too && current_argv_len == 1)
continue;
if (match == -1) /* first partial match */
match = i;
else if ((flags & FLAG_LONGONLY) ||
long_options[i].has_arg !=
long_options[match].has_arg ||
long_options[i].flag != long_options[match].flag ||
long_options[i].val != long_options[match].val)
second_partial_match = 1;
}
if (!exact_match && second_partial_match) {
/* ambiguous abbreviation */
if (PRINT_ERROR)
warningx(ambig,
#ifdef GNU_COMPATIBLE
current_dash,
#endif
(int)current_argv_len,
current_argv);
optopt = 0;
return (BADCH);
}
if (match != -1) { /* option found */
if (long_options[match].has_arg == no_argument
&& has_equal) {
if (PRINT_ERROR)
warningx(noarg,
#ifdef GNU_COMPATIBLE
current_dash,
#endif
(int)current_argv_len,
current_argv);
/*
* XXX: GNU sets optopt to val regardless of flag
*/
if (long_options[match].flag == NULL)
optopt = long_options[match].val;
else
optopt = 0;
#ifdef GNU_COMPATIBLE
return (BADCH);
#else
return (BADARG);
#endif
}
if (long_options[match].has_arg == required_argument ||
long_options[match].has_arg == optional_argument) {
if (has_equal)
optarg = has_equal;
else if (long_options[match].has_arg ==
required_argument) {
/*
* optional argument doesn't use next nargv
*/
optarg = nargv[optind++];
}
}
if ((long_options[match].has_arg == required_argument)
&& (optarg == NULL)) {
/*
* Missing argument; leading ':' indicates no error
* should be generated.
*/
if (PRINT_ERROR)
warningx(recargstring,
#ifdef GNU_COMPATIBLE
current_dash,
#endif
current_argv);
/*
* XXX: GNU sets optopt to val regardless of flag
*/
if (long_options[match].flag == NULL)
optopt = long_options[match].val;
else
optopt = 0;
--optind;
return (BADARG);
}
} else { /* unknown option */
if (short_too) {
--optind;
return (-1);
}
if (PRINT_ERROR)
warningx(illoptstring,
#ifdef GNU_COMPATIBLE
current_dash,
#endif
current_argv);
optopt = 0;
return (BADCH);
}
if (idx)
*idx = match;
if (long_options[match].flag) {
*long_options[match].flag = long_options[match].val;
return (0);
} else
return (long_options[match].val);
}
/*
* getopt_internal --
* Parse argc/argv argument vector. Called by user level routines.
*/
static int
getopt_internal(int nargc, char * const *nargv, const char *options,
const struct option *long_options, int *idx, int flags)
{
char *oli; /* option letter list index */
int optchar, short_too;
int posixly_correct; /* no static, can be changed on the fly */
if (options == NULL)
return (-1);
/*
* Disable GNU extensions if POSIXLY_CORRECT is set or options
* string begins with a '+'.
*/
posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
#ifdef GNU_COMPATIBLE
if (*options == '-')
flags |= FLAG_ALLARGS;
else if (posixly_correct || *options == '+')
flags &= ~FLAG_PERMUTE;
#else
if (posixly_correct || *options == '+')
flags &= ~FLAG_PERMUTE;
else if (*options == '-')
flags |= FLAG_ALLARGS;
#endif
if (*options == '+' || *options == '-')
options++;
/*
* XXX Some GNU programs (like cvs) set optind to 0 instead of
* XXX using optreset. Work around this braindamage.
*/
if (optind == 0)
optind = optreset = 1;
optarg = NULL;
if (optreset)
nonopt_start = nonopt_end = -1;
start:
if (optreset || !*place) { /* update scanning pointer */
optreset = 0;
if (optind >= nargc) { /* end of argument vector */
place = EMSG;
if (nonopt_end != -1) {
/* do permutation, if we have to */
permute_args(nonopt_start, nonopt_end,
optind, nargv);
optind -= nonopt_end - nonopt_start;
}
else if (nonopt_start != -1) {
/*
* If we skipped non-options, set optind
* to the first of them.
*/
optind = nonopt_start;
}
nonopt_start = nonopt_end = -1;
return (-1);
}
if (*(place = nargv[optind]) != '-' ||
#ifdef GNU_COMPATIBLE
place[1] == '\0') {
#else
(place[1] == '\0' && strchr(options, '-') == NULL)) {
#endif
place = EMSG; /* found non-option */
if (flags & FLAG_ALLARGS) {
/*
* GNU extension:
* return non-option as argument to option 1
*/
optarg = nargv[optind++];
return (INORDER);
}
if (!(flags & FLAG_PERMUTE)) {
/*
* If no permutation wanted, stop parsing
* at first non-option.
*/
return (-1);
}
/* do permutation */
if (nonopt_start == -1)
nonopt_start = optind;
else if (nonopt_end != -1) {
permute_args(nonopt_start, nonopt_end,
optind, nargv);
nonopt_start = optind -
(nonopt_end - nonopt_start);
nonopt_end = -1;
}
optind++;
/* process next argument */
goto start;
}
if (nonopt_start != -1 && nonopt_end == -1)
nonopt_end = optind;
/*
* If we have "-" do nothing, if "--" we are done.
*/
if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
optind++;
place = EMSG;
/*
* We found an option (--), so if we skipped
* non-options, we have to permute.
*/
if (nonopt_end != -1) {
permute_args(nonopt_start, nonopt_end,
optind, nargv);
optind -= nonopt_end - nonopt_start;
}
nonopt_start = nonopt_end = -1;
return (-1);
}
}
/*
* Check long options if:
* 1) we were passed some
* 2) the arg is not just "-"
* 3) either the arg starts with -- we are getopt_long_only()
*/
if (long_options != NULL && place != nargv[optind] &&
(*place == '-' || (flags & FLAG_LONGONLY))) {
short_too = 0;
#ifdef GNU_COMPATIBLE
dash_prefix = D_PREFIX;
#endif
if (*place == '-') {
place++; /* --foo long option */
#ifdef GNU_COMPATIBLE
dash_prefix = DD_PREFIX;
#endif
} else if (*place != ':' && strchr(options, *place) != NULL)
short_too = 1; /* could be short option too */
optchar = parse_long_options(nargv, options, long_options,
idx, short_too, flags);
if (optchar != -1) {
place = EMSG;
return (optchar);
}
}
if ((optchar = (int)*place++) == (int)':' ||
(optchar == (int)'-' && *place != '\0') ||
(oli = strchr(options, optchar)) == NULL) {
/*
* If the user specified "-" and '-' isn't listed in
* options, return -1 (non-option) as per POSIX.
* Otherwise, it is an unknown option character (or ':').
*/
if (optchar == (int)'-' && *place == '\0')
return (-1);
if (!*place)
++optind;
#ifdef GNU_COMPATIBLE
if (PRINT_ERROR)
warningx(posixly_correct ? illoptchar : gnuoptchar,
optchar);
#else
if (PRINT_ERROR)
warningx(illoptchar, optchar);
#endif
optopt = optchar;
return (BADCH);
}
if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
/* -W long-option */
if (*place) /* no space */
/* NOTHING */;
else if (++optind >= nargc) { /* no arg */
place = EMSG;
if (PRINT_ERROR)
warningx(recargchar, optchar);
optopt = optchar;
return (BADARG);
} else /* white space */
place = nargv[optind];
#ifdef GNU_COMPATIBLE
dash_prefix = W_PREFIX;
#endif
optchar = parse_long_options(nargv, options, long_options,
idx, 0, flags);
place = EMSG;
return (optchar);
}
if (*++oli != ':') { /* doesn't take argument */
if (!*place)
++optind;
} else { /* takes (optional) argument */
optarg = NULL;
if (*place) /* no white space */
optarg = place;
else if (oli[1] != ':') { /* arg not optional */
if (++optind >= nargc) { /* no arg */
place = EMSG;
if (PRINT_ERROR)
warningx(recargchar, optchar);
optopt = optchar;
return (BADARG);
} else
optarg = nargv[optind];
}
place = EMSG;
++optind;
}
/* dump back option letter */
return (optchar);
}
#ifdef REPLACE_GETOPT
/*
* getopt --
* Parse argc/argv argument vector.
*/
int
getopt(int nargc, char * const *nargv, const char *options)
{
/*
* We don't pass FLAG_PERMUTE to getopt_internal() since
* the BSD getopt(3) (unlike GNU) has never done this.
*
* Furthermore, since many privileged programs call getopt()
* before dropping privileges it makes sense to keep things
* as simple (and bug-free) as possible.
*/
return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
}
#endif /* REPLACE_GETOPT */
/*
* getopt_long --
* Parse argc/argv argument vector.
*/
int
getopt_long(int nargc, char * const *nargv, const char *options,
const struct option *long_options, int *idx)
{
return (getopt_internal(nargc, nargv, options, long_options, idx,
FLAG_PERMUTE));
}
/*
* getopt_long_only --
* Parse argc/argv argument vector.
*/
int
getopt_long_only(int nargc, char * const *nargv, const char *options,
const struct option *long_options, int *idx)
{
return (getopt_internal(nargc, nargv, options, long_options, idx,
FLAG_PERMUTE|FLAG_LONGONLY));
}

89
lib/util/gidlist.c Normal file
View File

@@ -0,0 +1,89 @@
/*
* Copyright (c) 2013-2014 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 <grp.h>
#define DEFAULT_TEXT_DOMAIN "sudo"
#include "gettext.h" /* must be included before missing.h */
#include "missing.h"
#include "alloc.h"
#include "fatal.h"
#include "sudo_debug.h"
#include "sudo_util.h"
/*
* Parse a comma-separated list of gids into an allocated array of GETGROUPS_T.
* If a pointer to the base gid is specified, it is stored as the first element
* in the array.
* Returns the number of gids in the allocated array.
*/
int
parse_gid_list(const char *gidstr, const gid_t *basegid, GETGROUPS_T **gidsp)
{
int ngids = 0;
GETGROUPS_T *gids;
const char *cp = gidstr;
const char *errstr;
char *ep;
debug_decl(atoid, SUDO_DEBUG_UTIL)
/* Count groups. */
if (*cp != '\0') {
ngids++;
do {
if (*cp++ == ',')
ngids++;
} while (*cp != '\0');
}
/* Base gid is optional. */
if (basegid != NULL)
ngids++;
/* Allocate and fill in array. */
if (ngids != 0) {
gids = emallocarray(ngids, sizeof(GETGROUPS_T));
ngids = 0;
if (basegid != NULL)
gids[ngids++] = *basegid;
cp = gidstr;
do {
gids[ngids] = (GETGROUPS_T) atoid(cp, ",", &ep, &errstr);
if (errstr != NULL) {
warningx(U_("%s: %s"), cp, U_(errstr));
free(gids);
debug_return_int(-1);
}
if (basegid == NULL || gids[ngids] != *basegid)
ngids++;
cp = ep + 1;
} while (*ep != '\0');
*gidsp = gids;
}
debug_return_int(ngids);
}

904
lib/util/glob.c Normal file
View File

@@ -0,0 +1,904 @@
/*
* Copyright (c) 2008-2010 Todd C. Miller <Todd.Miller@courtesan.com>
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Guido van Rossum.
*
* 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 the University 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 REGENTS 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 REGENTS 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.
*
* @(#)glob.c 8.3 (Berkeley) 10/13/93
*/
/*
* glob(3) -- a superset of the one defined in POSIX 1003.2.
*
* The [!...] convention to negate a range is supported (SysV, Posix, ksh).
*
* Optional extra services, controlled by flags not defined by POSIX:
*
* GLOB_MAGCHAR:
* Set in gl_flags if pattern contained a globbing character.
* GLOB_TILDE:
* expand ~user/foo to the /home/dir/of/user/foo
* GLOB_BRACE:
* expand {1,2}{a,b} to 1a 1b 2a 2b
* gl_matchc:
* Number of matches in the current invocation of glob.
*/
#include <config.h>
#ifndef HAVE_GLOB
#include <sys/types.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 */
#if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS)
# include <malloc.h>
#endif /* HAVE_MALLOC_H && !STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <ctype.h>
#ifdef HAVE_DIRENT_H
# include <dirent.h>
#else
# define dirent direct
# 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 <errno.h>
#include <limits.h>
#include <pwd.h>
#include "missing.h"
#include "compat/glob.h"
#include "compat/charclass.h"
#define DOLLAR '$'
#define DOT '.'
#define EOS '\0'
#define LBRACKET '['
#define NOT '!'
#define QUESTION '?'
#define QUOTE '\\'
#define RANGE '-'
#define RBRACKET ']'
#define SEP '/'
#define STAR '*'
#define TILDE '~'
#define UNDERSCORE '_'
#define LBRACE '{'
#define RBRACE '}'
#define SLASH '/'
#define COMMA ','
#ifndef DEBUG
#define M_QUOTE 0x8000
#define M_PROTECT 0x4000
#define M_MASK 0xffff
#define M_ASCII 0x00ff
typedef unsigned short Char;
#else
#define M_QUOTE 0x80
#define M_PROTECT 0x40
#define M_MASK 0xff
#define M_ASCII 0x7f
typedef char Char;
#endif
#define CHAR(c) ((Char)((c)&M_ASCII))
#define META(c) ((Char)((c)|M_QUOTE))
#define M_ALL META('*')
#define M_END META(']')
#define M_NOT META('!')
#define M_ONE META('?')
#define M_RNG META('-')
#define M_SET META('[')
#define M_CLASS META(':')
#define ismeta(c) (((c)&M_QUOTE) != 0)
static int compare(const void *, const void *);
static int g_Ctoc(const Char *, char *, unsigned int);
static int g_lstat(Char *, struct stat *, glob_t *);
static DIR *g_opendir(Char *, glob_t *);
static Char *g_strchr(const Char *, int);
static int g_strncmp(const Char *, const char *, size_t);
static int g_stat(Char *, struct stat *, glob_t *);
static int glob0(const Char *, glob_t *);
static int glob1(Char *, Char *, glob_t *);
static int glob2(Char *, Char *, Char *, Char *, Char *, Char *,
glob_t *);
static int glob3(Char *, Char *, Char *, Char *, Char *, Char *,
Char *, Char *, glob_t *);
static int globextend(const Char *, glob_t *);
static const Char *
globtilde(const Char *, Char *, size_t, glob_t *);
static int globexp1(const Char *, glob_t *);
static int globexp2(const Char *, const Char *, glob_t *, int *);
static int match(Char *, Char *, Char *);
#ifdef DEBUG
static void qprintf(const char *, Char *);
#endif
int
rpl_glob(const char *pattern, int flags, int (*errfunc)(const char *, int),
glob_t *pglob)
{
const unsigned char *patnext;
int c;
Char *bufnext, *bufend, patbuf[PATH_MAX];
patnext = (unsigned char *) pattern;
if (!(flags & GLOB_APPEND)) {
pglob->gl_pathc = 0;
pglob->gl_pathv = NULL;
if (!(flags & GLOB_DOOFFS))
pglob->gl_offs = 0;
}
pglob->gl_flags = flags & ~GLOB_MAGCHAR;
pglob->gl_errfunc = errfunc;
pglob->gl_matchc = 0;
bufnext = patbuf;
bufend = bufnext + PATH_MAX - 1;
if (flags & GLOB_NOESCAPE)
while (bufnext < bufend && (c = *patnext++) != EOS)
*bufnext++ = c;
else {
/* Protect the quoted characters. */
while (bufnext < bufend && (c = *patnext++) != EOS)
if (c == QUOTE) {
if ((c = *patnext++) == EOS) {
c = QUOTE;
--patnext;
}
*bufnext++ = c | M_PROTECT;
} else
*bufnext++ = c;
}
*bufnext = EOS;
if (flags & GLOB_BRACE)
return globexp1(patbuf, pglob);
else
return glob0(patbuf, pglob);
}
/*
* Expand recursively a glob {} pattern. When there is no more expansion
* invoke the standard globbing routine to glob the rest of the magic
* characters
*/
static int
globexp1(const Char *pattern, glob_t *pglob)
{
const Char* ptr = pattern;
int rv;
/* Protect a single {}, for find(1), like csh */
if (pattern[0] == LBRACE && pattern[1] == RBRACE && pattern[2] == EOS)
return glob0(pattern, pglob);
while ((ptr = (const Char *) g_strchr(ptr, LBRACE)) != NULL)
if (!globexp2(ptr, pattern, pglob, &rv))
return rv;
return glob0(pattern, pglob);
}
/*
* Recursive brace globbing helper. Tries to expand a single brace.
* If it succeeds then it invokes globexp1 with the new pattern.
* If it fails then it tries to glob the rest of the pattern and returns.
*/
static int
globexp2(const Char *ptr, const Char *pattern, glob_t *pglob, int *rv)
{
int i;
Char *lm, *ls;
const Char *pe, *pm, *pl;
Char patbuf[PATH_MAX];
/* copy part up to the brace */
for (lm = patbuf, pm = pattern; pm != ptr; *lm++ = *pm++)
continue;
*lm = EOS;
ls = lm;
/* Find the balanced brace */
for (i = 0, pe = ++ptr; *pe; pe++)
if (*pe == LBRACKET) {
/* Ignore everything between [] */
for (pm = pe++; *pe != RBRACKET && *pe != EOS; pe++)
continue;
if (*pe == EOS) {
/*
* We could not find a matching RBRACKET.
* Ignore and just look for RBRACE
*/
pe = pm;
}
} else if (*pe == LBRACE)
i++;
else if (*pe == RBRACE) {
if (i == 0)
break;
i--;
}
/* Non matching braces; just glob the pattern */
if (i != 0 || *pe == EOS) {
*rv = glob0(patbuf, pglob);
return 0;
}
for (i = 0, pl = pm = ptr; pm <= pe; pm++) {
switch (*pm) {
case LBRACKET:
/* Ignore everything between [] */
for (pl = pm++; *pm != RBRACKET && *pm != EOS; pm++)
continue;
if (*pm == EOS) {
/*
* We could not find a matching RBRACKET.
* Ignore and just look for RBRACE
*/
pm = pl;
}
break;
case LBRACE:
i++;
break;
case RBRACE:
if (i) {
i--;
break;
}
/* FALLTHROUGH */
case COMMA:
if (i && *pm == COMMA)
break;
else {
/* Append the current string */
for (lm = ls; (pl < pm); *lm++ = *pl++)
continue;
/*
* Append the rest of the pattern after the
* closing brace
*/
for (pl = pe + 1; (*lm++ = *pl++) != EOS; )
continue;
/* Expand the current pattern */
#ifdef DEBUG
qprintf("globexp2:", patbuf);
#endif
*rv = globexp1(patbuf, pglob);
/* move after the comma, to the next string */
pl = pm + 1;
}
break;
default:
break;
}
}
*rv = 0;
return 0;
}
/*
* expand tilde from the passwd file.
*/
static const Char *
globtilde(const Char *pattern, Char *patbuf, size_t patbuf_len, glob_t *pglob)
{
struct passwd *pwd;
char *h;
const Char *p;
Char *b, *eb;
if (*pattern != TILDE || !(pglob->gl_flags & GLOB_TILDE))
return pattern;
/* Copy up to the end of the string or / */
eb = &patbuf[patbuf_len - 1];
for (p = pattern + 1, h = (char *) patbuf;
h < (char *)eb && *p && *p != SLASH; *h++ = *p++)
continue;
*h = EOS;
if (((char *) patbuf)[0] == EOS) {
/*
* handle a plain ~ or ~/ by expanding $HOME
* first and then trying the password file
*/
if ((h = getenv("HOME")) == NULL) {
if ((pwd = getpwuid(getuid())) == NULL)
return pattern;
else
h = pwd->pw_dir;
}
} else {
/*
* Expand a ~user
*/
if ((pwd = getpwnam((char*) patbuf)) == NULL)
return pattern;
else
h = pwd->pw_dir;
}
/* Copy the home directory */
for (b = patbuf; b < eb && *h; *b++ = *h++)
continue;
/* Append the rest of the pattern */
while (b < eb && (*b++ = *p++) != EOS)
continue;
*b = EOS;
return patbuf;
}
static int
g_strncmp(const Char *s1, const char *s2, size_t n)
{
int rv = 0;
while (n--) {
rv = *(Char *)s1 - *(const unsigned char *)s2++;
if (rv)
break;
if (*s1++ == '\0')
break;
}
return rv;
}
static int
g_charclass(const Char **patternp, Char **bufnextp)
{
const Char *pattern = *patternp + 1;
Char *bufnext = *bufnextp;
const Char *colon;
struct cclass *cc;
size_t len;
if ((colon = g_strchr(pattern, ':')) == NULL || colon[1] != ']')
return 1; /* not a character class */
len = (size_t)(colon - pattern);
for (cc = cclasses; cc->name != NULL; cc++) {
if (!g_strncmp(pattern, cc->name, len) && cc->name[len] == '\0')
break;
}
if (cc->name == NULL)
return -1; /* invalid character class */
*bufnext++ = M_CLASS;
*bufnext++ = (Char)(cc - &cclasses[0]);
*bufnextp = bufnext;
*patternp += len + 3;
return 0;
}
/*
* The main glob() routine: compiles the pattern (optionally processing
* quotes), calls glob1() to do the real pattern matching, and finally
* sorts the list (unless unsorted operation is requested). Returns 0
* if things went well, nonzero if errors occurred. It is not an error
* to find no matches.
*/
static int
glob0(const Char *pattern, glob_t *pglob)
{
const Char *qpatnext;
int c, err, oldpathc;
Char *bufnext, patbuf[PATH_MAX];
qpatnext = globtilde(pattern, patbuf, PATH_MAX, pglob);
oldpathc = pglob->gl_pathc;
bufnext = patbuf;
/* We don't need to check for buffer overflow any more. */
while ((c = *qpatnext++) != EOS) {
switch (c) {
case LBRACKET:
c = *qpatnext;
if (c == NOT)
++qpatnext;
if (*qpatnext == EOS ||
g_strchr(qpatnext+1, RBRACKET) == NULL) {
*bufnext++ = LBRACKET;
if (c == NOT)
--qpatnext;
break;
}
*bufnext++ = M_SET;
if (c == NOT)
*bufnext++ = M_NOT;
c = *qpatnext++;
do {
if (c == LBRACKET && *qpatnext == ':') {
do {
err = g_charclass(&qpatnext,
&bufnext);
if (err)
break;
c = *qpatnext++;
} while (c == LBRACKET && *qpatnext == ':');
if (err == -1 &&
!(pglob->gl_flags & GLOB_NOCHECK))
return GLOB_NOMATCH;
if (c == RBRACKET)
break;
}
*bufnext++ = CHAR(c);
if (*qpatnext == RANGE &&
(c = qpatnext[1]) != RBRACKET) {
*bufnext++ = M_RNG;
*bufnext++ = CHAR(c);
qpatnext += 2;
}
} while ((c = *qpatnext++) != RBRACKET);
pglob->gl_flags |= GLOB_MAGCHAR;
*bufnext++ = M_END;
break;
case QUESTION:
pglob->gl_flags |= GLOB_MAGCHAR;
*bufnext++ = M_ONE;
break;
case STAR:
pglob->gl_flags |= GLOB_MAGCHAR;
/* collapse adjacent stars to one,
* to avoid exponential behavior
*/
if (bufnext == patbuf || bufnext[-1] != M_ALL)
*bufnext++ = M_ALL;
break;
default:
*bufnext++ = CHAR(c);
break;
}
}
*bufnext = EOS;
#ifdef DEBUG
qprintf("glob0:", patbuf);
#endif
if ((err = glob1(patbuf, patbuf + PATH_MAX - 1, pglob)) != 0)
return err;
/*
* If there was no match we are going to append the pattern
* if GLOB_NOCHECK was specified.
*/
if (pglob->gl_pathc == oldpathc) {
if (pglob->gl_flags & GLOB_NOCHECK)
return globextend(pattern, pglob);
else
return GLOB_NOMATCH;
}
if (!(pglob->gl_flags & GLOB_NOSORT))
qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc,
pglob->gl_pathc - oldpathc, sizeof(char *), compare);
return 0;
}
static int
compare(const void *p, const void *q)
{
return strcmp(*(char **)p, *(char **)q);
}
static int
glob1(Char *pattern, Char *pattern_last, glob_t *pglob)
{
Char pathbuf[PATH_MAX];
/* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */
if (*pattern == EOS)
return 0;
return glob2(pathbuf, pathbuf + PATH_MAX - 1,
pathbuf, pathbuf + PATH_MAX - 1,
pattern, pattern_last, pglob);
}
/*
* The functions glob2 and glob3 are mutually recursive; there is one level
* of recursion for each segment in the pattern that contains one or more
* meta characters.
*/
static int
glob2(Char *pathbuf, Char *pathbuf_last, Char *pathend, Char *pathend_last,
Char *pattern, Char *pattern_last, glob_t *pglob)
{
struct stat sb;
Char *p, *q;
int anymeta;
/*
* Loop over pattern segments until end of pattern or until
* segment with meta character found.
*/
for (anymeta = 0;;) {
if (*pattern == EOS) { /* End of pattern? */
*pathend = EOS;
if (g_lstat(pathbuf, &sb, pglob))
return 0;
if (((pglob->gl_flags & GLOB_MARK) &&
pathend[-1] != SEP) && (S_ISDIR(sb.st_mode) ||
(S_ISLNK(sb.st_mode) &&
(g_stat(pathbuf, &sb, pglob) == 0) &&
S_ISDIR(sb.st_mode)))) {
if (pathend+1 > pathend_last)
return 1;
*pathend++ = SEP;
*pathend = EOS;
}
++pglob->gl_matchc;
return globextend(pathbuf, pglob);
}
/* Find end of next segment, copy tentatively to pathend. */
q = pathend;
p = pattern;
while (*p != EOS && *p != SEP) {
if (ismeta(*p))
anymeta = 1;
if (q+1 > pathend_last)
return 1;
*q++ = *p++;
}
if (!anymeta) { /* No expansion, do next segment. */
pathend = q;
pattern = p;
while (*pattern == SEP) {
if (pathend+1 > pathend_last)
return 1;
*pathend++ = *pattern++;
}
} else
/* Need expansion, recurse. */
return glob3(pathbuf, pathbuf_last, pathend,
pathend_last, pattern, pattern_last,
p, pattern_last, pglob);
}
/* NOTREACHED */
}
static int
glob3(Char *pathbuf, Char *pathbuf_last, Char *pathend, Char *pathend_last,
Char *pattern, Char *pattern_last, Char *restpattern,
Char *restpattern_last, glob_t *pglob)
{
struct dirent *dp;
DIR *dirp;
int err;
char buf[PATH_MAX];
if (pathend > pathend_last)
return 1;
*pathend = EOS;
errno = 0;
if ((dirp = g_opendir(pathbuf, pglob)) == NULL) {
/* TODO: don't call for ENOENT or ENOTDIR? */
if (pglob->gl_errfunc) {
if (g_Ctoc(pathbuf, buf, sizeof(buf)))
return GLOB_ABORTED;
if (pglob->gl_errfunc(buf, errno) ||
pglob->gl_flags & GLOB_ERR)
return GLOB_ABORTED;
}
return 0;
}
err = 0;
/* Search directory for matching names. */
while ((dp = readdir(dirp))) {
unsigned char *sc;
Char *dc;
/* Initial DOT must be matched literally. */
if (dp->d_name[0] == DOT && *pattern != DOT)
continue;
dc = pathend;
sc = (unsigned char *) dp->d_name;
while (dc < pathend_last && (*dc++ = *sc++) != EOS)
continue;
if (dc >= pathend_last) {
*dc = EOS;
err = 1;
break;
}
if (!match(pathend, pattern, restpattern)) {
*pathend = EOS;
continue;
}
err = glob2(pathbuf, pathbuf_last, --dc, pathend_last,
restpattern, restpattern_last, pglob);
if (err)
break;
}
closedir(dirp);
return err;
}
/*
* Extend the gl_pathv member of a glob_t structure to accommodate a new item,
* add the new item, and update gl_pathc.
*
* This assumes the BSD realloc, which only copies the block when its size
* crosses a power-of-two boundary; for v7 realloc, this would cause quadratic
* behavior.
*
* Return 0 if new item added, error code if memory couldn't be allocated.
*
* Invariant of the glob_t structure:
* Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and
* gl_pathv points to (gl_offs + gl_pathc + 1) items.
*/
static int
globextend(const Char *path, glob_t *pglob)
{
char **pathv;
int i;
unsigned int newsize, len;
char *copy;
const Char *p;
newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs);
pathv = pglob->gl_pathv ?
(char **)realloc((char *)pglob->gl_pathv, newsize) :
(char **)malloc(newsize);
if (pathv == NULL) {
if (pglob->gl_pathv) {
free(pglob->gl_pathv);
pglob->gl_pathv = NULL;
}
return GLOB_NOSPACE;
}
if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) {
/* first time around -- clear initial gl_offs items */
pathv += pglob->gl_offs;
for (i = pglob->gl_offs; --i >= 0; )
*--pathv = NULL;
}
pglob->gl_pathv = pathv;
for (p = path; *p++;)
continue;
len = (size_t)(p - path);
if ((copy = malloc(len)) != NULL) {
if (g_Ctoc(path, copy, len)) {
free(copy);
return GLOB_NOSPACE;
}
pathv[pglob->gl_offs + pglob->gl_pathc++] = copy;
}
pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
return copy == NULL ? GLOB_NOSPACE : 0;
}
/*
* pattern matching function for filenames. Each occurrence of the *
* pattern causes a recursion level.
*/
static int
match(Char *name, Char *pat, Char *patend)
{
int ok, negate_range;
Char c, k;
while (pat < patend) {
c = *pat++;
switch (c & M_MASK) {
case M_ALL:
if (pat == patend)
return 1;
do {
if (match(name, pat, patend))
return 1;
} while (*name++ != EOS);
return 0;
case M_ONE:
if (*name++ == EOS)
return 0;
break;
case M_SET:
ok = 0;
if ((k = *name++) == EOS)
return 0;
if ((negate_range = ((*pat & M_MASK) == M_NOT)) != EOS)
++pat;
while (((c = *pat++) & M_MASK) != M_END) {
if ((c & M_MASK) == M_CLASS) {
int idx = *pat & M_MASK;
if (idx < NCCLASSES &&
cclasses[idx].isctype(k))
ok = 1;
++pat;
}
if ((*pat & M_MASK) == M_RNG) {
if (c <= k && k <= pat[1])
ok = 1;
pat += 2;
} else if (c == k)
ok = 1;
}
if (ok == negate_range)
return 0;
break;
default:
if (*name++ != c)
return 0;
break;
}
}
return *name == EOS;
}
/* Free allocated data belonging to a glob_t structure. */
void
rpl_globfree(glob_t *pglob)
{
int i;
char **pp;
if (pglob->gl_pathv != NULL) {
pp = pglob->gl_pathv + pglob->gl_offs;
for (i = pglob->gl_pathc; i--; ++pp)
if (*pp)
free(*pp);
free(pglob->gl_pathv);
pglob->gl_pathv = NULL;
}
}
static DIR *
g_opendir(Char *str, glob_t *pglob)
{
char buf[PATH_MAX];
if (!*str) {
buf[0] = '.';
buf[1] = '\0';
} else {
if (g_Ctoc(str, buf, sizeof(buf)))
return NULL;
}
return opendir(buf);
}
static int
g_lstat(Char *fn, struct stat *sb, glob_t *pglob)
{
char buf[PATH_MAX];
if (g_Ctoc(fn, buf, sizeof(buf)))
return -1;
return lstat(buf, sb);
}
static int
g_stat(Char *fn, struct stat *sb, glob_t *pglob)
{
char buf[PATH_MAX];
if (g_Ctoc(fn, buf, sizeof(buf)))
return -1;
return stat(buf, sb);
}
static Char *
g_strchr(const Char *str, int ch)
{
do {
if (*str == ch)
return (Char *)str;
} while (*str++);
return NULL;
}
static int
g_Ctoc(const Char *str, char *buf, unsigned int len)
{
while (len--) {
if ((*buf++ = *str++) == EOS)
return 0;
}
return 1;
}
#ifdef DEBUG
static void
qprintf(const char *str, Char *s)
{
Char *p;
(void)printf("%s:\n", str);
for (p = s; *p; p++)
(void)printf("%c", CHAR(*p));
(void)printf("\n");
for (p = s; *p; p++)
(void)printf("%c", *p & M_PROTECT ? '"' : ' ');
(void)printf("\n");
for (p = s; *p; p++)
(void)printf("%c", ismeta(*p) ? '_' : ' ');
(void)printf("\n");
}
#endif /* DEBUG */
#endif /* HAVE_GLOB */

258
lib/util/inet_pton.c Normal file
View File

@@ -0,0 +1,258 @@
/* $OpenBSD: inet_pton.c,v 1.8 2010/05/06 15:47:14 claudio Exp $ */
/* Copyright (c) 1996 by Internet Software Consortium.
*
* 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 INTERNET SOFTWARE CONSORTIUM DISCLAIMS
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
* CONSORTIUM 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>
#if !defined(HAVE_INET_PTON)
#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#ifdef HAVE_STRING_H
# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
# include <memory.h>
# endif
# include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#include <errno.h>
#include "missing.h"
#ifndef EAFNOSUPPORT
# define EAFNOSUPPORT EINVAL
#endif
#ifndef NS_INADDRSZ
# ifdef INADDRSZ
# define NS_INADDRSZ INADDRSZ
# else
# define NS_INADDRSZ 4
# endif
#endif
#ifndef NS_IN6ADDRSZ
# ifdef IN6ADDRSZ
# define NS_IN6ADDRSZ IN6ADDRSZ
# else
# define NS_IN6ADDRSZ 16
# endif
#endif
#ifndef NS_INT16SZ
# ifdef INT16SZ
# define NS_INT16SZ INT16SZ
# else
# define NS_INT16SZ 2
# endif
#endif
/*
* WARNING: Don't even consider trying to compile this on a system where
* sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
*/
/* int
* inet_pton4(src, dst)
* like inet_aton() but without all the hexadecimal and shorthand.
* return:
* 1 if `src' is a valid dotted quad, else 0.
* notice:
* does not touch `dst' unless it's returning 1.
* author:
* Paul Vixie, 1996.
*/
static int
inet_pton4(const char *src, u_char *dst)
{
const char digits[] = "0123456789";
int saw_digit, octets, ch;
u_char tmp[NS_INADDRSZ], *tp;
saw_digit = 0;
octets = 0;
/* cppcheck-suppress uninitvar */
*(tp = tmp) = '\0';
while ((ch = (unsigned char)*src++) != '\0') {
const char *pch;
if ((pch = strchr(digits, ch)) != NULL) {
u_int new = *tp * 10 + (pch - digits);
if (new > 255)
return (0);
if (!saw_digit) {
if (++octets > 4)
return (0);
saw_digit = 1;
}
*tp = new;
} else if (ch == '.' && saw_digit) {
if (octets == 4)
return (0);
*++tp = 0;
saw_digit = 0;
} else
return (0);
}
if (octets < 4)
return (0);
memcpy(dst, tmp, NS_INADDRSZ);
return (1);
}
#ifdef HAVE_STRUCT_IN6_ADDR
/* int
* inet_pton6(src, dst)
* convert presentation level address to network order binary form.
* return:
* 1 if `src' is a valid [RFC1884 2.2] address, else 0.
* notice:
* does not touch `dst' unless it's returning 1.
* credit:
* inspired by Mark Andrews.
* author:
* Paul Vixie, 1996.
*/
static int
inet_pton6(const char *src, u_char *dst)
{
const char xdigits_l[] = "0123456789abcdef",
xdigits_u[] = "0123456789ABCDEF";
u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
const char *xdigits, *curtok;
int ch, saw_xdigit, count_xdigit;
u_int val;
/* cppcheck-suppress uninitvar */
memset((tp = tmp), 0, NS_IN6ADDRSZ);
endp = tp + NS_IN6ADDRSZ;
colonp = NULL;
/* Leading :: requires some special handling. */
if (*src == ':')
if (*++src != ':')
return (0);
curtok = src;
saw_xdigit = count_xdigit = 0;
val = 0;
while ((ch = (unsigned char)*src++) != '\0') {
const char *pch;
if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
pch = strchr((xdigits = xdigits_u), ch);
if (pch != NULL) {
if (count_xdigit >= 4)
return (0);
val <<= 4;
val |= (pch - xdigits);
if (val > 0xffff)
return (0);
saw_xdigit = 1;
count_xdigit++;
continue;
}
if (ch == ':') {
curtok = src;
if (!saw_xdigit) {
if (colonp)
return (0);
colonp = tp;
continue;
} else if (*src == '\0') {
return (0);
}
if (tp + NS_INT16SZ > endp)
return (0);
*tp++ = (u_char) (val >> 8) & 0xff;
*tp++ = (u_char) val & 0xff;
saw_xdigit = 0;
count_xdigit = 0;
val = 0;
continue;
}
if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
inet_pton4(curtok, tp) > 0) {
tp += NS_INADDRSZ;
saw_xdigit = 0;
count_xdigit = 0;
break; /* '\0' was seen by inet_pton4(). */
}
return (0);
}
if (saw_xdigit) {
if (tp + NS_INT16SZ > endp)
return (0);
*tp++ = (u_char) (val >> 8) & 0xff;
*tp++ = (u_char) val & 0xff;
}
if (colonp != NULL) {
/*
* Since some memmove()'s erroneously fail to handle
* overlapping regions, we'll do the shift by hand.
*/
const long n = tp - colonp;
long i;
if (tp == endp)
return (0);
for (i = 1; i <= n; i++) {
endp[- i] = colonp[n - i];
colonp[n - i] = 0;
}
tp = endp;
}
if (tp != endp)
return (0);
memcpy(dst, tmp, NS_IN6ADDRSZ);
return (1);
}
#endif /* HAVE_STRUCT_IN6_ADDR */
/* int
* inet_pton(af, src, dst)
* convert from presentation format (which usually means ASCII printable)
* to network format (which is usually some kind of binary format).
* return:
* 1 if the address was valid for the specified address family
* 0 if the address wasn't valid (`dst' is untouched in this case)
* -1 if some other error occurred (`dst' is untouched in this case, too)
* author:
* Paul Vixie, 1996.
*/
int
inet_pton(int af, const char *src, void *dst)
{
switch (af) {
case AF_INET:
return (inet_pton4(src, dst));
#ifdef HAVE_STRUCT_IN6_ADDR
case AF_INET6:
return (inet_pton6(src, dst));
#endif /* HAVE_STRUCT_IN6_ADDR */
default:
errno = EAFNOSUPPORT;
return (-1);
}
/* NOTREACHED */
}
#endif /* HAVE_INET_PTON */

32
lib/util/isblank.c Normal file
View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2008, 2010-2011, 2013
* 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>
#ifndef HAVE_ISBLANK
#include <sys/types.h>
#include "missing.h"
#undef isblank
int
isblank(int ch)
{
return ch == ' ' || ch == '\t';
}
#endif /* HAVE_ISBLANK */

275
lib/util/lbuf.c Normal file
View File

@@ -0,0 +1,275 @@
/*
* Copyright (c) 2007-2013 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 <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>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <ctype.h>
#include "missing.h"
#include "alloc.h"
#include "fatal.h"
#include "lbuf.h"
#include "sudo_debug.h"
void
lbuf_init(struct lbuf *lbuf, int (*output)(const char *),
int indent, const char *continuation, int cols)
{
debug_decl(lbuf_init, SUDO_DEBUG_UTIL)
lbuf->output = output;
lbuf->continuation = continuation;
lbuf->indent = indent;
lbuf->cols = cols;
lbuf->len = 0;
lbuf->size = 0;
lbuf->buf = NULL;
debug_return;
}
void
lbuf_destroy(struct lbuf *lbuf)
{
debug_decl(lbuf_destroy, SUDO_DEBUG_UTIL)
efree(lbuf->buf);
lbuf->buf = NULL;
debug_return;
}
static void
lbuf_expand(struct lbuf *lbuf, int extra)
{
if (lbuf->len + extra + 1 >= lbuf->size) {
do {
lbuf->size += 256;
} while (lbuf->len + extra + 1 >= lbuf->size);
lbuf->buf = erealloc(lbuf->buf, lbuf->size);
}
}
/*
* Parse the format and append strings, only %s and %% escapes are supported.
* Any characters in set are quoted with a backslash.
*/
void
lbuf_append_quoted(struct lbuf *lbuf, const char *set, const char *fmt, ...)
{
va_list ap;
int len;
char *cp, *s;
debug_decl(lbuf_append_quoted, SUDO_DEBUG_UTIL)
va_start(ap, fmt);
while (*fmt != '\0') {
if (fmt[0] == '%' && fmt[1] == 's') {
if ((s = va_arg(ap, char *)) == NULL)
goto done;
while ((cp = strpbrk(s, set)) != NULL) {
len = (int)(cp - s);
lbuf_expand(lbuf, len + 2);
memcpy(lbuf->buf + lbuf->len, s, len);
lbuf->len += len;
lbuf->buf[lbuf->len++] = '\\';
lbuf->buf[lbuf->len++] = *cp;
s = cp + 1;
}
if (*s != '\0') {
len = strlen(s);
lbuf_expand(lbuf, len);
memcpy(lbuf->buf + lbuf->len, s, len);
lbuf->len += len;
}
fmt += 2;
continue;
}
lbuf_expand(lbuf, 2);
if (strchr(set, *fmt) != NULL)
lbuf->buf[lbuf->len++] = '\\';
lbuf->buf[lbuf->len++] = *fmt++;
}
done:
if (lbuf->size != 0)
lbuf->buf[lbuf->len] = '\0';
va_end(ap);
debug_return;
}
/*
* Parse the format and append strings, only %s and %% escapes are supported.
*/
void
lbuf_append(struct lbuf *lbuf, const char *fmt, ...)
{
va_list ap;
int len;
char *s;
debug_decl(lbuf_append, SUDO_DEBUG_UTIL)
va_start(ap, fmt);
while (*fmt != '\0') {
if (fmt[0] == '%' && fmt[1] == 's') {
if ((s = va_arg(ap, char *)) == NULL)
goto done;
len = strlen(s);
lbuf_expand(lbuf, len);
memcpy(lbuf->buf + lbuf->len, s, len);
lbuf->len += len;
fmt += 2;
continue;
}
lbuf_expand(lbuf, 1);
lbuf->buf[lbuf->len++] = *fmt++;
}
done:
if (lbuf->size != 0)
lbuf->buf[lbuf->len] = '\0';
va_end(ap);
debug_return;
}
static void
lbuf_println(struct lbuf *lbuf, char *line, int len)
{
char *cp, save;
int i, have, contlen;
debug_decl(lbuf_println, SUDO_DEBUG_UTIL)
contlen = lbuf->continuation ? strlen(lbuf->continuation) : 0;
/*
* Print the buffer, splitting the line as needed on a word
* boundary.
*/
cp = line;
have = lbuf->cols;
while (cp != NULL && *cp != '\0') {
char *ep = NULL;
int need = len - (int)(cp - line);
if (need > have) {
have -= contlen; /* subtract for continuation char */
if ((ep = memrchr(cp, ' ', have)) == NULL)
ep = memchr(cp + have, ' ', need - have);
if (ep != NULL)
need = (int)(ep - cp);
}
if (cp != line) {
/* indent continued lines */
/* XXX - build up string instead? */
for (i = 0; i < lbuf->indent; i++)
lbuf->output(" ");
}
/* NUL-terminate cp for the output function and restore afterwards */
save = cp[need];
cp[need] = '\0';
lbuf->output(cp);
cp[need] = save;
cp = ep;
/*
* If there is more to print, reset have, incremement cp past
* the whitespace, and print a line continuaton char if needed.
*/
if (cp != NULL) {
have = lbuf->cols - lbuf->indent;
ep = line + len;
while (cp < ep && isblank((unsigned char)*cp)) {
cp++;
}
if (contlen)
lbuf->output(lbuf->continuation);
}
lbuf->output("\n");
}
debug_return;
}
/*
* Print the buffer with word wrap based on the tty width.
* The lbuf is reset on return.
*/
void
lbuf_print(struct lbuf *lbuf)
{
char *cp, *ep;
int len;
debug_decl(lbuf_print, SUDO_DEBUG_UTIL)
if (lbuf->buf == NULL || lbuf->len == 0)
goto done;
/* For very small widths just give up... */
len = lbuf->continuation ? strlen(lbuf->continuation) : 0;
if (lbuf->cols <= lbuf->indent + len + 20) {
if (lbuf->len > 0) {
lbuf->buf[lbuf->len] = '\0';
lbuf->output(lbuf->buf);
if (lbuf->buf[lbuf->len - 1] != '\n')
lbuf->output("\n");
}
goto done;
}
/* Print each line in the buffer */
for (cp = lbuf->buf; cp != NULL && *cp != '\0'; ) {
if (*cp == '\n') {
lbuf->output("\n");
cp++;
} else {
len = lbuf->len - (cp - lbuf->buf);
if ((ep = memchr(cp, '\n', len)) != NULL)
len = (int)(ep - cp);
if (len)
lbuf_println(lbuf, cp, len);
cp = ep ? ep + 1 : NULL;
}
}
done:
lbuf->len = 0; /* reset the buffer for re-use. */
debug_return;
}

44
lib/util/memrchr.c Normal file
View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2007, 2010-2011, 2013
* 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>
#ifndef HAVE_MEMRCHR
#include <sys/types.h>
#include "missing.h"
/*
* Reverse memchr()
* Find the last occurrence of 'c' in the buffer 's' of size 'n'.
*/
void *
memrchr(const void *s, int c, size_t n)
{
const unsigned char *cp;
if (n != 0) {
cp = (unsigned char *)s + n;
do {
if (*(--cp) == (unsigned char)c)
return (void *)cp;
} while (--n != 0);
}
return (void *)0;
}
#endif /* HAVE_MEMRCHR */

71
lib/util/memset_s.c Normal file
View File

@@ -0,0 +1,71 @@
/*
* Copyright (c) 2013 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 <errno.h>
#include <limits.h>
#if defined(HAVE_STDINT_H)
# include <stdint.h>
#elif defined(HAVE_INTTYPES_H)
# include <inttypes.h>
#endif
#include "missing.h"
#ifndef RSIZE_MAX
# if defined(SIZE_MAX)
# define RSIZE_MAX (SIZE_MAX >> 1)
# elif defined(__LP64__)
# define RSIZE_MAX 0x7fffffffffffffffUL
# else
# define RSIZE_MAX 0x7fffffffU
# endif
#endif
/*
* Simple implementation of C11 memset_s() function.
* We use a volatile pointer when updating the byte string.
* Most compilers will avoid optimizing away access to a
* volatile pointer, even if the pointer appears to be unused
* after the call.
*
* Note that C11 does not specify the return value on error, only
* that it be non-zero. We use EINVAL for all errors.
*/
errno_t
memset_s(void *v, rsize_t smax, int c, rsize_t n)
{
errno_t ret = 0;
volatile unsigned char *s = v;
/* Fatal runtime-constraint violations. */
if (s == NULL || smax > RSIZE_MAX) {
ret = errno = EINVAL;
goto done;
}
/* Non-fatal runtime-constraint violation, n must not exceed smax. */
if (n > smax) {
n = smax;
ret = errno = EINVAL;
}
/* Updating through a volatile pointer should not be optimized away. */
while (n--)
*s++ = (unsigned char)c;
done:
return ret;
}

59
lib/util/mksiglist.c Normal file
View File

@@ -0,0 +1,59 @@
/*
* Copyright (c) 2010-2012 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 <signal.h>
#include "missing.h"
__dso_public int main(int argc, char *argv[]);
int
main(int argc, char *argv[])
{
static char *sudo_sys_siglist[NSIG];
int i;
#include "compat/mksiglist.h"
printf("#include <config.h>\n");
printf("#include <signal.h>\n");
printf("#include \"missing.h\"\n\n");
printf("const char *const sudo_sys_siglist[NSIG] = {\n");
for (i = 0; i < NSIG; i++) {
if (sudo_sys_siglist[i] != NULL) {
printf(" \"%s\",\n", sudo_sys_siglist[i]);
} else {
printf(" \"Signal %d\",\n", i);
}
}
printf("};\n");
exit(0);
}

59
lib/util/mksigname.c Normal file
View File

@@ -0,0 +1,59 @@
/*
* Copyright (c) 2010-2012 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 <signal.h>
#include "missing.h"
__dso_public int main(int argc, char *argv[]);
int
main(int argc, char *argv[])
{
static char *sudo_sys_signame[NSIG];
int i;
#include "compat/mksigname.h"
printf("#include <config.h>\n");
printf("#include <signal.h>\n");
printf("#include \"missing.h\"\n\n");
printf("const char *const sudo_sys_signame[NSIG] = {\n");
for (i = 0; i < NSIG; i++) {
if (sudo_sys_signame[i] != NULL) {
printf(" \"%s\",\n", sudo_sys_signame[i]);
} else {
printf(" \"Signal %d\",\n", i);
}
}
printf("};\n");
exit(0);
}

162
lib/util/mktemp.c Normal file
View File

@@ -0,0 +1,162 @@
/*
* Copyright (c) 2001, 2003, 2004, 2008-2011, 2013
* 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>
#if !defined(HAVE_MKSTEMPS) || !defined(HAVE_MKDTEMP)
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif /* HAVE_STDLIB_H */
#include <ctype.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#ifdef TIME_WITH_SYS_TIME
# include <time.h>
#endif
#include "missing.h"
#define MKTEMP_FILE 1
#define MKTEMP_DIR 2
#define TEMPCHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
#define NUM_CHARS (sizeof(TEMPCHARS) - 1)
#ifndef INT_MAX
#define INT_MAX 0x7fffffff
#endif
#ifdef HAVE_RANDOM
# define RAND random
# define SRAND srandom
# define SEED_T unsigned int
#else
# ifdef HAVE_LRAND48
# define RAND lrand48
# define SRAND srand48
# define SEED_T long
# else
# define RAND rand
# define SRAND srand
# define SEED_T unsigned int
# endif
#endif
static void
seed_random(void)
{
SEED_T seed;
struct timeval tv;
/*
* Seed from time of day and process id multiplied by small primes.
*/
(void) gettimeofday(&tv, NULL);
seed = (tv.tv_sec % 10000) * 523 + tv.tv_usec * 13 +
(getpid() % 1000) * 983;
SRAND(seed);
}
static unsigned int
get_random(void)
{
static int initialized;
if (!initialized) {
seed_random();
initialized = 1;
}
return RAND() & 0xffffffff;
}
static int
mktemp_internal(char *path, int slen, int mode)
{
char *start, *cp, *ep;
const char *tempchars = TEMPCHARS;
unsigned int r, tries;
int fd;
for (ep = path; *ep; ep++)
;
if (path + slen >= ep) {
errno = EINVAL;
return -1;
}
ep -= slen;
tries = 1;
for (start = ep; start > path && start[-1] == 'X'; start--) {
if (tries < INT_MAX / NUM_CHARS)
tries *= NUM_CHARS;
}
tries *= 2;
do {
for (cp = start; *cp; cp++) {
r = get_random() % NUM_CHARS;
*cp = tempchars[r];
}
switch (mode) {
case MKTEMP_FILE:
fd = open(path, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR);
if (fd != -1 || errno != EEXIST)
return fd;
break;
case MKTEMP_DIR:
if (mkdir(path, S_IRWXU) == 0)
return 0;
if (errno != EEXIST)
return -1;
break;
}
} while (--tries);
errno = EEXIST;
return -1;
}
#ifndef HAVE_MKSTEMPS
int
mkstemps(char *path, int slen)
{
return mktemp_internal(path, slen, MKTEMP_FILE);
}
#endif /* HAVE_MKSTEMPS */
#ifndef HAVE_MKDTEMP
char *
mkdtemp(char *path)
{
if (mktemp_internal(path, 0, MKTEMP_DIR) == -1)
return NULL;
return path;
}
#endif /* HAVE_MKDTEMP */
#endif /* !HAVE_MKSTEMPS || !HAVE_MKDTEMP */

124
lib/util/progname.c Normal file
View File

@@ -0,0 +1,124 @@
/*
* Copyright (c) 2013 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>
/* Large files not supported by procfs.h */
#if defined(HAVE_PROCFS_H) || defined(HAVE_SYS_PROCFS_H)
# undef _FILE_OFFSET_BITS
# undef _LARGE_FILES
#endif
#include <sys/types.h>
#ifdef HAVE_PSTAT_GETPROC
# include <sys/param.h>
# include <sys/pstat.h>
#endif
#if defined(HAVE_PROCFS_H)
# include <procfs.h>
#elif defined(HAVE_SYS_PROCFS_H)
# include <sys/procfs.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>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#include <errno.h>
#include <fcntl.h>
#include "missing.h"
#include "sudo_util.h"
#if defined(HAVE_GETPROGNAME) || defined(HAVE___PROGNAME)
/* STUB */
void
initprogname(const char *name)
{
return;
}
#else
static const char *progname = "";
void
initprogname(const char *name)
{
const char *base;
#ifdef HAVE_PSTAT_GETPROC
static char ucomm[PST_UCOMMLEN];
struct pst_status pstat;
int rc;
/*
* Determine the progname from pst_ucomm in struct pst_status.
* We may get EOVERFLOW if the whole thing doesn't fit but that is OK.
*/
rc = pstat_getproc(&pstat, sizeof(pstat), (size_t)0, (int)getpid());
if (rc != -1 || errno == EOVERFLOW) {
strlcpy(ucomm, pstat.pst_ucomm, sizeof(ucomm));
progname = ucomm;
return;
}
#elif defined(HAVE_PROCFS_H) || defined(HAVE_SYS_PROCFS_H)
/* XXX - configure check for psinfo.pr_fname */
static char ucomm[PRFNSZ];
struct psinfo psinfo;
char path[PATH_MAX];
ssize_t nread;
int fd;
/* Try to determine the tty from pr_ttydev in /proc/pid/psinfo. */
snprintf(path, sizeof(path), "/proc/%u/psinfo", (unsigned int)getpid());
if ((fd = open(path, O_RDONLY, 0)) != -1) {
nread = read(fd, &psinfo, sizeof(psinfo));
close(fd);
if (nread == (ssize_t)sizeof(psinfo)) {
strlcpy(ucomm, psinfo.pr_fname, sizeof(ucomm));
progname = ucomm;
return;
}
}
#endif /* HAVE_PSTAT_GETPROC */
if ((base = strrchr(name, '/')) != NULL) {
base++;
} else {
base = name;
}
progname = base;
}
const char *
getprogname(void)
{
return progname;
}
#endif /* !HAVE_GETPROGNAME && !HAVE___PROGNAME */

108
lib/util/pw_dup.c Normal file
View File

@@ -0,0 +1,108 @@
/*
* Copyright (c) 2000, 2002, 2012-2013
* 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>
#ifndef HAVE_PW_DUP
#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>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS)
# include <malloc.h>
#endif /* HAVE_MALLOC_H && !STDC_HEADERS */
#include <pwd.h>
#define PW_SIZE(name, size) \
do { \
if (pw->name) { \
size = strlen(pw->name) + 1; \
total += size; \
} \
} while (0)
#define PW_COPY(name, size) \
do { \
if (pw->name) { \
(void)memcpy(cp, pw->name, size); \
newpw->name = cp; \
cp += size; \
} \
} while (0)
struct passwd *
pw_dup(const struct passwd *pw)
{
size_t nsize = 0, psize = 0, gsize = 0, dsize = 0, ssize = 0, total;
#ifdef HAVE_LOGIN_CAP_H
size_t csize = 0;
#endif
struct passwd *newpw;
char *cp;
/* Allocate in one big chunk for easy freeing */
total = sizeof(struct passwd);
PW_SIZE(pw_name, nsize);
PW_SIZE(pw_passwd, psize);
#ifdef HAVE_LOGIN_CAP_H
PW_SIZE(pw_class, csize);
#endif
PW_SIZE(pw_gecos, gsize);
PW_SIZE(pw_dir, dsize);
PW_SIZE(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.
*/
(void)memcpy(newpw, pw, sizeof(struct passwd));
cp += sizeof(struct passwd);
PW_COPY(pw_name, nsize);
PW_COPY(pw_passwd, psize);
#ifdef HAVE_LOGIN_CAP_H
PW_COPY(pw_class, csize);
#endif
PW_COPY(pw_gecos, gsize);
PW_COPY(pw_dir, dsize);
PW_COPY(pw_shell, ssize);
return newpw;
}
#endif /* HAVE_PW_DUP */

View File

@@ -0,0 +1,186 @@
/*
* Copyright (c) 2014 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 */
#ifdef HAVE_STDBOOL_H
# include <stdbool.h>
#else
# include "compat/stdbool.h"
#endif
#include "missing.h"
#include "sudo_util.h"
#include "fatal.h"
__dso_public int main(int argc, char *argv[]);
/* atobool() tests */
static struct atobool_data {
const char *bool_str;
int value;
} atobool_data[] = {
{ "true", true },
{ "false", false },
{ "TrUe", true },
{ "fAlSe", false },
{ "1", true },
{ "0", false },
{ "on", true },
{ "off", false },
{ "yes", true },
{ "no", false },
{ "nope", -1 },
{ "10", -1 },
{ "one", -1 },
{ "zero", -1 },
{ NULL, 0 }
};
static int
test_atobool(int *ntests)
{
struct atobool_data *d;
int errors = 0;
int value;
for (d = atobool_data; d->bool_str != NULL; d++) {
(*ntests)++;
value = atobool(d->bool_str);
if (value != d->value) {
warningx_nodebug("FAIL: %s != %d", d->bool_str, d->value);
errors++;
}
}
return errors;
}
/* atoid() tests */
static struct atoid_data {
const char *idstr;
id_t id;
const char *sep;
const char *ep;
} atoid_data[] = {
{ "0,1", 0, ",", "," },
{ "10", 10, NULL, NULL },
{ "-2", -2, NULL, NULL },
{ "-2", 4294967294U, NULL, NULL },
{ "4294967294", 4294967294U, NULL, NULL },
{ NULL, 0, NULL, NULL }
};
static int
test_atoid(int *ntests)
{
struct atoid_data *d;
const char *errstr;
char *ep;
int errors = 0;
id_t value;
for (d = atoid_data; d->idstr != NULL; d++) {
(*ntests)++;
errstr = "some error";
value = atoid(d->idstr, d->sep, &ep, &errstr);
if (errstr != NULL) {
if (d->id != (id_t)-1) {
warningx_nodebug("FAIL: %s: %s", d->idstr, errstr);
errors++;
}
} else if (value != d->id) {
warningx_nodebug("FAIL: %s != %u", d->idstr, (unsigned int)d->id);
errors++;
} else if (d->ep != NULL && ep[0] != d->ep[0]) {
warningx_nodebug("FAIL: ep[0] %d != %d", (int)(unsigned char)ep[0],
(int)(unsigned char)d->ep[0]);
errors++;
}
}
return errors;
}
/* atomode() tests */
static struct atomode_data {
const char *mode_str;
mode_t mode;
} atomode_data[] = {
{ "755", 0755 },
{ "007", 007 },
{ "7", 7 },
{ "8", -1 },
{ NULL, 0 }
};
static int
test_atomode(int *ntests)
{
struct atomode_data *d;
const char *errstr;
int errors = 0;
mode_t mode;
for (d = atomode_data; d->mode_str != NULL; d++) {
(*ntests)++;
errstr = "some error";
mode = atomode(d->mode_str, &errstr);
if (errstr != NULL) {
if (d->mode != (mode_t)-1) {
warningx_nodebug("FAIL: %s: %s", d->mode_str, errstr);
errors++;
}
} else if (mode != d->mode) {
warningx_nodebug("FAIL: %s != 0%o", d->mode_str,
(unsigned int) d->mode);
errors++;
}
}
return errors;
}
/*
* Simple tests for atobool(), atoid(), atomode().
*/
int
main(int argc, char *argv[])
{
int errors = 0;
int ntests = 0;
initprogname(argc > 0 ? argv[0] : "atofoo");
errors += test_atobool(&ntests);
errors += test_atoid(&ntests);
errors += test_atomode(&ntests);
printf("%s: %d tests run, %d errors, %d%% success rate\n", getprogname(),
ntests, errors, (ntests - errors) * 100 / ntests);
exit(errors);
}

View File

@@ -0,0 +1,80 @@
/* $OpenBSD: fnm_test.c,v 1.1 2008/10/01 23:04:58 millert Exp $ */
/*
* Public domain, 2008, Todd C. Miller <Todd.Miller@courtesan.com>
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_STRING_H
# include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#ifdef HAVE_FNMATCH
# include <fnmatch.h>
#else
# include "compat/fnmatch.h"
#endif
#include "missing.h"
__dso_public int main(int argc, char *argv[]);
int
main(int argc, char *argv[])
{
FILE *fp = stdin;
char pattern[1024], string[1024], flagstr[1024];
int errors = 0, tests = 0, flags, got, want;
if (argc > 1) {
if ((fp = fopen(argv[1], "r")) == NULL) {
perror(argv[1]);
exit(1);
}
}
/*
* Read in test file, which is formatted thusly:
*
* pattern string flags expected_result
*
*/
for (;;) {
got = fscanf(fp, "%s %s %s %d\n", pattern, string, flagstr,
&want);
if (got == EOF)
break;
if (got == 4) {
flags = 0;
if (strcmp(flagstr, "FNM_NOESCAPE") == 0)
flags |= FNM_NOESCAPE;
else if (strcmp(flagstr, "FNM_PATHNAME") == 0)
flags |= FNM_PATHNAME;
else if (strcmp(flagstr, "FNM_PERIOD") == 0)
flags |= FNM_PERIOD;
else if (strcmp(flagstr, "FNM_LEADING_DIR") == 0)
flags |= FNM_LEADING_DIR;
else if (strcmp(flagstr, "FNM_CASEFOLD") == 0)
flags |= FNM_CASEFOLD;
got = fnmatch(pattern, string, flags);
if (got != want) {
fprintf(stderr,
"fnmatch: %s %s %d: want %d, got %d\n",
pattern, string, flags, want, got);
errors++;
}
tests++;
}
}
if (tests != 0) {
printf("fnmatch: %d test%s run, %d errors, %d%% success rate\n",
tests, tests == 1 ? "" : "s", errors,
(tests - errors) * 100 / tests);
}
exit(errors);
}

View File

@@ -0,0 +1,5 @@
/bin/[[:alpha:][:alnum:]]* /bin/ls FNM_PATHNAME 0
/bin/[[:alpha:][:alnum:]]* /bin/LS FNM_CASEFOLD 0
/bin/[[:opper:][:alnum:]]* /bin/ls NONE 1
[[:alpha:][:alnum:]]*.c foo1.c FNM_PERIOD 0
[[:upper:]]* FOO NONE 0

View File

@@ -0,0 +1,47 @@
fake/bin/[
fake/bin/cat
fake/bin/chgrp
fake/bin/chio
fake/bin/chmod
fake/bin/cksum
fake/bin/cp
fake/bin/cpio
fake/bin/csh
fake/bin/date
fake/bin/dd
fake/bin/df
fake/bin/domainname
fake/bin/echo
fake/bin/ed
fake/bin/eject
fake/bin/expr
fake/bin/hostname
fake/bin/kill
fake/bin/ksh
fake/bin/ln
fake/bin/ls
fake/bin/md5
fake/bin/mkdir
fake/bin/mt
fake/bin/mv
fake/bin/pax
fake/bin/ps
fake/bin/pwd
fake/bin/rcp
fake/bin/rksh
fake/bin/rm
fake/bin/rmail
fake/bin/rmd160
fake/bin/rmdir
fake/bin/sh
fake/bin/sha1
fake/bin/sha256
fake/bin/sha384
fake/bin/sha512
fake/bin/sleep
fake/bin/stty
fake/bin/sum
fake/bin/sync
fake/bin/systrace
fake/bin/tar
fake/bin/test

View File

@@ -0,0 +1,212 @@
/* $OpenBSD: globtest.c,v 1.1 2008/10/01 23:04:36 millert Exp $ */
/*
* Public domain, 2008, Todd C. Miller <Todd.Miller@courtesan.com>
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_STRING_H
# include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#ifdef HAVE_GLOB
# include <glob.h>
#else
# include "compat/glob.h"
#endif
#include <errno.h>
#include "missing.h"
#define MAX_RESULTS 256
struct gl_entry {
int flags;
int nresults;
char pattern[1024];
char *results[MAX_RESULTS];
};
int test_glob(struct gl_entry *);
__dso_public int main(int argc, char *argv[]);
int
main(int argc, char **argv)
{
FILE *fp = stdin;
char buf[2048], *cp, *ep;
int errors = 0, tests = 0, lineno;
struct gl_entry entry;
size_t len;
if (argc > 1) {
if ((fp = fopen(argv[1], "r")) == NULL) {
perror(argv[1]);
exit(1);
}
}
/*
* Read in test file, which is formatted thusly:
*
* [pattern] <flags>
* result1
* result2
* result3
* ...
*
*/
lineno = 0;
memset(&entry, 0, sizeof(entry));
while (fgets(buf, sizeof(buf), fp) != NULL) {
lineno++;
len = strlen(buf);
if (len > 0) {
if (buf[len - 1] != '\n') {
fprintf(stderr,
"globtest: missing newline at EOF\n");
exit(1);
}
buf[--len] = '\0';
}
if (len == 0)
continue; /* blank line */
if (buf[0] == '[') {
/* check previous pattern */
if (entry.pattern[0]) {
errors += test_glob(&entry);
tests++;
}
/* start new entry */
if ((cp = strrchr(buf + 1, ']')) == NULL) {
fprintf(stderr,
"globtest: invalid entry on line %d\n",
lineno);
exit(1);
}
len = cp - buf - 1;
if (len >= sizeof(entry.pattern)) {
fprintf(stderr,
"globtest: pattern too big on line %d\n",
lineno);
exit(1);
}
memcpy(entry.pattern, buf + 1, len);
entry.pattern[len] = '\0';
cp += 2;
if (*cp++ != '<') {
fprintf(stderr,
"globtest: invalid entry on line %d\n",
lineno);
exit(1);
}
ep = strchr(cp, '>');
if (ep == NULL) {
fprintf(stderr,
"globtest: invalid entry on line %d\n",
lineno);
exit(1);
}
*ep = '\0';
entry.flags = 0;
for ((cp = strtok(cp, "|")); cp != NULL; (cp = strtok(NULL, "|"))) {
if (strcmp(cp, "GLOB_APPEND") == 0)
entry.flags |= GLOB_APPEND;
else if (strcmp(cp, "GLOB_DOOFFS") == 0)
entry.flags |= GLOB_DOOFFS;
else if (strcmp(cp, "GLOB_ERR") == 0)
entry.flags |= GLOB_ERR;
else if (strcmp(cp, "GLOB_MARK") == 0)
entry.flags |= GLOB_MARK;
else if (strcmp(cp, "GLOB_NOCHECK") == 0)
entry.flags |= GLOB_NOCHECK;
else if (strcmp(cp, "GLOB_NOSORT") == 0)
entry.flags |= GLOB_NOSORT;
else if (strcmp(cp, "GLOB_NOESCAPE") == 0)
entry.flags |= GLOB_NOESCAPE;
else if (strcmp(cp, "GLOB_BRACE") == 0)
entry.flags |= GLOB_BRACE;
else if (strcmp(cp, "GLOB_TILDE") == 0)
entry.flags |= GLOB_TILDE;
else if (strcmp(cp, "NONE") != 0) {
fprintf(stderr,
"globtest: invalid flags on line %d\n",
lineno);
exit(1);
}
}
entry.nresults = 0;
continue;
}
if (!entry.pattern[0]) {
fprintf(stderr, "globtest: missing entry on line %d\n",
lineno);
exit(1);
}
if (entry.nresults + 1 > MAX_RESULTS) {
fprintf(stderr,
"globtest: too many results for %s, max %d\n",
entry.pattern, MAX_RESULTS);
exit(1);
}
entry.results[entry.nresults++] = strdup(buf);
}
if (entry.pattern[0]) {
errors += test_glob(&entry); /* test last pattern */
tests++;
}
if (tests != 0) {
printf("glob: %d test%s run, %d errors, %d%% success rate\n",
tests, tests == 1 ? "" : "s", errors,
(tests - errors) * 100 / tests);
}
exit(errors);
}
int test_glob(struct gl_entry *entry)
{
glob_t gl;
char **ap;
int nmatches = 0, i = 0;
if (glob(entry->pattern, entry->flags, NULL, &gl) != 0) {
fprintf(stderr, "glob failed: %s: %s\n", entry->pattern,
strerror(errno));
exit(1);
}
for (ap = gl.gl_pathv; *ap != NULL; ap++)
nmatches++;
if (nmatches != entry->nresults)
goto mismatch;
for (i = 0; i < entry->nresults; i++) {
if (strcmp(gl.gl_pathv[i], entry->results[i]) != 0)
goto mismatch;
free(entry->results[i]);
}
return 0;
mismatch:
if (nmatches != entry->nresults) {
fprintf(stderr,
"globtest: mismatch in number of results (found %d, expected %d) for pattern %s\n",
nmatches, entry->nresults, entry->pattern);
} else {
fprintf(stderr, "globtest: mismatch for pattern %s, flags 0x%x "
"(found \"%s\", expected \"%s\")\n", entry->pattern, entry->flags,
gl.gl_pathv[i], entry->results[i]);
while (i < entry->nresults)
free(entry->results[i++]);
}
return 1;
}

View File

@@ -0,0 +1,64 @@
[fake/bin/[[:alpha:]]*] <NONE>
fake/bin/cat
fake/bin/chgrp
fake/bin/chio
fake/bin/chmod
fake/bin/cksum
fake/bin/cp
fake/bin/cpio
fake/bin/csh
fake/bin/date
fake/bin/dd
fake/bin/df
fake/bin/domainname
fake/bin/echo
fake/bin/ed
fake/bin/eject
fake/bin/expr
fake/bin/hostname
fake/bin/kill
fake/bin/ksh
fake/bin/ln
fake/bin/ls
fake/bin/md5
fake/bin/mkdir
fake/bin/mt
fake/bin/mv
fake/bin/pax
fake/bin/ps
fake/bin/pwd
fake/bin/rcp
fake/bin/rksh
fake/bin/rm
fake/bin/rmail
fake/bin/rmd160
fake/bin/rmdir
fake/bin/sh
fake/bin/sha1
fake/bin/sha256
fake/bin/sha384
fake/bin/sha512
fake/bin/sleep
fake/bin/stty
fake/bin/sum
fake/bin/sync
fake/bin/systrace
fake/bin/tar
fake/bin/test
[fake/bin/rm{,dir,ail}] <GLOB_BRACE>
fake/bin/rm
fake/bin/rmdir
fake/bin/rmail
[fake/bin/sha[[:digit:]]] <NONE>
fake/bin/sha1
[fake/bin/sha[[:digit:]]*] <NONE>
fake/bin/sha1
fake/bin/sha256
fake/bin/sha384
fake/bin/sha512
[fake/bin/ca[a-z]] <NONE>
fake/bin/cat

View File

@@ -0,0 +1,100 @@
/*
* Copyright (c) 2013 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 */
#ifdef HAVE_STRING_H
# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
# include <memory.h>
# endif
# include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#ifdef HAVE_STDBOOL_H
# include <stdbool.h>
#else
# include "compat/stdbool.h"
#endif
#include "missing.h"
#include "sudo_conf.h"
#include "sudo_util.h"
static void sudo_conf_dump(void);
__dso_public int main(int argc, char *argv[]);
/*
* Simple test driver for sudo_conf().
* Parses the given configuration file and dumps the resulting
* sudo_conf_data struct to the standard output.
*/
int
main(int argc, char *argv[])
{
initprogname(argc > 0 ? argv[0] : "conf_test");
if (argc != 2) {
fprintf(stderr, "usage: %s conf_file\n", getprogname());
exit(1);
}
sudo_conf_read(argv[1]);
sudo_conf_dump();
exit(0);
}
static void
sudo_conf_dump(void)
{
struct plugin_info_list *plugins = sudo_conf_plugins();
struct plugin_info *info;
printf("Set disable_coredump %s\n",
sudo_conf_disable_coredump() ? "true" : "false");
printf("Set group_source %s\n",
sudo_conf_group_source() == GROUP_SOURCE_ADAPTIVE ? "adaptive" :
sudo_conf_group_source() == GROUP_SOURCE_STATIC ? "static" : "dynamic");
printf("Set max_groups %d\n", sudo_conf_max_groups());
if (sudo_conf_debug_flags() != NULL)
printf("Debug %s %s\n", getprogname(), sudo_conf_debug_flags());
if (sudo_conf_askpass_path() != NULL)
printf("Path askpass %s\n", sudo_conf_askpass_path());
#ifdef _PATH_SUDO_NOEXEC
if (sudo_conf_noexec_path() != NULL)
printf("Path noexec %s\n", sudo_conf_noexec_path());
#endif
TAILQ_FOREACH(info, plugins, entries) {
printf("Plugin %s %s", info->symbol_name, info->path);
if (info->options) {
char * const * op;
for (op = info->options; *op != NULL; op++)
printf(" %s", *op);
}
putchar('\n');
}
}

View File

@@ -0,0 +1,72 @@
#
# Sample /etc/sudo.conf file
#
# Format:
# Plugin plugin_name plugin_path plugin_options ...
# Path askpass /path/to/askpass
# Path noexec /path/to/sudo_noexec.so
# Debug sudo /var/log/sudo_debug all@warn
# Set disable_coredump true
#
# Sudo plugins:
#
# The plugin_path is relative to ${prefix}/libexec unless fully qualified.
# The plugin_name corresponds to a global symbol in the plugin
# that contains the plugin interface structure.
# The plugin_options are optional.
#
# The sudoers plugin is used by default if no Plugin lines are present.
Plugin sudoers_policy sudoers.so
Plugin sudoers_io sudoers.so
#
# Sudo askpass:
#
# An askpass helper program may be specified to provide a graphical
# password prompt for "sudo -A" support. Sudo does not ship with its
# own askpass program but can use the OpenSSH askpass.
#
# Use the OpenSSH askpass
Path askpass /usr/X11R6/bin/ssh-askpass
#
# Use the Gnome OpenSSH askpass
#Path askpass /usr/libexec/openssh/gnome-ssh-askpass
#
# Sudo noexec:
#
# Path to a shared library containing dummy versions of the execv(),
# execve() and fexecve() library functions that just return an error.
# This is used to implement the "noexec" functionality on systems that
# support C<LD_PRELOAD> or its equivalent.
# The compiled-in value is usually sufficient and should only be changed
# if you rename or move the sudo_noexec.so file.
#
Path noexec /usr/libexec/sudo_noexec.so
#
# Core dumps:
#
# By default, sudo disables core dumps while it is executing (they
# are re-enabled for the command that is run).
# To aid in debugging sudo problems, you may wish to enable core
# dumps by setting "disable_coredump" to false.
#
Set disable_coredump false
#
# User groups:
#
# Sudo passes the user's group list to the policy plugin.
# If the user is a member of the maximum number of groups (usually 16),
# sudo will query the group database directly to be sure to include
# the full list of groups.
#
# On some systems, this can be expensive so the behavior is configurable.
# The "group_source" setting has three possible values:
# static - use the user's list of groups returned by the kernel.
# dynamic - query the group database to find the list of groups.
# adaptive - if user is in less than the maximum number of groups.
# use the kernel list, else query the group database.
#
Set group_source static

View File

@@ -0,0 +1,6 @@
Set disable_coredump false
Set group_source static
Set max_groups -1
Path askpass /usr/X11R6/bin/ssh-askpass
Plugin sudoers_policy sudoers.so
Plugin sudoers_io sudoers.so

View File

View File

@@ -0,0 +1,3 @@
Set disable_coredump true
Set group_source adaptive
Set max_groups -1

View File

@@ -0,0 +1,2 @@
Plugin sudoers_policy sudoers.so sudoers_file=/etc/sudoers sudoers_mode=0400 sudoers_gid=0 sudoers_uid=0
Plugin sudoers_io sudoers.so

View File

@@ -0,0 +1,5 @@
Set disable_coredump true
Set group_source adaptive
Set max_groups -1
Plugin sudoers_policy sudoers.so sudoers_file=/etc/sudoers sudoers_mode=0400 sudoers_gid=0 sudoers_uid=0
Plugin sudoers_io sudoers.so

View File

@@ -0,0 +1 @@
Set disable_coredump foo

View File

@@ -0,0 +1,3 @@
Set disable_coredump true
Set group_source adaptive
Set max_groups -1

View File

@@ -0,0 +1 @@
conf_test: invalid max groups `0' in regress/sudo_conf/test5.in, line 1

View File

@@ -0,0 +1 @@
Set max_groups 0

View File

@@ -0,0 +1,3 @@
Set disable_coredump true
Set group_source adaptive
Set max_groups -1

View File

@@ -0,0 +1 @@
Set max_groups 16

View File

@@ -0,0 +1,3 @@
Set disable_coredump true
Set group_source adaptive
Set max_groups 16

View File

@@ -0,0 +1,69 @@
/*
* Copyright (c) 2013 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 */
#ifdef HAVE_STRING_H
# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
# include <memory.h>
# endif
# include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#ifdef HAVE_STDBOOL_H
# include <stdbool.h>
#else
# include "compat/stdbool.h"
#endif
#include "missing.h"
#include "fileops.h"
#include "sudo_util.h"
__dso_public int main(int argc, char *argv[]);
/*
* Simple test driver for sudo_parseln().
* Behaves similarly to "cat -n" but with comment removal
* and line continuation.
*/
int
main(int argc, char *argv[])
{
unsigned int lineno = 0;
size_t linesize = 0;
char *line = NULL;
initprogname(argc > 0 ? argv[0] : "parseln_test");
while (sudo_parseln(&line, &linesize, &lineno, stdin) != -1)
printf("%6u\t%s\n", lineno, line);
free(line);
exit(0);
}

View File

@@ -0,0 +1,72 @@
#
# Sample /etc/sudo.conf file
#
# Format:
# Plugin plugin_name plugin_path plugin_options ...
# Path askpass /path/to/askpass
# Path noexec /path/to/sudo_noexec.so
# Debug sudo /var/log/sudo_debug all@warn
# Set disable_coredump true
#
# Sudo plugins:
#
# The plugin_path is relative to ${prefix}/libexec unless fully qualified.
# The plugin_name corresponds to a global symbol in the plugin
# that contains the plugin interface structure.
# The plugin_options are optional.
#
# The sudoers plugin is used by default if no Plugin lines are present.
Plugin sudoers_policy sudoers.so
Plugin sudoers_io sudoers.so
#
# Sudo askpass:
#
# An askpass helper program may be specified to provide a graphical
# password prompt for "sudo -A" support. Sudo does not ship with its
# own askpass program but can use the OpenSSH askpass.
#
# Use the OpenSSH askpass
#Path askpass /usr/X11R6/bin/ssh-askpass
#
# Use the Gnome OpenSSH askpass
#Path askpass /usr/libexec/openssh/gnome-ssh-askpass
#
# Sudo noexec:
#
# Path to a shared library containing dummy versions of the execv(),
# execve() and fexecve() library functions that just return an error.
# This is used to implement the "noexec" functionality on systems that
# support C<LD_PRELOAD> or its equivalent.
# The compiled-in value is usually sufficient and should only be changed
# if you rename or move the sudo_noexec.so file.
#
#Path noexec /usr/libexec/sudo_noexec.so
#
# Core dumps:
#
# By default, sudo disables core dumps while it is executing (they
# are re-enabled for the command that is run).
# To aid in debugging sudo problems, you may wish to enable core
# dumps by setting "disable_coredump" to false.
#
#Set disable_coredump false
#
# User groups:
#
# Sudo passes the user's group list to the policy plugin.
# If the user is a member of the maximum number of groups (usually 16),
# sudo will query the group database directly to be sure to include
# the full list of groups.
#
# On some systems, this can be expensive so the behavior is configurable.
# The "group_source" setting has three possible values:
# static - use the user's list of groups returned by the kernel.
# dynamic - query the group database to find the list of groups.
# adaptive - if user is in less than the maximum number of groups.
# use the kernel list, else query the group database.
#
#Set group_source static

View File

@@ -0,0 +1,72 @@
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 Plugin sudoers_policy sudoers.so
20 Plugin sudoers_io sudoers.so
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

View File

@@ -0,0 +1,8 @@
this \
is all \
one line
# this is a comment, and does not get continued\
trim the \
leading \
white \
space

View File

@@ -0,0 +1,3 @@
3 this is all one line
4
8 trim the leading white space

View File

@@ -0,0 +1 @@
line continuation at EOF \

View File

@@ -0,0 +1 @@
1 line continuation at EOF

View File

@@ -0,0 +1,4 @@
line contin\
uation raw
line contin\
uation indented

View File

@@ -0,0 +1,2 @@
2 line continuation raw
4 line continuation indented

View File

@@ -0,0 +1 @@
\

View File

@@ -0,0 +1,3 @@
leading and trailing white space
# a comment
\

View File

@@ -0,0 +1,2 @@
1 leading and trailing white space
2

View File

@@ -0,0 +1,208 @@
/*
* Copyright (c) 2013 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 */
#ifdef HAVE_STRING_H
# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
# include <memory.h>
# endif
# include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#ifdef HAVE_STDBOOL_H
# include <stdbool.h>
#else
# include "compat/stdbool.h"
#endif
#include "missing.h"
#include "fatal.h"
#include "queue.h"
#include "sudo_util.h"
__dso_public int main(int argc, char *argv[]);
/*
* Note: HLTQ_ENTRY is intentionally in the middle of the struct
* to catch bad assumptions in the PREV/NEXT macros.
*/
struct test_data {
int a;
HLTQ_ENTRY(test_data) entries;
char b;
};
TAILQ_HEAD(test_data_list, test_data);
/*
* Simple tests for headless tail queue macros.
*/
int
main(int argc, char *argv[])
{
struct test_data d1, d2, d3;
struct test_data *hltq;
struct test_data_list tq;
int errors = 0;
int ntests = 0;
initprogname(argc > 0 ? argv[0] : "hltq_test");
/*
* Initialize three data elements and concatenate them in order.
*/
HLTQ_INIT(&d1, entries);
d1.a = 1;
d1.b = 'a';
if (HLTQ_FIRST(&d1) != &d1) {
warningx_nodebug("FAIL: HLTQ_FIRST(1 entry) doesn't return first element: got %p, expected %p", HLTQ_FIRST(&d1), &d1);
errors++;
}
ntests++;
if (HLTQ_LAST(&d1, test_data, entries) != &d1) {
warningx_nodebug("FAIL: HLTQ_LAST(1 entry) doesn't return first element: got %p, expected %p", HLTQ_LAST(&d1, test_data, entries), &d1);
errors++;
}
ntests++;
if (HLTQ_PREV(&d1, test_data, entries) != NULL) {
warningx_nodebug("FAIL: HLTQ_PREV(1 entry) doesn't return NULL: got %p", HLTQ_PREV(&d1, test_data, entries));
errors++;
}
ntests++;
HLTQ_INIT(&d2, entries);
d2.a = 2;
d2.b = 'b';
HLTQ_INIT(&d3, entries);
d3.a = 3;
d3.b = 'c';
HLTQ_CONCAT(&d1, &d2, entries);
HLTQ_CONCAT(&d1, &d3, entries);
hltq = &d1;
/*
* Verify that HLTQ_FIRST, HLTQ_LAST, HLTQ_NEXT, HLTQ_PREV
* work as expected.
*/
if (HLTQ_FIRST(hltq) != &d1) {
warningx_nodebug("FAIL: HLTQ_FIRST(3 entries) doesn't return first element: got %p, expected %p", HLTQ_FIRST(hltq), &d1);
errors++;
}
ntests++;
if (HLTQ_LAST(hltq, test_data, entries) != &d3) {
warningx_nodebug("FAIL: HLTQ_LAST(3 entries) doesn't return third element: got %p, expected %p", HLTQ_LAST(hltq, test_data, entries), &d3);
errors++;
}
ntests++;
if (HLTQ_NEXT(&d1, entries) != &d2) {
warningx_nodebug("FAIL: HLTQ_NEXT(&d1) doesn't return &d2: got %p, expected %p", HLTQ_NEXT(&d1, entries), &d2);
errors++;
}
ntests++;
if (HLTQ_NEXT(&d2, entries) != &d3) {
warningx_nodebug("FAIL: HLTQ_NEXT(&d2) doesn't return &d3: got %p, expected %p", HLTQ_NEXT(&d2, entries), &d3);
errors++;
}
ntests++;
if (HLTQ_NEXT(&d3, entries) != NULL) {
warningx_nodebug("FAIL: HLTQ_NEXT(&d3) doesn't return NULL: got %p", HLTQ_NEXT(&d3, entries));
errors++;
}
ntests++;
if (HLTQ_PREV(&d1, test_data, entries) != NULL) {
warningx_nodebug("FAIL: HLTQ_PREV(&d1) doesn't return NULL: got %p", HLTQ_PREV(&d1, test_data, entries));
errors++;
}
ntests++;
if (HLTQ_PREV(&d2, test_data, entries) != &d1) {
warningx_nodebug("FAIL: HLTQ_PREV(&d2) doesn't return &d1: got %p, expected %p", HLTQ_PREV(&d2, test_data, entries), &d1);
errors++;
}
ntests++;
if (HLTQ_PREV(&d3, test_data, entries) != &d2) {
warningx_nodebug("FAIL: HLTQ_PREV(&d3) doesn't return &d2: got %p, expected %p", HLTQ_PREV(&d3, test_data, entries), &d2);
errors++;
}
ntests++;
/* Test conversion to TAILQ. */
HLTQ_TO_TAILQ(&tq, hltq, entries);
if (TAILQ_FIRST(&tq) != &d1) {
warningx_nodebug("FAIL: TAILQ_FIRST(&tq) doesn't return first element: got %p, expected %p", TAILQ_FIRST(&tq), &d1);
errors++;
}
ntests++;
if (TAILQ_LAST(&tq, test_data_list) != &d3) {
warningx_nodebug("FAIL: TAILQ_LAST(&tq) doesn't return third element: got %p, expected %p", TAILQ_LAST(&tq, test_data_list), &d3);
errors++;
}
ntests++;
if (TAILQ_NEXT(&d1, entries) != &d2) {
warningx_nodebug("FAIL: TAILQ_NEXT(&d1) doesn't return &d2: got %p, expected %p", TAILQ_NEXT(&d1, entries), &d2);
errors++;
}
ntests++;
if (TAILQ_NEXT(&d2, entries) != &d3) {
warningx_nodebug("FAIL: TAILQ_NEXT(&d2) doesn't return &d3: got %p, expected %p", TAILQ_NEXT(&d2, entries), &d3);
errors++;
}
ntests++;
if (TAILQ_NEXT(&d3, entries) != NULL) {
warningx_nodebug("FAIL: TAILQ_NEXT(&d3) doesn't return NULL: got %p", TAILQ_NEXT(&d3, entries));
errors++;
}
ntests++;
if (TAILQ_PREV(&d1, test_data_list, entries) != NULL) {
warningx_nodebug("FAIL: TAILQ_PREV(&d1) doesn't return NULL: got %p", TAILQ_PREV(&d1, test_data_list, entries));
errors++;
}
ntests++;
if (TAILQ_PREV(&d2, test_data_list, entries) != &d1) {
warningx_nodebug("FAIL: TAILQ_PREV(&d2) doesn't return &d1: got %p, expected %p", TAILQ_PREV(&d2, test_data_list, entries), &d1);
errors++;
}
ntests++;
if (TAILQ_PREV(&d3, test_data_list, entries) != &d2) {
warningx_nodebug("FAIL: TAILQ_PREV(&d3) doesn't return &d2: got %p, expected %p", TAILQ_PREV(&d3, test_data_list, entries), &d2);
errors++;
}
ntests++;
printf("%s: %d tests run, %d errors, %d%% success rate\n", getprogname(),
ntests, errors, (ntests - errors) * 100 / ntests);
exit(errors);
}

83
lib/util/secure_path.c Normal file
View File

@@ -0,0 +1,83 @@
/*
* Copyright (c) 2012 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/stat.h>
#include <stdio.h>
#ifdef HAVE_STRING_H
# include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <errno.h>
#include "missing.h"
#include "sudo_debug.h"
#include "secure_path.h"
/*
* Verify that path is the right type and not writable by other users.
*/
int
sudo_secure_path(const char *path, unsigned int type, uid_t uid, gid_t gid, struct stat *sbp)
{
struct stat sb;
int rval = SUDO_PATH_MISSING;
debug_decl(sudo_secure_path, SUDO_DEBUG_UTIL)
if (path != NULL && stat(path, &sb) == 0) {
if ((sb.st_mode & _S_IFMT) != type) {
rval = SUDO_PATH_BAD_TYPE;
} else if (uid != (uid_t)-1 && sb.st_uid != uid) {
rval = SUDO_PATH_WRONG_OWNER;
} else if (sb.st_mode & S_IWOTH) {
rval = SUDO_PATH_WORLD_WRITABLE;
} else if (ISSET(sb.st_mode, S_IWGRP) &&
(gid == (gid_t)-1 || sb.st_gid != gid)) {
rval = SUDO_PATH_GROUP_WRITABLE;
} else {
rval = SUDO_PATH_SECURE;
}
if (sbp)
(void) memcpy(sbp, &sb, sizeof(struct stat));
}
debug_return_int(rval);
}
/*
* Verify that path is a regular file and not writable by other users.
*/
int
sudo_secure_file(const char *path, uid_t uid, gid_t gid, struct stat *sbp)
{
return sudo_secure_path(path, _S_IFREG, uid, gid, sbp);
}
/*
* Verify that path is a directory and not writable by other users.
*/
int
sudo_secure_dir(const char *path, uid_t uid, gid_t gid, struct stat *sbp)
{
return sudo_secure_path(path, _S_IFDIR, uid, gid, sbp);
}

60
lib/util/setgroups.c Normal file
View File

@@ -0,0 +1,60 @@
/*
* Copyright (c) 2011-2012 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 <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_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <errno.h>
#include <grp.h>
#include <limits.h>
#include "missing.h"
#include "sudo_debug.h"
#include "sudo_util.h"
int
sudo_setgroups(int ngids, const GETGROUPS_T *gids)
{
int maxgids, rval;
debug_decl(sudo_setgroups, SUDO_DEBUG_UTIL)
rval = setgroups(ngids, (GETGROUPS_T *)gids);
if (rval == -1 && errno == EINVAL) {
/* Too many groups, try again with fewer. */
#if defined(HAVE_SYSCONF) && defined(_SC_NGROUPS_MAX)
maxgids = (int)sysconf(_SC_NGROUPS_MAX);
if (maxgids == -1)
#endif
maxgids = NGROUPS_MAX;
if (ngids > maxgids)
rval = setgroups(maxgids, (GETGROUPS_T *)gids);
}
debug_return_int(rval);
}

526
lib/util/sha2.c Normal file
View File

@@ -0,0 +1,526 @@
/*
* Copyright (c) 2013 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.
*/
/*
* Implementation of SHA-224, SHA-256, SHA-384 and SHA-512
* as per FIPS 180-4: Secure Hash Standard (SHS)
* http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
*
* Derived from the public domain SHA-1 and SHA-2 implementations
* by Steve Reid and Wei Dai respectively.
*/
#include <config.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>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#if defined(HAVE_STDINT_H)
# include <stdint.h>
#elif defined(HAVE_INTTYPES_H)
# include <inttypes.h>
#endif
#if defined(HAVE_ENDIAN_H)
# include <endian.h>
#elif defined(HAVE_SYS_ENDIAN_H)
# include <sys/endian.h>
#elif defined(HAVE_MACHINE_ENDIAN_H)
# include <machine/endian.h>
#else
# include "compat/endian.h"
#endif
#include "missing.h"
#include "compat/sha2.h"
/*
* SHA-2 operates on 32-bit and 64-bit words in big endian byte order.
* The following macros convert between character arrays and big endian words.
*/
#define BE8TO32(x, y) do { \
(x) = (((uint32_t)((y)[0] & 255) << 24) | \
((uint32_t)((y)[1] & 255) << 16) | \
((uint32_t)((y)[2] & 255) << 8) | \
((uint32_t)((y)[3] & 255))); \
} while (0)
#define BE8TO64(x, y) do { \
(x) = (((uint64_t)((y)[0] & 255) << 56) | \
((uint64_t)((y)[1] & 255) << 48) | \
((uint64_t)((y)[2] & 255) << 40) | \
((uint64_t)((y)[3] & 255) << 32) | \
((uint64_t)((y)[4] & 255) << 24) | \
((uint64_t)((y)[5] & 255) << 16) | \
((uint64_t)((y)[6] & 255) << 8) | \
((uint64_t)((y)[7] & 255))); \
} while (0)
#define BE32TO8(x, y) do { \
(x)[0] = (uint8_t)(((y) >> 24) & 255); \
(x)[1] = (uint8_t)(((y) >> 16) & 255); \
(x)[2] = (uint8_t)(((y) >> 8) & 255); \
(x)[3] = (uint8_t)((y) & 255); \
} while (0)
#define BE64TO8(x, y) do { \
(x)[0] = (uint8_t)(((y) >> 56) & 255); \
(x)[1] = (uint8_t)(((y) >> 48) & 255); \
(x)[2] = (uint8_t)(((y) >> 40) & 255); \
(x)[3] = (uint8_t)(((y) >> 32) & 255); \
(x)[4] = (uint8_t)(((y) >> 24) & 255); \
(x)[5] = (uint8_t)(((y) >> 16) & 255); \
(x)[6] = (uint8_t)(((y) >> 8) & 255); \
(x)[7] = (uint8_t)((y) & 255); \
} while (0)
#define rotrFixed(x,y) (y ? ((x>>y) | (x<<(sizeof(x)*8-y))) : x)
#define blk0(i) (W[i])
#define blk2(i) (W[i&15]+=s1(W[(i-2)&15])+W[(i-7)&15]+s0(W[(i-15)&15]))
#define Ch(x,y,z) (z^(x&(y^z)))
#define Maj(x,y,z) (y^((x^y)&(y^z)))
#define a(i) T[(0-i)&7]
#define b(i) T[(1-i)&7]
#define c(i) T[(2-i)&7]
#define d(i) T[(3-i)&7]
#define e(i) T[(4-i)&7]
#define f(i) T[(5-i)&7]
#define g(i) T[(6-i)&7]
#define h(i) T[(7-i)&7]
void
SHA224Init(SHA2_CTX *ctx)
{
memset(ctx, 0, sizeof(*ctx));
ctx->state.st32[0] = 0xc1059ed8UL;
ctx->state.st32[1] = 0x367cd507UL;
ctx->state.st32[2] = 0x3070dd17UL;
ctx->state.st32[3] = 0xf70e5939UL;
ctx->state.st32[4] = 0xffc00b31UL;
ctx->state.st32[5] = 0x68581511UL;
ctx->state.st32[6] = 0x64f98fa7UL;
ctx->state.st32[7] = 0xbefa4fa4UL;
}
void
SHA224Transform(uint32_t state[8], const uint8_t buffer[SHA224_BLOCK_LENGTH])
{
SHA256Transform(state, buffer);
}
void
SHA224Update(SHA2_CTX *ctx, const uint8_t *data, size_t len)
{
SHA256Update(ctx, data, len);
}
void
SHA224Pad(SHA2_CTX *ctx)
{
SHA256Pad(ctx);
}
void
SHA224Final(uint8_t digest[SHA224_DIGEST_LENGTH], SHA2_CTX *ctx)
{
SHA256Pad(ctx);
if (digest != NULL) {
#if BYTE_ORDER == BIG_ENDIAN
memcpy(digest, ctx->state.st32, SHA224_DIGEST_LENGTH);
#else
unsigned int i;
for (i = 0; i < 7; i++)
BE32TO8(digest + (i * 4), ctx->state.st32[i]);
#endif
memset(ctx, 0, sizeof(*ctx));
}
}
static const uint32_t SHA256_K[64] = {
0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL,
0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL,
0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL,
0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL,
0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL,
0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL,
0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL,
0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL,
0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL,
0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL,
0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL,
0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL,
0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
};
void
SHA256Init(SHA2_CTX *ctx)
{
memset(ctx, 0, sizeof(*ctx));
ctx->state.st32[0] = 0x6a09e667UL;
ctx->state.st32[1] = 0xbb67ae85UL;
ctx->state.st32[2] = 0x3c6ef372UL;
ctx->state.st32[3] = 0xa54ff53aUL;
ctx->state.st32[4] = 0x510e527fUL;
ctx->state.st32[5] = 0x9b05688cUL;
ctx->state.st32[6] = 0x1f83d9abUL;
ctx->state.st32[7] = 0x5be0cd19UL;
}
/* Round macros for SHA256 */
#define R(i) do { \
h(i)+=S1(e(i))+Ch(e(i),f(i),g(i))+SHA256_K[i+j]+(j?blk2(i):blk0(i)); \
d(i)+=h(i); \
h(i)+=S0(a(i))+Maj(a(i),b(i),c(i)); \
} while (0)
#define S0(x) (rotrFixed(x,2)^rotrFixed(x,13)^rotrFixed(x,22))
#define S1(x) (rotrFixed(x,6)^rotrFixed(x,11)^rotrFixed(x,25))
#define s0(x) (rotrFixed(x,7)^rotrFixed(x,18)^(x>>3))
#define s1(x) (rotrFixed(x,17)^rotrFixed(x,19)^(x>>10))
void
SHA256Transform(uint32_t state[8], const uint8_t data[SHA256_BLOCK_LENGTH])
{
uint32_t W[16];
uint32_t T[8];
unsigned int j;
/* Copy context state to working vars. */
memcpy(T, state, sizeof(T));
/* Copy data to W in big endian format. */
#if BYTE_ORDER == BIG_ENDIAN
memcpy(W, data, sizeof(W));
#else
for (j = 0; j < 16; j++) {
BE8TO32(W[j], data);
data += 4;
}
#endif
/* 64 operations, partially loop unrolled. */
for (j = 0; j < 64; j += 16)
{
R( 0); R( 1); R( 2); R( 3);
R( 4); R( 5); R( 6); R( 7);
R( 8); R( 9); R(10); R(11);
R(12); R(13); R(14); R(15);
}
/* Add the working vars back into context state. */
state[0] += a(0);
state[1] += b(0);
state[2] += c(0);
state[3] += d(0);
state[4] += e(0);
state[5] += f(0);
state[6] += g(0);
state[7] += h(0);
/* Cleanup */
memset_s(T, sizeof(T), 0, sizeof(T));
memset_s(W, sizeof(W), 0, sizeof(W));
}
#undef S0
#undef S1
#undef s0
#undef s1
#undef R
void
SHA256Update(SHA2_CTX *ctx, const uint8_t *data, size_t len)
{
size_t i = 0, j;
j = (size_t)((ctx->count[0] >> 3) & (SHA256_BLOCK_LENGTH - 1));
ctx->count[0] += (len << 3);
if ((j + len) > SHA256_BLOCK_LENGTH - 1) {
memcpy(&ctx->buffer[j], data, (i = SHA256_BLOCK_LENGTH - j));
SHA256Transform(ctx->state.st32, ctx->buffer);
for ( ; i + SHA256_BLOCK_LENGTH - 1 < len; i += SHA256_BLOCK_LENGTH)
SHA256Transform(ctx->state.st32, (uint8_t *)&data[i]);
j = 0;
}
memcpy(&ctx->buffer[j], &data[i], len - i);
}
void
SHA256Pad(SHA2_CTX *ctx)
{
uint8_t finalcount[8];
/* Store unpadded message length in bits in big endian format. */
BE64TO8(finalcount, ctx->count[0]);
/* Append a '1' bit (0x80) to the message. */
SHA256Update(ctx, (uint8_t *)"\200", 1);
/* Pad message such that the resulting length modulo 512 is 448. */
while ((ctx->count[0] & 504) != 448)
SHA256Update(ctx, (uint8_t *)"\0", 1);
/* Append length of message in bits and do final SHA256Transform(). */
SHA256Update(ctx, finalcount, sizeof(finalcount));
}
void
SHA256Final(uint8_t digest[SHA256_DIGEST_LENGTH], SHA2_CTX *ctx)
{
SHA256Pad(ctx);
if (digest != NULL) {
#if BYTE_ORDER == BIG_ENDIAN
memcpy(digest, ctx->state.st32, SHA256_DIGEST_LENGTH);
#else
unsigned int i;
for (i = 0; i < 8; i++)
BE32TO8(digest + (i * 4), ctx->state.st32[i]);
#endif
memset(ctx, 0, sizeof(*ctx));
}
}
void
SHA384Init(SHA2_CTX *ctx)
{
memset(ctx, 0, sizeof(*ctx));
ctx->state.st64[0] = 0xcbbb9d5dc1059ed8ULL;
ctx->state.st64[1] = 0x629a292a367cd507ULL;
ctx->state.st64[2] = 0x9159015a3070dd17ULL;
ctx->state.st64[3] = 0x152fecd8f70e5939ULL;
ctx->state.st64[4] = 0x67332667ffc00b31ULL;
ctx->state.st64[5] = 0x8eb44a8768581511ULL;
ctx->state.st64[6] = 0xdb0c2e0d64f98fa7ULL;
ctx->state.st64[7] = 0x47b5481dbefa4fa4ULL;
}
void
SHA384Transform(uint64_t state[8], const uint8_t data[SHA384_BLOCK_LENGTH])
{
SHA512Transform(state, data);
}
void
SHA384Update(SHA2_CTX *ctx, const uint8_t *data, size_t len)
{
SHA512Update(ctx, data, len);
}
void
SHA384Pad(SHA2_CTX *ctx)
{
SHA512Pad(ctx);
}
void
SHA384Final(uint8_t digest[SHA384_DIGEST_LENGTH], SHA2_CTX *ctx)
{
SHA384Pad(ctx);
if (digest != NULL) {
#if BYTE_ORDER == BIG_ENDIAN
memcpy(digest, ctx->state.st64, SHA384_DIGEST_LENGTH);
#else
unsigned int i;
for (i = 0; i < 6; i++)
BE64TO8(digest + (i * 8), ctx->state.st64[i]);
#endif
memset(ctx, 0, sizeof(*ctx));
}
}
static const uint64_t SHA512_K[80] = {
0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL,
0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,
0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
0xd807aa98a3030242ULL, 0x12835b0145706fbeULL,
0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL,
0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,
0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL,
0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL,
0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,
0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL,
0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL,
0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,
0xd192e819d6ef5218ULL, 0xd69906245565a910ULL,
0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL,
0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,
0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL,
0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL,
0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,
0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,
0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL,
0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
0x28db77f523047d84ULL, 0x32caab7b40c72493ULL,
0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,
0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL
};
void
SHA512Init(SHA2_CTX *ctx)
{
memset(ctx, 0, sizeof(*ctx));
ctx->state.st64[0] = 0x6a09e667f3bcc908ULL;
ctx->state.st64[1] = 0xbb67ae8584caa73bULL;
ctx->state.st64[2] = 0x3c6ef372fe94f82bULL;
ctx->state.st64[3] = 0xa54ff53a5f1d36f1ULL;
ctx->state.st64[4] = 0x510e527fade682d1ULL;
ctx->state.st64[5] = 0x9b05688c2b3e6c1fULL;
ctx->state.st64[6] = 0x1f83d9abfb41bd6bULL;
ctx->state.st64[7] = 0x5be0cd19137e2179ULL;
}
/* Round macros for SHA512 */
#define R(i) do { \
h(i)+=S1(e(i))+Ch(e(i),f(i),g(i))+SHA512_K[i+j]+(j?blk2(i):blk0(i)); \
d(i)+=h(i); \
h(i)+=S0(a(i))+Maj(a(i),b(i),c(i)); \
} while (0)
#define S0(x) (rotrFixed(x,28)^rotrFixed(x,34)^rotrFixed(x,39))
#define S1(x) (rotrFixed(x,14)^rotrFixed(x,18)^rotrFixed(x,41))
#define s0(x) (rotrFixed(x,1)^rotrFixed(x,8)^(x>>7))
#define s1(x) (rotrFixed(x,19)^rotrFixed(x,61)^(x>>6))
void
SHA512Transform(uint64_t state[8], const uint8_t data[SHA512_BLOCK_LENGTH])
{
uint64_t W[16];
uint64_t T[8];
unsigned int j;
/* Copy context state to working vars. */
memcpy(T, state, sizeof(T));
/* Copy data to W in big endian format. */
#if BYTE_ORDER == BIG_ENDIAN
memcpy(W, data, sizeof(W));
#else
for (j = 0; j < 16; j++) {
BE8TO64(W[j], data);
data += 8;
}
#endif
/* 80 operations, partially loop unrolled. */
for (j = 0; j < 80; j += 16)
{
R( 0); R( 1); R( 2); R( 3);
R( 4); R( 5); R( 6); R( 7);
R( 8); R( 9); R(10); R(11);
R(12); R(13); R(14); R(15);
}
/* Add the working vars back into context state. */
state[0] += a(0);
state[1] += b(0);
state[2] += c(0);
state[3] += d(0);
state[4] += e(0);
state[5] += f(0);
state[6] += g(0);
state[7] += h(0);
/* Cleanup. */
memset_s(T, sizeof(T), 0, sizeof(T));
memset_s(W, sizeof(W), 0, sizeof(W));
}
void
SHA512Update(SHA2_CTX *ctx, const uint8_t *data, size_t len)
{
size_t i = 0, j;
j = (size_t)((ctx->count[0] >> 3) & (SHA512_BLOCK_LENGTH - 1));
ctx->count[0] += (len << 3);
if (ctx->count[0] < (len << 3))
ctx->count[1]++;
if ((j + len) > SHA512_BLOCK_LENGTH - 1) {
memcpy(&ctx->buffer[j], data, (i = SHA512_BLOCK_LENGTH - j));
SHA512Transform(ctx->state.st64, ctx->buffer);
for ( ; i + SHA512_BLOCK_LENGTH - 1 < len; i += SHA512_BLOCK_LENGTH)
SHA512Transform(ctx->state.st64, (uint8_t *)&data[i]);
j = 0;
}
memcpy(&ctx->buffer[j], &data[i], len - i);
}
void
SHA512Pad(SHA2_CTX *ctx)
{
uint8_t finalcount[16];
/* Store unpadded message length in bits in big endian format. */
BE64TO8(finalcount, ctx->count[1]);
BE64TO8(finalcount + 8, ctx->count[0]);
/* Append a '1' bit (0x80) to the message. */
SHA512Update(ctx, (uint8_t *)"\200", 1);
/* Pad message such that the resulting length modulo 1024 is 896. */
while ((ctx->count[0] & 1008) != 896)
SHA512Update(ctx, (uint8_t *)"\0", 1);
/* Append length of message in bits and do final SHA512Transform(). */
SHA512Update(ctx, finalcount, sizeof(finalcount));
}
void
SHA512Final(uint8_t digest[SHA512_DIGEST_LENGTH], SHA2_CTX *ctx)
{
SHA512Pad(ctx);
if (digest != NULL) {
#if BYTE_ORDER == BIG_ENDIAN
memcpy(digest, ctx->state.st64, SHA512_DIGEST_LENGTH);
#else
unsigned int i;
for (i = 0; i < 8; i++)
BE64TO8(digest + (i * 8), ctx->state.st64[i]);
#endif
memset(ctx, 0, sizeof(*ctx));
}
}

80
lib/util/sig2str.c Normal file
View File

@@ -0,0 +1,80 @@
/*
* Copyright (c) 2012-2013 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>
#ifndef HAVE_SIG2STR
#include <sys/types.h>
#include <errno.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>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#include <signal.h>
#include <unistd.h>
#include "missing.h"
#if defined(HAVE_DECL_SYS_SIGNAME) && HAVE_DECL_SYS_SIGNAME == 1
# define sudo_sys_signame sys_signame
#elif defined(HAVE_DECL__SYS_SIGNAME) && HAVE_DECL__SYS_SIGNAME == 1
# define sudo_sys_signame _sys_signame
#elif defined(HAVE_DECL___SYS_SIGNAME) && HAVE_DECL___SYS_SIGNAME == 1
# define sudo_sys_signame __sys_signame
#elif defined(HAVE_DECL_SYS_SIGABBREV) && HAVE_DECL_SYS_SIGABBREV == 1
# define sudo_sys_signame sys_sigabbrev
#else
# ifdef HAVE_SYS_SIGABBREV
/* sys_sigabbrev is not declared by glibc */
# define sudo_sys_signame sys_sigabbrev
# endif
extern const char *const sudo_sys_signame[NSIG];
#endif
/*
* Translate signal number to name.
*/
int
sig2str(int signo, char *signame)
{
#if defined(SIGRTMIN) && defined(SIGRTMAX)
/* Realtime signal support as per Solaris. */
if (signo >= SIGRTMIN && signo <= SIGRTMAX) {
snprintf(signame, SIG2STR_MAX, "RTMIN+%d", (signo - SIGRTMIN));
return 0;
}
#endif
if (signo > 0 && signo < NSIG && sudo_sys_signame[signo] != NULL) {
strlcpy(signame, sudo_sys_signame[signo], SIG2STR_MAX);
return 0;
}
errno = EINVAL;
return -1;
}
#endif /* HAVE_SIG2STR */

56
lib/util/siglist.in Normal file
View File

@@ -0,0 +1,56 @@
#
# List of signals used to build sys_siglist (see mksiglist.c)
# Adapted from pdksh; public domain
#
# Note that if a system has multiple defines for the same signal
# (eg, SIGABRT vs SIGIOT, SIGCHLD vs SIGCLD), only the first one
# will be seen, so the order in this list is important.
#
HUP Hangup
INT Interrupt
QUIT Quit
ILL Illegal instruction
TRAP Trace trap
# before IOT (ABRT is posix and ABRT is sometimes the same as IOT)
ABRT Abort
IOT IOT instruction
EMT EMT trap
FPE Floating point exception
KILL Killed
# before BUS (Older Linux doesn't really have a BUS, but defines it to UNUSED)
UNUSED Unused
BUS Bus error
SEGV Memory fault
SYS Bad system call
PIPE Broken pipe
ALRM Alarm clock
TERM Terminated
STKFLT Stack fault
# before POLL (POLL is sometimes the same as IO)
IO I/O possible
XCPU CPU time limit exceeded
XFSZ File size limit exceeded
VTALRM Virtual timer expired
PROF Profiling timer expired
WINCH Window size change
LOST File lock lost
USR1 User defined signal 1
USR2 User defined signal 2
PWR Power-fail/Restart
POLL Pollable event occurred
STOP Stopped (signal)
TSTP Stopped
CONT Continued
# before CLD (CHLD is posix and CHLD is sometimes the same as CLD)
CHLD Child exited
CLD Child exited
TTIN Stopped (tty input)
TTOU Stopped (tty output)
INFO Information request
URG Urgent I/O condition
# Solaris (svr4?) signals
WAITING No runnable LWPs
LWP Inter-LWP signal
FREEZE Checkpoint freeze
THAW Checkpoint thaw
CANCEL Thread cancellation

663
lib/util/snprintf.c Normal file
View File

@@ -0,0 +1,663 @@
/*
* Copyright (c) 1999-2005, 2008, 2010-2013
* Todd C. Miller <Todd.Miller@courtesan.com>
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Chris Torek.
*
* 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 the University 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 REGENTS 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 REGENTS 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.
*
* From: @(#)vfprintf.c 8.1 (Berkeley) 6/4/93
*/
/*
* v?snprintf/v?asprintf based on 4.4BSD stdio.
* NOTE: does not support floating point.
*/
#include <config.h>
#if !defined(HAVE_VSNPRINTF) || !defined(HAVE_SNPRINTF) || \
!defined(HAVE_VASPRINTF) || !defined(HAVE_ASPRINTF) || \
defined(PREFER_PORTABLE_SNPRINTF)
#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 */
#if defined(HAVE_STDINT_H)
# include <stdint.h>
#elif defined(HAVE_INTTYPES_H)
# include <inttypes.h>
#endif
#ifdef HAVE_STRING_H
# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
# include <memory.h>
# endif
# include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS)
# include <malloc.h>
#endif /* HAVE_MALLOC_H && !STDC_HEADERS */
#include <limits.h>
#include <stdarg.h>
#include "missing.h"
static int xxxprintf(char **, size_t, int, const char *, va_list);
/*
* Macros for converting digits to letters and vice versa
*/
#define to_digit(c) ((c) - '0')
#define is_digit(c) ((unsigned int)to_digit(c) <= 9)
#define to_char(n) ((n) + '0')
/*
* Flags used during conversion.
*/
#define ALT 0x001 /* alternate form */
#define HEXPREFIX 0x002 /* add 0x or 0X prefix */
#define LADJUST 0x004 /* left adjustment */
#define LONGDBL 0x008 /* long double; unimplemented */
#define LONGINT 0x010 /* long integer */
#define LLONGINT 0x020 /* quad integer */
#define SHORTINT 0x040 /* short integer */
#define ZEROPAD 0x080 /* zero (as opposed to blank) pad */
#define BUF 68
/*
* Convert an unsigned long to ASCII for printf purposes, returning
* a pointer to the first character of the string representation.
* Octal numbers can be forced to have a leading zero; hex numbers
* use the given digits.
*/
static char *
__ultoa(unsigned long val, char *endp, int base, int octzero, char *xdigs)
{
char *cp = endp;
long sval;
/*
* Handle the three cases separately, in the hope of getting
* better/faster code.
*/
switch (base) {
case 10:
if (val < 10) { /* many numbers are 1 digit */
*--cp = to_char(val);
return cp;
}
/*
* On many machines, unsigned arithmetic is harder than
* signed arithmetic, so we do at most one unsigned mod and
* divide; this is sufficient to reduce the range of
* the incoming value to where signed arithmetic works.
*/
if (val > LONG_MAX) {
*--cp = to_char(val % 10);
sval = val / 10;
} else
sval = val;
do {
*--cp = to_char(sval % 10);
sval /= 10;
} while (sval != 0);
break;
case 8:
do {
*--cp = to_char(val & 7);
val >>= 3;
} while (val);
if (octzero && *cp != '0')
*--cp = '0';
break;
case 16:
do {
*--cp = xdigs[val & 15];
val >>= 4;
} while (val);
break;
default: /* oops */
abort();
}
return cp;
}
/* Identical to __ultoa, but for quads. */
#if SIZEOF_LONG_INT == 8
# define __ulltoa(v, e, b, o, x) __ultoa((unsigned long)(v), (e), (b), (o), (x))
#else
static char *
__ulltoa(unsigned long long val, char *endp, int base, int octzero, char *xdigs)
{
char *cp = endp;
long long sval;
/* quick test for small values; __ultoa is typically much faster */
/* (perhaps instead we should run until small, then call __ultoa?) */
if (val <= (unsigned long long)ULONG_MAX)
return __ultoa((unsigned long)val, endp, base, octzero, xdigs);
switch (base) {
case 10:
if (val < 10) {
*--cp = to_char(val % 10);
return cp;
}
if (val > LLONG_MAX) {
*--cp = to_char(val % 10);
sval = val / 10;
} else
sval = val;
do {
*--cp = to_char(sval % 10);
sval /= 10;
} while (sval != 0);
break;
case 8:
do {
*--cp = to_char(val & 7);
val >>= 3;
} while (val);
if (octzero && *cp != '0')
*--cp = '0';
break;
case 16:
do {
*--cp = xdigs[val & 15];
val >>= 4;
} while (val);
break;
default: /* oops */
abort();
}
return cp;
}
#endif /* !SIZEOF_LONG_INT */
/*
* Actual printf innards.
*/
static int
xxxprintf(char **strp, size_t strsize, int alloc, const char *fmt0, va_list ap)
{
char *fmt; /* format string */
int ch; /* character from fmt */
int n; /* handy integer (short term usage) */
char *cp; /* handy char pointer (short term usage) */
int flags; /* flags as above */
int ret; /* return value accumulator */
int width; /* width from format (%8d), or 0 */
int prec; /* precision from format (%.3d), or -1 */
char sign; /* sign prefix (' ', '+', '-', or \0) */
unsigned long ulval = 0; /* integer arguments %[diouxX] */
unsigned long long ullval = 0; /* long long arguments %ll[diouxX] */
int base; /* base for [diouxX] conversion */
int dprec; /* a copy of prec if [diouxX], 0 otherwise */
int fieldsz; /* field size expanded by sign, etc */
int realsz; /* field size expanded by dprec */
int size; /* size of converted field or string */
char *xdigs = ""; /* digits for [xX] conversion */
char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */
char ox[2]; /* space for 0x hex-prefix */
char *str; /* pointer to string to fill */
char *estr; /* pointer to last char in str */
/*
* Choose PADSIZE to trade efficiency vs. size. If larger printf
* fields occur frequently, increase PADSIZE and make the initialisers
* below longer.
*/
#define PADSIZE 16 /* pad chunk size */
static char blanks[PADSIZE] =
{' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
static char zeroes[PADSIZE] =
{'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
/* Print chars to "str", (allocate as needed if alloc is set). */
#define PRINT(ptr, len) do { \
const char *p = ptr; \
const char *endp = ptr + len; \
while (p < endp && (str < estr || alloc)) { \
if (alloc && str >= estr) { \
char *t; \
strsize = (strsize << 1) + 1; \
if (!(t = (char *)realloc(*strp, strsize))) { \
free(str); \
*strp = NULL; \
ret = -1; \
goto done; \
} \
str = t + (str - *strp); \
estr = t + strsize - 1; \
*strp = t; \
} \
*str++ = *p++; \
} \
} while (0)
/* BEWARE, PAD uses `n'. */
#define PAD(plen, pstr) do { \
if ((n = (plen)) > 0) { \
while (n > PADSIZE) { \
PRINT(pstr, PADSIZE); \
n -= PADSIZE; \
} \
PRINT(pstr, n); \
} \
} while (0)
/*
* To extend shorts properly, we need both signed and unsigned
* argument extraction methods.
*/
#define SARG() \
(flags&LONGINT ? va_arg(ap, long) : \
flags&SHORTINT ? (long)(short)va_arg(ap, int) : \
(long)va_arg(ap, int))
#define UARG() \
(flags&LONGINT ? va_arg(ap, unsigned long) : \
flags&SHORTINT ? (unsigned long)(unsigned short)va_arg(ap, int) : \
(unsigned long)va_arg(ap, unsigned int))
fmt = (char *)fmt0;
ret = 0;
if (alloc) {
strsize = 128;
*strp = str = (char *)malloc(strsize);
if (str == NULL) {
ret = -1;
goto done;
}
estr = str + 127;
} else {
str = *strp;
if (strsize)
estr = str + strsize - 1;
else
estr = NULL;
}
/*
* Scan the format for conversions (`%' character).
*/
for (;;) {
for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
/* void */;
if ((n = fmt - cp) != 0) {
PRINT(cp, n);
ret += n;
}
if (ch == '\0')
goto done;
fmt++; /* skip over '%' */
flags = 0;
dprec = 0;
width = 0;
prec = -1;
sign = '\0';
rflag: ch = *fmt++;
reswitch: switch (ch) {
case ' ':
/*
* ``If the space and + flags both appear, the space
* flag will be ignored.''
* -- ANSI X3J11
*/
if (!sign)
sign = ' ';
goto rflag;
case '#':
flags |= ALT;
goto rflag;
case '*':
/*
* ``A negative field width argument is taken as a
* - flag followed by a positive field width.''
* -- ANSI X3J11
* They don't exclude field widths read from args.
*/
if ((width = va_arg(ap, int)) >= 0)
goto rflag;
width = -width;
/* FALLTHROUGH */
case '-':
flags |= LADJUST;
goto rflag;
case '+':
sign = '+';
goto rflag;
case '.':
if ((ch = *fmt++) == '*') {
n = va_arg(ap, int);
prec = n < 0 ? -1 : n;
goto rflag;
}
n = 0;
while (is_digit(ch)) {
n = 10 * n + to_digit(ch);
ch = *fmt++;
}
prec = n < 0 ? -1 : n;
goto reswitch;
case '0':
/*
* ``Note that 0 is taken as a flag, not as the
* beginning of a field width.''
* -- ANSI X3J11
*/
flags |= ZEROPAD;
goto rflag;
case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
n = 0;
do {
n = 10 * n + to_digit(ch);
ch = *fmt++;
} while (is_digit(ch));
width = n;
goto reswitch;
case 'h':
flags |= SHORTINT;
goto rflag;
case 'l':
if (*fmt == 'l') {
fmt++;
flags |= LLONGINT;
} else {
flags |= LONGINT;
}
goto rflag;
case 'c':
*(cp = buf) = va_arg(ap, int);
size = 1;
sign = '\0';
break;
case 'D':
flags |= LONGINT;
/*FALLTHROUGH*/
case 'd':
case 'i':
if (flags & LLONGINT) {
ullval = va_arg(ap, long long);
if ((long long)ullval < 0) {
ullval = -ullval;
sign = '-';
}
} else {
ulval = SARG();
if ((long)ulval < 0) {
ulval = -ulval;
sign = '-';
}
}
base = 10;
goto number;
case 'n':
if (flags & LLONGINT)
*va_arg(ap, long long *) = ret;
else if (flags & LONGINT)
*va_arg(ap, long *) = ret;
else if (flags & SHORTINT)
*va_arg(ap, short *) = ret;
else
*va_arg(ap, int *) = ret;
continue; /* no output */
case 'O':
flags |= LONGINT;
/*FALLTHROUGH*/
case 'o':
if (flags & LLONGINT)
ullval = va_arg(ap, unsigned long long);
else
ulval = UARG();
base = 8;
goto nosign;
case 'p':
/*
* ``The argument shall be a pointer to void. The
* value of the pointer is converted to a sequence
* of printable characters, in an implementation-
* defined manner.''
* -- ANSI X3J11
*/
ulval = (unsigned long)va_arg(ap, void *);
base = 16;
xdigs = "0123456789abcdef";
flags = (flags & ~LLONGINT) | HEXPREFIX;
ch = 'x';
goto nosign;
case 's':
if ((cp = va_arg(ap, char *)) == NULL)
cp = "(null)";
if (prec >= 0) {
/*
* can't use strlen; can only look for the
* NUL in the first `prec' characters, and
* strlen() will go further.
*/
char *p = memchr(cp, 0, prec);
if (p != NULL) {
size = p - cp;
if (size > prec)
size = prec;
} else
size = prec;
} else
size = strlen(cp);
sign = '\0';
break;
case 'U':
flags |= LONGINT;
/*FALLTHROUGH*/
case 'u':
if (flags & LLONGINT)
ullval = va_arg(ap, unsigned long long);
else
ulval = UARG();
base = 10;
goto nosign;
case 'X':
xdigs = "0123456789ABCDEF";
goto hex;
case 'x':
xdigs = "0123456789abcdef";
hex:
if (flags & LLONGINT)
ullval = va_arg(ap, unsigned long long);
else
ulval = UARG();
base = 16;
/* leading 0x/X only if non-zero */
if (flags & ALT &&
(flags & LLONGINT ? ullval != 0 : ulval != 0))
flags |= HEXPREFIX;
/* unsigned conversions */
nosign: sign = '\0';
/*
* ``... diouXx conversions ... if a precision is
* specified, the 0 flag will be ignored.''
* -- ANSI X3J11
*/
number: if ((dprec = prec) >= 0)
flags &= ~ZEROPAD;
/*
* ``The result of converting a zero value with an
* explicit precision of zero is no characters.''
* -- ANSI X3J11
*/
cp = buf + BUF;
if (flags & LLONGINT) {
if (ullval != 0 || prec != 0)
cp = __ulltoa(ullval, cp, base,
flags & ALT, xdigs);
} else {
if (ulval != 0 || prec != 0)
cp = __ultoa(ulval, cp, base,
flags & ALT, xdigs);
}
size = buf + BUF - cp;
break;
default: /* "%?" prints ?, unless ? is NUL */
if (ch == '\0')
goto done;
/* pretend it was %c with argument ch */
cp = buf;
*cp = ch;
size = 1;
sign = '\0';
break;
}
/*
* All reasonable formats wind up here. At this point, `cp'
* points to a string which (if not flags&LADJUST) should be
* padded out to `width' places. If flags&ZEROPAD, it should
* first be prefixed by any sign or other prefix; otherwise,
* it should be blank padded before the prefix is emitted.
* After any left-hand padding and prefixing, emit zeroes
* required by a decimal [diouxX] precision, then print the
* string proper, then emit zeroes required by any leftover
* floating precision; finally, if LADJUST, pad with blanks.
*
* Compute actual size, so we know how much to pad.
* fieldsz excludes decimal prec; realsz includes it.
*/
fieldsz = size;
if (sign)
fieldsz++;
else if (flags & HEXPREFIX)
fieldsz += 2;
realsz = dprec > fieldsz ? dprec : fieldsz;
/* right-adjusting blank padding */
if ((flags & (LADJUST|ZEROPAD)) == 0)
PAD(width - realsz, blanks);
/* prefix */
if (sign) {
PRINT(&sign, 1);
} else if (flags & HEXPREFIX) {
ox[0] = '0';
ox[1] = ch;
PRINT(ox, 2);
}
/* right-adjusting zero padding */
if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
PAD(width - realsz, zeroes);
/* leading zeroes from decimal precision */
PAD(dprec - fieldsz, zeroes);
/* the string or number proper */
PRINT(cp, size);
/* left-adjusting padding (always blank) */
if (flags & LADJUST)
PAD(width - realsz, blanks);
/* finally, adjust ret */
ret += width > realsz ? width : realsz;
}
done:
if (strsize)
*str = '\0';
return ret;
/* NOTREACHED */
}
#if !defined(HAVE_VSNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
int
rpl_vsnprintf(char *str, size_t n, const char *fmt, va_list ap)
{
return xxxprintf(&str, n, 0, fmt, ap);
}
#endif /* !HAVE_VSNPRINTF || PREFER_PORTABLE_SNPRINTF */
#if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
int
rpl_snprintf(char *str, size_t n, char const *fmt, ...)
{
int ret;
va_list ap;
va_start(ap, fmt);
ret = xxxprintf(&str, n, 0, fmt, ap);
va_end(ap);
return ret;
}
#endif /* !HAVE_SNPRINTF || PREFER_PORTABLE_SNPRINTF */
#if !defined(HAVE_VASPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
int
rpl_vasprintf(char **str, const char *fmt, va_list ap)
{
return xxxprintf(str, 0, 1, fmt, ap);
}
#endif /* !HAVE_VASPRINTF || PREFER_PORTABLE_SNPRINTF */
#if !defined(HAVE_ASPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
int
rpl_asprintf(char **str, char const *fmt, ...)
{
int ret;
va_list ap;
va_start(ap, fmt);
ret = xxxprintf(str, 0, 1, fmt, ap);
va_end(ap);
return ret;
}
#endif /* !HAVE_ASPRINTF || PREFER_PORTABLE_SNPRINTF */
#endif /* !HAVE_VSNPRINTF || !HAVE_SNPRINTF || !HAVE_VASPRINTF || !HAVE_ASPRINTF || PREFER_PORTABLE_SNPRINTF */

63
lib/util/strlcat.c Normal file
View File

@@ -0,0 +1,63 @@
/* $OpenBSD: strlcat.c,v 1.8 2001/05/13 15:40:15 deraadt Exp $ */
/*
* Copyright (c) 1998, 2003-2005, 2010-2011, 2013
* 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>
#ifndef HAVE_STRLCAT
#include <sys/types.h>
#include <string.h>
#include "missing.h"
/*
* Appends src to string dst of size siz (unlike strncat, siz is the
* full size of dst, not space left). At most siz-1 characters
* will be copied. Always NUL terminates (unless siz <= strlen(dst)).
* Returns strlen(src) + MIN(siz, strlen(initial dst)).
* If retval >= siz, truncation occurred.
*/
size_t
strlcat(char *dst, const char *src, size_t siz)
{
char *d = dst;
const char *s = src;
size_t n = siz;
size_t dlen;
/* Find the end of dst and adjust bytes left but don't go past end */
while (n-- != 0 && *d != '\0')
d++;
dlen = d - dst;
n = siz - dlen;
if (n == 0)
return dlen + strlen(s);
while (*s != '\0') {
if (n != 1) {
*d++ = *s;
n--;
}
s++;
}
*d = '\0';
return dlen + (s - src); /* count does not include NUL */
}
#endif /* HAVE_STRLCAT */

58
lib/util/strlcpy.c Normal file
View File

@@ -0,0 +1,58 @@
/* $OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $ */
/*
* Copyright (c) 1998, 2003-2005, 2010-2011, 2013
* 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>
#ifndef HAVE_STRLCPY
#include <sys/types.h>
#include "missing.h"
/*
* Copy src to string dst of size siz. At most siz-1 characters
* will be copied. Always NUL terminates (unless siz == 0).
* Returns strlen(src); if retval >= siz, truncation occurred.
*/
size_t
strlcpy(char *dst, const char *src, size_t siz)
{
char *d = dst;
const char *s = src;
size_t n = siz;
/* Copy as many bytes as will fit */
if (n != 0 && --n != 0) {
do {
if ((*d++ = *s++) == 0)
break;
} while (--n != 0);
}
/* Not enough room in dst, add NUL and traverse rest of src */
if (n == 0) {
if (siz != 0)
*d = '\0'; /* NUL-terminate dst */
while (*s++)
;
}
return s - src - 1; /* count does not include NUL */
}
#endif /* HAVE_STRLCPY */

52
lib/util/strsignal.c Normal file
View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) 2009-2013 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>
#ifndef HAVE_STRSIGNAL
#include <sys/types.h>
#include <stdio.h>
#include <signal.h>
#define DEFAULT_TEXT_DOMAIN "sudo"
#include "gettext.h" /* must be included before missing.h */
#include "missing.h"
#if defined(HAVE_DECL_SYS_SIGLIST) && HAVE_DECL_SYS_SIGLIST == 1
# define sudo_sys_siglist sys_siglist
#elif defined(HAVE_DECL__SYS_SIGLIST) && HAVE_DECL__SYS_SIGLIST == 1
# define sudo_sys_siglist _sys_siglist
#elif defined(HAVE_DECL___SYS_SIGLIST) && HAVE_DECL___SYS_SIGLIST == 1
# define sudo_sys_siglist __sys_siglist
#else
extern const char *const sudo_sys_siglist[NSIG];
#endif
/*
* Get signal description string
*/
char *
strsignal(int signo)
{
if (signo > 0 && signo < NSIG && sudo_sys_siglist[signo] != NULL)
return (char *)sudo_sys_siglist[signo];
/* XXX - should be "Unknown signal: %d" */
return _("Unknown signal");
}
#endif /* HAVE_STRSIGNAL */

200
lib/util/strtonum.c Normal file
View File

@@ -0,0 +1,200 @@
/*
* Copyright (c) 2013 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 <ctype.h>
#include <errno.h>
#include <limits.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>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#define DEFAULT_TEXT_DOMAIN "sudo"
#include "gettext.h" /* must be included before missing.h */
#include "missing.h"
#ifdef HAVE_STRTONUM
/*
* The OpenBSD strtonum error string too short to be translated sensibly.
* This wrapper just changes errstr as follows:
* invalid -> invalid value
* too large -> value too large
* too small -> value too small
*/
long long
rpl_strtonum(const char *str, long long minval, long long maxval,
const char **errstrp)
{
long long retval;
const char *errstr;
# undef strtonum
retval = strtonum(str, minval, maxval, &errstr);
if (errstr != NULL) {
if (errno == EINVAL) {
errstr = N_("invalid value");
} else if (errno == ERANGE) {
errstr = strcmp(errstr, "too large") == 0 ?
N_("value too large") : N_("value too small");
}
}
if (errstrp != NULL)
*errstrp = errstr;
return retval;
}
#else
enum strtonum_err {
STN_VALID,
STN_INVALID,
STN_TOOSMALL,
STN_TOOBIG
};
/*
* Convert a string to a number in the range [minval, maxval]
*/
long long
rpl_strtonum(const char *str, long long minval, long long maxval,
const char **errstrp)
{
const unsigned char *ustr = (const unsigned char *)str;
enum strtonum_err errval = STN_VALID;
long long lastval, result = 0;
unsigned char dig, sign;
int remainder;
if (minval > maxval) {
errval = STN_INVALID;
goto done;
}
/* Trim leading space and check sign, if any. */
while (isspace(*ustr)) {
ustr++;
}
switch (*ustr) {
case '-':
sign = '-';
ustr++;
break;
case '+':
ustr++;
/* FALLTHROUGH */
default:
sign = '+';
break;
}
/*
* To prevent overflow we determine the highest (or lowest in
* the case of negative numbers) value result can have *before*
* if its multiplied (divided) by 10 as well as the remainder.
* If result matches this value and the next digit is larger than
* the remainder, we know the result is out of range.
* The remainder is always positive since it is compared against
* an unsigned digit.
*/
if (sign == '-') {
lastval = minval / 10;
remainder = -(minval % 10);
if (remainder < 0) {
lastval += 1;
remainder += 10;
}
while ((dig = *ustr++) != '\0') {
if (!isdigit(dig)) {
errval = STN_INVALID;
break;
}
dig -= '0';
if (result < lastval || (result == lastval && dig > remainder)) {
errval = STN_TOOSMALL;
break;
} else {
result *= 10;
result -= dig;
}
}
if (result > maxval)
errval = STN_TOOBIG;
} else {
lastval = maxval / 10;
remainder = maxval % 10;
while ((dig = *ustr++) != '\0') {
if (!isdigit(dig)) {
errval = STN_INVALID;
break;
}
dig -= '0';
if (result > lastval || (result == lastval && dig > remainder)) {
errval = STN_TOOBIG;
break;
} else {
result *= 10;
result += dig;
}
}
if (result < minval)
errval = STN_TOOSMALL;
}
done:
switch (errval) {
case STN_VALID:
if (errstrp != NULL)
*errstrp = NULL;
break;
case STN_INVALID:
result = 0;
errno = EINVAL;
if (errstrp != NULL)
*errstrp = N_("invalid value");
break;
case STN_TOOSMALL:
result = 0;
errno = ERANGE;
if (errstrp != NULL)
*errstrp = N_("value too small");
break;
case STN_TOOBIG:
result = 0;
errno = ERANGE;
if (errstrp != NULL)
*errstrp = N_("value too large");
break;
}
return result;
}
#endif /* HAVE_STRTONUM */

459
lib/util/sudo_conf.c Normal file
View File

@@ -0,0 +1,459 @@
/*
* Copyright (c) 2009-2014 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/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_STDBOOL_H
# include <stdbool.h>
#else
# include "compat/stdbool.h"
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#define DEFAULT_TEXT_DOMAIN "sudo"
#include "gettext.h" /* must be included before missing.h */
#define SUDO_ERROR_WRAP 0
#include "missing.h"
#include "alloc.h"
#include "fatal.h"
#include "fileops.h"
#include "pathnames.h"
#include "sudo_plugin.h"
#include "sudo_conf.h"
#include "sudo_debug.h"
#include "sudo_util.h"
#include "secure_path.h"
#ifdef __TANDEM
# define ROOT_UID 65535
#else
# define ROOT_UID 0
#endif
struct sudo_conf_table {
const char *name;
unsigned int namelen;
void (*setter)(const char *entry, const char *conf_file);
};
struct sudo_conf_paths {
const char *pname;
unsigned int pnamelen;
const char *pval;
};
static void set_debug(const char *entry, const char *conf_file);
static void set_path(const char *entry, const char *conf_file);
static void set_plugin(const char *entry, const char *conf_file);
static void set_variable(const char *entry, const char *conf_file);
static void set_var_disable_coredump(const char *entry, const char *conf_file);
static void set_var_group_source(const char *entry, const char *conf_file);
static void set_var_max_groups(const char *entry, const char *conf_file);
static void set_var_probe_interfaces(const char *entry, const char *conf_file);
static unsigned int conf_lineno;
static struct sudo_conf_table sudo_conf_table[] = {
{ "Debug", sizeof("Debug") - 1, set_debug },
{ "Path", sizeof("Path") - 1, set_path },
{ "Plugin", sizeof("Plugin") - 1, set_plugin },
{ "Set", sizeof("Set") - 1, set_variable },
{ NULL }
};
static struct sudo_conf_table sudo_conf_table_vars[] = {
{ "disable_coredump", sizeof("disable_coredump") - 1, set_var_disable_coredump },
{ "group_source", sizeof("group_source") - 1, set_var_group_source },
{ "max_groups", sizeof("max_groups") - 1, set_var_max_groups },
{ "probe_interfaces", sizeof("probe_interfaces") - 1, set_var_probe_interfaces },
{ NULL }
};
static struct sudo_conf_data {
bool disable_coredump;
bool probe_interfaces;
int group_source;
int max_groups;
const char *debug_flags;
struct plugin_info_list plugins;
struct sudo_conf_paths paths[5];
} sudo_conf_data = {
true,
true,
GROUP_SOURCE_ADAPTIVE,
-1,
NULL,
TAILQ_HEAD_INITIALIZER(sudo_conf_data.plugins),
{
#define SUDO_CONF_ASKPASS_IDX 0
{ "askpass", sizeof("askpass") - 1, _PATH_SUDO_ASKPASS },
#define SUDO_CONF_SESH_IDX 1
{ "sesh", sizeof("sesh") - 1, _PATH_SUDO_SESH },
#ifdef _PATH_SUDO_NOEXEC
#define SUDO_CONF_NOEXEC_IDX 2
{ "noexec", sizeof("noexec") - 1, _PATH_SUDO_NOEXEC },
#endif
#ifdef _PATH_SUDO_PLUGIN_DIR
#define SUDO_CONF_PLUGIN_IDX 3
{ "plugin", sizeof("plugin") - 1, _PATH_SUDO_PLUGIN_DIR },
#endif
{ NULL }
}
};
/*
* "Set variable_name value"
*/
static void
set_variable(const char *entry, const char *conf_file)
{
struct sudo_conf_table *var;
for (var = sudo_conf_table_vars; var->name != NULL; var++) {
if (strncmp(entry, var->name, var->namelen) == 0 &&
isblank((unsigned char)entry[var->namelen])) {
entry += var->namelen + 1;
while (isblank((unsigned char)*entry))
entry++;
var->setter(entry, conf_file);
break;
}
}
}
static void
set_var_disable_coredump(const char *entry, const char *conf_file)
{
int val = atobool(entry);
if (val != -1)
sudo_conf_data.disable_coredump = val;
}
static void
set_var_group_source(const char *entry, const char *conf_file)
{
if (strcasecmp(entry, "adaptive") == 0) {
sudo_conf_data.group_source = GROUP_SOURCE_ADAPTIVE;
} else if (strcasecmp(entry, "static") == 0) {
sudo_conf_data.group_source = GROUP_SOURCE_STATIC;
} else if (strcasecmp(entry, "dynamic") == 0) {
sudo_conf_data.group_source = GROUP_SOURCE_DYNAMIC;
} else {
warningx(U_("unsupported group source `%s' in %s, line %d"), entry,
conf_file, conf_lineno);
}
}
static void
set_var_max_groups(const char *entry, const char *conf_file)
{
int max_groups;
max_groups = strtonum(entry, 1, INT_MAX, NULL);
if (max_groups > 0) {
sudo_conf_data.max_groups = max_groups;
} else {
warningx(U_("invalid max groups `%s' in %s, line %d"), entry,
conf_file, conf_lineno);
}
}
static void
set_var_probe_interfaces(const char *entry, const char *conf_file)
{
int val = atobool(entry);
if (val != -1)
sudo_conf_data.probe_interfaces = val;
}
/*
* "Debug progname debug_file debug_flags"
*/
static void
set_debug(const char *entry, const char *conf_file)
{
size_t filelen, proglen;
const char *progname;
char *debug_file, *debug_flags;
/* Is this debug setting for me? */
progname = getprogname();
if (strcmp(progname, "sudoedit") == 0)
progname = "sudo";
proglen = strlen(progname);
if (strncmp(entry, progname, proglen) != 0 ||
!isblank((unsigned char)entry[proglen]))
return;
entry += proglen + 1;
while (isblank((unsigned char)*entry))
entry++;
debug_flags = strpbrk(entry, " \t");
if (debug_flags == NULL)
return;
filelen = (size_t)(debug_flags - entry);
while (isblank((unsigned char)*debug_flags))
debug_flags++;
/* Set debug file and parse the flags (init debug as soon as possible). */
debug_file = estrndup(entry, filelen);
debug_flags = estrdup(debug_flags);
sudo_debug_init(debug_file, debug_flags);
efree(debug_file);
sudo_conf_data.debug_flags = debug_flags;
}
static void
set_path(const char *entry, const char *conf_file)
{
const char *name, *path;
struct sudo_conf_paths *cur;
/* Parse Path line */
name = entry;
path = strpbrk(entry, " \t");
if (path == NULL)
return;
while (isblank((unsigned char)*path))
path++;
/* Match supported paths, ignore the rest. */
for (cur = sudo_conf_data.paths; cur->pname != NULL; cur++) {
if (strncasecmp(name, cur->pname, cur->pnamelen) == 0 &&
isblank((unsigned char)name[cur->pnamelen])) {
cur->pval = estrdup(path);
break;
}
}
}
static void
set_plugin(const char *entry, const char *conf_file)
{
struct plugin_info *info;
const char *name, *path, *cp, *ep;
char **options = NULL;
size_t namelen, pathlen;
unsigned int nopts;
/* Parse Plugin line */
name = entry;
path = strpbrk(entry, " \t");
if (path == NULL)
return;
namelen = (size_t)(path - name);
while (isblank((unsigned char)*path))
path++;
if ((cp = strpbrk(path, " \t")) != NULL) {
/* Convert any options to an array. */
pathlen = (size_t)(cp - path);
while (isblank((unsigned char)*cp))
cp++;
/* Count number of options and allocate array. */
for (ep = cp, nopts = 1; (ep = strpbrk(ep, " \t")) != NULL; nopts++) {
while (isblank((unsigned char)*ep))
ep++;
}
options = emallocarray(nopts + 1, sizeof(*options));
/* Fill in options array, there is at least one element. */
for (nopts = 0; (ep = strpbrk(cp, " \t")) != NULL; ) {
options[nopts++] = estrndup(cp, (size_t)(ep - cp));
while (isblank((unsigned char)*ep))
ep++;
cp = ep;
}
options[nopts++] = estrdup(cp);
options[nopts] = NULL;
} else {
/* No extra options. */
pathlen = strlen(path);
}
info = ecalloc(1, sizeof(*info));
info->symbol_name = estrndup(name, namelen);
info->path = estrndup(path, pathlen);
info->options = options;
info->lineno = conf_lineno;
TAILQ_INSERT_TAIL(&sudo_conf_data.plugins, info, entries);
}
const char *
sudo_conf_askpass_path(void)
{
return sudo_conf_data.paths[SUDO_CONF_ASKPASS_IDX].pval;
}
const char *
sudo_conf_sesh_path(void)
{
return sudo_conf_data.paths[SUDO_CONF_SESH_IDX].pval;
}
#ifdef _PATH_SUDO_NOEXEC
const char *
sudo_conf_noexec_path(void)
{
return sudo_conf_data.paths[SUDO_CONF_NOEXEC_IDX].pval;
}
#endif
#ifdef _PATH_SUDO_PLUGIN_DIR
const char *
sudo_conf_plugin_dir_path(void)
{
return sudo_conf_data.paths[SUDO_CONF_PLUGIN_IDX].pval;
}
#endif
const char *
sudo_conf_debug_flags(void)
{
return sudo_conf_data.debug_flags;
}
int
sudo_conf_group_source(void)
{
return sudo_conf_data.group_source;
}
int
sudo_conf_max_groups(void)
{
return sudo_conf_data.max_groups;
}
struct plugin_info_list *
sudo_conf_plugins(void)
{
return &sudo_conf_data.plugins;
}
bool
sudo_conf_disable_coredump(void)
{
return sudo_conf_data.disable_coredump;
}
bool
sudo_conf_probe_interfaces(void)
{
return sudo_conf_data.probe_interfaces;
}
/*
* Reads in /etc/sudo.conf and populates sudo_conf_data.
*/
void
sudo_conf_read(const char *conf_file)
{
struct sudo_conf_table *cur;
struct stat sb;
FILE *fp;
char *cp, *line = NULL;
char *prev_locale = estrdup(setlocale(LC_ALL, NULL));
size_t linesize = 0;
/* Parse sudo.conf in the "C" locale. */
if (prev_locale[0] != 'C' || prev_locale[1] != '\0')
setlocale(LC_ALL, "C");
if (conf_file == NULL) {
conf_file = _PATH_SUDO_CONF;
switch (sudo_secure_file(conf_file, ROOT_UID, -1, &sb)) {
case SUDO_PATH_SECURE:
break;
case SUDO_PATH_MISSING:
/* Root should always be able to read sudo.conf. */
if (errno != ENOENT && geteuid() == ROOT_UID)
warning(U_("unable to stat %s"), conf_file);
goto done;
case SUDO_PATH_BAD_TYPE:
warningx(U_("%s is not a regular file"), conf_file);
goto done;
case SUDO_PATH_WRONG_OWNER:
warningx(U_("%s is owned by uid %u, should be %u"),
conf_file, (unsigned int) sb.st_uid, ROOT_UID);
goto done;
case SUDO_PATH_WORLD_WRITABLE:
warningx(U_("%s is world writable"), conf_file);
goto done;
case SUDO_PATH_GROUP_WRITABLE:
warningx(U_("%s is group writable"), conf_file);
goto done;
default:
/* NOTREACHED */
goto done;
}
}
if ((fp = fopen(conf_file, "r")) == NULL) {
if (errno != ENOENT && geteuid() == ROOT_UID)
warning(U_("unable to open %s"), conf_file);
goto done;
}
conf_lineno = 0;
while (sudo_parseln(&line, &linesize, &conf_lineno, fp) != -1) {
if (*(cp = line) == '\0')
continue; /* empty line or comment */
for (cur = sudo_conf_table; cur->name != NULL; cur++) {
if (strncasecmp(cp, cur->name, cur->namelen) == 0 &&
isblank((unsigned char)cp[cur->namelen])) {
cp += cur->namelen;
while (isblank((unsigned char)*cp))
cp++;
cur->setter(cp, conf_file);
break;
}
}
}
fclose(fp);
free(line);
done:
/* Restore locale if needed. */
if (prev_locale[0] != 'C' || prev_locale[1] != '\0')
setlocale(LC_ALL, prev_locale);
efree(prev_locale);
}

589
lib/util/sudo_debug.c Normal file
View File

@@ -0,0 +1,589 @@
/*
* Copyright (c) 2011-2013 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/stat.h>
#include <sys/uio.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_STDBOOL_H
# include <stdbool.h>
#else
# include "compat/stdbool.h"
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#define DEFAULT_TEXT_DOMAIN "sudo"
#include "gettext.h" /* must be included before missing.h */
#include "missing.h"
#include "alloc.h"
#include "fatal.h"
#include "sudo_plugin.h"
#include "sudo_debug.h"
#include "sudo_util.h"
/*
* The debug priorities and subsystems are currently hard-coded.
* In the future we might consider allowing plugins to register their
* own subsystems and provide direct access to the debugging API.
*/
/* Note: this must match the order in sudo_debug.h */
const char *const sudo_debug_priorities[] = {
"crit",
"err",
"warn",
"notice",
"diag",
"info",
"trace",
"debug",
NULL
};
/* Note: this must match the order in sudo_debug.h */
const char *const sudo_debug_subsystems[] = {
"main",
"args",
"exec",
"pty",
"utmp",
"conv",
"pcomm",
"util",
"netif",
"audit",
"edit",
"selinux",
"ldap",
"match",
"parser",
"alias",
"defaults",
"auth",
"env",
"logging",
"nss",
"rbtree",
"perms",
"plugin",
"hooks",
"sssd",
"event",
NULL
};
#define NUM_SUBSYSTEMS (sizeof(sudo_debug_subsystems) / sizeof(sudo_debug_subsystems[0]) - 1)
/* Values for sudo_debug_mode */
#define SUDO_DEBUG_MODE_DISABLED 0
#define SUDO_DEBUG_MODE_FILE 1
#define SUDO_DEBUG_MODE_CONV 2
static int sudo_debug_settings[NUM_SUBSYSTEMS];
static int sudo_debug_fd = -1;
static int sudo_debug_mode;
static char sudo_debug_pidstr[(((sizeof(int) * 8) + 2) / 3) + 3];
static size_t sudo_debug_pidlen;
static const int num_subsystems = NUM_SUBSYSTEMS;
/*
* Parse settings string from sudo.conf and open debugfile.
* Returns 1 on success, 0 if cannot open debugfile.
* Unsupported subsystems and priorities are silently ignored.
*/
int sudo_debug_init(const char *debugfile, const char *settings)
{
char *buf, *cp, *subsys, *pri;
int i, j;
/* Make sure we are not already initialized. */
if (sudo_debug_mode != SUDO_DEBUG_MODE_DISABLED)
return 1;
/* Init per-subsystems settings to -1 since 0 is a valid priority. */
for (i = 0; i < num_subsystems; i++)
sudo_debug_settings[i] = -1;
/* Open debug file if specified. */
if (debugfile != NULL) {
if (sudo_debug_fd != -1)
close(sudo_debug_fd);
sudo_debug_fd = open(debugfile, O_WRONLY|O_APPEND, S_IRUSR|S_IWUSR);
if (sudo_debug_fd == -1) {
/* Create debug file as needed and set group ownership. */
if (errno == ENOENT) {
sudo_debug_fd = open(debugfile, O_WRONLY|O_APPEND|O_CREAT,
S_IRUSR|S_IWUSR);
}
if (sudo_debug_fd == -1)
return 0;
ignore_result(fchown(sudo_debug_fd, (uid_t)-1, 0));
}
(void)fcntl(sudo_debug_fd, F_SETFD, FD_CLOEXEC);
sudo_debug_mode = SUDO_DEBUG_MODE_FILE;
} else {
/* Called from the plugin, no debug file. */
sudo_debug_mode = SUDO_DEBUG_MODE_CONV;
}
/* Stash the pid string so we only have to format it once. */
(void)snprintf(sudo_debug_pidstr, sizeof(sudo_debug_pidstr), "[%d] ",
(int)getpid());
sudo_debug_pidlen = strlen(sudo_debug_pidstr);
/* Parse settings string. */
if ((buf = strdup(settings)) == NULL)
return 0;
for ((cp = strtok(buf, ",")); cp != NULL; (cp = strtok(NULL, ","))) {
/* Should be in the form subsys@pri. */
subsys = cp;
if ((pri = strchr(cp, '@')) == NULL)
continue;
*pri++ = '\0';
/* Look up priority and subsystem, fill in sudo_debug_settings[]. */
for (i = 0; sudo_debug_priorities[i] != NULL; i++) {
if (strcasecmp(pri, sudo_debug_priorities[i]) == 0) {
for (j = 0; sudo_debug_subsystems[j] != NULL; j++) {
if (strcasecmp(subsys, "all") == 0) {
sudo_debug_settings[j] = i;
continue;
}
if (strcasecmp(subsys, sudo_debug_subsystems[j]) == 0) {
sudo_debug_settings[j] = i;
break;
}
}
break;
}
}
}
free(buf);
return 1;
}
pid_t
sudo_debug_fork(void)
{
pid_t pid;
if ((pid = fork()) == 0) {
(void)snprintf(sudo_debug_pidstr, sizeof(sudo_debug_pidstr), "[%d] ",
(int)getpid());
sudo_debug_pidlen = strlen(sudo_debug_pidstr);
}
return pid;
}
void
sudo_debug_enter(const char *func, const char *file, int line,
int subsys)
{
sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
"-> %s @ %s:%d", func, file, line);
}
void
sudo_debug_exit(const char *func, const char *file, int line,
int subsys)
{
sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
"<- %s @ %s:%d", func, file, line);
}
void
sudo_debug_exit_int(const char *func, const char *file, int line,
int subsys, int rval)
{
sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
"<- %s @ %s:%d := %d", func, file, line, rval);
}
void
sudo_debug_exit_long(const char *func, const char *file, int line,
int subsys, long rval)
{
sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
"<- %s @ %s:%d := %ld", func, file, line, rval);
}
void
sudo_debug_exit_size_t(const char *func, const char *file, int line,
int subsys, size_t rval)
{
/* XXX - should use %zu but our snprintf.c doesn't support it */
sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
"<- %s @ %s:%d := %lu", func, file, line, (unsigned long)rval);
}
/* We use int, not bool, here for functions that return -1 on error. */
void
sudo_debug_exit_bool(const char *func, const char *file, int line,
int subsys, int rval)
{
if (rval == true || rval == false) {
sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
"<- %s @ %s:%d := %s", func, file, line, rval ? "true" : "false");
} else {
sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
"<- %s @ %s:%d := %d", func, file, line, rval);
}
}
void
sudo_debug_exit_str(const char *func, const char *file, int line,
int subsys, const char *rval)
{
sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
"<- %s @ %s:%d := %s", func, file, line, rval ? rval : "(null)");
}
void
sudo_debug_exit_str_masked(const char *func, const char *file, int line,
int subsys, const char *rval)
{
static const char stars[] = "********************************************************************************";
int len = rval ? strlen(rval) : sizeof("(null)") - 1;
sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
"<- %s @ %s:%d := %.*s", func, file, line, len, rval ? stars : "(null)");
}
void
sudo_debug_exit_ptr(const char *func, const char *file, int line,
int subsys, const void *rval)
{
sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
"<- %s @ %s:%d := %p", func, file, line, rval);
}
static void
sudo_debug_write_conv(const char *func, const char *file, int lineno,
const char *str, int len, int errno_val)
{
/* Remove trailing newlines. */
while (len > 0 && str[len - 1] == '\n')
len--;
if (len > 0) {
if (func != NULL && file != NULL) {
if (errno_val) {
sudo_printf(SUDO_CONV_DEBUG_MSG, "%.*s: %s @ %s() %s:%d",
len, str, strerror(errno_val), func, file, lineno);
} else {
sudo_printf(SUDO_CONV_DEBUG_MSG, "%.*s @ %s() %s:%d",
len, str, func, file, lineno);
}
} else {
if (errno_val) {
sudo_printf(SUDO_CONV_DEBUG_MSG, "%.*s: %s",
len, str, strerror(errno_val));
} else {
sudo_printf(SUDO_CONV_DEBUG_MSG, "%.*s", len, str);
}
}
} else if (errno_val) {
/* Only print error string. */
if (func != NULL && file != NULL) {
sudo_printf(SUDO_CONV_DEBUG_MSG, "%s @ %s() %s:%d",
strerror(errno_val), func, file, lineno);
} else {
sudo_printf(SUDO_CONV_DEBUG_MSG, "%s", strerror(errno_val));
}
}
}
static void
sudo_debug_write_file(const char *func, const char *file, int lineno,
const char *str, int len, int errno_val)
{
char *timestr, numbuf[(((sizeof(int) * 8) + 2) / 3) + 2];
time_t now;
struct iovec iov[12];
int iovcnt = 3;
/* Prepend program name and pid with a trailing space. */
iov[1].iov_base = (char *)getprogname();
iov[1].iov_len = strlen(iov[1].iov_base);
iov[2].iov_base = sudo_debug_pidstr;
iov[2].iov_len = sudo_debug_pidlen;
/* Add string, trimming any trailing newlines. */
while (len > 0 && str[len - 1] == '\n')
len--;
if (len > 0) {
iov[iovcnt].iov_base = (char *)str;
iov[iovcnt].iov_len = len;
iovcnt++;
}
/* Append error string if errno is specified. */
if (errno_val) {
if (len > 0) {
iov[iovcnt].iov_base = ": ";
iov[iovcnt].iov_len = 2;
iovcnt++;
}
iov[iovcnt].iov_base = strerror(errno_val);
iov[iovcnt].iov_len = strlen(iov[iovcnt].iov_base);
iovcnt++;
}
/* If function, file and lineno are specified, append them. */
if (func != NULL && file != NULL && lineno != 0) {
iov[iovcnt].iov_base = " @ ";
iov[iovcnt].iov_len = 3;
iovcnt++;
iov[iovcnt].iov_base = (char *)func;
iov[iovcnt].iov_len = strlen(func);
iovcnt++;
iov[iovcnt].iov_base = "() ";
iov[iovcnt].iov_len = 3;
iovcnt++;
iov[iovcnt].iov_base = (char *)file;
iov[iovcnt].iov_len = strlen(file);
iovcnt++;
(void)snprintf(numbuf, sizeof(numbuf), ":%d", lineno);
iov[iovcnt].iov_base = numbuf;
iov[iovcnt].iov_len = strlen(numbuf);
iovcnt++;
}
/* Append newline. */
iov[iovcnt].iov_base = "\n";
iov[iovcnt].iov_len = 1;
iovcnt++;
/* Do timestamp last due to ctime's static buffer. */
time(&now);
timestr = ctime(&now) + 4;
timestr[15] = ' '; /* replace year with a space */
timestr[16] = '\0';
iov[0].iov_base = timestr;
iov[0].iov_len = 16;
/* Write message in a single syscall */
ignore_result(writev(sudo_debug_fd, iov, iovcnt));
}
void
sudo_debug_write2(const char *func, const char *file, int lineno,
const char *str, int len, int errno_val)
{
switch (sudo_debug_mode) {
case SUDO_DEBUG_MODE_CONV:
sudo_debug_write_conv(func, file, lineno, str, len, errno_val);
break;
case SUDO_DEBUG_MODE_FILE:
sudo_debug_write_file(func, file, lineno, str, len, errno_val);
break;
}
}
/* XXX - turn into a macro */
void
sudo_debug_write(const char *str, int len, int errno_val)
{
sudo_debug_write2(NULL, NULL, 0, str, len, errno_val);
}
void
sudo_debug_vprintf2(const char *func, const char *file, int lineno, int level,
const char *fmt, va_list ap)
{
int buflen, pri, subsys, saved_errno = errno;
char *buf = NULL;
if (!sudo_debug_mode)
return;
/* Extract pri and subsystem from level. */
pri = SUDO_DEBUG_PRI(level);
subsys = SUDO_DEBUG_SUBSYS(level);
/* Make sure we want debug info at this level. */
if (subsys < num_subsystems && sudo_debug_settings[subsys] >= pri) {
buflen = fmt ? vasprintf(&buf, fmt, ap) : 0;
if (buflen != -1) {
int errcode = ISSET(level, SUDO_DEBUG_ERRNO) ? saved_errno : 0;
if (ISSET(level, SUDO_DEBUG_LINENO))
sudo_debug_write2(func, file, lineno, buf, buflen, errcode);
else
sudo_debug_write2(NULL, NULL, 0, buf, buflen, errcode);
free(buf);
}
}
errno = saved_errno;
}
#ifdef NO_VARIADIC_MACROS
void
sudo_debug_printf_nvm(int pri, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
sudo_debug_vprintf2(NULL, NULL, 0, pri, fmt, ap);
va_end(ap);
}
#endif /* NO_VARIADIC_MACROS */
void
sudo_debug_printf2(const char *func, const char *file, int lineno, int level,
const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
sudo_debug_vprintf2(func, file, lineno, level, fmt, ap);
va_end(ap);
}
void
sudo_debug_execve2(int level, const char *path, char *const argv[], char *const envp[])
{
char * const *av;
char *buf, *cp;
int buflen, pri, subsys, log_envp = 0;
size_t plen;
if (!sudo_debug_mode)
return;
/* Extract pri and subsystem from level. */
pri = SUDO_DEBUG_PRI(level);
subsys = SUDO_DEBUG_SUBSYS(level);
/* Make sure we want debug info at this level. */
if (subsys >= num_subsystems || sudo_debug_settings[subsys] < pri)
return;
/* Log envp for debug level "debug". */
if (sudo_debug_settings[subsys] >= SUDO_DEBUG_DEBUG - 1 && envp[0] != NULL)
log_envp = 1;
#define EXEC_PREFIX "exec "
/* Alloc and build up buffer. */
plen = strlen(path);
buflen = sizeof(EXEC_PREFIX) -1 + plen;
if (argv[0] != NULL) {
buflen += sizeof(" []") - 1;
for (av = argv; *av; av++)
buflen += strlen(*av) + 1;
buflen--;
}
if (log_envp) {
buflen += sizeof(" []") - 1;
for (av = envp; *av; av++)
buflen += strlen(*av) + 1;
buflen--;
}
buf = malloc(buflen + 1);
if (buf == NULL)
return;
/* Copy prefix and command. */
memcpy(buf, EXEC_PREFIX, sizeof(EXEC_PREFIX) - 1);
cp = buf + sizeof(EXEC_PREFIX) - 1;
memcpy(cp, path, plen);
cp += plen;
/* Copy argv. */
if (argv[0] != NULL) {
*cp++ = ' ';
*cp++ = '[';
for (av = argv; *av; av++) {
size_t avlen = strlen(*av);
memcpy(cp, *av, avlen);
cp += avlen;
*cp++ = ' ';
}
cp[-1] = ']';
}
if (log_envp) {
*cp++ = ' ';
*cp++ = '[';
for (av = envp; *av; av++) {
size_t avlen = strlen(*av);
memcpy(cp, *av, avlen);
cp += avlen;
*cp++ = ' ';
}
cp[-1] = ']';
}
*cp = '\0';
sudo_debug_write(buf, buflen, 0);
free(buf);
}
/*
* Getter for the debug descriptor.
*/
int
sudo_debug_fd_get(void)
{
return sudo_debug_fd;
}
/*
* Setter for the debug descriptor.
*/
int
sudo_debug_fd_set(int fd)
{
if (sudo_debug_fd != -1 && fd != sudo_debug_fd) {
if (dup2(sudo_debug_fd, fd) == -1)
return -1;
(void)fcntl(fd, F_SETFD, FD_CLOEXEC);
close(sudo_debug_fd);
sudo_debug_fd = fd;
}
return sudo_debug_fd;
}

327
lib/util/sudo_dso.c Normal file
View File

@@ -0,0 +1,327 @@
/*
* Copyright (c) 2010, 2012-2014 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 */
#ifdef HAVE_STRING_H
# include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#if defined(HAVE_SHL_LOAD)
# include <dl.h>
#elif defined(HAVE_DLOPEN)
# include <dlfcn.h>
#endif
#include <errno.h>
#include "sudo_dso.h"
#include "missing.h"
/*
* Pointer for statically compiled symbols.
*/
static struct sudo_preload_table *preload_table;
void
sudo_dso_preload_table(struct sudo_preload_table *table)
{
preload_table = table;
}
#if defined(HAVE_SHL_LOAD)
# ifndef DYNAMIC_PATH
# define DYNAMIC_PATH 0
# endif
void *
sudo_dso_load(const char *path, int mode)
{
struct sudo_preload_table *pt;
int flags = DYNAMIC_PATH | BIND_VERBOSE;
if (mode == 0)
mode = SUDO_DSO_LAZY; /* default behavior */
/* Check prelinked symbols first. */
if (preload_table != NULL) {
for (pt = preload_table; pt->handle != NULL; pt++) {
if (pt->path != NULL && strcmp(path, pt->path) == 0)
return pt->handle;
}
}
/* We don't support SUDO_DSO_GLOBAL or SUDO_DSO_LOCAL yet. */
if (ISSET(mode, SUDO_DSO_LAZY))
flags |= BIND_DEFERRED;
if (ISSET(mode, SUDO_DSO_NOW))
flags |= BIND_IMMEDIATE;
return (void *)shl_load(path, flags, 0L);
}
int
sudo_dso_unload(void *handle)
{
struct sudo_preload_table *pt;
/* Check prelinked symbols first. */
if (preload_table != NULL) {
for (pt = preload_table; pt->handle != NULL; pt++) {
if (pt->handle == handle)
return 0;
}
}
return shl_unload((shl_t)handle);
}
void *
sudo_dso_findsym(void *vhandle, const char *symbol)
{
struct sudo_preload_table *pt;
shl_t handle = vhandle;
void *value = NULL;
/* Check prelinked symbols first. */
if (preload_table != NULL) {
for (pt = preload_table; pt->handle != NULL; pt++) {
if (pt->handle == handle) {
struct sudo_preload_symbol *sym;
for (sym = pt->symbols; sym->name != NULL; sym++) {
if (strcmp(sym->name, symbol) == 0)
return sym->addr;
}
errno = ENOENT;
return NULL;
}
}
}
/*
* Note that the behavior of of SUDO_DSO_NEXT and SUDO_DSO_SELF
* differs from most implementations when called from
* a shared library.
*/
if (vhandle == SUDO_DSO_NEXT) {
/* Iterate over all shared libs looking for symbol. */
shl_t myhandle = PROG_HANDLE;
struct shl_descriptor *desc;
int idx = 0;
/* Find program's real handle. */
if (shl_gethandle(PROG_HANDLE, &desc) == 0)
myhandle = desc->handle;
while (shl_get(idx++, &desc) == 0) {
if (desc->handle == myhandle)
continue;
if (shl_findsym(&desc->handle, symbol, TYPE_UNDEFINED, &value) == 0)
break;
}
} else {
if (vhandle == SUDO_DSO_DEFAULT)
handle = NULL;
else if (vhandle == SUDO_DSO_SELF)
handle = PROG_HANDLE;
(void)shl_findsym(&handle, symbol, TYPE_UNDEFINED, &value);
}
return value;
}
char *
sudo_dso_strerror(void)
{
return strerror(errno);
}
#elif defined(HAVE_DLOPEN)
# ifndef RTLD_GLOBAL
# define RTLD_GLOBAL 0
# endif
void *
sudo_dso_load(const char *path, int mode)
{
struct sudo_preload_table *pt;
int flags = 0;
/* Check prelinked symbols first. */
if (preload_table != NULL) {
for (pt = preload_table; pt->handle != NULL; pt++) {
if (pt->path != NULL && strcmp(path, pt->path) == 0)
return pt->handle;
}
}
/* Map SUDO_DSO_* -> RTLD_* */
if (ISSET(mode, SUDO_DSO_LAZY))
flags |= RTLD_LAZY;
if (ISSET(mode, SUDO_DSO_NOW))
flags |= RTLD_NOW;
if (ISSET(mode, SUDO_DSO_GLOBAL))
flags |= RTLD_GLOBAL;
if (ISSET(mode, SUDO_DSO_LOCAL))
flags |= RTLD_LOCAL;
return dlopen(path, flags);
}
int
sudo_dso_unload(void *handle)
{
struct sudo_preload_table *pt;
/* Check prelinked symbols first. */
if (preload_table != NULL) {
for (pt = preload_table; pt->handle != NULL; pt++) {
if (pt->handle == handle)
return 0;
}
}
return dlclose(handle);
}
void *
sudo_dso_findsym(void *handle, const char *symbol)
{
struct sudo_preload_table *pt;
/* Check prelinked symbols first. */
if (preload_table != NULL) {
for (pt = preload_table; pt->handle != NULL; pt++) {
if (pt->handle == handle) {
struct sudo_preload_symbol *sym;
for (sym = pt->symbols; sym->name != NULL; sym++) {
if (strcmp(sym->name, symbol) == 0)
return sym->addr;
}
errno = ENOENT;
return NULL;
}
}
}
/*
* Not all implementations support the special handles.
*/
if (handle == SUDO_DSO_NEXT) {
# ifdef RTLD_NEXT
handle = RTLD_NEXT;
# else
errno = ENOENT;
return NULL;
# endif
} else if (handle == SUDO_DSO_DEFAULT) {
# ifdef RTLD_DEFAULT
handle = RTLD_DEFAULT;
# else
errno = ENOENT;
return NULL;
# endif
} else if (handle == SUDO_DSO_SELF) {
# ifdef RTLD_SELF
handle = RTLD_SELF;
# else
errno = ENOENT;
return NULL;
# endif
}
return dlsym(handle, symbol);
}
char *
sudo_dso_strerror(void)
{
return dlerror();
}
#else /* !HAVE_SHL_LOAD && !HAVE_DLOPEN */
/*
* Emulate dlopen() using a static list of symbols compiled into sudo.
*/
void *
sudo_dso_load(const char *path, int mode)
{
struct sudo_preload_table *pt;
/* Check prelinked symbols first. */
if (preload_table != NULL) {
for (pt = preload_table; pt->handle != NULL; pt++) {
if (pt->path != NULL && strcmp(path, pt->path) == 0)
return pt->handle;
}
}
return NULL;
}
int
sudo_dso_unload(void *handle)
{
struct sudo_preload_table *pt;
if (preload_table != NULL) {
for (pt = preload_table; pt->handle != NULL; pt++) {
if (pt->handle == handle)
return 0;
}
}
return -1;
}
void *
sudo_dso_findsym(void *handle, const char *symbol)
{
struct sudo_preload_table *pt;
if (preload_table != NULL) {
for (pt = preload_table; pt->handle != NULL; pt++) {
if (pt->handle == handle) {
struct sudo_preload_symbol *sym;
for (sym = pt->symbols; sym->name != NULL; sym++) {
if (strcmp(sym->name, symbol) == 0)
return sym->addr;
}
}
}
}
errno = ENOENT;
return NULL;
}
char *
sudo_dso_strerror(void)
{
return strerror(errno);
}
#endif /* !HAVE_SHL_LOAD && !HAVE_DLOPEN */

70
lib/util/sudo_printf.c Normal file
View File

@@ -0,0 +1,70 @@
/*
* Copyright (c) 2010-2012 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 <stdarg.h>
#include <errno.h>
#include "missing.h"
#include "sudo_plugin.h"
#include "sudo_debug.h"
int
_sudo_printf(int msg_type, const char *fmt, ...)
{
va_list ap;
char *buf;
int len = -1;
switch (msg_type) {
case SUDO_CONV_INFO_MSG:
va_start(ap, fmt);
len = vfprintf(stdout, fmt, ap);
va_end(ap);
break;
case SUDO_CONV_ERROR_MSG:
va_start(ap, fmt);
len = vfprintf(stderr, fmt, ap);
va_end(ap);
break;
case SUDO_CONV_DEBUG_MSG:
/* XXX - add debug version of vfprintf()? */
va_start(ap, fmt);
len = vasprintf(&buf, fmt, ap);
va_end(ap);
if (len != -1)
sudo_debug_write(buf, len, 0);
break;
default:
errno = EINVAL;
break;
}
return len;
}
sudo_printf_t sudo_printf = _sudo_printf;

253
lib/util/term.c Normal file
View File

@@ -0,0 +1,253 @@
/*
* Copyright (c) 2011-2014 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 */
#ifdef HAVE_STRING_H
# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
# include <memory.h>
# endif
# include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#include <errno.h>
#include <signal.h>
#include <termios.h>
#include <unistd.h>
#include "missing.h"
#include "sudo_debug.h"
#include "sudo_util.h"
#ifndef TCSASOFT
# define TCSASOFT 0
#endif
#ifndef ECHONL
# define ECHONL 0
#endif
#ifndef IEXTEN
# define IEXTEN 0
#endif
#ifndef IUCLC
# define IUCLC 0
#endif
#ifndef _POSIX_VDISABLE
# ifdef VDISABLE
# define _POSIX_VDISABLE VDISABLE
# else
# define _POSIX_VDISABLE 0
# endif
#endif
static struct termios term, oterm;
static int changed;
/* tgetpass() needs to know the erase and kill chars for cbreak mode. */
int term_erase;
int term_kill;
static volatile sig_atomic_t got_sigttou;
/*
* SIGTTOU signal handler for term_restore that just sets a flag.
*/
static void sigttou(int signo)
{
got_sigttou = 1;
}
/*
* Like tcsetattr() but restarts on EINTR _except_ for SIGTTOU.
* Returns 0 on success or -1 on failure, setting errno.
* Sets got_sigttou on failure if interrupted by SIGTTOU.
*/
static int
tcsetattr_nobg(int fd, int flags, struct termios *tp)
{
sigaction_t sa, osa;
int rc;
/*
* If we receive SIGTTOU from tcsetattr() it means we are
* not in the foreground process group.
* This should be less racy than using tcgetpgrp().
*/
memset(&sa, 0, sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_handler = sigttou;
got_sigttou = 0;
sigaction(SIGTTOU, &sa, &osa);
do {
rc = tcsetattr(fd, flags, tp);
} while (rc != 0 && errno == EINTR && !got_sigttou);
sigaction(SIGTTOU, &osa, NULL);
return rc;
}
/*
* Restore saved terminal settings if we are in the foreground process group.
* Returns true on success or false on failure.
*/
bool
term_restore(int fd, bool flush)
{
debug_decl(term_restore, SUDO_DEBUG_UTIL)
if (changed) {
const int flags = flush ? (TCSASOFT|TCSAFLUSH) : (TCSASOFT|TCSADRAIN);
if (tcsetattr_nobg(fd, flags, &oterm) != 0)
debug_return_bool(false);
changed = 0;
}
debug_return_bool(true);
}
/*
* Disable terminal echo.
* Returns true on success or false on failure.
*/
bool
term_noecho(int fd)
{
debug_decl(term_noecho, SUDO_DEBUG_UTIL)
again:
if (!changed && tcgetattr(fd, &oterm) != 0)
debug_return_bool(false);
(void) memcpy(&term, &oterm, sizeof(term));
CLR(term.c_lflag, ECHO|ECHONL);
#ifdef VSTATUS
term.c_cc[VSTATUS] = _POSIX_VDISABLE;
#endif
if (tcsetattr_nobg(fd, TCSADRAIN|TCSASOFT, &term) == 0) {
changed = 1;
debug_return_bool(true);
}
if (got_sigttou) {
/* We were in the background, so oterm is probably bogus. */
kill(getpid(), SIGTTOU);
goto again;
}
debug_return_bool(false);
}
/*
* Set terminal to raw mode.
* Returns true on success or false on failure.
*/
bool
term_raw(int fd, int isig)
{
struct termios term;
debug_decl(term_raw, SUDO_DEBUG_UTIL)
again:
if (!changed && tcgetattr(fd, &oterm) != 0)
return 0;
(void) memcpy(&term, &oterm, sizeof(term));
/* Set terminal to raw mode */
term.c_cc[VMIN] = 1;
term.c_cc[VTIME] = 0;
CLR(term.c_iflag, ICRNL | IGNCR | INLCR | IUCLC | IXON);
CLR(term.c_oflag, OPOST);
CLR(term.c_lflag, ECHO | ICANON | ISIG | IEXTEN);
if (isig)
SET(term.c_lflag, ISIG);
if (tcsetattr_nobg(fd, TCSADRAIN|TCSASOFT, &term) == 0) {
changed = 1;
debug_return_bool(true);
}
if (got_sigttou) {
/* We were in the background, so oterm is probably bogus. */
kill(getpid(), SIGTTOU);
goto again;
}
debug_return_bool(false);
}
/*
* Set terminal to cbreak mode.
* Returns true on success or false on failure.
*/
bool
term_cbreak(int fd)
{
debug_decl(term_cbreak, SUDO_DEBUG_UTIL)
again:
if (!changed && tcgetattr(fd, &oterm) != 0)
return 0;
(void) memcpy(&term, &oterm, sizeof(term));
/* Set terminal to half-cooked mode */
term.c_cc[VMIN] = 1;
term.c_cc[VTIME] = 0;
/* cppcheck-suppress redundantAssignment */
CLR(term.c_lflag, ECHO | ECHONL | ICANON | IEXTEN);
/* cppcheck-suppress redundantAssignment */
SET(term.c_lflag, ISIG);
#ifdef VSTATUS
term.c_cc[VSTATUS] = _POSIX_VDISABLE;
#endif
if (tcsetattr_nobg(fd, TCSADRAIN|TCSASOFT, &term) == 0) {
term_erase = term.c_cc[VERASE];
term_kill = term.c_cc[VKILL];
changed = 1;
debug_return_bool(true);
}
if (got_sigttou) {
/* We were in the background, so oterm is probably bogus. */
kill(getpid(), SIGTTOU);
goto again;
}
debug_return_bool(false);
}
/*
* Copy terminal settings from one descriptor to another.
* Returns true on success or false on failure.
*/
bool
term_copy(int src, int dst)
{
struct termios tt;
debug_decl(term_copy, SUDO_DEBUG_UTIL)
again:
if (tcgetattr(src, &tt) != 0)
debug_return_bool(false);
if (tcsetattr_nobg(dst, TCSANOW|TCSASOFT, &tt) == 0)
debug_return_bool(true);
if (got_sigttou) {
/* We were in the background, so oterm is probably bogus. */
kill(getpid(), SIGTTOU);
goto again;
}
debug_return_bool(false);
}

93
lib/util/ttysize.c Normal file
View File

@@ -0,0 +1,93 @@
/*
* Copyright (c) 2010-2012 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/ioctl.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_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <termios.h>
#include <limits.h>
#include "missing.h"
#include "sudo_debug.h"
#include "sudo_util.h"
/* Compatibility with older tty systems. */
#if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
# define TIOCGWINSZ TIOCGSIZE
# define winsize ttysize
# define ws_col ts_cols
# define ws_row ts_lines
#endif
#ifdef TIOCGWINSZ
static int
get_ttysize_ioctl(int *rowp, int *colp)
{
struct winsize wsize;
debug_decl(get_ttysize_ioctl, SUDO_DEBUG_EXEC)
if (ioctl(STDERR_FILENO, TIOCGWINSZ, &wsize) == 0 &&
wsize.ws_row != 0 && wsize.ws_col != 0) {
*rowp = wsize.ws_row;
*colp = wsize.ws_col;
debug_return_int(0);
}
debug_return_int(-1);
}
#else
static int
get_ttysize_ioctl(int *rowp, int *colp)
{
return -1;
}
#endif /* TIOCGWINSZ */
void
get_ttysize(int *rowp, int *colp)
{
debug_decl(fork_cmnd, SUDO_DEBUG_EXEC)
if (get_ttysize_ioctl(rowp, colp) == -1) {
char *p;
/* Fall back on $LINES and $COLUMNS. */
if ((p = getenv("LINES")) == NULL ||
(*rowp = strtonum(p, 1, INT_MAX, NULL)) <= 0) {
*rowp = 24;
}
if ((p = getenv("COLUMNS")) == NULL ||
(*colp = strtonum(p, 1, INT_MAX, NULL)) <= 0) {
*colp = 80;
}
}
debug_return;
}

73
lib/util/utimes.c Normal file
View File

@@ -0,0 +1,73 @@
/*
* Copyright (c) 2004-2005, 2007, 2010-2011, 2013
* 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>
#if !defined(HAVE_UTIMES) || (!defined(HAVE_FUTIMES) && !defined(HAVE_FUTIMESAT))
#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#ifdef TIME_WITH_SYS_TIME
# include <time.h>
#endif
#ifdef HAVE_UTIME_H
# include <utime.h>
#else
# include "compat/utime.h"
#endif
#include "missing.h"
#ifndef HAVE_UTIMES
/*
* Emulate utimes() via utime()
*/
int
utimes(const char *file, const struct timeval *times)
{
if (times != NULL) {
struct utimbuf utb;
utb.actime = (time_t)(times[0].tv_sec + times[0].tv_usec / 1000000);
utb.modtime = (time_t)(times[1].tv_sec + times[1].tv_usec / 1000000);
return utime(file, &utb);
} else
return utime(file, NULL);
}
#endif /* !HAVE_UTIMES */
#ifdef HAVE_FUTIME
/*
* Emulate futimes() via futime()
*/
int
futimes(int fd, const struct timeval *times)
{
if (times != NULL) {
struct utimbuf utb;
utb.actime = (time_t)(times[0].tv_sec + times[0].tv_usec / 1000000);
utb.modtime = (time_t)(times[1].tv_sec + times[1].tv_usec / 1000000);
return futime(fd, &utb);
} else
return futime(fd, NULL);
}
#endif /* HAVE_FUTIME */
#endif /* !HAVE_UTIMES || (!HAVE_FUTIMES && !HAVE_FUTIMESAT) */