Top level directory reorg
Move src/po -> po Combine common and compat -> lib/util Move zlib -> lib/zlib
This commit is contained in:
433
lib/util/Makefile.in
Normal file
433
lib/util/Makefile.in
Normal 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
207
lib/util/aix.c
Normal 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, ®istry, 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
271
lib/util/alloc.c
Normal 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
84
lib/util/atobool.c
Normal 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
125
lib/util/atoid.c
Normal 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
68
lib/util/atomode.c
Normal 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
80
lib/util/clock_gettime.c
Normal 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
152
lib/util/closefrom.c
Normal 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
403
lib/util/event.c
Normal 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
183
lib/util/event_poll.c
Normal 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
212
lib/util/event_select.c
Normal 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
228
lib/util/fatal.c
Normal 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
234
lib/util/fileops.c
Normal 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
66
lib/util/fmt_string.c
Normal 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
477
lib/util/fnmatch.c
Normal 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
422
lib/util/getaddrinfo.c
Normal 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
267
lib/util/getcwd.c
Normal 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
362
lib/util/getgrouplist.c
Normal 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
107
lib/util/getline.c
Normal 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
630
lib/util/getopt_long.c
Normal 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
89
lib/util/gidlist.c
Normal 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
904
lib/util/glob.c
Normal 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
258
lib/util/inet_pton.c
Normal 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
32
lib/util/isblank.c
Normal 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
275
lib/util/lbuf.c
Normal 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
44
lib/util/memrchr.c
Normal 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
71
lib/util/memset_s.c
Normal 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
59
lib/util/mksiglist.c
Normal 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
59
lib/util/mksigname.c
Normal 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
162
lib/util/mktemp.c
Normal 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
124
lib/util/progname.c
Normal 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
108
lib/util/pw_dup.c
Normal 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 */
|
186
lib/util/regress/atofoo/atofoo_test.c
Normal file
186
lib/util/regress/atofoo/atofoo_test.c
Normal 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);
|
||||
}
|
80
lib/util/regress/fnmatch/fnm_test.c
Normal file
80
lib/util/regress/fnmatch/fnm_test.c
Normal 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);
|
||||
}
|
5
lib/util/regress/fnmatch/fnm_test.in
Normal file
5
lib/util/regress/fnmatch/fnm_test.in
Normal 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
|
47
lib/util/regress/glob/files
Normal file
47
lib/util/regress/glob/files
Normal 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
|
212
lib/util/regress/glob/globtest.c
Normal file
212
lib/util/regress/glob/globtest.c
Normal 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;
|
||||
}
|
64
lib/util/regress/glob/globtest.in
Normal file
64
lib/util/regress/glob/globtest.in
Normal 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
|
100
lib/util/regress/sudo_conf/conf_test.c
Normal file
100
lib/util/regress/sudo_conf/conf_test.c
Normal 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');
|
||||
}
|
||||
}
|
72
lib/util/regress/sudo_conf/test1.in
Normal file
72
lib/util/regress/sudo_conf/test1.in
Normal 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
|
6
lib/util/regress/sudo_conf/test1.out.ok
Normal file
6
lib/util/regress/sudo_conf/test1.out.ok
Normal 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
|
0
lib/util/regress/sudo_conf/test2.in
Normal file
0
lib/util/regress/sudo_conf/test2.in
Normal file
3
lib/util/regress/sudo_conf/test2.out.ok
Normal file
3
lib/util/regress/sudo_conf/test2.out.ok
Normal file
@@ -0,0 +1,3 @@
|
||||
Set disable_coredump true
|
||||
Set group_source adaptive
|
||||
Set max_groups -1
|
2
lib/util/regress/sudo_conf/test3.in
Normal file
2
lib/util/regress/sudo_conf/test3.in
Normal 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
|
5
lib/util/regress/sudo_conf/test3.out.ok
Normal file
5
lib/util/regress/sudo_conf/test3.out.ok
Normal 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
|
1
lib/util/regress/sudo_conf/test4.in
Normal file
1
lib/util/regress/sudo_conf/test4.in
Normal file
@@ -0,0 +1 @@
|
||||
Set disable_coredump foo
|
3
lib/util/regress/sudo_conf/test4.out.ok
Normal file
3
lib/util/regress/sudo_conf/test4.out.ok
Normal file
@@ -0,0 +1,3 @@
|
||||
Set disable_coredump true
|
||||
Set group_source adaptive
|
||||
Set max_groups -1
|
1
lib/util/regress/sudo_conf/test5.err.ok
Normal file
1
lib/util/regress/sudo_conf/test5.err.ok
Normal file
@@ -0,0 +1 @@
|
||||
conf_test: invalid max groups `0' in regress/sudo_conf/test5.in, line 1
|
1
lib/util/regress/sudo_conf/test5.in
Normal file
1
lib/util/regress/sudo_conf/test5.in
Normal file
@@ -0,0 +1 @@
|
||||
Set max_groups 0
|
3
lib/util/regress/sudo_conf/test5.out.ok
Normal file
3
lib/util/regress/sudo_conf/test5.out.ok
Normal file
@@ -0,0 +1,3 @@
|
||||
Set disable_coredump true
|
||||
Set group_source adaptive
|
||||
Set max_groups -1
|
1
lib/util/regress/sudo_conf/test6.in
Normal file
1
lib/util/regress/sudo_conf/test6.in
Normal file
@@ -0,0 +1 @@
|
||||
Set max_groups 16
|
3
lib/util/regress/sudo_conf/test6.out.ok
Normal file
3
lib/util/regress/sudo_conf/test6.out.ok
Normal file
@@ -0,0 +1,3 @@
|
||||
Set disable_coredump true
|
||||
Set group_source adaptive
|
||||
Set max_groups 16
|
69
lib/util/regress/sudo_parseln/parseln_test.c
Normal file
69
lib/util/regress/sudo_parseln/parseln_test.c
Normal 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);
|
||||
}
|
72
lib/util/regress/sudo_parseln/test1.in
Normal file
72
lib/util/regress/sudo_parseln/test1.in
Normal 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
|
72
lib/util/regress/sudo_parseln/test1.out.ok
Normal file
72
lib/util/regress/sudo_parseln/test1.out.ok
Normal 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
|
8
lib/util/regress/sudo_parseln/test2.in
Normal file
8
lib/util/regress/sudo_parseln/test2.in
Normal file
@@ -0,0 +1,8 @@
|
||||
this \
|
||||
is all \
|
||||
one line
|
||||
# this is a comment, and does not get continued\
|
||||
trim the \
|
||||
leading \
|
||||
white \
|
||||
space
|
3
lib/util/regress/sudo_parseln/test2.out.ok
Normal file
3
lib/util/regress/sudo_parseln/test2.out.ok
Normal file
@@ -0,0 +1,3 @@
|
||||
3 this is all one line
|
||||
4
|
||||
8 trim the leading white space
|
1
lib/util/regress/sudo_parseln/test3.in
Normal file
1
lib/util/regress/sudo_parseln/test3.in
Normal file
@@ -0,0 +1 @@
|
||||
line continuation at EOF \
|
1
lib/util/regress/sudo_parseln/test3.out.ok
Normal file
1
lib/util/regress/sudo_parseln/test3.out.ok
Normal file
@@ -0,0 +1 @@
|
||||
1 line continuation at EOF
|
4
lib/util/regress/sudo_parseln/test4.in
Normal file
4
lib/util/regress/sudo_parseln/test4.in
Normal file
@@ -0,0 +1,4 @@
|
||||
line contin\
|
||||
uation raw
|
||||
line contin\
|
||||
uation indented
|
2
lib/util/regress/sudo_parseln/test4.out.ok
Normal file
2
lib/util/regress/sudo_parseln/test4.out.ok
Normal file
@@ -0,0 +1,2 @@
|
||||
2 line continuation raw
|
||||
4 line continuation indented
|
1
lib/util/regress/sudo_parseln/test5.in
Normal file
1
lib/util/regress/sudo_parseln/test5.in
Normal file
@@ -0,0 +1 @@
|
||||
\
|
0
lib/util/regress/sudo_parseln/test5.out.ok
Normal file
0
lib/util/regress/sudo_parseln/test5.out.ok
Normal file
3
lib/util/regress/sudo_parseln/test6.in
Normal file
3
lib/util/regress/sudo_parseln/test6.in
Normal file
@@ -0,0 +1,3 @@
|
||||
leading and trailing white space
|
||||
# a comment
|
||||
\
|
2
lib/util/regress/sudo_parseln/test6.out.ok
Normal file
2
lib/util/regress/sudo_parseln/test6.out.ok
Normal file
@@ -0,0 +1,2 @@
|
||||
1 leading and trailing white space
|
||||
2
|
208
lib/util/regress/tailq/hltq_test.c
Normal file
208
lib/util/regress/tailq/hltq_test.c
Normal 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
83
lib/util/secure_path.c
Normal 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
60
lib/util/setgroups.c
Normal 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
526
lib/util/sha2.c
Normal 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
80
lib/util/sig2str.c
Normal 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
56
lib/util/siglist.in
Normal 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
663
lib/util/snprintf.c
Normal 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
63
lib/util/strlcat.c
Normal 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
58
lib/util/strlcpy.c
Normal 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
52
lib/util/strsignal.c
Normal 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
200
lib/util/strtonum.c
Normal 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
459
lib/util/sudo_conf.c
Normal 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
589
lib/util/sudo_debug.c
Normal 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
327
lib/util/sudo_dso.c
Normal 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
70
lib/util/sudo_printf.c
Normal 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
253
lib/util/term.c
Normal 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
93
lib/util/ttysize.c
Normal 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
73
lib/util/utimes.c
Normal 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) */
|
Reference in New Issue
Block a user