diff options
Diffstat (limited to 'libmisc')
66 files changed, 14126 insertions, 0 deletions
diff --git a/libmisc/.indent.pro b/libmisc/.indent.pro new file mode 100644 index 00000000..fe572bb7 --- /dev/null +++ b/libmisc/.indent.pro @@ -0,0 +1,5 @@ +-kr +-i8 +-bad +-pcs +-l80 diff --git a/libmisc/Makefile.am b/libmisc/Makefile.am new file mode 100644 index 00000000..9da2ec3f --- /dev/null +++ b/libmisc/Makefile.am @@ -0,0 +1,68 @@ + +EXTRA_DIST = .indent.pro xgetXXbyYY.c + +INCLUDES = -I$(top_srcdir)/lib + +noinst_LIBRARIES = libmisc.a + +libmisc_a_SOURCES = \ + addgrps.c \ + age.c \ + audit_help.c \ + basename.c \ + chkname.c \ + chkname.h \ + chowndir.c \ + chowntty.c \ + cleanup.c \ + cleanup_group.c \ + cleanup_user.c \ + console.c \ + copydir.c \ + entry.c \ + env.c \ + failure.c \ + failure.h \ + find_new_gid.c \ + find_new_uid.c \ + getdate.h \ + getdate.y \ + getgr_nam_gid.c \ + getrange.c \ + hushed.c \ + isexpired.c \ + limits.c \ + list.c log.c \ + loginprompt.c \ + mail.c \ + motd.c \ + myname.c \ + obscure.c \ + pam_pass.c \ + pam_pass_non_interractive.c \ + pwd2spwd.c \ + pwdcheck.c \ + pwd_init.c \ + remove_tree.c \ + rlogin.c \ + root_flag.c \ + salt.c \ + setugid.c \ + setupenv.c \ + shell.c \ + strtoday.c \ + sub.c \ + sulog.c \ + ttytype.c \ + tz.c \ + ulimit.c \ + user_busy.c \ + utmp.c \ + valid.c \ + xgetpwnam.c \ + xgetpwuid.c \ + xgetgrnam.c \ + xgetgrgid.c \ + xgetspnam.c \ + xmalloc.c \ + yesno.c diff --git a/libmisc/Makefile.in b/libmisc/Makefile.in new file mode 100644 index 00000000..48913604 --- /dev/null +++ b/libmisc/Makefile.in @@ -0,0 +1,675 @@ +# Makefile.in generated by automake 1.11.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software +# Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__make_dryrun = \ + { \ + am__dry=no; \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + echo 'am--echo: ; @echo "AM" OK' | $(MAKE) -f - 2>/dev/null \ + | grep '^AM OK$$' >/dev/null || am__dry=yes;; \ + *) \ + for am__flg in $$MAKEFLAGS; do \ + case $$am__flg in \ + *=*|--*) ;; \ + *n*) am__dry=yes; break;; \ + esac; \ + done;; \ + esac; \ + test $$am__dry = yes; \ + } +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = libmisc +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in getdate.c +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LIBRARIES = $(noinst_LIBRARIES) +ARFLAGS = cru +libmisc_a_AR = $(AR) $(ARFLAGS) +libmisc_a_LIBADD = +am_libmisc_a_OBJECTS = addgrps.$(OBJEXT) age.$(OBJEXT) \ + audit_help.$(OBJEXT) basename.$(OBJEXT) chkname.$(OBJEXT) \ + chowndir.$(OBJEXT) chowntty.$(OBJEXT) cleanup.$(OBJEXT) \ + cleanup_group.$(OBJEXT) cleanup_user.$(OBJEXT) \ + console.$(OBJEXT) copydir.$(OBJEXT) entry.$(OBJEXT) \ + env.$(OBJEXT) failure.$(OBJEXT) find_new_gid.$(OBJEXT) \ + find_new_uid.$(OBJEXT) getdate.$(OBJEXT) \ + getgr_nam_gid.$(OBJEXT) getrange.$(OBJEXT) hushed.$(OBJEXT) \ + isexpired.$(OBJEXT) limits.$(OBJEXT) list.$(OBJEXT) \ + log.$(OBJEXT) loginprompt.$(OBJEXT) mail.$(OBJEXT) \ + motd.$(OBJEXT) myname.$(OBJEXT) obscure.$(OBJEXT) \ + pam_pass.$(OBJEXT) pam_pass_non_interractive.$(OBJEXT) \ + pwd2spwd.$(OBJEXT) pwdcheck.$(OBJEXT) pwd_init.$(OBJEXT) \ + remove_tree.$(OBJEXT) rlogin.$(OBJEXT) root_flag.$(OBJEXT) \ + salt.$(OBJEXT) setugid.$(OBJEXT) setupenv.$(OBJEXT) \ + shell.$(OBJEXT) strtoday.$(OBJEXT) sub.$(OBJEXT) \ + sulog.$(OBJEXT) ttytype.$(OBJEXT) tz.$(OBJEXT) \ + ulimit.$(OBJEXT) user_busy.$(OBJEXT) utmp.$(OBJEXT) \ + valid.$(OBJEXT) xgetpwnam.$(OBJEXT) xgetpwuid.$(OBJEXT) \ + xgetgrnam.$(OBJEXT) xgetgrgid.$(OBJEXT) xgetspnam.$(OBJEXT) \ + xmalloc.$(OBJEXT) yesno.$(OBJEXT) +libmisc_a_OBJECTS = $(am_libmisc_a_OBJECTS) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +@MAINTAINER_MODE_FALSE@am__skipyacc = test -f $@ || +YACCCOMPILE = $(YACC) $(AM_YFLAGS) $(YFLAGS) +LTYACCCOMPILE = $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(YACC) $(AM_YFLAGS) $(YFLAGS) +YLWRAP = $(top_srcdir)/ylwrap +SOURCES = $(libmisc_a_SOURCES) +DIST_SOURCES = $(libmisc_a_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GREP = @GREP@ +GROUP_NAME_MAX_LENGTH = @GROUP_NAME_MAX_LENGTH@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBACL = @LIBACL@ +LIBATTR = @LIBATTR@ +LIBAUDIT = @LIBAUDIT@ +LIBCRACK = @LIBCRACK@ +LIBCRYPT = @LIBCRYPT@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBMD = @LIBMD@ +LIBOBJS = @LIBOBJS@ +LIBPAM = @LIBPAM@ +LIBS = @LIBS@ +LIBSELINUX = @LIBSELINUX@ +LIBSEMANAGE = @LIBSEMANAGE@ +LIBSKEY = @LIBSKEY@ +LIBTCB = @LIBTCB@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +POSUB = @POSUB@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +XMLCATALOG = @XMLCATALOG@ +XML_CATALOG_FILE = @XML_CATALOG_FILE@ +XSLTPROC = @XSLTPROC@ +YACC = @YACC@ +YFLAGS = @YFLAGS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +EXTRA_DIST = .indent.pro xgetXXbyYY.c +INCLUDES = -I$(top_srcdir)/lib +noinst_LIBRARIES = libmisc.a +libmisc_a_SOURCES = \ + addgrps.c \ + age.c \ + audit_help.c \ + basename.c \ + chkname.c \ + chkname.h \ + chowndir.c \ + chowntty.c \ + cleanup.c \ + cleanup_group.c \ + cleanup_user.c \ + console.c \ + copydir.c \ + entry.c \ + env.c \ + failure.c \ + failure.h \ + find_new_gid.c \ + find_new_uid.c \ + getdate.h \ + getdate.y \ + getgr_nam_gid.c \ + getrange.c \ + hushed.c \ + isexpired.c \ + limits.c \ + list.c log.c \ + loginprompt.c \ + mail.c \ + motd.c \ + myname.c \ + obscure.c \ + pam_pass.c \ + pam_pass_non_interractive.c \ + pwd2spwd.c \ + pwdcheck.c \ + pwd_init.c \ + remove_tree.c \ + rlogin.c \ + root_flag.c \ + salt.c \ + setugid.c \ + setupenv.c \ + shell.c \ + strtoday.c \ + sub.c \ + sulog.c \ + ttytype.c \ + tz.c \ + ulimit.c \ + user_busy.c \ + utmp.c \ + valid.c \ + xgetpwnam.c \ + xgetpwuid.c \ + xgetgrnam.c \ + xgetgrgid.c \ + xgetspnam.c \ + xmalloc.c \ + yesno.c + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj .y +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu libmisc/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu libmisc/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libmisc.a: $(libmisc_a_OBJECTS) $(libmisc_a_DEPENDENCIES) $(EXTRA_libmisc_a_DEPENDENCIES) + -rm -f libmisc.a + $(libmisc_a_AR) libmisc.a $(libmisc_a_OBJECTS) $(libmisc_a_LIBADD) + $(RANLIB) libmisc.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/addgrps.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/age.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/audit_help.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/basename.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chkname.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chowndir.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chowntty.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cleanup.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cleanup_group.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cleanup_user.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/console.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/copydir.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/entry.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/env.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/failure.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/find_new_gid.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/find_new_uid.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getdate.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getgr_nam_gid.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getrange.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hushed.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isexpired.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/limits.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/list.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/loginprompt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/motd.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/myname.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/obscure.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_pass.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_pass_non_interractive.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pwd2spwd.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pwd_init.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pwdcheck.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/remove_tree.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rlogin.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/root_flag.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/salt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/setugid.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/setupenv.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shell.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strtoday.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sub.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sulog.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ttytype.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tz.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ulimit.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/user_busy.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utmp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/valid.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xgetgrgid.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xgetgrnam.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xgetpwnam.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xgetpwuid.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xgetspnam.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xmalloc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/yesno.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +.y.c: + $(am__skipyacc) $(SHELL) $(YLWRAP) $< y.tab.c $@ y.tab.h $*.h y.output $*.output -- $(YACCCOMPILE) + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -rm -f getdate.c +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLIBRARIES ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/libmisc/addgrps.c b/libmisc/addgrps.c new file mode 100644 index 00000000..a2b4f8e7 --- /dev/null +++ b/libmisc/addgrps.c @@ -0,0 +1,131 @@ +/* + * Copyright (c) 1989 - 1994, Julianne Frances Haugh + * Copyright (c) 1996 - 1998, Marek Michałkiewicz + * Copyright (c) 2001 - 2006, Tomasz Kłoczko + * Copyright (c) 2007 - 2009, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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> + +#if defined (HAVE_SETGROUPS) && ! defined (USE_PAM) + +#include "prototypes.h" +#include "defines.h" + +#include <stdio.h> +#include <grp.h> +#include <errno.h> + +#ident "$Id: addgrps.c 3318 2011-06-02 15:36:29Z nekral-guest $" + +#define SEP ",:" +/* + * Add groups with names from LIST (separated by commas or colons) + * to the supplementary group set. Silently ignore groups which are + * already there. Warning: uses strtok(). + */ +int add_groups (const char *list) +{ + GETGROUPS_T *grouplist, *tmp; + size_t i; + int ngroups; + bool added; + char *token; + char buf[1024]; + + if (strlen (list) >= sizeof (buf)) { + errno = EINVAL; + return -1; + } + strcpy (buf, list); + + i = 16; + for (;;) { + grouplist = (gid_t *) malloc (i * sizeof (GETGROUPS_T)); + if (NULL == grouplist) { + return -1; + } + ngroups = getgroups (i, grouplist); + if ( ( (-1 == ngroups) + && (EINVAL != errno)) + || (i > (size_t)ngroups)) { + /* Unexpected failure of getgroups or successful + * reception of the groups */ + break; + } + /* not enough room, so try allocating a larger buffer */ + free (grouplist); + i *= 2; + } + if (ngroups < 0) { + free (grouplist); + return -1; + } + + added = false; + for (token = strtok (buf, SEP); NULL != token; token = strtok (NULL, SEP)) { + struct group *grp; + + grp = getgrnam (token); /* local, no need for xgetgrnam */ + if (NULL == grp) { + fprintf (stderr, _("Warning: unknown group %s\n"), + token); + continue; + } + + for (i = 0; i < (size_t)ngroups && grouplist[i] != grp->gr_gid; i++); + + if (i < (size_t)ngroups) { + continue; + } + + if (ngroups >= sysconf (_SC_NGROUPS_MAX)) { + fputs (_("Warning: too many groups\n"), stderr); + break; + } + tmp = (gid_t *) realloc (grouplist, (size_t)(ngroups + 1) * sizeof (GETGROUPS_T)); + if (NULL == tmp) { + free (grouplist); + return -1; + } + tmp[ngroups] = grp->gr_gid; + ngroups++; + grouplist = tmp; + added = true; + } + + if (added) { + return setgroups ((size_t)ngroups, grouplist); + } + + return 0; +} +#else /* HAVE_SETGROUPS && !USE_PAM */ +extern int errno; /* warning: ANSI C forbids an empty source file */ +#endif /* HAVE_SETGROUPS && !USE_PAM */ + diff --git a/libmisc/age.c b/libmisc/age.c new file mode 100644 index 00000000..3ec44d41 --- /dev/null +++ b/libmisc/age.c @@ -0,0 +1,201 @@ +/* + * Copyright (c) 1989 - 1994, Julianne Frances Haugh + * Copyright (c) 1996 - 1998, Marek Michałkiewicz + * Copyright (c) 2001 - 2006, Tomasz Kłoczko + * Copyright (c) 2008 - 2009, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <config.h> + +#include <sys/types.h> +#include <stdio.h> +#include <time.h> +#include <errno.h> +#include "prototypes.h" +#include "defines.h" +#include "exitcodes.h" +#include <pwd.h> +#include <grp.h> + +#ident "$Id: age.c 2775 2009-04-23 17:33:21Z nekral-guest $" + +#ifndef PASSWD_PROGRAM +#define PASSWD_PROGRAM "/bin/passwd" +#endif +/* + * expire - force password change if password expired + * + * expire() calls /bin/passwd to change the user's password + * if it has expired. + */ +int expire (const struct passwd *pw, /*@null@*/const struct spwd *sp) +{ + int status; + pid_t child; + pid_t pid; + + if (NULL == sp) { + return 0; + } + + /* + * See if the user's password has expired, and if so + * force them to change their password. + */ + + status = isexpired (pw, sp); + switch (status) { + case 0: + return 0; + case 1: + (void) fputs (_("Your password has expired."), stdout); + break; + case 2: + (void) fputs (_("Your password is inactive."), stdout); + break; + case 3: + (void) fputs (_("Your login has expired."), stdout); + break; + } + + /* + * Setting the maximum valid period to less than the minimum + * valid period means that the minimum period will never + * occur while the password is valid, so the user can never + * change that password. + */ + + if ((status > 1) || (sp->sp_max < sp->sp_min)) { + (void) puts (_(" Contact the system administrator.")); + exit (EXIT_FAILURE); + } + (void) puts (_(" Choose a new password.")); + (void) fflush (stdout); + + /* + * Close all the files so that unauthorized access won't + * occur. This needs to be done anyway because those files + * might become stale after "passwd" is executed. + */ + + endspent (); + endpwent (); +#ifdef SHADOWGRP + endsgent (); +#endif + endgrent (); + + /* + * Execute the /bin/passwd command. The exit status will be + * examined to see what the result is. If there are any + * errors the routine will exit. This forces the user to + * change their password before being able to use the account. + */ + + pid = fork (); + if (0 == pid) { + int err; + + /* + * Set the UID to be that of the user. This causes + * passwd to work just like it would had they executed + * it from the command line while logged in. + */ +#if defined(HAVE_INITGROUPS) && ! defined(USE_PAM) + if (setup_uid_gid (pw, false) != 0) +#else + if (setup_uid_gid (pw) != 0) +#endif + { + _exit (126); + } + + (void) execl (PASSWD_PROGRAM, PASSWD_PROGRAM, pw->pw_name, (char *) 0); + err = errno; + perror ("Can't execute " PASSWD_PROGRAM); + _exit ((ENOENT == err) ? E_CMD_NOTFOUND : E_CMD_NOEXEC); + } else if ((pid_t) -1 == pid) { + perror ("fork"); + exit (EXIT_FAILURE); + } + + while (((child = wait (&status)) != pid) && (child != (pid_t)-1)); + + if ((child == pid) && (0 == status)) { + return 1; + } + + exit (EXIT_FAILURE); + /*@notreached@*/} + +/* + * agecheck - see if warning is needed for password expiration + * + * agecheck sees how many days until the user's password is going + * to expire and warns the user of the pending password expiration. + */ + +void agecheck (/*@null@*/const struct spwd *sp) +{ + long now = (long) time ((time_t *) 0) / SCALE; + long remain; + + if (NULL == sp) { + return; + } + + /* + * The last, max, and warn fields must be supported or the + * warning period cannot be calculated. + */ + + if ( (-1 == sp->sp_lstchg) + || (-1 == sp->sp_max) + || (-1 == sp->sp_warn)) { + return; + } + + if (0 == sp->sp_lstchg) { + (void) puts (_("You must change your password.")); + return; + } + + remain = sp->sp_lstchg + sp->sp_max - now; + if (remain <= sp->sp_warn) { + remain /= DAY / SCALE; + if (remain > 1) { + (void) printf (_("Your password will expire in %ld days.\n"), + remain); + } else if (1 == remain) { + (void) puts (_("Your password will expire tomorrow.")); + } else if (remain == 0) { + (void) puts (_("Your password will expire today.")); + } + } +} + diff --git a/libmisc/audit_help.c b/libmisc/audit_help.c new file mode 100644 index 00000000..1aadaa37 --- /dev/null +++ b/libmisc/audit_help.c @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2005 , Red Hat, Inc. + * Copyright (c) 2005 , Tomasz Kłoczko + * Copyright (c) 2008 , Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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. + */ + +/* + * Audit helper functions used throughout shadow + * + */ + +#include <config.h> + +#ifdef WITH_AUDIT + +#include <stdlib.h> +#include <syslog.h> +#include <stdarg.h> +#include <libaudit.h> +#include <errno.h> +#include <stdio.h> +#include "prototypes.h" +int audit_fd; + +void audit_help_open (void) +{ + audit_fd = audit_open (); + if (audit_fd < 0) { + /* You get these only when the kernel doesn't have + * audit compiled in. */ + if ( (errno == EINVAL) + || (errno == EPROTONOSUPPORT) + || (errno == EAFNOSUPPORT)) { + return; + } + (void) fputs (_("Cannot open audit interface - aborting.\n"), + stderr); + exit (EXIT_FAILURE); + } +} + +/* + * This function will log a message to the audit system using a predefined + * message format. Parameter usage is as follows: + * + * type - type of message: AUDIT_USER_CHAUTHTOK for changing any account + * attributes. + * pgname - program's name + * op - operation. "adding user", "changing finger info", "deleting group" + * name - user's account or group name. If not available use NULL. + * id - uid or gid that the operation is being performed on. This is used + * only when user is NULL. + */ +void audit_logger (int type, unused const char *pgname, const char *op, + const char *name, unsigned int id, + shadow_audit_result result) +{ + if (audit_fd < 0) { + return; + } else { + audit_log_acct_message (audit_fd, type, NULL, op, name, id, + NULL, NULL, NULL, (int) result); + } +} + +void audit_logger_message (const char *message, shadow_audit_result result) +{ + if (audit_fd < 0) { + return; + } else { + audit_log_user_message (audit_fd, + AUDIT_USYS_CONFIG, + message, + NULL, /* hostname */ + NULL, /* addr */ + NULL, /* tty */ + (int) result); + } +} + +#else /* WITH_AUDIT */ +extern int errno; /* warning: ANSI C forbids an empty source file */ +#endif /* WITH_AUDIT */ + diff --git a/libmisc/basename.c b/libmisc/basename.c new file mode 100644 index 00000000..e242e9a4 --- /dev/null +++ b/libmisc/basename.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 1990 - 1994, Julianne Frances Haugh + * Copyright (c) 1996 - 1997, Marek Michałkiewicz + * Copyright (c) 2003 - 2005, Tomasz Kłoczko + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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. + */ + +/* + * basename.c - not worth copyrighting :-). Some versions of Linux libc + * already have basename(), other versions don't. To avoid confusion, + * we will not use the function from libc and use a different name here. + * --marekm + */ + +#include <config.h> + +#ident "$Id: basename.c 3493 2011-09-18 21:02:43Z nekral-guest $" + +#include "defines.h" +#include "prototypes.h" +/*@observer@*/const char *Basename (const char *str) +{ + char *cp = strrchr (str, '/'); + + return (NULL != cp) ? cp + 1 : str; +} diff --git a/libmisc/chkname.c b/libmisc/chkname.c new file mode 100644 index 00000000..42111aeb --- /dev/null +++ b/libmisc/chkname.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 1990 - 1994, Julianne Frances Haugh + * Copyright (c) 1996 - 2000, Marek Michałkiewicz + * Copyright (c) 2001 - 2005, Tomasz Kłoczko + * Copyright (c) 2005 - 2008, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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. + */ + +/* + * is_valid_user_name(), is_valid_group_name() - check the new user/group + * name for validity; + * return values: + * true - OK + * false - bad name + */ + +#include <config.h> + +#ident "$Id: chkname.c 2828 2009-04-28 19:14:05Z nekral-guest $" + +#include <ctype.h> +#include "defines.h" +#include "chkname.h" + +static bool is_valid_name (const char *name) +{ + /* + * User/group names must match [a-z_][a-z0-9_-]*[$] + */ + if (('\0' == *name) || + !((('a' <= *name) && ('z' >= *name)) || ('_' == *name))) { + return false; + } + + while ('\0' != *++name) { + if (!(( ('a' <= *name) && ('z' >= *name) ) || + ( ('0' <= *name) && ('9' >= *name) ) || + ('_' == *name) || + ('-' == *name) || + ( ('$' == *name) && ('\0' == *(name + 1)) ) + )) { + return false; + } + } + + return true; +} + +bool is_valid_user_name (const char *name) +{ + /* + * User names are limited by whatever utmp can + * handle. + */ + if (strlen (name) > USER_NAME_MAX_LENGTH) { + return false; + } + + return is_valid_name (name); +} + +bool is_valid_group_name (const char *name) +{ + /* + * Arbitrary limit for group names. + * HP-UX 10 limits to 16 characters + */ + if ( (GROUP_NAME_MAX_LENGTH > 0) + && (strlen (name) > GROUP_NAME_MAX_LENGTH)) { + return false; + } + + return is_valid_name (name); +} + diff --git a/libmisc/chkname.h b/libmisc/chkname.h new file mode 100644 index 00000000..dbe6c8a0 --- /dev/null +++ b/libmisc/chkname.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 1990 - 1994, Julianne Frances Haugh + * Copyright (c) 1997 - 2000, Marek Michałkiewicz + * Copyright (c) 2005 , Tomasz Kłoczko + * Copyright (c) 2008 , Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* $Id: chkname.h 2036 2008-05-25 23:31:10Z nekral-guest $ */ +#ifndef _CHKNAME_H_ +#define _CHKNAME_H_ + +/* + * is_valid_user_name(), is_valid_group_name() - check the new user/group + * name for validity; + * return values: + * true - OK + * false - bad name + */ + +#include "defines.h" + +extern bool is_valid_user_name (const char *name); +extern bool is_valid_group_name (const char *name); + +#endif diff --git a/libmisc/chowndir.c b/libmisc/chowndir.c new file mode 100644 index 00000000..b5a894e6 --- /dev/null +++ b/libmisc/chowndir.c @@ -0,0 +1,201 @@ +/* + * Copyright (c) 1992 - 1993, Julianne Frances Haugh + * Copyright (c) 1996 - 2000, Marek Michałkiewicz + * Copyright (c) 2003 - 2005, Tomasz Kłoczko + * Copyright (c) 2010 - , Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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> + +#ident "$Id: chowndir.c 3465 2011-08-14 14:00:14Z nekral-guest $" + +#include <sys/types.h> +#include <sys/stat.h> +#include "prototypes.h" +#include "defines.h" +#include <fcntl.h> +#include <stdio.h> +/* + * chown_tree - change ownership of files in a directory tree + * + * chown_dir() walks a directory tree and changes the ownership + * of all files owned by the provided user ID. + * + * Only files owned (resp. group-owned) by old_uid (resp. by old_gid) + * will have their ownership (resp. group-ownership) modified, unless + * old_uid (resp. old_gid) is set to -1. + * + * new_uid and new_gid can be set to -1 to indicate that no owner or + * group-owner shall be changed. + */ +int chown_tree (const char *root, + uid_t old_uid, + uid_t new_uid, + gid_t old_gid, + gid_t new_gid) +{ + char *new_name; + size_t new_name_len; + int rc = 0; + struct DIRECT *ent; + struct stat sb; + DIR *dir; + + new_name = malloc (1024); + if (NULL == new_name) { + return -1; + } + new_name_len = 1024; + + /* + * Make certain the directory exists. This routine is called + * directly by the invoker, or recursively. + */ + + if (access (root, F_OK) != 0) { + free (new_name); + return -1; + } + + /* + * Open the directory and read each entry. Every entry is tested + * to see if it is a directory, and if so this routine is called + * recursively. If not, it is checked to see if an ownership + * shall be changed. + */ + + dir = opendir (root); + if (NULL == dir) { + free (new_name); + return -1; + } + + while ((ent = readdir (dir))) { + size_t ent_name_len; + uid_t tmpuid = (uid_t) -1; + gid_t tmpgid = (gid_t) -1; + + /* + * Skip the "." and ".." entries + */ + + if ( (strcmp (ent->d_name, ".") == 0) + || (strcmp (ent->d_name, "..") == 0)) { + continue; + } + + /* + * Make the filename for both the source and the + * destination files. + */ + + ent_name_len = strlen (root) + strlen (ent->d_name) + 2; + if (ent_name_len > new_name_len) { + /*@only@*/char *tmp = realloc (new_name, ent_name_len); + if (NULL == tmp) { + rc = -1; + break; + } + new_name = tmp; + new_name_len = ent_name_len; + } + + (void) snprintf (new_name, new_name_len, "%s/%s", root, ent->d_name); + + /* Don't follow symbolic links! */ + if (LSTAT (new_name, &sb) == -1) { + continue; + } + + if (S_ISDIR (sb.st_mode) && !S_ISLNK (sb.st_mode)) { + + /* + * Do the entire subdirectory. + */ + + rc = chown_tree (new_name, old_uid, new_uid, + old_gid, new_gid); + if (0 != rc) { + break; + } + } +#ifndef HAVE_LCHOWN + /* don't use chown (follows symbolic links!) */ + if (S_ISLNK (sb.st_mode)) { + continue; + } +#endif + /* + * By default, the IDs are not changed (-1). + * + * If the file is not owned by the user, the owner is not + * changed. + * + * If the file is not group-owned by the group, the + * group-owner is not changed. + */ + if (((uid_t) -1 == old_uid) || (sb.st_uid == old_uid)) { + tmpuid = new_uid; + } + if (((gid_t) -1 == old_gid) || (sb.st_gid == old_gid)) { + tmpgid = new_gid; + } + if (((uid_t) -1 != tmpuid) || ((gid_t) -1 != tmpgid)) { + rc = LCHOWN (new_name, tmpuid, tmpgid); + if (0 != rc) { + break; + } + } + } + + free (new_name); + (void) closedir (dir); + + /* + * Now do the root of the tree + */ + + if ((0 == rc) && (stat (root, &sb) == 0)) { + uid_t tmpuid = (uid_t) -1; + gid_t tmpgid = (gid_t) -1; + if (((uid_t) -1 == old_uid) || (sb.st_uid == old_uid)) { + tmpuid = new_uid; + } + if (((gid_t) -1 == old_gid) || (sb.st_gid == old_gid)) { + tmpgid = new_gid; + } + if (((uid_t) -1 != tmpuid) || ((gid_t) -1 != tmpgid)) { + rc = LCHOWN (root, tmpuid, tmpgid); + } + } else { + rc = -1; + } + + return rc; +} + diff --git a/libmisc/chowntty.c b/libmisc/chowntty.c new file mode 100644 index 00000000..c9b53728 --- /dev/null +++ b/libmisc/chowntty.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 1989 - 1994, Julianne Frances Haugh + * Copyright (c) 1996 - 2001, Marek Michałkiewicz + * Copyright (c) 2003 - 2005, Tomasz Kłoczko + * Copyright (c) 2007 - 2009, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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> + +#ident "$Id: chowntty.c 3232 2010-08-22 19:13:53Z nekral-guest $" + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <errno.h> +#include <grp.h> +#include "prototypes.h" +#include "defines.h" +#include <pwd.h> +#include "getdef.h" + +/* + * chown_tty() sets the login tty to be owned by the new user ID + * with TTYPERM modes + */ + +void chown_tty (const struct passwd *info) +{ + struct group *grent; + gid_t gid; + + /* + * See if login.defs has some value configured for the port group + * ID. Otherwise, use the user's primary group ID. + */ + + grent = getgr_nam_gid (getdef_str ("TTYGROUP")); + if (NULL != grent) { + gid = grent->gr_gid; + } else { + gid = info->pw_gid; + } + + /* + * Change the permissions on the TTY to be owned by the user with + * the group as determined above. + */ + + if ( (fchown (STDIN_FILENO, info->pw_uid, gid) != 0) + || (fchmod (STDIN_FILENO, (mode_t)getdef_num ("TTYPERM", 0600)) != 0)) { + int err = errno; + + fprintf (stderr, + _("Unable to change owner or mode of tty stdin: %s"), + strerror (err)); + SYSLOG ((LOG_WARN, + "unable to change owner or mode of tty stdin for user `%s': %s\n", + info->pw_name, strerror (err))); + if (EROFS != err) { + closelog (); + exit (EXIT_FAILURE); + } + } +#ifdef __linux__ + /* + * Please don't add code to chown /dev/vcs* to the user logging in - + * it's a potential security hole. I wouldn't like the previous user + * to hold the file descriptor open and watch my screen. We don't + * have the *BSD revoke() system call yet, and vhangup() only works + * for tty devices (which vcs* is not). --marekm + */ +#endif +} + diff --git a/libmisc/cleanup.c b/libmisc/cleanup.c new file mode 100644 index 00000000..bd83a230 --- /dev/null +++ b/libmisc/cleanup.c @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2008 - 2011, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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 <assert.h> +#include <stdio.h> + +#include "prototypes.h" + +/* + * The cleanup_functions stack. + */ +#define CLEANUP_FUNCTIONS 10 + +typedef /*@null@*/void * parg_t; + +static cleanup_function cleanup_functions[CLEANUP_FUNCTIONS]; +static parg_t cleanup_function_args[CLEANUP_FUNCTIONS]; +static pid_t cleanup_pid = 0; + +/* + * - Cleanup functions shall not fail. + * - You should register do_cleanups with atexit. + * - You should add cleanup functions to the stack with add_cleanup when + * an operation is expected to be executed later, and remove it from the + * stack with del_cleanup when it has been executed. + * + **/ + +/* + * do_cleanups - perform the actions stored in the cleanup_functions stack. + * + * Cleanup action are not executed on exit of the processes started by the + * parent (first caller of add_cleanup). + * + * It is intended to be used as: + * atexit (do_cleanups); + */ +void do_cleanups (void) +{ + unsigned int i; + + /* Make sure there were no overflow */ + assert (NULL == cleanup_functions[CLEANUP_FUNCTIONS-1]); + + if (getpid () != cleanup_pid) { + return; + } + + i = CLEANUP_FUNCTIONS; + do { + i--; + if (cleanup_functions[i] != NULL) { + cleanup_functions[i] (cleanup_function_args[i]); + } + } while (i>0); +} + +/* + * add_cleanup - Add a cleanup_function to the cleanup_functions stack. + */ +void add_cleanup (/*@notnull@*/cleanup_function pcf, /*@null@*/void *arg) +{ + unsigned int i; + assert (NULL != pcf); + + assert (NULL == cleanup_functions[CLEANUP_FUNCTIONS-2]); + + if (0 == cleanup_pid) { + cleanup_pid = getpid (); + } + + /* Add the cleanup_function at the end of the stack */ + for (i=0; NULL != cleanup_functions[i]; i++); + cleanup_functions[i] = pcf; + cleanup_function_args[i] = arg; +} + +/* + * del_cleanup - Remove a cleanup_function from the cleanup_functions stack. + */ +void del_cleanup (/*@notnull@*/cleanup_function pcf) +{ + unsigned int i; + assert (NULL != pcf); + + /* Find the pcf cleanup function */ + for (i=0; i<CLEANUP_FUNCTIONS; i++) { + if (cleanup_functions[i] == pcf) { + break; + } + } + + /* Make sure the cleanup function was found */ + assert (i<CLEANUP_FUNCTIONS); + + /* Move the rest of the cleanup functions */ + for (; i<CLEANUP_FUNCTIONS; i++) { + /* Make sure the cleanup function was specified only once */ + assert ( (i == (CLEANUP_FUNCTIONS -1)) + || (cleanup_functions[i+1] != pcf)); + + if (i == (CLEANUP_FUNCTIONS -1)) { + cleanup_functions[i] = NULL; + cleanup_function_args[i] = NULL; + } else { + cleanup_functions[i] = cleanup_functions[i+1]; + cleanup_function_args[i] = cleanup_function_args[i+1]; + } + + /* A NULL indicates the end of the stack */ + if (NULL == cleanup_functions[i]) { + break; + } + } +} + diff --git a/libmisc/cleanup_group.c b/libmisc/cleanup_group.c new file mode 100644 index 00000000..d07adc71 --- /dev/null +++ b/libmisc/cleanup_group.c @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2008 , Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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 <assert.h> +#include <stdio.h> + +#include "defines.h" +#include "groupio.h" +#include "sgroupio.h" +#include "prototypes.h" + +/* + * cleanup_report_add_group - Report failure to add a group to the system + * + * It should be registered when it is decided to add a group to the system. + */ +void cleanup_report_add_group (void *group_name) +{ + const char *name = (const char *)group_name; + + SYSLOG ((LOG_ERR, "failed to add group %s", name)); +#ifdef WITH_AUDIT + audit_logger (AUDIT_ADD_GROUP, Prog, + "", + name, AUDIT_NO_ID, + SHADOW_AUDIT_FAILURE); +#endif +} + +/* + * cleanup_report_del_group - Report failure to remove a group from the system + * + * It should be registered when it is decided to remove a group from the system. + */ +void cleanup_report_del_group (void *group_name) +{ + const char *name = (const char *)group_name; + + SYSLOG ((LOG_ERR, "failed to remove group %s", name)); +#ifdef WITH_AUDIT + audit_logger (AUDIT_DEL_GROUP, Prog, + "", + name, AUDIT_NO_ID, + SHADOW_AUDIT_FAILURE); +#endif +} + +void cleanup_report_mod_group (void *cleanup_info) +{ + const struct cleanup_info_mod *info; + info = (const struct cleanup_info_mod *)cleanup_info; + + SYSLOG ((LOG_ERR, + "failed to change %s (%s)", + gr_dbname (), + info->action)); +#ifdef WITH_AUDIT + audit_logger (AUDIT_USER_ACCT, Prog, + info->audit_msg, + info->name, AUDIT_NO_ID, + SHADOW_AUDIT_FAILURE); +#endif +} + +#ifdef SHADOWGRP +void cleanup_report_mod_gshadow (void *cleanup_info) +{ + const struct cleanup_info_mod *info; + info = (const struct cleanup_info_mod *)cleanup_info; + + SYSLOG ((LOG_ERR, + "failed to change %s (%s)", + sgr_dbname (), + info->action)); +#ifdef WITH_AUDIT + audit_logger (AUDIT_USER_ACCT, Prog, + info->audit_msg, + info->name, AUDIT_NO_ID, + SHADOW_AUDIT_FAILURE); +#endif +} +#endif + +/* + * cleanup_report_add_group_group - Report failure to add a group to group + * + * It should be registered when it is decided to add a group to the + * group database. + */ +void cleanup_report_add_group_group (void *group_name) +{ + const char *name = (const char *)group_name; + + SYSLOG ((LOG_ERR, "failed to add group %s to %s", name, gr_dbname ())); +#ifdef WITH_AUDIT + audit_logger (AUDIT_ADD_GROUP, Prog, + "adding group to /etc/group", + name, AUDIT_NO_ID, + SHADOW_AUDIT_FAILURE); +#endif +} + +#ifdef SHADOWGRP +/* + * cleanup_report_add_group_gshadow - Report failure to add a group to gshadow + * + * It should be registered when it is decided to add a group to the + * gshadow database. + */ +void cleanup_report_add_group_gshadow (void *group_name) +{ + const char *name = (const char *)group_name; + + SYSLOG ((LOG_ERR, "failed to add group %s to %s", name, sgr_dbname ())); +#ifdef WITH_AUDIT + audit_logger (AUDIT_ADD_GROUP, Prog, + "adding group to /etc/gshadow", + name, AUDIT_NO_ID, + SHADOW_AUDIT_FAILURE); +#endif +} +#endif + +/* + * cleanup_report_del_group_group - Report failure to remove a group from the + * regular group database + * + * It should be registered when it is decided to remove a group from the + * regular group database. + */ +void cleanup_report_del_group_group (void *group_name) +{ + const char *name = (const char *)group_name; + + SYSLOG ((LOG_ERR, + "failed to remove group %s from %s", + name, gr_dbname ())); +#ifdef WITH_AUDIT + audit_logger (AUDIT_ADD_GROUP, Prog, + "removing group from /etc/group", + name, AUDIT_NO_ID, + SHADOW_AUDIT_FAILURE); +#endif +} + +#ifdef SHADOWGRP +/* + * cleanup_report_del_group_gshadow - Report failure to remove a group from + * gshadow + * + * It should be registered when it is decided to remove a group from the + * gshadow database. + */ +void cleanup_report_del_group_gshadow (void *group_name) +{ + const char *name = (const char *)group_name; + + SYSLOG ((LOG_ERR, + "failed to remove group %s from %s", + name, sgr_dbname ())); +#ifdef WITH_AUDIT + audit_logger (AUDIT_ADD_GROUP, Prog, + "removing group from /etc/gshadow", + name, AUDIT_NO_ID, + SHADOW_AUDIT_FAILURE); +#endif +} +#endif + +/* + * cleanup_unlock_group - Unlock the group file + * + * It should be registered after the group file is successfully locked. + */ +void cleanup_unlock_group (unused void *arg) +{ + if (gr_unlock () == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); +#ifdef WITH_AUDIT + audit_logger_message ("unlocking group file", + SHADOW_AUDIT_FAILURE); +#endif + } +} + +#ifdef SHADOWGRP +/* + * cleanup_unlock_gshadow - Unlock the gshadow file + * + * It should be registered after the gshadow file is successfully locked. + */ +void cleanup_unlock_gshadow (unused void *arg) +{ + if (sgr_unlock () == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); +#ifdef WITH_AUDIT + audit_logger_message ("unlocking gshadow file", + SHADOW_AUDIT_FAILURE); +#endif + } +} +#endif + diff --git a/libmisc/cleanup_user.c b/libmisc/cleanup_user.c new file mode 100644 index 00000000..6e497512 --- /dev/null +++ b/libmisc/cleanup_user.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2008 , Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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 <assert.h> +#include <stdio.h> + +#include "defines.h" +#include "pwio.h" +#include "shadowio.h" +#include "prototypes.h" + +/* + * cleanup_report_add_user - Report failure to add an user to the system + * + * It should be registered when it is decided to add an user to the system. + */ +void cleanup_report_add_user (void *user_name) +{ + const char *name = (const char *)user_name; + + SYSLOG ((LOG_ERR, "failed to add user %s", name)); +#ifdef WITH_AUDIT + audit_logger (AUDIT_ADD_USER, Prog, + "", + name, AUDIT_NO_ID, + SHADOW_AUDIT_FAILURE); +#endif +} + +void cleanup_report_mod_passwd (void *cleanup_info) +{ + const struct cleanup_info_mod *info; + info = (const struct cleanup_info_mod *)cleanup_info; + + SYSLOG ((LOG_ERR, + "failed to change %s (%s)", + pw_dbname (), + info->action)); +#ifdef WITH_AUDIT + audit_logger (AUDIT_USER_ACCT, Prog, + info->audit_msg, + info->name, AUDIT_NO_ID, + SHADOW_AUDIT_FAILURE); +#endif +} + +/* + * cleanup_report_add_user_passwd - Report failure to add an user to + * /etc/passwd + * + * It should be registered when it is decided to add an user to the + * /etc/passwd database. + */ +void cleanup_report_add_user_passwd (void *user_name) +{ + const char *name = (const char *)user_name; + + SYSLOG ((LOG_ERR, "failed to add user %s to %s", name, pw_dbname ())); +#ifdef WITH_AUDIT + audit_logger (AUDIT_ADD_USER, Prog, + "adding user to /etc/passwd", + name, AUDIT_NO_ID, + SHADOW_AUDIT_FAILURE); +#endif +} + +/* + * cleanup_report_add_user_shadow - Report failure to add an user to + * /etc/shadow + * + * It should be registered when it is decided to add an user to the + * /etc/shadow database. + */ +void cleanup_report_add_user_shadow (void *user_name) +{ + const char *name = (const char *)user_name; + + SYSLOG ((LOG_ERR, "failed to add user %s to %s", name, spw_dbname ())); +#ifdef WITH_AUDIT + audit_logger (AUDIT_ADD_USER, Prog, + "adding user to /etc/shadow", + name, AUDIT_NO_ID, + SHADOW_AUDIT_FAILURE); +#endif +} + +/* + * cleanup_unlock_passwd - Unlock the /etc/passwd database + * + * It should be registered after the passwd database is successfully locked. + */ +void cleanup_unlock_passwd (unused void *arg) +{ + if (pw_unlock () == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); +#ifdef WITH_AUDIT + audit_logger_message ("unlocking passwd file", + SHADOW_AUDIT_FAILURE); +#endif + } +} + +/* + * cleanup_unlock_shadow - Unlock the /etc/shadow database + * + * It should be registered after the shadow database is successfully locked. + */ +void cleanup_unlock_shadow (unused void *arg) +{ + if (spw_unlock () == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); +#ifdef WITH_AUDIT + audit_logger_message ("unlocking shadow file", + SHADOW_AUDIT_FAILURE); +#endif + } +} + diff --git a/libmisc/console.c b/libmisc/console.c new file mode 100644 index 00000000..f840b2ed --- /dev/null +++ b/libmisc/console.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 1991 , Julianne Frances Haugh + * Copyright (c) 1991 , Chip Rosenthal + * Copyright (c) 1996 - 1998, Marek Michałkiewicz + * Copyright (c) 2003 - 2005, Tomasz Kłoczko + * Copyright (c) 2007 - 2010, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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 "defines.h" +#include <stdio.h> +#include "getdef.h" +#include "prototypes.h" + +#ident "$Id: console.c 3289 2010-11-19 21:54:41Z nekral-guest $" + +/* local function prototypes */ +static bool is_listed (const char *cfgin, const char *tty, bool def); + +/* + * This is now rather generic function which decides if "tty" is listed + * under "cfgin" in config (directly or indirectly). Fallback to default if + * something is bad. + */ +static bool is_listed (const char *cfgin, const char *tty, bool def) +{ + FILE *fp; + char buf[200], *s; + const char *cons; + + /* + * If the CONSOLE configuration definition isn't given, + * fallback to default. + */ + + cons = getdef_str (cfgin); + if (NULL == cons) { + return def; + } + + /* + * If this isn't a filename, then it is a ":" delimited list of + * console devices upon which root logins are allowed. + */ + + if (*cons != '/') { + char *pbuf; + strcpy (buf, cons); + pbuf = &buf[0]; + while ((s = strtok (pbuf, ":")) != NULL) { + if (strcmp (s, tty) == 0) { + return true; + } + + pbuf = NULL; + } + return false; + } + + /* + * If we can't open the console list, then call everything a + * console - otherwise root will never be allowed to login. + */ + + fp = fopen (cons, "r"); + if (NULL == fp) { + return def; + } + + /* + * See if this tty is listed in the console file. + */ + + while (fgets (buf, (int) sizeof (buf), fp) != NULL) { + buf[strlen (buf) - 1] = '\0'; + if (strcmp (buf, tty) == 0) { + (void) fclose (fp); + return true; + } + } + + /* + * This tty isn't a console. + */ + + (void) fclose (fp); + return false; +} + +/* + * console - return 1 if the "tty" is a console device, else 0. + * + * Note - we need to take extreme care here to avoid locking out root logins + * if something goes awry. That's why we do things like call everything a + * console if the consoles file can't be opened. Because of this, we must + * warn the user to protect against the remove of the consoles file since + * that would allow an unauthorized root login. + */ + +bool console (const char *tty) +{ + if (strncmp (tty, "/dev/", 5) == 0) { + tty += 5; + } + + return is_listed ("CONSOLE", tty, true); +} + diff --git a/libmisc/copydir.c b/libmisc/copydir.c new file mode 100644 index 00000000..958b7102 --- /dev/null +++ b/libmisc/copydir.c @@ -0,0 +1,839 @@ +/* + * Copyright (c) 1991 - 1994, Julianne Frances Haugh + * Copyright (c) 1996 - 2001, Marek Michałkiewicz + * Copyright (c) 2003 - 2006, Tomasz Kłoczko + * Copyright (c) 2007 - 2010, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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> + +#ident "$Id: copydir.c 3706 2012-02-13 19:16:29Z nekral-guest $" + +#include <assert.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/time.h> +#include <fcntl.h> +#include <stdio.h> +#include "prototypes.h" +#include "defines.h" +#ifdef WITH_SELINUX +#include <selinux/selinux.h> +#endif /* WITH_SELINUX */ +#if defined(WITH_ACL) || defined(WITH_ATTR) +#include <stdarg.h> +#include <attr/error_context.h> +#endif /* WITH_ACL || WITH_ATTR */ +#ifdef WITH_ACL +#include <acl/libacl.h> +#endif /* WITH_ACL */ +#ifdef WITH_ATTR +#include <attr/libattr.h> +#endif /* WITH_ATTR */ + + +static /*@null@*/const char *src_orig; +static /*@null@*/const char *dst_orig; + +struct link_name { + dev_t ln_dev; + ino_t ln_ino; + nlink_t ln_count; + char *ln_name; + /*@dependent@*/struct link_name *ln_next; +}; +static /*@exposed@*/struct link_name *links; + +static int copy_entry (const char *src, const char *dst, + bool reset_selinux, + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid); +static int copy_dir (const char *src, const char *dst, + bool reset_selinux, + const struct stat *statp, const struct timeval mt[], + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid); +#ifdef S_IFLNK +static /*@null@*/char *readlink_malloc (const char *filename); +static int copy_symlink (const char *src, const char *dst, + unused bool reset_selinux, + const struct stat *statp, const struct timeval mt[], + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid); +#endif /* S_IFLNK */ +static int copy_hardlink (const char *dst, + unused bool reset_selinux, + struct link_name *lp); +static int copy_special (const char *src, const char *dst, + bool reset_selinux, + const struct stat *statp, const struct timeval mt[], + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid); +static int copy_file (const char *src, const char *dst, + bool reset_selinux, + const struct stat *statp, const struct timeval mt[], + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid); +static int chown_if_needed (const char *dst, const struct stat *statp, + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid); +static int lchown_if_needed (const char *dst, const struct stat *statp, + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid); +static int fchown_if_needed (int fdst, const struct stat *statp, + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid); + +#if defined(WITH_ACL) || defined(WITH_ATTR) +/* + * error_acl - format the error messages for the ACL and EQ libraries. + */ +static void error_acl (struct error_context *ctx, const char *fmt, ...) +{ + va_list ap; + + /* ignore the case when destination does not support ACLs + * or extended attributes */ + if (ENOTSUP == errno) { + errno = 0; + return; + } + + va_start (ap, fmt); + (void) fprintf (stderr, _("%s: "), Prog); + if (vfprintf (stderr, fmt, ap) != 0) { + (void) fputs (_(": "), stderr); + } + (void) fprintf (stderr, "%s\n", strerror (errno)); + va_end (ap); +} + +static struct error_context ctx = { + error_acl +}; +#endif /* WITH_ACL || WITH_ATTR */ + +/* + * remove_link - delete a link from the linked list + */ +static void remove_link (/*@only@*/struct link_name *ln) +{ + struct link_name *lp; + + if (links == ln) { + links = ln->ln_next; + free (ln->ln_name); + free (ln); + return; + } + for (lp = links; NULL !=lp; lp = lp->ln_next) { + if (lp->ln_next == ln) { + break; + } + } + + if (NULL == lp) { + free (ln->ln_name); + free (ln); + return; + } + + lp->ln_next = lp->ln_next->ln_next; + free (ln->ln_name); + free (ln); +} + +/* + * check_link - see if a file is really a link + */ + +static /*@exposed@*/ /*@null@*/struct link_name *check_link (const char *name, const struct stat *sb) +{ + struct link_name *lp; + size_t src_len; + size_t dst_len; + size_t name_len; + size_t len; + + /* copy_tree () must be the entry point */ + assert (NULL != src_orig); + assert (NULL != dst_orig); + + for (lp = links; NULL != lp; lp = lp->ln_next) { + if ((lp->ln_dev == sb->st_dev) && (lp->ln_ino == sb->st_ino)) { + return lp; + } + } + + if (sb->st_nlink == 1) { + return NULL; + } + + lp = (struct link_name *) xmalloc (sizeof *lp); + src_len = strlen (src_orig); + dst_len = strlen (dst_orig); + name_len = strlen (name); + lp->ln_dev = sb->st_dev; + lp->ln_ino = sb->st_ino; + lp->ln_count = sb->st_nlink; + len = name_len - src_len + dst_len + 1; + lp->ln_name = (char *) xmalloc (len); + (void) snprintf (lp->ln_name, len, "%s%s", dst_orig, name + src_len); + lp->ln_next = links; + links = lp; + + return NULL; +} + +/* + * copy_tree - copy files in a directory tree + * + * copy_tree() walks a directory tree and copies ordinary files + * as it goes. + * + * When reset_selinux is enabled, extended attributes (and thus + * SELinux attributes) are not copied. + * + * old_uid and new_uid are used to set the ownership of the copied + * files. Unless old_uid is set to -1, only the files owned by + * old_uid have their ownership changed to new_uid. In addition, if + * new_uid is set to -1, no ownership will be changed. + * + * The same logic applies for the group-ownership and + * old_gid/new_gid. + */ +int copy_tree (const char *src_root, const char *dst_root, + bool copy_root, bool reset_selinux, + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid) +{ + int err = 0; + bool set_orig = false; + struct DIRECT *ent; + DIR *dir; + + if (copy_root) { + struct stat sb; + if (access (dst_root, F_OK) == 0) { + return -1; + } + + if (LSTAT (src_root, &sb) == -1) { + return -1; + } + + if (!S_ISDIR (sb.st_mode)) { + fprintf (stderr, + "%s: %s is not a directory", + Prog, src_root); + return -1; + } + + return copy_entry (src_root, dst_root, reset_selinux, + old_uid, new_uid, old_gid, new_gid); + } + + /* + * Make certain both directories exist. This routine is called + * after the home directory is created, or recursively after the + * target is created. It assumes the target directory exists. + */ + + if ( (access (src_root, F_OK) != 0) + || (access (dst_root, F_OK) != 0)) { + return -1; + } + + /* + * Open the source directory and read each entry. Every file + * entry in the directory is copied with the UID and GID set + * to the provided values. As an added security feature only + * regular files (and directories ...) are copied, and no file + * is made set-ID. + */ + dir = opendir (src_root); + if (NULL == dir) { + return -1; + } + + if (src_orig == NULL) { + src_orig = src_root; + dst_orig = dst_root; + set_orig = true; + } + while ((0 == err) && (ent = readdir (dir)) != NULL) { + /* + * Skip the "." and ".." entries + */ + if ((strcmp (ent->d_name, ".") != 0) && + (strcmp (ent->d_name, "..") != 0)) { + char *src_name; + char *dst_name; + size_t src_len = strlen (ent->d_name) + 2; + size_t dst_len = strlen (ent->d_name) + 2; + src_len += strlen (src_root); + dst_len += strlen (dst_root); + + src_name = (char *) malloc (src_len); + dst_name = (char *) malloc (dst_len); + + if ((NULL == src_name) || (NULL == dst_name)) { + err = -1; + } else { + /* + * Build the filename for both the source and + * the destination files. + */ + (void) snprintf (src_name, src_len, "%s/%s", + src_root, ent->d_name); + (void) snprintf (dst_name, dst_len, "%s/%s", + dst_root, ent->d_name); + + err = copy_entry (src_name, dst_name, + reset_selinux, + old_uid, new_uid, + old_gid, new_gid); + } + if (NULL != src_name) { + free (src_name); + } + if (NULL != dst_name) { + free (dst_name); + } + } + } + (void) closedir (dir); + + if (set_orig) { + src_orig = NULL; + dst_orig = NULL; + /* FIXME: clean links + * Since there can be hardlinks elsewhere on the device, + * we cannot check that all the hardlinks were found: + assert (NULL == links); + */ + } + +#ifdef WITH_SELINUX + /* Reset SELinux to create files with default contexts. + * Note that the context is only reset on exit of copy_tree (it is + * assumed that the program would quit without needing a restored + * context if copy_tree failed previously), and that copy_tree can + * be called recursively (hence the context is set on the + * sub-functions of copy_entry). + */ + if (reset_selinux_file_context () != 0) { + err = -1; + } +#endif /* WITH_SELINUX */ + + return err; +} + +/* + * copy_entry - copy the entry of a directory + * + * Copy the entry src to dst. + * Depending on the type of entry, this function will forward the + * request to copy_dir(), copy_symlink(), copy_hardlink(), + * copy_special(), or copy_file(). + * + * The access and modification time will not be modified. + * + * The permissions will be set to new_uid/new_gid. + * + * If new_uid (resp. new_gid) is equal to -1, the user (resp. group) will + * not be modified. + * + * Only the files owned (resp. group-owned) by old_uid (resp. + * old_gid) will be modified, unless old_uid (resp. old_gid) is set + * to -1. + */ +static int copy_entry (const char *src, const char *dst, + bool reset_selinux, + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid) +{ + int err = 0; + struct stat sb; + struct link_name *lp; + struct timeval mt[2]; + + if (LSTAT (src, &sb) == -1) { + /* If we cannot stat the file, do not care. */ + } else { +#ifdef HAVE_STRUCT_STAT_ST_ATIM + mt[0].tv_sec = sb.st_atim.tv_sec; + mt[0].tv_usec = sb.st_atim.tv_nsec / 1000; +#else /* !HAVE_STRUCT_STAT_ST_ATIM */ + mt[0].tv_sec = sb.st_atime; +# ifdef HAVE_STRUCT_STAT_ST_ATIMENSEC + mt[0].tv_usec = sb.st_atimensec / 1000; +# else /* !HAVE_STRUCT_STAT_ST_ATIMENSEC */ + mt[0].tv_usec = 0; +# endif /* !HAVE_STRUCT_STAT_ST_ATIMENSEC */ +#endif /* !HAVE_STRUCT_STAT_ST_ATIM */ + +#ifdef HAVE_STRUCT_STAT_ST_MTIM + mt[1].tv_sec = sb.st_mtim.tv_sec; + mt[1].tv_usec = sb.st_mtim.tv_nsec / 1000; +#else /* !HAVE_STRUCT_STAT_ST_MTIM */ + mt[1].tv_sec = sb.st_mtime; +# ifdef HAVE_STRUCT_STAT_ST_MTIMENSEC + mt[1].tv_usec = sb.st_mtimensec / 1000; +# else /* !HAVE_STRUCT_STAT_ST_MTIMENSEC */ + mt[1].tv_usec = 0; +# endif /* !HAVE_STRUCT_STAT_ST_MTIMENSEC */ +#endif /* !HAVE_STRUCT_STAT_ST_MTIM */ + + if (S_ISDIR (sb.st_mode)) { + err = copy_dir (src, dst, reset_selinux, &sb, mt, + old_uid, new_uid, old_gid, new_gid); + } + +#ifdef S_IFLNK + /* + * Copy any symbolic links + */ + + else if (S_ISLNK (sb.st_mode)) { + err = copy_symlink (src, dst, reset_selinux, &sb, mt, + old_uid, new_uid, old_gid, new_gid); + } +#endif /* S_IFLNK */ + + /* + * See if this is a previously copied link + */ + + else if ((lp = check_link (src, &sb)) != NULL) { + err = copy_hardlink (dst, reset_selinux, lp); + } + + /* + * Deal with FIFOs and special files. The user really + * shouldn't have any of these, but it seems like it + * would be nice to copy everything ... + */ + + else if (!S_ISREG (sb.st_mode)) { + err = copy_special (src, dst, reset_selinux, &sb, mt, + old_uid, new_uid, old_gid, new_gid); + } + + /* + * Create the new file and copy the contents. The new + * file will be owned by the provided UID and GID values. + */ + + else { + err = copy_file (src, dst, reset_selinux, &sb, mt, + old_uid, new_uid, old_gid, new_gid); + } + } + + return err; +} + +/* + * copy_dir - copy a directory + * + * Copy a directory (recursively) from src to dst. + * + * statp, mt, old_uid, new_uid, old_gid, and new_gid are used to set + * the access and modification and the access rights. + * + * Return 0 on success, -1 on error. + */ +static int copy_dir (const char *src, const char *dst, + bool reset_selinux, + const struct stat *statp, const struct timeval mt[], + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid) +{ + int err = 0; + + /* + * Create a new target directory, make it owned by + * the user and then recursively copy that directory. + */ + +#ifdef WITH_SELINUX + if (set_selinux_file_context (dst) != 0) { + return -1; + } +#endif /* WITH_SELINUX */ + if ( (mkdir (dst, statp->st_mode) != 0) + || (chown_if_needed (dst, statp, + old_uid, new_uid, old_gid, new_gid) != 0) +#ifdef WITH_ACL + || ( (perm_copy_file (src, dst, &ctx) != 0) + && (errno != 0)) +#else /* !WITH_ACL */ + || (chmod (dst, statp->st_mode) != 0) +#endif /* !WITH_ACL */ +#ifdef WITH_ATTR + /* + * If the third parameter is NULL, all extended attributes + * except those that define Access Control Lists are copied. + * ACLs are excluded by default because copying them between + * file systems with and without ACL support needs some + * additional logic so that no unexpected permissions result. + */ + || ( !reset_selinux + && (attr_copy_file (src, dst, NULL, &ctx) != 0) + && (errno != 0)) +#endif /* WITH_ATTR */ + || (copy_tree (src, dst, false, reset_selinux, + old_uid, new_uid, old_gid, new_gid) != 0) + || (utimes (dst, mt) != 0)) { + err = -1; + } + + return err; +} + +#ifdef S_IFLNK +/* + * readlink_malloc - wrapper for readlink + * + * return NULL on error. + * The return string shall be freed by the caller. + */ +static /*@null@*/char *readlink_malloc (const char *filename) +{ + size_t size = 1024; + + while (true) { + ssize_t nchars; + char *buffer = (char *) malloc (size); + if (NULL == buffer) { + return NULL; + } + + nchars = readlink (filename, buffer, size); + + if (nchars < 0) { + free(buffer); + return NULL; + } + + if ((size_t) nchars < size) { /* The buffer was large enough */ + /* readlink does not nul-terminate */ + buffer[nchars] = '\0'; + return buffer; + } + + /* Try again with a bigger buffer */ + free (buffer); + size *= 2; + } +} + +/* + * copy_symlink - copy a symlink + * + * Copy a symlink from src to dst. + * + * statp, mt, old_uid, new_uid, old_gid, and new_gid are used to set + * the access and modification and the access rights. + * + * Return 0 on success, -1 on error. + */ +static int copy_symlink (const char *src, const char *dst, + unused bool reset_selinux, + const struct stat *statp, const struct timeval mt[], + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid) +{ + char *oldlink; + + /* copy_tree () must be the entry point */ + assert (NULL != src_orig); + assert (NULL != dst_orig); + + /* + * Get the name of the file which the link points + * to. If that name begins with the original + * source directory name, that part of the link + * name will be replaced with the original + * destination directory name. + */ + + oldlink = readlink_malloc (src); + if (NULL == oldlink) { + return -1; + } + + /* If src was a link to an entry of the src_orig directory itself, + * create a link to the corresponding entry in the dst_orig + * directory. + */ + if (strncmp (oldlink, src_orig, strlen (src_orig)) == 0) { + size_t len = strlen (dst_orig) + strlen (oldlink) - strlen (src_orig) + 1; + char *dummy = (char *) xmalloc (len); + (void) snprintf (dummy, len, "%s%s", + dst_orig, + oldlink + strlen (src_orig)); + free (oldlink); + oldlink = dummy; + } + +#ifdef WITH_SELINUX + if (set_selinux_file_context (dst) != 0) { + free (oldlink); + return -1; + } +#endif /* WITH_SELINUX */ + if ( (symlink (oldlink, dst) != 0) + || (lchown_if_needed (dst, statp, + old_uid, new_uid, old_gid, new_gid) != 0)) { + /* FIXME: there are no modes on symlinks, right? + * ACL could be copied, but this would be much more + * complex than calling perm_copy_file. + * Ditto for Extended Attributes. + * We currently only document that ACL and Extended + * Attributes are not copied. + */ + free (oldlink); + return -1; + } + free (oldlink); + +#ifdef HAVE_LUTIMES + /* 2007-10-18: We don't care about + * exit status of lutimes because + * it returns ENOSYS on many system + * - not implemented + */ + (void) lutimes (dst, mt); +#endif /* HAVE_LUTIMES */ + + return 0; +} +#endif /* S_IFLNK */ + +/* + * copy_hardlink - copy a hardlink + * + * Copy a hardlink from src to dst. + * + * Return 0 on success, -1 on error. + */ +static int copy_hardlink (const char *dst, + unused bool reset_selinux, + struct link_name *lp) +{ + /* FIXME: selinux, ACL, Extended Attributes needed? */ + + if (link (lp->ln_name, dst) != 0) { + return -1; + } + + /* If the file could be unlinked, decrement the links counter, + * and forget about this link if it was the last reference */ + lp->ln_count--; + if (lp->ln_count <= 0) { + remove_link (lp); + } + + return 0; +} + +/* + * copy_special - copy a special file + * + * Copy a special file from src to dst. + * + * statp, mt, old_uid, new_uid, old_gid, and new_gid are used to set + * the access and modification and the access rights. + * + * Return 0 on success, -1 on error. + */ +static int copy_special (const char *src, const char *dst, + bool reset_selinux, + const struct stat *statp, const struct timeval mt[], + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid) +{ + int err = 0; + +#ifdef WITH_SELINUX + if (set_selinux_file_context (dst) != 0) { + return -1; + } +#endif /* WITH_SELINUX */ + + if ( (mknod (dst, statp->st_mode & ~07777, statp->st_rdev) != 0) + || (chown_if_needed (dst, statp, + old_uid, new_uid, old_gid, new_gid) != 0) +#ifdef WITH_ACL + || ( (perm_copy_file (src, dst, &ctx) != 0) + && (errno != 0)) +#else /* !WITH_ACL */ + || (chmod (dst, statp->st_mode & 07777) != 0) +#endif /* !WITH_ACL */ +#ifdef WITH_ATTR + /* + * If the third parameter is NULL, all extended attributes + * except those that define Access Control Lists are copied. + * ACLs are excluded by default because copying them between + * file systems with and without ACL support needs some + * additional logic so that no unexpected permissions result. + */ + || ( !reset_selinux + && (attr_copy_file (src, dst, NULL, &ctx) != 0) + && (errno != 0)) +#endif /* WITH_ATTR */ + || (utimes (dst, mt) != 0)) { + err = -1; + } + + return err; +} + +/* + * copy_file - copy a file + * + * Copy a file from src to dst. + * + * statp, mt, old_uid, new_uid, old_gid, and new_gid are used to set + * the access and modification and the access rights. + * + * Return 0 on success, -1 on error. + */ +static int copy_file (const char *src, const char *dst, + bool reset_selinux, + const struct stat *statp, const struct timeval mt[], + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid) +{ + int err = 0; + int ifd; + int ofd; + char buf[1024]; + ssize_t cnt; + + ifd = open (src, O_RDONLY); + if (ifd < 0) { + return -1; + } +#ifdef WITH_SELINUX + if (set_selinux_file_context (dst) != 0) { + return -1; + } +#endif /* WITH_SELINUX */ + ofd = open (dst, O_WRONLY | O_CREAT | O_TRUNC, statp->st_mode & 07777); + if ( (ofd < 0) + || (fchown_if_needed (ofd, statp, + old_uid, new_uid, old_gid, new_gid) != 0) +#ifdef WITH_ACL + || ( (perm_copy_fd (src, ifd, dst, ofd, &ctx) != 0) + && (errno != 0)) +#else /* !WITH_ACL */ + || (fchmod (ofd, statp->st_mode & 07777) != 0) +#endif /* !WITH_ACL */ +#ifdef WITH_ATTR + /* + * If the third parameter is NULL, all extended attributes + * except those that define Access Control Lists are copied. + * ACLs are excluded by default because copying them between + * file systems with and without ACL support needs some + * additional logic so that no unexpected permissions result. + */ + || ( !reset_selinux + && (attr_copy_fd (src, ifd, dst, ofd, NULL, &ctx) != 0) + && (errno != 0)) +#endif /* WITH_ATTR */ + ) { + (void) close (ifd); + return -1; + } + + while ((cnt = read (ifd, buf, sizeof buf)) > 0) { + if (write (ofd, buf, (size_t)cnt) != cnt) { + (void) close (ifd); + return -1; + } + } + + (void) close (ifd); + +#ifdef HAVE_FUTIMES + if (futimes (ofd, mt) != 0) { + return -1; + } +#endif /* HAVE_FUTIMES */ + + if (close (ofd) != 0) { + return -1; + } + +#ifndef HAVE_FUTIMES + if (utimes(dst, mt) != 0) { + return -1; + } +#endif /* !HAVE_FUTIMES */ + + return err; +} + +#define def_chown_if_needed(chown_function, type_dst) \ +static int chown_function ## _if_needed (type_dst dst, \ + const struct stat *statp, \ + uid_t old_uid, uid_t new_uid, \ + gid_t old_gid, gid_t new_gid) \ +{ \ + uid_t tmpuid = (uid_t) -1; \ + gid_t tmpgid = (gid_t) -1; \ + \ + /* Use new_uid if old_uid is set to -1 or if the file was \ + * owned by the user. */ \ + if (((uid_t) -1 == old_uid) || (statp->st_uid == old_uid)) { \ + tmpuid = new_uid; \ + } \ + /* Otherwise, or if new_uid was set to -1, we keep the same \ + * owner. */ \ + if ((uid_t) -1 == tmpuid) { \ + tmpuid = statp->st_uid; \ + } \ + \ + if (((gid_t) -1 == old_gid) || (statp->st_gid == old_gid)) { \ + tmpgid = new_gid; \ + } \ + if ((gid_t) -1 == tmpgid) { \ + tmpgid = statp->st_gid; \ + } \ + \ + return chown_function (dst, tmpuid, tmpgid); \ +} + +def_chown_if_needed (chown, const char *) +def_chown_if_needed (lchown, const char *) +def_chown_if_needed (fchown, int) + diff --git a/libmisc/entry.c b/libmisc/entry.c new file mode 100644 index 00000000..20ad824c --- /dev/null +++ b/libmisc/entry.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 1989 - 1994, Julianne Frances Haugh + * Copyright (c) 1996 - 2000, Marek Michałkiewicz + * Copyright (c) 2003 - 2005, Tomasz Kłoczko + * Copyright (c) 2007 - 2008, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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> + +#ident "$Id: entry.c 1980 2008-04-27 00:40:09Z nekral-guest $" + +#include <sys/types.h> +#include <stdio.h> +#include "prototypes.h" +#include "defines.h" +#include <pwd.h> + +void pw_entry (const char *name, struct passwd *pwent) +{ + struct passwd *passwd; + + struct spwd *spwd; + + if (!(passwd = getpwnam (name))) { /* local, no need for xgetpwnam */ + pwent->pw_name = (char *) 0; + return; + } else { + pwent->pw_name = xstrdup (passwd->pw_name); + pwent->pw_uid = passwd->pw_uid; + pwent->pw_gid = passwd->pw_gid; + pwent->pw_gecos = xstrdup (passwd->pw_gecos); + pwent->pw_dir = xstrdup (passwd->pw_dir); + pwent->pw_shell = xstrdup (passwd->pw_shell); +#if !defined(AUTOSHADOW) + /* local, no need for xgetspnam */ + if ((spwd = getspnam (name))) { + pwent->pw_passwd = xstrdup (spwd->sp_pwdp); + return; + } +#endif + pwent->pw_passwd = xstrdup (passwd->pw_passwd); + } +} diff --git a/libmisc/env.c b/libmisc/env.c new file mode 100644 index 00000000..0a4816cd --- /dev/null +++ b/libmisc/env.c @@ -0,0 +1,275 @@ +/* + * Copyright (c) 1989 - 1992, Julianne Frances Haugh + * Copyright (c) 1996 - 1999, Marek Michałkiewicz + * Copyright (c) 2003 - 2005, Tomasz Kłoczko + * Copyright (c) 2008 - 2009, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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> + +#ident "$Id: env.c 3362 2011-06-16 21:25:36Z nekral-guest $" + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "prototypes.h" +#include "defines.h" +/* + * NEWENVP_STEP must be a power of two. This is the number + * of (char *) pointers to allocate at a time, to avoid using + * realloc() too often. + */ +#define NEWENVP_STEP 16 +size_t newenvc = 0; +/*@null@*/char **newenvp = NULL; +extern char **environ; + +static const char *forbid[] = { + "_RLD_=", + "BASH_ENV=", /* GNU creeping featurism strikes again... */ + "ENV=", + "HOME=", + "IFS=", + "KRB_CONF=", + "LD_", /* anything with the LD_ prefix */ + "LIBPATH=", + "MAIL=", + "NLSPATH=", + "PATH=", + "SHELL=", + "SHLIB_PATH=", + (char *) 0 +}; + +/* these are allowed, but with no slashes inside + (to work around security problems in GNU gettext) */ +static const char *noslash[] = { + "LANG=", + "LANGUAGE=", + "LC_", /* anything with the LC_ prefix */ + (char *) 0 +}; + +/* + * initenv() must be called once before using addenv(). + */ +void initenv (void) +{ + newenvp = (char **) xmalloc (NEWENVP_STEP * sizeof (char *)); + *newenvp = NULL; +} + + +void addenv (const char *string, /*@null@*/const char *value) +{ + char *cp, *newstring; + size_t i; + size_t n; + + if (NULL != value) { + size_t len = strlen (string) + strlen (value) + 2; + int wlen; + newstring = xmalloc (len); + wlen = snprintf (newstring, len, "%s=%s", string, value); + assert (wlen == (int) len -1); + } else { + newstring = xstrdup (string); + } + + /* + * Search for a '=' character within the string and if none is found + * just ignore the whole string. + */ + + cp = strchr (newstring, '='); + if (NULL == cp) { + free (newstring); + return; + } + + n = (size_t) (cp - newstring); + + /* + * If this environment variable is already set, change its value. + */ + for (i = 0; i < newenvc; i++) { + if ( (strncmp (newstring, newenvp[i], n) == 0) + && (('=' == newenvp[i][n]) || ('\0' == newenvp[i][n]))) { + break; + } + } + + if (i < newenvc) { + free (newenvp[i]); + newenvp[i] = newstring; + return; + } + + /* + * Otherwise, save the new environment variable + */ + newenvp[newenvc++] = newstring; + + /* + * And extend the environment if needed. + */ + + /* + * Check whether newenvc is a multiple of NEWENVP_STEP. + * If so we have to resize the vector. + * the expression (newenvc & (NEWENVP_STEP - 1)) == 0 + * is equal to (newenvc % NEWENVP_STEP) == 0 + * as long as NEWENVP_STEP is a power of 2. + */ + + if ((newenvc & (NEWENVP_STEP - 1)) == 0) { + char **__newenvp; + size_t newsize; + + /* + * If the resize operation succeeds we can + * happily go on, else print a message. + */ + + newsize = (newenvc + NEWENVP_STEP) * sizeof (char *); + __newenvp = (char **) realloc (newenvp, newsize); + + if (NULL != __newenvp) { + /* + * If this is our current environment, update + * environ so that it doesn't point to some + * free memory area (realloc() could move it). + */ + if (environ == newenvp) { + environ = __newenvp; + } + newenvp = __newenvp; + } else { + (void) fputs (_("Environment overflow\n"), stderr); + newenvc--; + free (newenvp[newenvc]); + } + } + + /* + * The last entry of newenvp must be NULL + */ + + newenvp[newenvc] = NULL; +} + + +/* + * set_env - copy command line arguments into the environment + */ +void set_env (int argc, char *const *argv) +{ + int noname = 1; + char variable[1024]; + char *cp; + + for (; argc > 0; argc--, argv++) { + if (strlen (*argv) >= sizeof variable) { + continue; /* ignore long entries */ + } + + cp = strchr (*argv, '='); + if (NULL == cp) { + int wlen; + wlen = snprintf (variable, sizeof variable, "L%d", noname); + assert (wlen < (int) sizeof(variable)); + noname++; + addenv (variable, *argv); + } else { + const char **p; + + for (p = forbid; NULL != *p; p++) { + if (strncmp (*argv, *p, strlen (*p)) == 0) { + break; + } + } + + if (NULL != *p) { + strncpy (variable, *argv, (size_t)(cp - *argv)); + variable[cp - *argv] = '\0'; + printf (_("You may not change $%s\n"), + variable); + continue; + } + + addenv (*argv, NULL); + } + } +} + +/* + * sanitize_env - remove some nasty environment variables + * If you fall into a total paranoia, you should call this + * function for any root-setuid program or anything the user + * might change the environment with. 99% useless as almost + * all modern Unixes will handle setuid executables properly, + * but... I feel better with that silly precaution. -j. + */ + +void sanitize_env (void) +{ + char **envp = environ; + const char **bad; + char **cur; + char **move; + + for (cur = envp; NULL != *cur; cur++) { + for (bad = forbid; NULL != *bad; bad++) { + if (strncmp (*cur, *bad, strlen (*bad)) == 0) { + for (move = cur; NULL != *move; move++) { + *move = *(move + 1); + } + cur--; + break; + } + } + } + + for (cur = envp; NULL != *cur; cur++) { + for (bad = noslash; NULL != *bad; bad++) { + if (strncmp (*cur, *bad, strlen (*bad)) != 0) { + continue; + } + if (strchr (*cur, '/') == NULL) { + continue; /* OK */ + } + for (move = cur; NULL != *move; move++) { + *move = *(move + 1); + } + cur--; + break; + } + } +} + diff --git a/libmisc/failure.c b/libmisc/failure.c new file mode 100644 index 00000000..776c7a1d --- /dev/null +++ b/libmisc/failure.c @@ -0,0 +1,343 @@ +/* + * Copyright (c) 1989 - 1994, Julianne Frances Haugh + * Copyright (c) 1996 - 1998, Marek Michałkiewicz + * Copyright (c) 2002 - 2005, Tomasz Kłoczko + * Copyright (c) 2008 - 2010, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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> + +#ident "$Id: failure.c 3232 2010-08-22 19:13:53Z nekral-guest $" + +#include <fcntl.h> +#include <stdio.h> +#include <unistd.h> +#include "defines.h" +#include "faillog.h" +#include "getdef.h" +#include "failure.h" +#define YEAR (365L*DAY) +/* + * failure - make failure entry + * + * failure() creates a new (struct faillog) entry or updates an + * existing one with the current failed login information. + */ +void failure (uid_t uid, const char *tty, struct faillog *fl) +{ + int fd; + off_t offset_uid = (off_t) (sizeof *fl) * uid; + + /* + * Don't do anything if failure logging isn't set up. + */ + + if (access (FAILLOG_FILE, F_OK) != 0) { + return; + } + + fd = open (FAILLOG_FILE, O_RDWR); + if (fd < 0) { + SYSLOG ((LOG_WARN, + "Can't write faillog entry for UID %lu in %s.", + (unsigned long) uid, FAILLOG_FILE)); + return; + } + + /* + * The file is indexed by UID value meaning that shared UID's + * share failure log records. That's OK since they really + * share just about everything else ... + */ + + if ( (lseek (fd, offset_uid, SEEK_SET) != offset_uid) + || (read (fd, (char *) fl, sizeof *fl) != (ssize_t) sizeof *fl)) { + /* This is not necessarily a failure. The file is + * initially zero length. + * + * If lseek() or read() failed for any other reason, this + * might reset the counter. But the new failure will be + * logged. + */ + memzero (fl, sizeof *fl); + } + + /* + * Update the record. We increment the failure count to log the + * latest failure. The only concern here is overflow, and we'll + * check for that. The line name and time of day are both + * updated as well. + */ + + if (fl->fail_cnt + 1 > 0) { + fl->fail_cnt++; + } + + strncpy (fl->fail_line, tty, sizeof fl->fail_line); + (void) time (&fl->fail_time); + + /* + * Seek back to the correct position in the file and write the + * record out. Ideally we should lock the file in case the same + * account is being logged simultaneously. But the risk doesn't + * seem that great. + */ + + if ( (lseek (fd, offset_uid, SEEK_SET) != offset_uid) + || (write (fd, (char *) fl, sizeof *fl) != (ssize_t) sizeof *fl) + || (close (fd) != 0)) { + SYSLOG ((LOG_WARN, + "Can't write faillog entry for UID %lu in %s.", + (unsigned long) uid, FAILLOG_FILE)); + (void) close (fd); + } +} + +static bool too_many_failures (const struct faillog *fl) +{ + time_t now; + + if ((0 == fl->fail_max) || (fl->fail_cnt < fl->fail_max)) { + return false; + } + + if (0 == fl->fail_locktime) { + return true; /* locked until reset manually */ + } + + (void) time (&now); + if ((fl->fail_time + fl->fail_locktime) < now) { + return false; /* enough time since last failure */ + } + + return true; +} + +/* + * failcheck - check for failures > allowable + * + * failcheck() is called AFTER the password has been validated. If the + * account has been "attacked" with too many login failures, failcheck() + * returns 0 to indicate that the login should be denied even though + * the password is valid. + * + * failed indicates if the login failed AFTER the password has been + * validated. + */ + +int failcheck (uid_t uid, struct faillog *fl, bool failed) +{ + int fd; + struct faillog fail; + off_t offset_uid = (off_t) (sizeof *fl) * uid; + + /* + * Suppress the check if the log file isn't there. + */ + + if (access (FAILLOG_FILE, F_OK) != 0) { + return 1; + } + + fd = open (FAILLOG_FILE, failed?O_RDONLY:O_RDWR); + if (fd < 0) { + SYSLOG ((LOG_WARN, + "Can't open the faillog file (%s) to check UID %lu. " + "User access authorized.", + FAILLOG_FILE, (unsigned long) uid)); + return 1; + } + + /* + * Get the record from the file and determine if the user has + * exceeded the failure limit. If "max" is zero, any number + * of failures are permitted. Only when "max" is non-zero and + * "cnt" is greater than or equal to "max" is the account + * considered to be locked. + * + * If read fails, there is no record for this user yet (the + * file is initially zero length and extended by writes), so + * no need to reset the count. + */ + + if ( (lseek (fd, offset_uid, SEEK_SET) != offset_uid) + || (read (fd, (char *) fl, sizeof *fl) != (ssize_t) sizeof *fl)) { + (void) close (fd); + return 1; + } + + if (too_many_failures (fl)) { + (void) close (fd); + return 0; + } + + /* + * The record is updated if this is not a failure. The count will + * be reset to zero, but the rest of the information will be left + * in the record in case someone wants to see where the failed + * login originated. + */ + + if (!failed) { + fail = *fl; + fail.fail_cnt = 0; + + if ( (lseek (fd, offset_uid, SEEK_SET) != offset_uid) + || (write (fd, (const void *) &fail, sizeof fail) != (ssize_t) sizeof fail) + || (close (fd) != 0)) { + SYSLOG ((LOG_WARN, + "Can't reset faillog entry for UID %lu in %s.", + (unsigned long) uid, FAILLOG_FILE)); + (void) close (fd); + } + } else { + (void) close (fd); + } + + return 1; +} + +/* + * failprint - print line of failure information + * + * failprint takes a (struct faillog) entry and formats it into a + * message which is displayed at login time. + */ + +void failprint (const struct faillog *fail) +{ + struct tm *tp; + +#if HAVE_STRFTIME + char lasttimeb[256]; + char *lasttime = lasttimeb; +#else + char *lasttime; +#endif + time_t NOW; + + if (0 == fail->fail_cnt) { + return; + } + + tp = localtime (&(fail->fail_time)); + (void) time (&NOW); + +#if HAVE_STRFTIME + /* + * Print all information we have. + */ + (void) strftime (lasttimeb, sizeof lasttimeb, "%c", tp); +#else + + /* + * Do the same thing, but don't use strftime since it + * probably doesn't exist on this system + */ + lasttime = asctime (tp); + lasttime[24] = '\0'; + + if ((NOW - fail->fail_time) < YEAR) { + lasttime[19] = '\0'; + } + if ((NOW - fail->fail_time) < DAY) { + lasttime = lasttime + 11; + } + + if (' ' == *lasttime) { + lasttime++; + } +#endif + /*@-formatconst@*/ + (void) printf (ngettext ("%d failure since last login.\n" + "Last was %s on %s.\n", + "%d failures since last login.\n" + "Last was %s on %s.\n", + (unsigned long) fail->fail_cnt), + fail->fail_cnt, lasttime, fail->fail_line); + /*@=formatconst@*/ +} + +/* + * failtmp - update the cumulative failure log + * + * failtmp updates the (struct utmp) formatted failure log which + * maintains a record of all login failures. + */ + +void failtmp (const char *username, +#ifdef USE_UTMPX + const struct utmpx *failent +#else /* !USE_UTMPX */ + const struct utmp *failent +#endif /* !USE_UTMPX */ + ) +{ + const char *ftmp; + int fd; + + /* + * Get the name of the failure file. If no file has been defined + * in login.defs, don't do this. + */ + + ftmp = getdef_str ("FTMP_FILE"); + if (NULL == ftmp) { + return; + } + + /* + * Open the file for append. It must already exist for this + * feature to be used. + */ + + if (access (ftmp, F_OK) != 0) { + return; + } + + fd = open (ftmp, O_WRONLY | O_APPEND); + if (-1 == fd) { + SYSLOG ((LOG_WARN, + "Can't append failure of user %s to %s.", + username, ftmp)); + return; + } + + /* + * Append the new failure record and close the log file. + */ + + if ( (write (fd, (const void *) failent, sizeof *failent) != (ssize_t) sizeof *failent) + || (close (fd) != 0)) { + SYSLOG ((LOG_WARN, + "Can't append failure of user %s to %s.", + username, ftmp)); + (void) close (fd); + } +} + diff --git a/libmisc/failure.h b/libmisc/failure.h new file mode 100644 index 00000000..739dc885 --- /dev/null +++ b/libmisc/failure.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 1990 - 1994, Julianne Frances Haugh + * Copyright (c) 1997 - 2000, Marek Michałkiewicz + * Copyright (c) 2005 , Tomasz Kłoczko + * Copyright (c) 2008 - 2009, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* $Id: failure.h 2831 2009-04-28 19:19:33Z nekral-guest $ */ +#ifndef _FAILURE_H_ +#define _FAILURE_H_ + +#include "defines.h" +#include "faillog.h" +#ifdef USE_UTMPX +#include <utmpx.h> +#else /* !USE_UTMPX */ +#include <utmp.h> +#endif /* !USE_UTMPX */ + +/* + * failure - make failure entry + * + * failure() creates a new (struct faillog) entry or updates an + * existing one with the current failed login information. + */ +extern void failure (uid_t, const char *, struct faillog *); + +/* + * failcheck - check for failures > allowable + * + * failcheck() is called AFTER the password has been validated. If the + * account has been "attacked" with too many login failures, failcheck() + * returns FALSE to indicate that the login should be denied even though + * the password is valid. + */ +extern int failcheck (uid_t uid, struct faillog *fl, bool failed); + +/* + * failprint - print line of failure information + * + * failprint takes a (struct faillog) entry and formats it into a + * message which is displayed at login time. + */ +extern void failprint (const struct faillog *); + +/* + * failtmp - update the cummulative failure log + * + * failtmp updates the (struct utmp) formatted failure log which + * maintains a record of all login failures. + */ +#ifdef USE_UTMPX +extern void failtmp (const char *username, const struct utmpx *); +#else /* !USE_UTMPX */ +extern void failtmp (const char *username, const struct utmp *); +#endif /* !USE_UTMPX */ + +#endif + diff --git a/libmisc/find_new_gid.c b/libmisc/find_new_gid.c new file mode 100644 index 00000000..d3e51a4b --- /dev/null +++ b/libmisc/find_new_gid.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) 1991 - 1994, Julianne Frances Haugh + * Copyright (c) 2008 - 2011, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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 <assert.h> +#include <stdio.h> +#include <errno.h> + +#include "prototypes.h" +#include "groupio.h" +#include "getdef.h" + +/* + * find_new_gid - Find a new unused GID. + * + * If successful, find_new_gid provides an unused group ID in the + * [GID_MIN:GID_MAX] range. + * This ID should be higher than all the used GID, but if not possible, + * the lowest unused ID in the range will be returned. + * + * Return 0 on success, -1 if no unused GIDs are available. + */ +int find_new_gid (bool sys_group, + gid_t *gid, + /*@null@*/gid_t const *preferred_gid) +{ + const struct group *grp; + gid_t gid_min, gid_max, group_id; + bool *used_gids; + + assert (gid != NULL); + + if (!sys_group) { + gid_min = (gid_t) getdef_ulong ("GID_MIN", 1000UL); + gid_max = (gid_t) getdef_ulong ("GID_MAX", 60000UL); + if (gid_max < gid_min) { + (void) fprintf (stderr, + _("%s: Invalid configuration: GID_MIN (%lu), GID_MAX (%lu)\n"), + Prog, (unsigned long) gid_min, (unsigned long) gid_max); + return -1; + } + } else { + gid_min = (gid_t) getdef_ulong ("SYS_GID_MIN", 101UL); + gid_max = (gid_t) getdef_ulong ("GID_MIN", 1000UL) - 1; + gid_max = (gid_t) getdef_ulong ("SYS_GID_MAX", (unsigned long) gid_max); + if (gid_max < gid_min) { + (void) fprintf (stderr, + _("%s: Invalid configuration: SYS_GID_MIN (%lu), GID_MIN (%lu), SYS_GID_MAX (%lu)\n"), + Prog, (unsigned long) gid_min, getdef_ulong ("GID_MIN", 1000UL), (unsigned long) gid_max); + return -1; + } + } + used_gids = malloc (sizeof (bool) * (gid_max +1)); + if (NULL == used_gids) { + fprintf (stderr, + _("%s: failed to allocate memory: %s\n"), + Prog, strerror (errno)); + return -1; + } + memset (used_gids, false, sizeof (bool) * (gid_max + 1)); + + if ( (NULL != preferred_gid) + && (*preferred_gid >= gid_min) + && (*preferred_gid <= gid_max) + /* Check if the user exists according to NSS */ + && (getgrgid (*preferred_gid) == NULL) + /* Check also the local database in case of uncommitted + * changes */ + && (gr_locate_gid (*preferred_gid) == NULL)) { + *gid = *preferred_gid; + free (used_gids); + return 0; + } + + + /* + * Search the entire group file, + * looking for the largest unused value. + * + * We check the list of groups according to NSS (setgrent/getgrent), + * but we also check the local database (gr_rewind/gr_next) in case + * some groups were created but the changes were not committed yet. + */ + if (sys_group) { + gid_t id; + /* setgrent / getgrent / endgrent can be very slow with + * LDAP configurations (and many accounts). + * Since there is a limited amount of IDs to be tested + * for system accounts, we just check the existence + * of IDs with getgrgid. + */ + group_id = gid_max; + for (id = gid_max; id >= gid_min; id--) { + if (getgrgid (id) != NULL) { + group_id = id - 1; + used_gids[id] = true; + } + } + + (void) gr_rewind (); + while ((grp = gr_next ()) != NULL) { + if ((grp->gr_gid <= group_id) && (grp->gr_gid >= gid_min)) { + group_id = grp->gr_gid - 1; + } + /* create index of used GIDs */ + if (grp->gr_gid <= gid_max) { + used_gids[grp->gr_gid] = true; + } + } + } else { + group_id = gid_min; + setgrent (); + while ((grp = getgrent ()) != NULL) { + if ((grp->gr_gid >= group_id) && (grp->gr_gid <= gid_max)) { + group_id = grp->gr_gid + 1; + } + /* create index of used GIDs */ + if (grp->gr_gid <= gid_max) { + used_gids[grp->gr_gid] = true; + } + } + endgrent (); + + (void) gr_rewind (); + while ((grp = gr_next ()) != NULL) { + if ((grp->gr_gid >= group_id) && (grp->gr_gid <= gid_max)) { + group_id = grp->gr_gid + 1; + } + /* create index of used GIDs */ + if (grp->gr_gid <= gid_max) { + used_gids[grp->gr_gid] = true; + } + } + } + + /* + * If a group (resp. system group) with GID equal to GID_MAX (resp. + * GID_MIN) exists, the above algorithm will give us GID_MAX+1 + * (resp. GID_MIN-1) even if not unique. Search for the first free + * GID starting with GID_MIN (resp. GID_MAX). + */ + if (sys_group) { + if (group_id < gid_min) { + for (group_id = gid_max; group_id >= gid_min; group_id--) { + if (false == used_gids[group_id]) { + break; + } + } + if (group_id < gid_min) { + fprintf (stderr, + _("%s: Can't get unique system GID (no more available GIDs)\n"), + Prog); + SYSLOG ((LOG_WARN, + "no more available GID on the system")); + free (used_gids); + return -1; + } + } + } else { + if (group_id > gid_max) { + for (group_id = gid_min; group_id <= gid_max; group_id++) { + if (false == used_gids[group_id]) { + break; + } + } + if (group_id > gid_max) { + fprintf (stderr, + _("%s: Can't get unique GID (no more available GIDs)\n"), + Prog); + SYSLOG ((LOG_WARN, "no more available GID on the system")); + free (used_gids); + return -1; + } + } + } + + free (used_gids); + *gid = group_id; + return 0; +} + diff --git a/libmisc/find_new_uid.c b/libmisc/find_new_uid.c new file mode 100644 index 00000000..aac1909c --- /dev/null +++ b/libmisc/find_new_uid.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) 1991 - 1994, Julianne Frances Haugh + * Copyright (c) 2008 - 2011, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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 <assert.h> +#include <stdio.h> +#include <errno.h> + +#include "prototypes.h" +#include "pwio.h" +#include "getdef.h" + +/* + * find_new_uid - Find a new unused UID. + * + * If successful, find_new_uid provides an unused user ID in the + * [UID_MIN:UID_MAX] range. + * This ID should be higher than all the used UID, but if not possible, + * the lowest unused ID in the range will be returned. + * + * Return 0 on success, -1 if no unused UIDs are available. + */ +int find_new_uid (bool sys_user, + uid_t *uid, + /*@null@*/uid_t const *preferred_uid) +{ + const struct passwd *pwd; + uid_t uid_min, uid_max, user_id; + bool *used_uids; + + assert (uid != NULL); + + if (!sys_user) { + uid_min = (uid_t) getdef_ulong ("UID_MIN", 1000UL); + uid_max = (uid_t) getdef_ulong ("UID_MAX", 60000UL); + if (uid_max < uid_min) { + (void) fprintf (stderr, + _("%s: Invalid configuration: UID_MIN (%lu), UID_MAX (%lu)\n"), + Prog, (unsigned long) uid_min, (unsigned long) uid_max); + return -1; + } + } else { + uid_min = (uid_t) getdef_ulong ("SYS_UID_MIN", 101UL); + uid_max = (uid_t) getdef_ulong ("UID_MIN", 1000UL) - 1; + uid_max = (uid_t) getdef_ulong ("SYS_UID_MAX", (unsigned long) uid_max); + if (uid_max < uid_min) { + (void) fprintf (stderr, + _("%s: Invalid configuration: SYS_UID_MIN (%lu), UID_MIN (%lu), SYS_UID_MAX (%lu)\n"), + Prog, (unsigned long) uid_min, getdef_ulong ("UID_MIN", 1000UL), (unsigned long) uid_max); + return -1; + } + } + used_uids = malloc (sizeof (bool) * (uid_max +1)); + if (NULL == used_uids) { + fprintf (stderr, + _("%s: failed to allocate memory: %s\n"), + Prog, strerror (errno)); + return -1; + } + memset (used_uids, false, sizeof (bool) * (uid_max + 1)); + + if ( (NULL != preferred_uid) + && (*preferred_uid >= uid_min) + && (*preferred_uid <= uid_max) + /* Check if the user exists according to NSS */ + && (getpwuid (*preferred_uid) == NULL) + /* Check also the local database in case of uncommitted + * changes */ + && (pw_locate_uid (*preferred_uid) == NULL)) { + *uid = *preferred_uid; + free (used_uids); + return 0; + } + + + /* + * Search the entire password file, + * looking for the largest unused value. + * + * We check the list of users according to NSS (setpwent/getpwent), + * but we also check the local database (pw_rewind/pw_next) in case + * some users were created but the changes were not committed yet. + */ + if (sys_user) { + uid_t id; + /* setpwent / getpwent / endpwent can be very slow with + * LDAP configurations (and many accounts). + * Since there is a limited amount of IDs to be tested + * for system accounts, we just check the existence + * of IDs with getpwuid. + */ + user_id = uid_max; + for (id = uid_max; id >= uid_min; id--) { + if (getpwuid (id) != NULL) { + user_id = id - 1; + used_uids[id] = true; + } + } + + (void) pw_rewind (); + while ((pwd = pw_next ()) != NULL) { + if ((pwd->pw_uid <= user_id) && (pwd->pw_uid >= uid_min)) { + user_id = pwd->pw_uid - 1; + } + /* create index of used UIDs */ + if (pwd->pw_uid <= uid_max) { + used_uids[pwd->pw_uid] = true; + } + } + } else { + user_id = uid_min; + setpwent (); + while ((pwd = getpwent ()) != NULL) { + if ((pwd->pw_uid >= user_id) && (pwd->pw_uid <= uid_max)) { + user_id = pwd->pw_uid + 1; + } + /* create index of used UIDs */ + if (pwd->pw_uid <= uid_max) { + used_uids[pwd->pw_uid] = true; + } + } + endpwent (); + + (void) pw_rewind (); + while ((pwd = pw_next ()) != NULL) { + if ((pwd->pw_uid >= user_id) && (pwd->pw_uid <= uid_max)) { + user_id = pwd->pw_uid + 1; + } + /* create index of used UIDs */ + if (pwd->pw_uid <= uid_max) { + used_uids[pwd->pw_uid] = true; + } + } + } + + /* + * If a user (resp. system user) with UID equal to UID_MAX (resp. + * UID_MIN) exists, the above algorithm will give us UID_MAX+1 + * (resp. UID_MIN-1) even if not unique. Search for the first free + * UID starting with UID_MIN (resp. UID_MAX). + */ + if (sys_user) { + if (user_id < uid_min) { + for (user_id = uid_max; user_id >= uid_min; user_id--) { + if (false == used_uids[user_id]) { + break; + } + } + if (user_id < uid_min ) { + fprintf (stderr, + _("%s: Can't get unique system UID (no more available UIDs)\n"), + Prog); + SYSLOG ((LOG_WARN, + "no more available UID on the system")); + free (used_uids); + return -1; + } + } + } else { + if (user_id > uid_max) { + for (user_id = uid_min; user_id <= uid_max; user_id++) { + if (false == used_uids[user_id]) { + break; + } + } + if (user_id > uid_max) { + fprintf (stderr, + _("%s: Can't get unique UID (no more available UIDs)\n"), + Prog); + SYSLOG ((LOG_WARN, "no more available UID on the system")); + free (used_uids); + return -1; + } + } + } + + free (used_uids); + *uid = user_id; + return 0; +} + diff --git a/libmisc/getdate.c b/libmisc/getdate.c new file mode 100644 index 00000000..f637522b --- /dev/null +++ b/libmisc/getdate.c @@ -0,0 +1,2732 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* Skeleton implementation for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.3" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Using locations. */ +#define YYLSP_NEEDED 0 + + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + tAGO = 258, + tDAY = 259, + tDAY_UNIT = 260, + tDAYZONE = 261, + tDST = 262, + tHOUR_UNIT = 263, + tID = 264, + tMERIDIAN = 265, + tMINUTE_UNIT = 266, + tMONTH = 267, + tMONTH_UNIT = 268, + tSEC_UNIT = 269, + tSNUMBER = 270, + tUNUMBER = 271, + tYEAR_UNIT = 272, + tZONE = 273 + }; +#endif +/* Tokens. */ +#define tAGO 258 +#define tDAY 259 +#define tDAY_UNIT 260 +#define tDAYZONE 261 +#define tDST 262 +#define tHOUR_UNIT 263 +#define tID 264 +#define tMERIDIAN 265 +#define tMINUTE_UNIT 266 +#define tMONTH 267 +#define tMONTH_UNIT 268 +#define tSEC_UNIT 269 +#define tSNUMBER 270 +#define tUNUMBER 271 +#define tYEAR_UNIT 272 +#define tZONE 273 + + + + +/* Copy the first part of user declarations. */ +#line 1 "getdate.y" + +/* +** Originally written by Steven M. Bellovin <smb@research.att.com> while +** at the University of North Carolina at Chapel Hill. Later tweaked by +** a couple of people on Usenet. Completely overhauled by Rich $alz +** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990; +** +** This grammar has 13 shift/reduce conflicts. +** +** This code is in the public domain and has no copyright. +*/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +# ifdef FORCE_ALLOCA_H +# include <alloca.h> +# endif +#endif + +/* Since the code of getdate.y is not included in the Emacs executable + itself, there is no need to #define static in this file. Even if + the code were included in the Emacs executable, it probably + wouldn't do any harm to #undef it here; this will only cause + problems if we try to write to a static variable, which I don't + think this code needs to do. */ +#ifdef emacs +# undef static +#endif + +#include <stdio.h> +#include <ctype.h> +#include <time.h> + +#if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII)) +# define IN_CTYPE_DOMAIN(c) 1 +#else +# define IN_CTYPE_DOMAIN(c) isascii(c) +#endif + +#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c)) +#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c)) +#define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper (c)) +#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c)) + +/* ISDIGIT differs from ISDIGIT_LOCALE, as follows: + - Its arg may be any int or unsigned int; it need not be an unsigned char. + - It's guaranteed to evaluate its argument exactly once. + - It's typically faster. + Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that + only '0' through '9' are digits. Prefer ISDIGIT to ISDIGIT_LOCALE unless + it's important to use the locale's definition of `digit' even when the + host does not conform to Posix. */ +#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9) + +#include "getdate.h" + +#if defined (STDC_HEADERS) +# include <string.h> +#endif + +/* Some old versions of bison generate parsers that use bcopy. + That loses on systems that don't provide the function, so we have + to redefine it here. */ +#if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy) +# define bcopy(from, to, len) memcpy ((to), (from), (len)) +#endif + +/* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc), + as well as gratuitiously global symbol names, so we can have multiple + yacc generated parsers in the same program. Note that these are only + the variables produced by yacc. If other parser generators (bison, + byacc, etc) produce additional global names that conflict at link time, + then those parser generators need to be fixed instead of adding those + names to this list. */ + +#define yymaxdepth gd_maxdepth +#define yyparse gd_parse +#define yylex gd_lex +#define yyerror gd_error +#define yylval gd_lval +#define yychar gd_char +#define yydebug gd_debug +#define yypact gd_pact +#define yyr1 gd_r1 +#define yyr2 gd_r2 +#define yydef gd_def +#define yychk gd_chk +#define yypgo gd_pgo +#define yyact gd_act +#define yyexca gd_exca +#define yyerrflag gd_errflag +#define yynerrs gd_nerrs +#define yyps gd_ps +#define yypv gd_pv +#define yys gd_s +#define yy_yys gd_yys +#define yystate gd_state +#define yytmp gd_tmp +#define yyv gd_v +#define yy_yyv gd_yyv +#define yyval gd_val +#define yylloc gd_lloc +#define yyreds gd_reds /* With YYDEBUG defined */ +#define yytoks gd_toks /* With YYDEBUG defined */ +#define yylhs gd_yylhs +#define yylen gd_yylen +#define yydefred gd_yydefred +#define yydgoto gd_yydgoto +#define yysindex gd_yysindex +#define yyrindex gd_yyrindex +#define yygindex gd_yygindex +#define yytable gd_yytable +#define yycheck gd_yycheck + +static int yylex (void); +static int yyerror (const char *s); + +#define EPOCH 1970 +#define HOUR(x) ((x) * 60) + +#define MAX_BUFF_LEN 128 /* size of buffer to read the date into */ + +/* +** An entry in the lexical lookup table. +*/ +typedef struct _TABLE { + const char *name; + int type; + int value; +} TABLE; + + +/* +** Meridian: am, pm, or 24-hour style. +*/ +typedef enum _MERIDIAN { + MERam, MERpm, MER24 +} MERIDIAN; + + +/* +** Global variables. We could get rid of most of these by using a good +** union as the yacc stack. (This routine was originally written before +** yacc had the %union construct.) Maybe someday; right now we only use +** the %union very rarely. +*/ +static const char *yyInput; +static int yyDayOrdinal; +static int yyDayNumber; +static int yyHaveDate; +static int yyHaveDay; +static int yyHaveRel; +static int yyHaveTime; +static int yyHaveZone; +static int yyTimezone; +static int yyDay; +static int yyHour; +static int yyMinutes; +static int yyMonth; +static int yySeconds; +static int yyYear; +static MERIDIAN yyMeridian; +static int yyRelDay; +static int yyRelHour; +static int yyRelMinutes; +static int yyRelMonth; +static int yyRelSeconds; +static int yyRelYear; + + + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +#line 172 "getdate.y" +{ + int Number; + enum _MERIDIAN Meridian; +} +/* Line 187 of yacc.c. */ +#line 308 "getdate.c" + YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + + + +/* Copy the second part of user declarations. */ + + +/* Line 216 of yacc.c. */ +#line 321 "getdate.c" + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if YYENABLE_NLS +# if ENABLE_NLS +# include <libintl.h> /* INFRINGES ON USER NAME SPACE */ +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(e) ((void) (e)) +#else +# define YYUSE(e) /* empty */ +#endif + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(n) (n) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int i) +#else +static int +YYID (i) + int i; +#endif +{ + return i; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include <alloca.h> /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include <malloc.h> /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined _STDLIB_H \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss; + YYSTYPE yyvs; + }; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 2 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 50 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 22 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 11 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 51 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 61 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 273 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 20, 2, 2, 21, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 19, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const yytype_uint8 yyprhs[] = +{ + 0, 0, 3, 4, 7, 9, 11, 13, 15, 17, + 19, 22, 27, 32, 39, 46, 48, 50, 53, 55, + 58, 61, 65, 71, 75, 79, 82, 87, 90, 94, + 97, 99, 102, 105, 107, 110, 113, 115, 118, 121, + 123, 126, 129, 131, 134, 137, 139, 142, 145, 147, + 149, 150 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yytype_int8 yyrhs[] = +{ + 23, 0, -1, -1, 23, 24, -1, 25, -1, 26, + -1, 28, -1, 27, -1, 29, -1, 31, -1, 16, + 10, -1, 16, 19, 16, 32, -1, 16, 19, 16, + 15, -1, 16, 19, 16, 19, 16, 32, -1, 16, + 19, 16, 19, 16, 15, -1, 18, -1, 6, -1, + 18, 7, -1, 4, -1, 4, 20, -1, 16, 4, + -1, 16, 21, 16, -1, 16, 21, 16, 21, 16, + -1, 16, 15, 15, -1, 16, 12, 15, -1, 12, + 16, -1, 12, 16, 20, 16, -1, 16, 12, -1, + 16, 12, 16, -1, 30, 3, -1, 30, -1, 16, + 17, -1, 15, 17, -1, 17, -1, 16, 13, -1, + 15, 13, -1, 13, -1, 16, 5, -1, 15, 5, + -1, 5, -1, 16, 8, -1, 15, 8, -1, 8, + -1, 16, 11, -1, 15, 11, -1, 11, -1, 16, + 14, -1, 15, 14, -1, 14, -1, 16, -1, -1, + 10, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const yytype_uint16 yyrline[] = +{ + 0, 188, 188, 189, 192, 195, 198, 201, 204, 207, + 210, 216, 222, 231, 237, 249, 252, 256, 261, 265, + 269, 275, 279, 297, 303, 309, 313, 318, 322, 329, + 337, 340, 343, 346, 349, 352, 355, 358, 361, 364, + 367, 370, 373, 376, 379, 382, 385, 388, 391, 396, + 430, 433 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "tAGO", "tDAY", "tDAY_UNIT", "tDAYZONE", + "tDST", "tHOUR_UNIT", "tID", "tMERIDIAN", "tMINUTE_UNIT", "tMONTH", + "tMONTH_UNIT", "tSEC_UNIT", "tSNUMBER", "tUNUMBER", "tYEAR_UNIT", + "tZONE", "':'", "','", "'/'", "$accept", "spec", "item", "time", "zone", + "day", "date", "rel", "relunit", "number", "o_merid", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 58, + 44, 47 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 22, 23, 23, 24, 24, 24, 24, 24, 24, + 25, 25, 25, 25, 25, 26, 26, 26, 27, 27, + 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, + 29, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 31, + 32, 32 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 0, 2, 1, 1, 1, 1, 1, 1, + 2, 4, 4, 6, 6, 1, 1, 2, 1, 2, + 2, 3, 5, 3, 3, 2, 4, 2, 3, 2, + 1, 2, 2, 1, 2, 2, 1, 2, 2, 1, + 2, 2, 1, 2, 2, 1, 2, 2, 1, 1, + 0, 1 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 2, 0, 1, 18, 39, 16, 42, 45, 0, 36, + 48, 0, 49, 33, 15, 3, 4, 5, 7, 6, + 8, 30, 9, 19, 25, 38, 41, 44, 35, 47, + 32, 20, 37, 40, 10, 43, 27, 34, 46, 0, + 31, 0, 0, 17, 29, 0, 24, 28, 23, 50, + 21, 26, 51, 12, 0, 11, 0, 50, 22, 14, + 13 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 1, 15, 16, 17, 18, 19, 20, 21, 22, + 55 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -20 +static const yytype_int8 yypact[] = +{ + -20, 0, -20, -19, -20, -20, -20, -20, -13, -20, + -20, 30, 15, -20, 14, -20, -20, -20, -20, -20, + -20, 19, -20, -20, 4, -20, -20, -20, -20, -20, + -20, -20, -20, -20, -20, -20, -6, -20, -20, 16, + -20, 17, 23, -20, -20, 24, -20, -20, -20, 27, + 28, -20, -20, -20, 29, -20, 32, -8, -20, -20, + -20 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, + -7 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -1 +static const yytype_uint8 yytable[] = +{ + 2, 23, 52, 24, 3, 4, 5, 59, 6, 46, + 47, 7, 8, 9, 10, 11, 12, 13, 14, 31, + 32, 43, 44, 33, 45, 34, 35, 36, 37, 38, + 39, 48, 40, 49, 41, 25, 42, 52, 26, 50, + 51, 27, 53, 28, 29, 57, 54, 30, 58, 56, + 60 +}; + +static const yytype_uint8 yycheck[] = +{ + 0, 20, 10, 16, 4, 5, 6, 15, 8, 15, + 16, 11, 12, 13, 14, 15, 16, 17, 18, 4, + 5, 7, 3, 8, 20, 10, 11, 12, 13, 14, + 15, 15, 17, 16, 19, 5, 21, 10, 8, 16, + 16, 11, 15, 13, 14, 16, 19, 17, 16, 21, + 57 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 23, 0, 4, 5, 6, 8, 11, 12, 13, + 14, 15, 16, 17, 18, 24, 25, 26, 27, 28, + 29, 30, 31, 20, 16, 5, 8, 11, 13, 14, + 17, 4, 5, 8, 10, 11, 12, 13, 14, 15, + 17, 19, 21, 7, 3, 20, 15, 16, 15, 16, + 16, 16, 10, 15, 19, 32, 21, 16, 16, 15, + 32 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ + +#define YYFAIL goto yyerrlab + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK (1); \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + + +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (YYID (N)) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (YYID (0)) +#endif + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if YYLTYPE_IS_TRIVIAL +# define YY_LOCATION_PRINT(File, Loc) \ + fprintf (File, "%d.%d-%d.%d", \ + (Loc).first_line, (Loc).first_column, \ + (Loc).last_line, (Loc).last_column) +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (YYLEX_PARAM) +#else +# define YYLEX yylex () +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include <stdio.h> /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + yy_symbol_value_print (yyoutput, yytype, yyvaluep); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) +#else +static void +yy_stack_print (bottom, top) + yytype_int16 *bottom; + yytype_int16 *top; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, int yyrule) +#else +static void +yy_reduce_print (yyvsp, yyrule) + YYSTYPE *yyvsp; + int yyrule; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + fprintf (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + ); + fprintf (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, Rule); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into YYRESULT an error message about the unexpected token + YYCHAR while in state YYSTATE. Return the number of bytes copied, + including the terminating null byte. If YYRESULT is null, do not + copy anything; just return the number of bytes that would be + copied. As a special case, return 0 if an ordinary "syntax error" + message will do. Return YYSIZE_MAXIMUM if overflow occurs during + size calculation. */ +static YYSIZE_T +yysyntax_error (char *yyresult, int yystate, int yychar) +{ + int yyn = yypact[yystate]; + + if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) + return 0; + else + { + int yytype = YYTRANSLATE (yychar); + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + int yysize_overflow = 0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + int yyx; + +# if 0 + /* This is so xgettext sees the translatable formats that are + constructed on the fly. */ + YY_("syntax error, unexpected %s"); + YY_("syntax error, unexpected %s, expecting %s"); + YY_("syntax error, unexpected %s, expecting %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); +# endif + char *yyfmt; + char const *yyf; + static char const yyunexpected[] = "syntax error, unexpected %s"; + static char const yyexpecting[] = ", expecting %s"; + static char const yyor[] = " or %s"; + char yyformat[sizeof yyunexpected + + sizeof yyexpecting - 1 + + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) + * (sizeof yyor - 1))]; + char const *yyprefix = yyexpecting; + + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yycount = 1; + + yyarg[0] = yytname[yytype]; + yyfmt = yystpcpy (yyformat, yyunexpected); + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + yyformat[sizeof yyunexpected - 1] = '\0'; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + yyfmt = yystpcpy (yyfmt, yyprefix); + yyprefix = yyor; + } + + yyf = YY_(yyformat); + yysize1 = yysize + yystrlen (yyf); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + + if (yysize_overflow) + return YYSIZE_MAXIMUM; + + if (yyresult) + { + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + char *yyp = yyresult; + int yyi = 0; + while ((*yyp = *yyf) != '\0') + { + if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyf += 2; + } + else + { + yyp++; + yyf++; + } + } + } + return yysize; + } +} +#endif /* YYERROR_VERBOSE */ + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yymsg, yytype, yyvaluep) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + YYUSE (yyvaluep); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ + +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + + +/* The look-ahead symbol. */ +int yychar; + +/* The semantic value of the look-ahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ + + int yystate; + int yyn; + int yyresult; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Look-ahead token as an internal (translated) token number. */ + int yytoken = 0; +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss = yyssa; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp; + + + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + YYSIZE_T yystacksize = YYINITDEPTH; + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss; + yyvsp = yyvs; + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + look-ahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to look-ahead token. */ + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a look-ahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + if (yyn == YYFINAL) + YYACCEPT; + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the look-ahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + yystate = yyn; + *++yyvsp = yylval; + + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 4: +#line 192 "getdate.y" + { + yyHaveTime++; + } + break; + + case 5: +#line 195 "getdate.y" + { + yyHaveZone++; + } + break; + + case 6: +#line 198 "getdate.y" + { + yyHaveDate++; + } + break; + + case 7: +#line 201 "getdate.y" + { + yyHaveDay++; + } + break; + + case 8: +#line 204 "getdate.y" + { + yyHaveRel++; + } + break; + + case 10: +#line 210 "getdate.y" + { + yyHour = (yyvsp[(1) - (2)].Number); + yyMinutes = 0; + yySeconds = 0; + yyMeridian = (yyvsp[(2) - (2)].Meridian); + } + break; + + case 11: +#line 216 "getdate.y" + { + yyHour = (yyvsp[(1) - (4)].Number); + yyMinutes = (yyvsp[(3) - (4)].Number); + yySeconds = 0; + yyMeridian = (yyvsp[(4) - (4)].Meridian); + } + break; + + case 12: +#line 222 "getdate.y" + { + yyHour = (yyvsp[(1) - (4)].Number); + yyMinutes = (yyvsp[(3) - (4)].Number); + yyMeridian = MER24; + yyHaveZone++; + yyTimezone = ((yyvsp[(4) - (4)].Number) < 0 + ? -(yyvsp[(4) - (4)].Number) % 100 + (-(yyvsp[(4) - (4)].Number) / 100) * 60 + : - ((yyvsp[(4) - (4)].Number) % 100 + ((yyvsp[(4) - (4)].Number) / 100) * 60)); + } + break; + + case 13: +#line 231 "getdate.y" + { + yyHour = (yyvsp[(1) - (6)].Number); + yyMinutes = (yyvsp[(3) - (6)].Number); + yySeconds = (yyvsp[(5) - (6)].Number); + yyMeridian = (yyvsp[(6) - (6)].Meridian); + } + break; + + case 14: +#line 237 "getdate.y" + { + yyHour = (yyvsp[(1) - (6)].Number); + yyMinutes = (yyvsp[(3) - (6)].Number); + yySeconds = (yyvsp[(5) - (6)].Number); + yyMeridian = MER24; + yyHaveZone++; + yyTimezone = ((yyvsp[(6) - (6)].Number) < 0 + ? -(yyvsp[(6) - (6)].Number) % 100 + (-(yyvsp[(6) - (6)].Number) / 100) * 60 + : - ((yyvsp[(6) - (6)].Number) % 100 + ((yyvsp[(6) - (6)].Number) / 100) * 60)); + } + break; + + case 15: +#line 249 "getdate.y" + { + yyTimezone = (yyvsp[(1) - (1)].Number); + } + break; + + case 16: +#line 252 "getdate.y" + { + yyTimezone = (yyvsp[(1) - (1)].Number) - 60; + } + break; + + case 17: +#line 256 "getdate.y" + { + yyTimezone = (yyvsp[(1) - (2)].Number) - 60; + } + break; + + case 18: +#line 261 "getdate.y" + { + yyDayOrdinal = 1; + yyDayNumber = (yyvsp[(1) - (1)].Number); + } + break; + + case 19: +#line 265 "getdate.y" + { + yyDayOrdinal = 1; + yyDayNumber = (yyvsp[(1) - (2)].Number); + } + break; + + case 20: +#line 269 "getdate.y" + { + yyDayOrdinal = (yyvsp[(1) - (2)].Number); + yyDayNumber = (yyvsp[(2) - (2)].Number); + } + break; + + case 21: +#line 275 "getdate.y" + { + yyMonth = (yyvsp[(1) - (3)].Number); + yyDay = (yyvsp[(3) - (3)].Number); + } + break; + + case 22: +#line 279 "getdate.y" + { + /* Interpret as YYYY/MM/DD if $1 >= 1000, otherwise as MM/DD/YY. + The goal in recognizing YYYY/MM/DD is solely to support legacy + machine-generated dates like those in an RCS log listing. If + you want portability, use the ISO 8601 format. */ + if ((yyvsp[(1) - (5)].Number) >= 1000) + { + yyYear = (yyvsp[(1) - (5)].Number); + yyMonth = (yyvsp[(3) - (5)].Number); + yyDay = (yyvsp[(5) - (5)].Number); + } + else + { + yyMonth = (yyvsp[(1) - (5)].Number); + yyDay = (yyvsp[(3) - (5)].Number); + yyYear = (yyvsp[(5) - (5)].Number); + } + } + break; + + case 23: +#line 297 "getdate.y" + { + /* ISO 8601 format. yyyy-mm-dd. */ + yyYear = (yyvsp[(1) - (3)].Number); + yyMonth = -(yyvsp[(2) - (3)].Number); + yyDay = -(yyvsp[(3) - (3)].Number); + } + break; + + case 24: +#line 303 "getdate.y" + { + /* e.g. 17-JUN-1992. */ + yyDay = (yyvsp[(1) - (3)].Number); + yyMonth = (yyvsp[(2) - (3)].Number); + yyYear = -(yyvsp[(3) - (3)].Number); + } + break; + + case 25: +#line 309 "getdate.y" + { + yyMonth = (yyvsp[(1) - (2)].Number); + yyDay = (yyvsp[(2) - (2)].Number); + } + break; + + case 26: +#line 313 "getdate.y" + { + yyMonth = (yyvsp[(1) - (4)].Number); + yyDay = (yyvsp[(2) - (4)].Number); + yyYear = (yyvsp[(4) - (4)].Number); + } + break; + + case 27: +#line 318 "getdate.y" + { + yyMonth = (yyvsp[(2) - (2)].Number); + yyDay = (yyvsp[(1) - (2)].Number); + } + break; + + case 28: +#line 322 "getdate.y" + { + yyMonth = (yyvsp[(2) - (3)].Number); + yyDay = (yyvsp[(1) - (3)].Number); + yyYear = (yyvsp[(3) - (3)].Number); + } + break; + + case 29: +#line 329 "getdate.y" + { + yyRelSeconds = -yyRelSeconds; + yyRelMinutes = -yyRelMinutes; + yyRelHour = -yyRelHour; + yyRelDay = -yyRelDay; + yyRelMonth = -yyRelMonth; + yyRelYear = -yyRelYear; + } + break; + + case 31: +#line 340 "getdate.y" + { + yyRelYear += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); + } + break; + + case 32: +#line 343 "getdate.y" + { + yyRelYear += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); + } + break; + + case 33: +#line 346 "getdate.y" + { + yyRelYear++; + } + break; + + case 34: +#line 349 "getdate.y" + { + yyRelMonth += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); + } + break; + + case 35: +#line 352 "getdate.y" + { + yyRelMonth += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); + } + break; + + case 36: +#line 355 "getdate.y" + { + yyRelMonth++; + } + break; + + case 37: +#line 358 "getdate.y" + { + yyRelDay += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); + } + break; + + case 38: +#line 361 "getdate.y" + { + yyRelDay += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); + } + break; + + case 39: +#line 364 "getdate.y" + { + yyRelDay++; + } + break; + + case 40: +#line 367 "getdate.y" + { + yyRelHour += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); + } + break; + + case 41: +#line 370 "getdate.y" + { + yyRelHour += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); + } + break; + + case 42: +#line 373 "getdate.y" + { + yyRelHour++; + } + break; + + case 43: +#line 376 "getdate.y" + { + yyRelMinutes += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); + } + break; + + case 44: +#line 379 "getdate.y" + { + yyRelMinutes += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); + } + break; + + case 45: +#line 382 "getdate.y" + { + yyRelMinutes++; + } + break; + + case 46: +#line 385 "getdate.y" + { + yyRelSeconds += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); + } + break; + + case 47: +#line 388 "getdate.y" + { + yyRelSeconds += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); + } + break; + + case 48: +#line 391 "getdate.y" + { + yyRelSeconds++; + } + break; + + case 49: +#line 397 "getdate.y" + { + if ((yyHaveTime != 0) && (yyHaveDate != 0) && (yyHaveRel == 0)) + yyYear = (yyvsp[(1) - (1)].Number); + else + { + if ((yyvsp[(1) - (1)].Number)>10000) + { + yyHaveDate++; + yyDay= ((yyvsp[(1) - (1)].Number))%100; + yyMonth= ((yyvsp[(1) - (1)].Number)/100)%100; + yyYear = (yyvsp[(1) - (1)].Number)/10000; + } + else + { + yyHaveTime++; + if ((yyvsp[(1) - (1)].Number) < 100) + { + yyHour = (yyvsp[(1) - (1)].Number); + yyMinutes = 0; + } + else + { + yyHour = (yyvsp[(1) - (1)].Number) / 100; + yyMinutes = (yyvsp[(1) - (1)].Number) % 100; + } + yySeconds = 0; + yyMeridian = MER24; + } + } + } + break; + + case 50: +#line 430 "getdate.y" + { + (yyval.Meridian) = MER24; + } + break; + + case 51: +#line 434 "getdate.y" + { + (yyval.Meridian) = (yyvsp[(1) - (1)].Meridian); + } + break; + + +/* Line 1267 of yacc.c. */ +#line 1979 "getdate.c" + default: break; + } + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (YY_("syntax error")); +#else + { + YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); + if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) + { + YYSIZE_T yyalloc = 2 * yysize; + if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) + yyalloc = YYSTACK_ALLOC_MAXIMUM; + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yyalloc); + if (yymsg) + yymsg_alloc = yyalloc; + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + } + } + + if (0 < yysize && yysize <= yymsg_alloc) + { + (void) yysyntax_error (yymsg, yystate, yychar); + yyerror (yymsg); + } + else + { + yyerror (YY_("syntax error")); + if (yysize != 0) + goto yyexhaustedlab; + } + } +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse look-ahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse look-ahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + if (yyn == YYFINAL) + YYACCEPT; + + *++yyvsp = yylval; + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#ifndef yyoverflow +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEOF && yychar != YYEMPTY) + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + /* Do not reclaim the symbols of the rule which action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + +#line 439 "getdate.y" + + +/* Month and day table. */ +static TABLE const MonthDayTable[] = { + { "january", tMONTH, 1 }, + { "february", tMONTH, 2 }, + { "march", tMONTH, 3 }, + { "april", tMONTH, 4 }, + { "may", tMONTH, 5 }, + { "june", tMONTH, 6 }, + { "july", tMONTH, 7 }, + { "august", tMONTH, 8 }, + { "september", tMONTH, 9 }, + { "sept", tMONTH, 9 }, + { "october", tMONTH, 10 }, + { "november", tMONTH, 11 }, + { "december", tMONTH, 12 }, + { "sunday", tDAY, 0 }, + { "monday", tDAY, 1 }, + { "tuesday", tDAY, 2 }, + { "tues", tDAY, 2 }, + { "wednesday", tDAY, 3 }, + { "wednes", tDAY, 3 }, + { "thursday", tDAY, 4 }, + { "thur", tDAY, 4 }, + { "thurs", tDAY, 4 }, + { "friday", tDAY, 5 }, + { "saturday", tDAY, 6 }, + { NULL, 0, 0 } +}; + +/* Time units table. */ +static TABLE const UnitsTable[] = { + { "year", tYEAR_UNIT, 1 }, + { "month", tMONTH_UNIT, 1 }, + { "fortnight", tDAY_UNIT, 14 }, + { "week", tDAY_UNIT, 7 }, + { "day", tDAY_UNIT, 1 }, + { "hour", tHOUR_UNIT, 1 }, + { "minute", tMINUTE_UNIT, 1 }, + { "min", tMINUTE_UNIT, 1 }, + { "second", tSEC_UNIT, 1 }, + { "sec", tSEC_UNIT, 1 }, + { NULL, 0, 0 } +}; + +/* Assorted relative-time words. */ +static TABLE const OtherTable[] = { + { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 }, + { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 }, + { "today", tMINUTE_UNIT, 0 }, + { "now", tMINUTE_UNIT, 0 }, + { "last", tUNUMBER, -1 }, + { "this", tMINUTE_UNIT, 0 }, + { "next", tUNUMBER, 2 }, + { "first", tUNUMBER, 1 }, +/* { "second", tUNUMBER, 2 }, */ + { "third", tUNUMBER, 3 }, + { "fourth", tUNUMBER, 4 }, + { "fifth", tUNUMBER, 5 }, + { "sixth", tUNUMBER, 6 }, + { "seventh", tUNUMBER, 7 }, + { "eighth", tUNUMBER, 8 }, + { "ninth", tUNUMBER, 9 }, + { "tenth", tUNUMBER, 10 }, + { "eleventh", tUNUMBER, 11 }, + { "twelfth", tUNUMBER, 12 }, + { "ago", tAGO, 1 }, + { NULL, 0, 0 } +}; + +/* The timezone table. */ +static TABLE const TimezoneTable[] = { + { "gmt", tZONE, HOUR ( 0) }, /* Greenwich Mean */ + { "ut", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */ + { "utc", tZONE, HOUR ( 0) }, + { "wet", tZONE, HOUR ( 0) }, /* Western European */ + { "bst", tDAYZONE, HOUR ( 0) }, /* British Summer */ + { "wat", tZONE, HOUR ( 1) }, /* West Africa */ + { "at", tZONE, HOUR ( 2) }, /* Azores */ + { "ast", tZONE, HOUR ( 4) }, /* Atlantic Standard */ + { "adt", tDAYZONE, HOUR ( 4) }, /* Atlantic Daylight */ + { "est", tZONE, HOUR ( 5) }, /* Eastern Standard */ + { "edt", tDAYZONE, HOUR ( 5) }, /* Eastern Daylight */ + { "cst", tZONE, HOUR ( 6) }, /* Central Standard */ + { "cdt", tDAYZONE, HOUR ( 6) }, /* Central Daylight */ + { "mst", tZONE, HOUR ( 7) }, /* Mountain Standard */ + { "mdt", tDAYZONE, HOUR ( 7) }, /* Mountain Daylight */ + { "pst", tZONE, HOUR ( 8) }, /* Pacific Standard */ + { "pdt", tDAYZONE, HOUR ( 8) }, /* Pacific Daylight */ + { "yst", tZONE, HOUR ( 9) }, /* Yukon Standard */ + { "ydt", tDAYZONE, HOUR ( 9) }, /* Yukon Daylight */ + { "hst", tZONE, HOUR (10) }, /* Hawaii Standard */ + { "hdt", tDAYZONE, HOUR (10) }, /* Hawaii Daylight */ + { "cat", tZONE, HOUR (10) }, /* Central Alaska */ + { "ahst", tZONE, HOUR (10) }, /* Alaska-Hawaii Standard */ + { "nt", tZONE, HOUR (11) }, /* Nome */ + { "idlw", tZONE, HOUR (12) }, /* International Date Line West */ + { "cet", tZONE, -HOUR (1) }, /* Central European */ + { "met", tZONE, -HOUR (1) }, /* Middle European */ + { "mewt", tZONE, -HOUR (1) }, /* Middle European Winter */ + { "mest", tDAYZONE, -HOUR (1) }, /* Middle European Summer */ + { "mesz", tDAYZONE, -HOUR (1) }, /* Middle European Summer */ + { "swt", tZONE, -HOUR (1) }, /* Swedish Winter */ + { "sst", tDAYZONE, -HOUR (1) }, /* Swedish Summer */ + { "fwt", tZONE, -HOUR (1) }, /* French Winter */ + { "fst", tDAYZONE, -HOUR (1) }, /* French Summer */ + { "eet", tZONE, -HOUR (2) }, /* Eastern Europe, USSR Zone 1 */ + { "bt", tZONE, -HOUR (3) }, /* Baghdad, USSR Zone 2 */ + { "zp4", tZONE, -HOUR (4) }, /* USSR Zone 3 */ + { "zp5", tZONE, -HOUR (5) }, /* USSR Zone 4 */ + { "zp6", tZONE, -HOUR (6) }, /* USSR Zone 5 */ + { "wast", tZONE, -HOUR (7) }, /* West Australian Standard */ + { "wadt", tDAYZONE, -HOUR (7) }, /* West Australian Daylight */ + { "cct", tZONE, -HOUR (8) }, /* China Coast, USSR Zone 7 */ + { "jst", tZONE, -HOUR (9) }, /* Japan Standard, USSR Zone 8 */ + { "east", tZONE, -HOUR (10) }, /* Eastern Australian Standard */ + { "eadt", tDAYZONE, -HOUR (10) }, /* Eastern Australian Daylight */ + { "gst", tZONE, -HOUR (10) }, /* Guam Standard, USSR Zone 9 */ + { "nzt", tZONE, -HOUR (12) }, /* New Zealand */ + { "nzst", tZONE, -HOUR (12) }, /* New Zealand Standard */ + { "nzdt", tDAYZONE, -HOUR (12) }, /* New Zealand Daylight */ + { "idle", tZONE, -HOUR (12) }, /* International Date Line East */ + { NULL, 0, 0 } +}; + +/* Military timezone table. */ +static TABLE const MilitaryTable[] = { + { "a", tZONE, HOUR ( 1) }, + { "b", tZONE, HOUR ( 2) }, + { "c", tZONE, HOUR ( 3) }, + { "d", tZONE, HOUR ( 4) }, + { "e", tZONE, HOUR ( 5) }, + { "f", tZONE, HOUR ( 6) }, + { "g", tZONE, HOUR ( 7) }, + { "h", tZONE, HOUR ( 8) }, + { "i", tZONE, HOUR ( 9) }, + { "k", tZONE, HOUR ( 10) }, + { "l", tZONE, HOUR ( 11) }, + { "m", tZONE, HOUR ( 12) }, + { "n", tZONE, HOUR (- 1) }, + { "o", tZONE, HOUR (- 2) }, + { "p", tZONE, HOUR (- 3) }, + { "q", tZONE, HOUR (- 4) }, + { "r", tZONE, HOUR (- 5) }, + { "s", tZONE, HOUR (- 6) }, + { "t", tZONE, HOUR (- 7) }, + { "u", tZONE, HOUR (- 8) }, + { "v", tZONE, HOUR (- 9) }, + { "w", tZONE, HOUR (-10) }, + { "x", tZONE, HOUR (-11) }, + { "y", tZONE, HOUR (-12) }, + { "z", tZONE, HOUR ( 0) }, + { NULL, 0, 0 } +}; + + + + +static int yyerror (unused const char *s) +{ + return 0; +} + +static int ToHour (int Hours, MERIDIAN Meridian) +{ + switch (Meridian) + { + case MER24: + if (Hours < 0 || Hours > 23) + return -1; + return Hours; + case MERam: + if (Hours < 1 || Hours > 12) + return -1; + if (Hours == 12) + Hours = 0; + return Hours; + case MERpm: + if (Hours < 1 || Hours > 12) + return -1; + if (Hours == 12) + Hours = 0; + return Hours + 12; + default: + abort (); + } + /* NOTREACHED */ +} + +static int ToYear (int Year) +{ + if (Year < 0) + Year = -Year; + + /* XPG4 suggests that years 00-68 map to 2000-2068, and + years 69-99 map to 1969-1999. */ + if (Year < 69) + Year += 2000; + else if (Year < 100) + Year += 1900; + + return Year; +} + +static int LookupWord (char *buff) +{ + register char *p; + register char *q; + register const TABLE *tp; + int i; + bool abbrev; + + /* Make it lowercase. */ + for (p = buff; '\0' != *p; p++) + if (ISUPPER (*p)) + *p = tolower (*p); + + if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0) + { + yylval.Meridian = MERam; + return tMERIDIAN; + } + if (strcmp (buff, "pm") == 0 || strcmp (buff, "p.m.") == 0) + { + yylval.Meridian = MERpm; + return tMERIDIAN; + } + + /* See if we have an abbreviation for a month. */ + if (strlen (buff) == 3) + abbrev = true; + else if (strlen (buff) == 4 && buff[3] == '.') + { + abbrev = true; + buff[3] = '\0'; + } + else + abbrev = false; + + for (tp = MonthDayTable; tp->name; tp++) + { + if (abbrev) + { + if (strncmp (buff, tp->name, 3) == 0) + { + yylval.Number = tp->value; + return tp->type; + } + } + else if (strcmp (buff, tp->name) == 0) + { + yylval.Number = tp->value; + return tp->type; + } + } + + for (tp = TimezoneTable; tp->name; tp++) + if (strcmp (buff, tp->name) == 0) + { + yylval.Number = tp->value; + return tp->type; + } + + if (strcmp (buff, "dst") == 0) + return tDST; + + for (tp = UnitsTable; tp->name; tp++) + if (strcmp (buff, tp->name) == 0) + { + yylval.Number = tp->value; + return tp->type; + } + + /* Strip off any plural and try the units table again. */ + i = strlen (buff) - 1; + if (buff[i] == 's') + { + buff[i] = '\0'; + for (tp = UnitsTable; tp->name; tp++) + if (strcmp (buff, tp->name) == 0) + { + yylval.Number = tp->value; + return tp->type; + } + buff[i] = 's'; /* Put back for "this" in OtherTable. */ + } + + for (tp = OtherTable; tp->name; tp++) + if (strcmp (buff, tp->name) == 0) + { + yylval.Number = tp->value; + return tp->type; + } + + /* Military timezones. */ + if (buff[1] == '\0' && ISALPHA (*buff)) + { + for (tp = MilitaryTable; tp->name; tp++) + if (strcmp (buff, tp->name) == 0) + { + yylval.Number = tp->value; + return tp->type; + } + } + + /* Drop out any periods and try the timezone table again. */ + for (i = 0, p = q = buff; '\0' != *q; q++) + if (*q != '.') + *p++ = *q; + else + i++; + *p = '\0'; + if (0 != i) + for (tp = TimezoneTable; NULL != tp->name; tp++) + if (strcmp (buff, tp->name) == 0) + { + yylval.Number = tp->value; + return tp->type; + } + + return tID; +} + +static int +yylex (void) +{ + register char c; + register char *p; + char buff[20]; + int Count; + int sign; + + for (;;) + { + while (ISSPACE (*yyInput)) + yyInput++; + + if (ISDIGIT (c = *yyInput) || c == '-' || c == '+') + { + if (c == '-' || c == '+') + { + sign = c == '-' ? -1 : 1; + if (!ISDIGIT (*++yyInput)) + /* skip the '-' sign */ + continue; + } + else + sign = 0; + for (yylval.Number = 0; ISDIGIT (c = *yyInput++);) + yylval.Number = 10 * yylval.Number + c - '0'; + yyInput--; + if (sign < 0) + yylval.Number = -yylval.Number; + return (0 != sign) ? tSNUMBER : tUNUMBER; + } + if (ISALPHA (c)) + { + for (p = buff; (c = *yyInput++, ISALPHA (c)) || c == '.';) + if (p < &buff[sizeof buff - 1]) + *p++ = c; + *p = '\0'; + yyInput--; + return LookupWord (buff); + } + if (c != '(') + return *yyInput++; + Count = 0; + do + { + c = *yyInput++; + if (c == '\0') + return c; + if (c == '(') + Count++; + else if (c == ')') + Count--; + } + while (Count > 0); + } +} + +#define TM_YEAR_ORIGIN 1900 + +/* Yield A - B, measured in seconds. */ +static long difftm (struct tm *a, struct tm *b) +{ + int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); + int by = b->tm_year + (TM_YEAR_ORIGIN - 1); + long days = ( + /* difference in day of year */ + a->tm_yday - b->tm_yday + /* + intervening leap days */ + + ((ay >> 2) - (by >> 2)) + - (ay / 100 - by / 100) + + ((ay / 100 >> 2) - (by / 100 >> 2)) + /* + difference in years * 365 */ + + (long) (ay - by) * 365 + ); + return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour)) + + (a->tm_min - b->tm_min)) + + (a->tm_sec - b->tm_sec)); +} + +time_t get_date (const char *p, const time_t *now) +{ + struct tm tm, tm0, *tmp; + time_t Start; + + yyInput = p; + Start = now ? *now : time ((time_t *) NULL); + tmp = localtime (&Start); + yyYear = tmp->tm_year + TM_YEAR_ORIGIN; + yyMonth = tmp->tm_mon + 1; + yyDay = tmp->tm_mday; + yyHour = tmp->tm_hour; + yyMinutes = tmp->tm_min; + yySeconds = tmp->tm_sec; + yyMeridian = MER24; + yyRelSeconds = 0; + yyRelMinutes = 0; + yyRelHour = 0; + yyRelDay = 0; + yyRelMonth = 0; + yyRelYear = 0; + yyHaveDate = 0; + yyHaveDay = 0; + yyHaveRel = 0; + yyHaveTime = 0; + yyHaveZone = 0; + + if (yyparse () + || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1) + return -1; + + tm.tm_year = ToYear (yyYear) - TM_YEAR_ORIGIN + yyRelYear; + tm.tm_mon = yyMonth - 1 + yyRelMonth; + tm.tm_mday = yyDay + yyRelDay; + if ((yyHaveTime != 0) || + ( (yyHaveRel != 0) && (yyHaveDate == 0) && (yyHaveDay == 0) )) + { + tm.tm_hour = ToHour (yyHour, yyMeridian); + if (tm.tm_hour < 0) + return -1; + tm.tm_min = yyMinutes; + tm.tm_sec = yySeconds; + } + else + { + tm.tm_hour = tm.tm_min = tm.tm_sec = 0; + } + tm.tm_hour += yyRelHour; + tm.tm_min += yyRelMinutes; + tm.tm_sec += yyRelSeconds; + tm.tm_isdst = -1; + tm0 = tm; + + Start = mktime (&tm); + + if (Start == (time_t) -1) + { + + /* Guard against falsely reporting errors near the time_t boundaries + when parsing times in other time zones. For example, if the min + time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead + of UTC, then the min localtime value is 1970-01-01 08:00:00; if + we apply mktime to 1970-01-01 00:00:00 we will get an error, so + we apply mktime to 1970-01-02 08:00:00 instead and adjust the time + zone by 24 hours to compensate. This algorithm assumes that + there is no DST transition within a day of the time_t boundaries. */ + if (yyHaveZone) + { + tm = tm0; + if (tm.tm_year <= EPOCH - TM_YEAR_ORIGIN) + { + tm.tm_mday++; + yyTimezone -= 24 * 60; + } + else + { + tm.tm_mday--; + yyTimezone += 24 * 60; + } + Start = mktime (&tm); + } + + if (Start == (time_t) -1) + return Start; + } + + if (yyHaveDay && !yyHaveDate) + { + tm.tm_mday += ((yyDayNumber - tm.tm_wday + 7) % 7 + + 7 * (yyDayOrdinal - (0 < yyDayOrdinal))); + Start = mktime (&tm); + if (Start == (time_t) -1) + return Start; + } + + if (yyHaveZone) + { + long delta = yyTimezone * 60L + difftm (&tm, gmtime (&Start)); + if ((Start + delta < Start) != (delta < 0)) + return -1; /* time_t overflow */ + Start += delta; + } + + return Start; +} + +#if defined (TEST) + +/* ARGSUSED */ +int +main (ac, av) + int ac; + char *av[]; +{ + char buff[MAX_BUFF_LEN + 1]; + time_t d; + + (void) printf ("Enter date, or blank line to exit.\n\t> "); + (void) fflush (stdout); + + buff[MAX_BUFF_LEN] = 0; + while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0]) + { + d = get_date (buff, (time_t *) NULL); + if (d == -1) + (void) printf ("Bad format - couldn't convert.\n"); + else + (void) printf ("%s", ctime (&d)); + (void) printf ("\t> "); + (void) fflush (stdout); + } + exit (0); + /* NOTREACHED */ +} +#endif /* defined (TEST) */ + diff --git a/libmisc/getdate.h b/libmisc/getdate.h new file mode 100644 index 00000000..d1bb176d --- /dev/null +++ b/libmisc/getdate.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 1990 - 1994, Julianne Frances Haugh + * Copyright (c) 1997 - 2000, Marek Michałkiewicz + * Copyright (c) 2005 , Tomasz Kłoczko + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _GETDATE_H_ +#define _GETDATE_H_ + +#include <config.h> +#include "defines.h" + +time_t get_date (const char *p, /*@null@*/const time_t *now); +#endif diff --git a/libmisc/getdate.y b/libmisc/getdate.y new file mode 100644 index 00000000..ee00ca58 --- /dev/null +++ b/libmisc/getdate.y @@ -0,0 +1,977 @@ +%{ +/* +** Originally written by Steven M. Bellovin <smb@research.att.com> while +** at the University of North Carolina at Chapel Hill. Later tweaked by +** a couple of people on Usenet. Completely overhauled by Rich $alz +** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990; +** +** This grammar has 13 shift/reduce conflicts. +** +** This code is in the public domain and has no copyright. +*/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +# ifdef FORCE_ALLOCA_H +# include <alloca.h> +# endif +#endif + +/* Since the code of getdate.y is not included in the Emacs executable + itself, there is no need to #define static in this file. Even if + the code were included in the Emacs executable, it probably + wouldn't do any harm to #undef it here; this will only cause + problems if we try to write to a static variable, which I don't + think this code needs to do. */ +#ifdef emacs +# undef static +#endif + +#include <stdio.h> +#include <ctype.h> +#include <time.h> + +#if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII)) +# define IN_CTYPE_DOMAIN(c) 1 +#else +# define IN_CTYPE_DOMAIN(c) isascii(c) +#endif + +#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c)) +#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c)) +#define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper (c)) +#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c)) + +/* ISDIGIT differs from ISDIGIT_LOCALE, as follows: + - Its arg may be any int or unsigned int; it need not be an unsigned char. + - It's guaranteed to evaluate its argument exactly once. + - It's typically faster. + Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that + only '0' through '9' are digits. Prefer ISDIGIT to ISDIGIT_LOCALE unless + it's important to use the locale's definition of `digit' even when the + host does not conform to Posix. */ +#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9) + +#include "getdate.h" + +#if defined (STDC_HEADERS) +# include <string.h> +#endif + +/* Some old versions of bison generate parsers that use bcopy. + That loses on systems that don't provide the function, so we have + to redefine it here. */ +#if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy) +# define bcopy(from, to, len) memcpy ((to), (from), (len)) +#endif + +/* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc), + as well as gratuitiously global symbol names, so we can have multiple + yacc generated parsers in the same program. Note that these are only + the variables produced by yacc. If other parser generators (bison, + byacc, etc) produce additional global names that conflict at link time, + then those parser generators need to be fixed instead of adding those + names to this list. */ + +#define yymaxdepth gd_maxdepth +#define yyparse gd_parse +#define yylex gd_lex +#define yyerror gd_error +#define yylval gd_lval +#define yychar gd_char +#define yydebug gd_debug +#define yypact gd_pact +#define yyr1 gd_r1 +#define yyr2 gd_r2 +#define yydef gd_def +#define yychk gd_chk +#define yypgo gd_pgo +#define yyact gd_act +#define yyexca gd_exca +#define yyerrflag gd_errflag +#define yynerrs gd_nerrs +#define yyps gd_ps +#define yypv gd_pv +#define yys gd_s +#define yy_yys gd_yys +#define yystate gd_state +#define yytmp gd_tmp +#define yyv gd_v +#define yy_yyv gd_yyv +#define yyval gd_val +#define yylloc gd_lloc +#define yyreds gd_reds /* With YYDEBUG defined */ +#define yytoks gd_toks /* With YYDEBUG defined */ +#define yylhs gd_yylhs +#define yylen gd_yylen +#define yydefred gd_yydefred +#define yydgoto gd_yydgoto +#define yysindex gd_yysindex +#define yyrindex gd_yyrindex +#define yygindex gd_yygindex +#define yytable gd_yytable +#define yycheck gd_yycheck + +static int yylex (void); +static int yyerror (const char *s); + +#define EPOCH 1970 +#define HOUR(x) ((x) * 60) + +#define MAX_BUFF_LEN 128 /* size of buffer to read the date into */ + +/* +** An entry in the lexical lookup table. +*/ +typedef struct _TABLE { + const char *name; + int type; + int value; +} TABLE; + + +/* +** Meridian: am, pm, or 24-hour style. +*/ +typedef enum _MERIDIAN { + MERam, MERpm, MER24 +} MERIDIAN; + + +/* +** Global variables. We could get rid of most of these by using a good +** union as the yacc stack. (This routine was originally written before +** yacc had the %union construct.) Maybe someday; right now we only use +** the %union very rarely. +*/ +static const char *yyInput; +static int yyDayOrdinal; +static int yyDayNumber; +static int yyHaveDate; +static int yyHaveDay; +static int yyHaveRel; +static int yyHaveTime; +static int yyHaveZone; +static int yyTimezone; +static int yyDay; +static int yyHour; +static int yyMinutes; +static int yyMonth; +static int yySeconds; +static int yyYear; +static MERIDIAN yyMeridian; +static int yyRelDay; +static int yyRelHour; +static int yyRelMinutes; +static int yyRelMonth; +static int yyRelSeconds; +static int yyRelYear; + +%} + +%union { + int Number; + enum _MERIDIAN Meridian; +} + +%token tAGO tDAY tDAY_UNIT tDAYZONE tDST tHOUR_UNIT tID +%token tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT +%token tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE + +%type <Number> tDAY tDAY_UNIT tDAYZONE tHOUR_UNIT tMINUTE_UNIT +%type <Number> tMONTH tMONTH_UNIT +%type <Number> tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE +%type <Meridian> tMERIDIAN o_merid + +%% + +spec : /* NULL */ + | spec item + ; + +item : time { + yyHaveTime++; + } + | zone { + yyHaveZone++; + } + | date { + yyHaveDate++; + } + | day { + yyHaveDay++; + } + | rel { + yyHaveRel++; + } + | number + ; + +time : tUNUMBER tMERIDIAN { + yyHour = $1; + yyMinutes = 0; + yySeconds = 0; + yyMeridian = $2; + } + | tUNUMBER ':' tUNUMBER o_merid { + yyHour = $1; + yyMinutes = $3; + yySeconds = 0; + yyMeridian = $4; + } + | tUNUMBER ':' tUNUMBER tSNUMBER { + yyHour = $1; + yyMinutes = $3; + yyMeridian = MER24; + yyHaveZone++; + yyTimezone = ($4 < 0 + ? -$4 % 100 + (-$4 / 100) * 60 + : - ($4 % 100 + ($4 / 100) * 60)); + } + | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid { + yyHour = $1; + yyMinutes = $3; + yySeconds = $5; + yyMeridian = $6; + } + | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER { + yyHour = $1; + yyMinutes = $3; + yySeconds = $5; + yyMeridian = MER24; + yyHaveZone++; + yyTimezone = ($6 < 0 + ? -$6 % 100 + (-$6 / 100) * 60 + : - ($6 % 100 + ($6 / 100) * 60)); + } + ; + +zone : tZONE { + yyTimezone = $1; + } + | tDAYZONE { + yyTimezone = $1 - 60; + } + | + tZONE tDST { + yyTimezone = $1 - 60; + } + ; + +day : tDAY { + yyDayOrdinal = 1; + yyDayNumber = $1; + } + | tDAY ',' { + yyDayOrdinal = 1; + yyDayNumber = $1; + } + | tUNUMBER tDAY { + yyDayOrdinal = $1; + yyDayNumber = $2; + } + ; + +date : tUNUMBER '/' tUNUMBER { + yyMonth = $1; + yyDay = $3; + } + | tUNUMBER '/' tUNUMBER '/' tUNUMBER { + /* Interpret as YYYY/MM/DD if $1 >= 1000, otherwise as MM/DD/YY. + The goal in recognizing YYYY/MM/DD is solely to support legacy + machine-generated dates like those in an RCS log listing. If + you want portability, use the ISO 8601 format. */ + if ($1 >= 1000) + { + yyYear = $1; + yyMonth = $3; + yyDay = $5; + } + else + { + yyMonth = $1; + yyDay = $3; + yyYear = $5; + } + } + | tUNUMBER tSNUMBER tSNUMBER { + /* ISO 8601 format. yyyy-mm-dd. */ + yyYear = $1; + yyMonth = -$2; + yyDay = -$3; + } + | tUNUMBER tMONTH tSNUMBER { + /* e.g. 17-JUN-1992. */ + yyDay = $1; + yyMonth = $2; + yyYear = -$3; + } + | tMONTH tUNUMBER { + yyMonth = $1; + yyDay = $2; + } + | tMONTH tUNUMBER ',' tUNUMBER { + yyMonth = $1; + yyDay = $2; + yyYear = $4; + } + | tUNUMBER tMONTH { + yyMonth = $2; + yyDay = $1; + } + | tUNUMBER tMONTH tUNUMBER { + yyMonth = $2; + yyDay = $1; + yyYear = $3; + } + ; + +rel : relunit tAGO { + yyRelSeconds = -yyRelSeconds; + yyRelMinutes = -yyRelMinutes; + yyRelHour = -yyRelHour; + yyRelDay = -yyRelDay; + yyRelMonth = -yyRelMonth; + yyRelYear = -yyRelYear; + } + | relunit + ; + +relunit : tUNUMBER tYEAR_UNIT { + yyRelYear += $1 * $2; + } + | tSNUMBER tYEAR_UNIT { + yyRelYear += $1 * $2; + } + | tYEAR_UNIT { + yyRelYear++; + } + | tUNUMBER tMONTH_UNIT { + yyRelMonth += $1 * $2; + } + | tSNUMBER tMONTH_UNIT { + yyRelMonth += $1 * $2; + } + | tMONTH_UNIT { + yyRelMonth++; + } + | tUNUMBER tDAY_UNIT { + yyRelDay += $1 * $2; + } + | tSNUMBER tDAY_UNIT { + yyRelDay += $1 * $2; + } + | tDAY_UNIT { + yyRelDay++; + } + | tUNUMBER tHOUR_UNIT { + yyRelHour += $1 * $2; + } + | tSNUMBER tHOUR_UNIT { + yyRelHour += $1 * $2; + } + | tHOUR_UNIT { + yyRelHour++; + } + | tUNUMBER tMINUTE_UNIT { + yyRelMinutes += $1 * $2; + } + | tSNUMBER tMINUTE_UNIT { + yyRelMinutes += $1 * $2; + } + | tMINUTE_UNIT { + yyRelMinutes++; + } + | tUNUMBER tSEC_UNIT { + yyRelSeconds += $1 * $2; + } + | tSNUMBER tSEC_UNIT { + yyRelSeconds += $1 * $2; + } + | tSEC_UNIT { + yyRelSeconds++; + } + ; + +number : tUNUMBER + { + if ((yyHaveTime != 0) && (yyHaveDate != 0) && (yyHaveRel == 0)) + yyYear = $1; + else + { + if ($1>10000) + { + yyHaveDate++; + yyDay= ($1)%100; + yyMonth= ($1/100)%100; + yyYear = $1/10000; + } + else + { + yyHaveTime++; + if ($1 < 100) + { + yyHour = $1; + yyMinutes = 0; + } + else + { + yyHour = $1 / 100; + yyMinutes = $1 % 100; + } + yySeconds = 0; + yyMeridian = MER24; + } + } + } + ; + +o_merid : /* NULL */ + { + $$ = MER24; + } + | tMERIDIAN + { + $$ = $1; + } + ; + +%% + +/* Month and day table. */ +static TABLE const MonthDayTable[] = { + { "january", tMONTH, 1 }, + { "february", tMONTH, 2 }, + { "march", tMONTH, 3 }, + { "april", tMONTH, 4 }, + { "may", tMONTH, 5 }, + { "june", tMONTH, 6 }, + { "july", tMONTH, 7 }, + { "august", tMONTH, 8 }, + { "september", tMONTH, 9 }, + { "sept", tMONTH, 9 }, + { "october", tMONTH, 10 }, + { "november", tMONTH, 11 }, + { "december", tMONTH, 12 }, + { "sunday", tDAY, 0 }, + { "monday", tDAY, 1 }, + { "tuesday", tDAY, 2 }, + { "tues", tDAY, 2 }, + { "wednesday", tDAY, 3 }, + { "wednes", tDAY, 3 }, + { "thursday", tDAY, 4 }, + { "thur", tDAY, 4 }, + { "thurs", tDAY, 4 }, + { "friday", tDAY, 5 }, + { "saturday", tDAY, 6 }, + { NULL, 0, 0 } +}; + +/* Time units table. */ +static TABLE const UnitsTable[] = { + { "year", tYEAR_UNIT, 1 }, + { "month", tMONTH_UNIT, 1 }, + { "fortnight", tDAY_UNIT, 14 }, + { "week", tDAY_UNIT, 7 }, + { "day", tDAY_UNIT, 1 }, + { "hour", tHOUR_UNIT, 1 }, + { "minute", tMINUTE_UNIT, 1 }, + { "min", tMINUTE_UNIT, 1 }, + { "second", tSEC_UNIT, 1 }, + { "sec", tSEC_UNIT, 1 }, + { NULL, 0, 0 } +}; + +/* Assorted relative-time words. */ +static TABLE const OtherTable[] = { + { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 }, + { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 }, + { "today", tMINUTE_UNIT, 0 }, + { "now", tMINUTE_UNIT, 0 }, + { "last", tUNUMBER, -1 }, + { "this", tMINUTE_UNIT, 0 }, + { "next", tUNUMBER, 2 }, + { "first", tUNUMBER, 1 }, +/* { "second", tUNUMBER, 2 }, */ + { "third", tUNUMBER, 3 }, + { "fourth", tUNUMBER, 4 }, + { "fifth", tUNUMBER, 5 }, + { "sixth", tUNUMBER, 6 }, + { "seventh", tUNUMBER, 7 }, + { "eighth", tUNUMBER, 8 }, + { "ninth", tUNUMBER, 9 }, + { "tenth", tUNUMBER, 10 }, + { "eleventh", tUNUMBER, 11 }, + { "twelfth", tUNUMBER, 12 }, + { "ago", tAGO, 1 }, + { NULL, 0, 0 } +}; + +/* The timezone table. */ +static TABLE const TimezoneTable[] = { + { "gmt", tZONE, HOUR ( 0) }, /* Greenwich Mean */ + { "ut", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */ + { "utc", tZONE, HOUR ( 0) }, + { "wet", tZONE, HOUR ( 0) }, /* Western European */ + { "bst", tDAYZONE, HOUR ( 0) }, /* British Summer */ + { "wat", tZONE, HOUR ( 1) }, /* West Africa */ + { "at", tZONE, HOUR ( 2) }, /* Azores */ + { "ast", tZONE, HOUR ( 4) }, /* Atlantic Standard */ + { "adt", tDAYZONE, HOUR ( 4) }, /* Atlantic Daylight */ + { "est", tZONE, HOUR ( 5) }, /* Eastern Standard */ + { "edt", tDAYZONE, HOUR ( 5) }, /* Eastern Daylight */ + { "cst", tZONE, HOUR ( 6) }, /* Central Standard */ + { "cdt", tDAYZONE, HOUR ( 6) }, /* Central Daylight */ + { "mst", tZONE, HOUR ( 7) }, /* Mountain Standard */ + { "mdt", tDAYZONE, HOUR ( 7) }, /* Mountain Daylight */ + { "pst", tZONE, HOUR ( 8) }, /* Pacific Standard */ + { "pdt", tDAYZONE, HOUR ( 8) }, /* Pacific Daylight */ + { "yst", tZONE, HOUR ( 9) }, /* Yukon Standard */ + { "ydt", tDAYZONE, HOUR ( 9) }, /* Yukon Daylight */ + { "hst", tZONE, HOUR (10) }, /* Hawaii Standard */ + { "hdt", tDAYZONE, HOUR (10) }, /* Hawaii Daylight */ + { "cat", tZONE, HOUR (10) }, /* Central Alaska */ + { "ahst", tZONE, HOUR (10) }, /* Alaska-Hawaii Standard */ + { "nt", tZONE, HOUR (11) }, /* Nome */ + { "idlw", tZONE, HOUR (12) }, /* International Date Line West */ + { "cet", tZONE, -HOUR (1) }, /* Central European */ + { "met", tZONE, -HOUR (1) }, /* Middle European */ + { "mewt", tZONE, -HOUR (1) }, /* Middle European Winter */ + { "mest", tDAYZONE, -HOUR (1) }, /* Middle European Summer */ + { "mesz", tDAYZONE, -HOUR (1) }, /* Middle European Summer */ + { "swt", tZONE, -HOUR (1) }, /* Swedish Winter */ + { "sst", tDAYZONE, -HOUR (1) }, /* Swedish Summer */ + { "fwt", tZONE, -HOUR (1) }, /* French Winter */ + { "fst", tDAYZONE, -HOUR (1) }, /* French Summer */ + { "eet", tZONE, -HOUR (2) }, /* Eastern Europe, USSR Zone 1 */ + { "bt", tZONE, -HOUR (3) }, /* Baghdad, USSR Zone 2 */ + { "zp4", tZONE, -HOUR (4) }, /* USSR Zone 3 */ + { "zp5", tZONE, -HOUR (5) }, /* USSR Zone 4 */ + { "zp6", tZONE, -HOUR (6) }, /* USSR Zone 5 */ + { "wast", tZONE, -HOUR (7) }, /* West Australian Standard */ + { "wadt", tDAYZONE, -HOUR (7) }, /* West Australian Daylight */ + { "cct", tZONE, -HOUR (8) }, /* China Coast, USSR Zone 7 */ + { "jst", tZONE, -HOUR (9) }, /* Japan Standard, USSR Zone 8 */ + { "east", tZONE, -HOUR (10) }, /* Eastern Australian Standard */ + { "eadt", tDAYZONE, -HOUR (10) }, /* Eastern Australian Daylight */ + { "gst", tZONE, -HOUR (10) }, /* Guam Standard, USSR Zone 9 */ + { "nzt", tZONE, -HOUR (12) }, /* New Zealand */ + { "nzst", tZONE, -HOUR (12) }, /* New Zealand Standard */ + { "nzdt", tDAYZONE, -HOUR (12) }, /* New Zealand Daylight */ + { "idle", tZONE, -HOUR (12) }, /* International Date Line East */ + { NULL, 0, 0 } +}; + +/* Military timezone table. */ +static TABLE const MilitaryTable[] = { + { "a", tZONE, HOUR ( 1) }, + { "b", tZONE, HOUR ( 2) }, + { "c", tZONE, HOUR ( 3) }, + { "d", tZONE, HOUR ( 4) }, + { "e", tZONE, HOUR ( 5) }, + { "f", tZONE, HOUR ( 6) }, + { "g", tZONE, HOUR ( 7) }, + { "h", tZONE, HOUR ( 8) }, + { "i", tZONE, HOUR ( 9) }, + { "k", tZONE, HOUR ( 10) }, + { "l", tZONE, HOUR ( 11) }, + { "m", tZONE, HOUR ( 12) }, + { "n", tZONE, HOUR (- 1) }, + { "o", tZONE, HOUR (- 2) }, + { "p", tZONE, HOUR (- 3) }, + { "q", tZONE, HOUR (- 4) }, + { "r", tZONE, HOUR (- 5) }, + { "s", tZONE, HOUR (- 6) }, + { "t", tZONE, HOUR (- 7) }, + { "u", tZONE, HOUR (- 8) }, + { "v", tZONE, HOUR (- 9) }, + { "w", tZONE, HOUR (-10) }, + { "x", tZONE, HOUR (-11) }, + { "y", tZONE, HOUR (-12) }, + { "z", tZONE, HOUR ( 0) }, + { NULL, 0, 0 } +}; + + + + +static int yyerror (unused const char *s) +{ + return 0; +} + +static int ToHour (int Hours, MERIDIAN Meridian) +{ + switch (Meridian) + { + case MER24: + if (Hours < 0 || Hours > 23) + return -1; + return Hours; + case MERam: + if (Hours < 1 || Hours > 12) + return -1; + if (Hours == 12) + Hours = 0; + return Hours; + case MERpm: + if (Hours < 1 || Hours > 12) + return -1; + if (Hours == 12) + Hours = 0; + return Hours + 12; + default: + abort (); + } + /* NOTREACHED */ +} + +static int ToYear (int Year) +{ + if (Year < 0) + Year = -Year; + + /* XPG4 suggests that years 00-68 map to 2000-2068, and + years 69-99 map to 1969-1999. */ + if (Year < 69) + Year += 2000; + else if (Year < 100) + Year += 1900; + + return Year; +} + +static int LookupWord (char *buff) +{ + register char *p; + register char *q; + register const TABLE *tp; + int i; + bool abbrev; + + /* Make it lowercase. */ + for (p = buff; '\0' != *p; p++) + if (ISUPPER (*p)) + *p = tolower (*p); + + if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0) + { + yylval.Meridian = MERam; + return tMERIDIAN; + } + if (strcmp (buff, "pm") == 0 || strcmp (buff, "p.m.") == 0) + { + yylval.Meridian = MERpm; + return tMERIDIAN; + } + + /* See if we have an abbreviation for a month. */ + if (strlen (buff) == 3) + abbrev = true; + else if (strlen (buff) == 4 && buff[3] == '.') + { + abbrev = true; + buff[3] = '\0'; + } + else + abbrev = false; + + for (tp = MonthDayTable; tp->name; tp++) + { + if (abbrev) + { + if (strncmp (buff, tp->name, 3) == 0) + { + yylval.Number = tp->value; + return tp->type; + } + } + else if (strcmp (buff, tp->name) == 0) + { + yylval.Number = tp->value; + return tp->type; + } + } + + for (tp = TimezoneTable; tp->name; tp++) + if (strcmp (buff, tp->name) == 0) + { + yylval.Number = tp->value; + return tp->type; + } + + if (strcmp (buff, "dst") == 0) + return tDST; + + for (tp = UnitsTable; tp->name; tp++) + if (strcmp (buff, tp->name) == 0) + { + yylval.Number = tp->value; + return tp->type; + } + + /* Strip off any plural and try the units table again. */ + i = strlen (buff) - 1; + if (buff[i] == 's') + { + buff[i] = '\0'; + for (tp = UnitsTable; tp->name; tp++) + if (strcmp (buff, tp->name) == 0) + { + yylval.Number = tp->value; + return tp->type; + } + buff[i] = 's'; /* Put back for "this" in OtherTable. */ + } + + for (tp = OtherTable; tp->name; tp++) + if (strcmp (buff, tp->name) == 0) + { + yylval.Number = tp->value; + return tp->type; + } + + /* Military timezones. */ + if (buff[1] == '\0' && ISALPHA (*buff)) + { + for (tp = MilitaryTable; tp->name; tp++) + if (strcmp (buff, tp->name) == 0) + { + yylval.Number = tp->value; + return tp->type; + } + } + + /* Drop out any periods and try the timezone table again. */ + for (i = 0, p = q = buff; '\0' != *q; q++) + if (*q != '.') + *p++ = *q; + else + i++; + *p = '\0'; + if (0 != i) + for (tp = TimezoneTable; NULL != tp->name; tp++) + if (strcmp (buff, tp->name) == 0) + { + yylval.Number = tp->value; + return tp->type; + } + + return tID; +} + +static int +yylex (void) +{ + register char c; + register char *p; + char buff[20]; + int Count; + int sign; + + for (;;) + { + while (ISSPACE (*yyInput)) + yyInput++; + + if (ISDIGIT (c = *yyInput) || c == '-' || c == '+') + { + if (c == '-' || c == '+') + { + sign = c == '-' ? -1 : 1; + if (!ISDIGIT (*++yyInput)) + /* skip the '-' sign */ + continue; + } + else + sign = 0; + for (yylval.Number = 0; ISDIGIT (c = *yyInput++);) + yylval.Number = 10 * yylval.Number + c - '0'; + yyInput--; + if (sign < 0) + yylval.Number = -yylval.Number; + return (0 != sign) ? tSNUMBER : tUNUMBER; + } + if (ISALPHA (c)) + { + for (p = buff; (c = *yyInput++, ISALPHA (c)) || c == '.';) + if (p < &buff[sizeof buff - 1]) + *p++ = c; + *p = '\0'; + yyInput--; + return LookupWord (buff); + } + if (c != '(') + return *yyInput++; + Count = 0; + do + { + c = *yyInput++; + if (c == '\0') + return c; + if (c == '(') + Count++; + else if (c == ')') + Count--; + } + while (Count > 0); + } +} + +#define TM_YEAR_ORIGIN 1900 + +/* Yield A - B, measured in seconds. */ +static long difftm (struct tm *a, struct tm *b) +{ + int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); + int by = b->tm_year + (TM_YEAR_ORIGIN - 1); + long days = ( + /* difference in day of year */ + a->tm_yday - b->tm_yday + /* + intervening leap days */ + + ((ay >> 2) - (by >> 2)) + - (ay / 100 - by / 100) + + ((ay / 100 >> 2) - (by / 100 >> 2)) + /* + difference in years * 365 */ + + (long) (ay - by) * 365 + ); + return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour)) + + (a->tm_min - b->tm_min)) + + (a->tm_sec - b->tm_sec)); +} + +time_t get_date (const char *p, const time_t *now) +{ + struct tm tm, tm0, *tmp; + time_t Start; + + yyInput = p; + Start = now ? *now : time ((time_t *) NULL); + tmp = localtime (&Start); + yyYear = tmp->tm_year + TM_YEAR_ORIGIN; + yyMonth = tmp->tm_mon + 1; + yyDay = tmp->tm_mday; + yyHour = tmp->tm_hour; + yyMinutes = tmp->tm_min; + yySeconds = tmp->tm_sec; + yyMeridian = MER24; + yyRelSeconds = 0; + yyRelMinutes = 0; + yyRelHour = 0; + yyRelDay = 0; + yyRelMonth = 0; + yyRelYear = 0; + yyHaveDate = 0; + yyHaveDay = 0; + yyHaveRel = 0; + yyHaveTime = 0; + yyHaveZone = 0; + + if (yyparse () + || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1) + return -1; + + tm.tm_year = ToYear (yyYear) - TM_YEAR_ORIGIN + yyRelYear; + tm.tm_mon = yyMonth - 1 + yyRelMonth; + tm.tm_mday = yyDay + yyRelDay; + if ((yyHaveTime != 0) || + ( (yyHaveRel != 0) && (yyHaveDate == 0) && (yyHaveDay == 0) )) + { + tm.tm_hour = ToHour (yyHour, yyMeridian); + if (tm.tm_hour < 0) + return -1; + tm.tm_min = yyMinutes; + tm.tm_sec = yySeconds; + } + else + { + tm.tm_hour = tm.tm_min = tm.tm_sec = 0; + } + tm.tm_hour += yyRelHour; + tm.tm_min += yyRelMinutes; + tm.tm_sec += yyRelSeconds; + tm.tm_isdst = -1; + tm0 = tm; + + Start = mktime (&tm); + + if (Start == (time_t) -1) + { + + /* Guard against falsely reporting errors near the time_t boundaries + when parsing times in other time zones. For example, if the min + time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead + of UTC, then the min localtime value is 1970-01-01 08:00:00; if + we apply mktime to 1970-01-01 00:00:00 we will get an error, so + we apply mktime to 1970-01-02 08:00:00 instead and adjust the time + zone by 24 hours to compensate. This algorithm assumes that + there is no DST transition within a day of the time_t boundaries. */ + if (yyHaveZone) + { + tm = tm0; + if (tm.tm_year <= EPOCH - TM_YEAR_ORIGIN) + { + tm.tm_mday++; + yyTimezone -= 24 * 60; + } + else + { + tm.tm_mday--; + yyTimezone += 24 * 60; + } + Start = mktime (&tm); + } + + if (Start == (time_t) -1) + return Start; + } + + if (yyHaveDay && !yyHaveDate) + { + tm.tm_mday += ((yyDayNumber - tm.tm_wday + 7) % 7 + + 7 * (yyDayOrdinal - (0 < yyDayOrdinal))); + Start = mktime (&tm); + if (Start == (time_t) -1) + return Start; + } + + if (yyHaveZone) + { + long delta = yyTimezone * 60L + difftm (&tm, gmtime (&Start)); + if ((Start + delta < Start) != (delta < 0)) + return -1; /* time_t overflow */ + Start += delta; + } + + return Start; +} + +#if defined (TEST) + +/* ARGSUSED */ +int +main (ac, av) + int ac; + char *av[]; +{ + char buff[MAX_BUFF_LEN + 1]; + time_t d; + + (void) printf ("Enter date, or blank line to exit.\n\t> "); + (void) fflush (stdout); + + buff[MAX_BUFF_LEN] = 0; + while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0]) + { + d = get_date (buff, (time_t *) NULL); + if (d == -1) + (void) printf ("Bad format - couldn't convert.\n"); + else + (void) printf ("%s", ctime (&d)); + (void) printf ("\t> "); + (void) fflush (stdout); + } + exit (0); + /* NOTREACHED */ +} +#endif /* defined (TEST) */ diff --git a/libmisc/getgr_nam_gid.c b/libmisc/getgr_nam_gid.c new file mode 100644 index 00000000..027280a5 --- /dev/null +++ b/libmisc/getgr_nam_gid.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 1991 - 1994, Julianne Frances Haugh + * Copyright (c) 1996 - 2000, Marek Michałkiewicz + * Copyright (c) 2000 - 2006, Tomasz Kłoczko + * Copyright (c) 2007 - 2009, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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> + +#ident "$Id$" + +#include <stdlib.h> +#include <errno.h> +#include <grp.h> +#include "prototypes.h" + +/* + * getgr_nam_gid - Return a pointer to the group specified by a string. + * The string may be a valid GID or a valid groupname. + * If the group does not exist on the system, NULL is returned. + */ +extern /*@only@*//*@null@*/struct group *getgr_nam_gid (/*@null@*/const char *grname) +{ + long long int gid; + char *endptr; + + if (NULL == grname) { + return NULL; + } + + errno = 0; + gid = strtoll (grname, &endptr, 10); + if ( ('\0' != *grname) + && ('\0' == *endptr) + && (ERANGE != errno) + && (/*@+longintegral@*/gid == (gid_t)gid)/*@=longintegral@*/) { + return xgetgrgid ((gid_t) gid); + } + return xgetgrnam (grname); +} + diff --git a/libmisc/getrange.c b/libmisc/getrange.c new file mode 100644 index 00000000..9b5e6117 --- /dev/null +++ b/libmisc/getrange.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2008 , Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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> + +#ident "$Id: $" + +#include <ctype.h> +#include <stdlib.h> + +#include "defines.h" +#include "prototypes.h" + +/* + * Parse a range and indicate if the range is valid. + * Valid ranges are in the form: + * <long> -> min=max=long has_min has_max + * -<long> -> max=long !has_min has_max + * <long>- -> min=long has_min !has_max + * <long1>-<long2> -> min=long1 max=long2 has_min has_max + * + * If the range is valid, getrange returns 1. + * If the range is not valid, getrange returns 0. + */ +int getrange (char *range, + unsigned long *min, bool *has_min, + unsigned long *max, bool *has_max) +{ + char *endptr; + unsigned long n; + + if (NULL == range) { + return 0; + } + + if ('-' == range[0]) { + if (!isdigit(range[1])) { + /* invalid */ + return 0; + } + errno = 0; + n = strtoul (&range[1], &endptr, 10); + if (('\0' != *endptr) || (ERANGE == errno)) { + /* invalid */ + return 0; + } + /* -<long> */ + *has_min = false; + *has_max = true; + *max = n; + } else { + errno = 0; + n = strtoul (range, &endptr, 10); + if (ERANGE == errno) { + /* invalid */ + return 0; + } + switch (*endptr) { + case '\0': + /* <long> */ + *has_min = true; + *has_max = true; + *min = n; + *max = n; + break; + case '-': + endptr++; + if ('\0' == *endptr) { + /* <long>- */ + *has_min = true; + *has_max = false; + *min = n; + } else if (!isdigit (*endptr)) { + /* invalid */ + return 0; + } else { + *has_min = true; + *min = n; + errno = 0; + n = strtoul (endptr, &endptr, 10); + if ( ('\0' != *endptr) + || (ERANGE == errno)) { + /* invalid */ + return 0; + } + /* <long>-<long> */ + *has_max = true; + *max = n; + } + break; + default: + return 0; + } + } + + return 1; +} + diff --git a/libmisc/hushed.c b/libmisc/hushed.c new file mode 100644 index 00000000..a505c83e --- /dev/null +++ b/libmisc/hushed.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 1991 - 1993, Julianne Frances Haugh + * Copyright (c) 1991 - 1993, Chip Rosenthal + * Copyright (c) 1996 - 2000, Marek Michałkiewicz + * Copyright (c) 2003 - 2005, Tomasz Kłoczko + * Copyright (c) 2008 - 2010, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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> + +#ident "$Id: hushed.c 3232 2010-08-22 19:13:53Z nekral-guest $" + +#include <sys/types.h> +#include <stdio.h> +#include <pwd.h> +#include "defines.h" +#include "prototypes.h" +#include "getdef.h" +/* + * hushed - determine if a user receives login messages + * + * Look in the hushed-logins file (or user's home directory) to see + * if the user is to receive the login-time messages. + */ +bool hushed (const char *username) +{ + struct passwd *pw; + const char *hushfile; + char buf[BUFSIZ]; + bool found; + FILE *fp; + + /* + * Get the name of the file to use. If this option is not + * defined, default to a noisy login. + */ + + hushfile = getdef_str ("HUSHLOGIN_FILE"); + if (NULL == hushfile) { + return false; + } + + pw = getpwnam (username); + if (NULL == pw) { + return false; + } + + /* + * If this is not a fully rooted path then see if the + * file exists in the user's home directory. + */ + + if (hushfile[0] != '/') { + (void) snprintf (buf, sizeof (buf), "%s/%s", pw->pw_dir, hushfile); + return (access (buf, F_OK) == 0); + } + + /* + * If this is a fully rooted path then go through the file + * and see if this user, or its shell is in there. + */ + + fp = fopen (hushfile, "r"); + if (NULL == fp) { + return false; + } + for (found = false; !found && (fgets (buf, (int) sizeof buf, fp) == buf);) { + buf[strlen (buf) - 1] = '\0'; + found = (strcmp (buf, pw->pw_shell) == 0) || + (strcmp (buf, pw->pw_name) == 0); + } + (void) fclose (fp); + return found; +} + diff --git a/libmisc/isexpired.c b/libmisc/isexpired.c new file mode 100644 index 00000000..a28367a4 --- /dev/null +++ b/libmisc/isexpired.c @@ -0,0 +1,125 @@ +/* + * Copyright (c) 1989 - 1994, Julianne Frances Haugh + * Copyright (c) 1996 - 1997, Marek Michałkiewicz + * Copyright (c) 2001 - 2005, Tomasz Kłoczko + * Copyright (c) 2008 - 2009, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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. + */ + +/* + * Extracted from age.c and made part of libshadow.a - may be useful + * in other shadow-aware programs. --marekm + */ + +#include <config.h> + +#include <sys/types.h> +#include "prototypes.h" +#include "defines.h" +#include <pwd.h> +#include <time.h> + +#ident "$Id: isexpired.c 3362 2011-06-16 21:25:36Z nekral-guest $" + + +/* + * isexpired - determine if account is expired yet + * + * isexpired calculates the expiration date based on the + * password expiration criteria. + * + * Return value: + * 0: The password is still valid + * 1: The password has expired, it must be changed + * 2: The password has expired since a long time and the account is + * now disabled. (password cannot be changed) + * 3: The account has expired + */ +int isexpired (const struct passwd *pw, /*@null@*/const struct spwd *sp) +{ + long now; + + now = (long) time ((time_t *) 0) / SCALE; + + if (NULL == sp) { + return 0; + } + + /* + * Quick and easy - there is an expired account field + * along with an inactive account field. Do the expired + * one first since it is worse. + */ + + if ((sp->sp_expire > 0) && (now >= sp->sp_expire)) { + return 3; + } + + /* + * Last changed date 1970-01-01 (not very likely) means that + * the password must be changed on next login (passwd -e). + * + * The check for "x" is a workaround for RedHat NYS libc bug - + * if /etc/shadow doesn't exist, getspnam() still succeeds and + * returns sp_lstchg==0 (must change password) instead of -1! + */ + if ( (0 == sp->sp_lstchg) + && (strcmp (pw->pw_passwd, SHADOW_PASSWD_STRING) == 0)) { + return 1; + } + + if ( (sp->sp_lstchg > 0) + && (sp->sp_max >= 0) + && (sp->sp_inact >= 0) + && (now >= (sp->sp_lstchg + sp->sp_max + sp->sp_inact))) { + return 2; + } + + /* + * The last and max fields must be present for an account + * to have an expired password. A maximum of >10000 days + * is considered to be infinite. + */ + + if ( (-1 == sp->sp_lstchg) + || (-1 == sp->sp_max) + || (sp->sp_max >= ((10000L * DAY) / SCALE))) { + return 0; + } + + /* + * Calculate today's day and the day on which the password + * is going to expire. If that date has already passed, + * the password has expired. + */ + + if (now >= (sp->sp_lstchg + sp->sp_max)) { + return 1; + } + return 0; +} + diff --git a/libmisc/limits.c b/libmisc/limits.c new file mode 100644 index 00000000..110a4a65 --- /dev/null +++ b/libmisc/limits.c @@ -0,0 +1,611 @@ +/* + * Copyright (c) 1989 - 1994, Julianne Frances Haugh + * Copyright (c) 1996 - 1999, Marek Michałkiewicz + * Copyright (c) 2003 - 2006, Tomasz Kłoczko + * Copyright (c) 2007 - 2010, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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. + */ + +/* + * Separated from setup.c. --marekm + * Resource limits thanks to Cristian Gafton. + * Enhancements of resource limit code by Thomas Orgis <thomas@orgis.org> + */ + +#include <config.h> + +#ifndef USE_PAM + +#ident "$Id: limits.c 3558 2011-11-06 18:39:47Z nekral-guest $" + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <ctype.h> +#include "prototypes.h" +#include "defines.h" +#include <pwd.h> +#include "getdef.h" +#ifdef HAVE_SYS_RESOURCE_H +#include <sys/resource.h> +#define LIMITS +#endif +#ifdef LIMITS +#ifndef LIMITS_FILE +#define LIMITS_FILE "/etc/limits" +#endif +#define LOGIN_ERROR_RLIMIT 1 +#define LOGIN_ERROR_LOGIN 2 +/* Set a limit on a resource */ +/* + * rlimit - RLIMIT_XXXX + * value - string value to be read + * multiplier - value*multiplier is the actual limit + */ +static int setrlimit_value (unsigned int resource, + const char *value, + unsigned int multiplier) +{ + struct rlimit rlim; + rlim_t limit; + + /* The "-" is special, not belonging to a strange negative limit. + * It is infinity, in a controlled way. + */ + if ('-' == value[0]) { + limit = RLIM_INFINITY; + } + else { + /* We cannot use getlong here because it fails when there + * is more to the value than just this number! + * Also, we are limited to base 10 here (hex numbers will not + * work with the limit string parser as is anyway) + */ + char *endptr; + long longlimit = strtol (value, &endptr, 10); + if ((0 == longlimit) && (value == endptr)) { + /* No argument at all. No-op. + * FIXME: We could instead throw an error, though. + */ + return 0; + } + longlimit *= multiplier; + limit = (rlim_t)longlimit; + if (longlimit != limit) + { + /* FIXME: Again, silent error handling... + * Wouldn't screaming make more sense? + */ + return 0; + } + } + + rlim.rlim_cur = limit; + rlim.rlim_max = limit; + if (setrlimit (resource, &rlim) != 0) { + return LOGIN_ERROR_RLIMIT; + } + return 0; +} + + +static int set_prio (const char *value) +{ + long prio; + + if ( (getlong (value, &prio) == 0) + || (prio != (int) prio)) { + return 0; + } + if (setpriority (PRIO_PROCESS, 0, (int) prio) != 0) { + return LOGIN_ERROR_RLIMIT; + } + return 0; +} + + +static int set_umask (const char *value) +{ + unsigned long int mask; + + if ( (getulong (value, &mask) == 0) + || (mask != (mode_t) mask)) { + return 0; + } + + (void) umask ((mode_t) mask); + return 0; +} + + +/* Counts the number of user logins and check against the limit */ +static int check_logins (const char *name, const char *maxlogins) +{ +#ifdef USE_UTMPX + struct utmpx *ut; +#else /* !USE_UTMPX */ + struct utmp *ut; +#endif /* !USE_UTMPX */ + unsigned long limit, count; + + if (getulong (maxlogins, &limit) == 0) { + return 0; + } + + if (0 == limit) { /* maximum 0 logins ? */ + SYSLOG ((LOG_WARN, "No logins allowed for `%s'\n", name)); + return LOGIN_ERROR_LOGIN; + } + + count = 0; +#ifdef USE_UTMPX + setutxent (); + while ((ut = getutxent ())) +#else /* !USE_UTMPX */ + setutent (); + while ((ut = getutent ())) +#endif /* !USE_UTMPX */ + { + if (USER_PROCESS != ut->ut_type) { + continue; + } + if ('\0' == ut->ut_user[0]) { + continue; + } + if (strncmp (name, ut->ut_user, sizeof (ut->ut_user)) != 0) { + continue; + } + count++; + if (count > limit) { + break; + } + } +#ifdef USE_UTMPX + endutxent (); +#else /* !USE_UTMPX */ + endutent (); +#endif /* !USE_UTMPX */ + /* + * This is called after setutmp(), so the number of logins counted + * includes the user who is currently trying to log in. + */ + if (count > limit) { + SYSLOG ((LOG_WARN, + "Too many logins (max %lu) for %s\n", + limit, name)); + return LOGIN_ERROR_LOGIN; + } + return 0; +} + +/* Function setup_user_limits - checks/set limits for the curent login + * Original idea from Joel Katz's lshell. Ported to shadow-login + * by Cristian Gafton - gafton@sorosis.ro + * + * We are passed a string of the form ('BASH' constants for ulimit) + * [Aa][Cc][Dd][Ff][Mm][Nn][Rr][Ss][Tt][Uu][Ll][Pp][Ii][Oo] + * (eg. 'C2F256D2048N5' or 'C2 F256 D2048 N5') + * where: + * [Aa]: a = RLIMIT_AS max address space (KB) + * [Cc]: c = RLIMIT_CORE max core file size (KB) + * [Dd]: d = RLIMIT_DATA max data size (KB) + * [Ff]: f = RLIMIT_FSIZE max file size (KB) + * [Ii]: i = RLIMIT_NICE max nice value (0..39 translates to 20..-19) + * [Kk]: k = file creation masK (umask) + * [Ll]: l = max number of logins for this user + * [Mm]: m = RLIMIT_MEMLOCK max locked-in-memory address space (KB) + * [Nn]: n = RLIMIT_NOFILE max number of open files + * [Oo]: o = RLIMIT_RTPRIO max real time priority (linux/sched.h 0..MAX_RT_PRIO) + * [Pp]: p = process priority -20..20 (negative = high, positive = low) + * [Rr]: r = RLIMIT_RSS max resident set size (KB) + * [Ss]: s = RLIMIT_STACK max stack size (KB) + * [Tt]: t = RLIMIT_CPU max CPU time (MIN) + * [Uu]: u = RLIMIT_NPROC max number of processes + * + * NOTE: Remember to extend the "no-limits" string below when adding a new + * limit... + * + * Return value: + * 0 = okay, of course + * LOGIN_ERROR_RLIMIT = error setting some RLIMIT + * LOGIN_ERROR_LOGIN = error - too many logins for this user + * + * buf - the limits string + * name - the username + */ +static int do_user_limits (const char *buf, const char *name) +{ + const char *pp; + int retval = 0; + bool reported = false; + + pp = buf; + /* Skip leading whitespace. */ + while ((' ' == *pp) || ('\t' == *pp)) { + pp++; + } + + /* The special limit string "-" results in no limit for all known + * limits. + * We achieve that by parsing a full limit string, parts of it + * being ignored if a limit type is not known to the system. + * Though, there will be complaining for unknown limit types. + */ + if (strcmp (pp, "-") == 0) { + /* Remember to extend this, too, when adding new limits! + * Oh... but "unlimited" does not make sense for umask, + * or does it? (K-) + */ + pp = "A- C- D- F- I- L- M- N- O- P- R- S- T- U-"; + } + + while ('\0' != *pp) { + switch (*pp++) { +#ifdef RLIMIT_AS + case 'a': + case 'A': + /* RLIMIT_AS - max address space (KB) */ + retval |= setrlimit_value (RLIMIT_AS, pp, 1024); + break; +#endif +#ifdef RLIMIT_CORE + case 'c': + case 'C': + /* RLIMIT_CORE - max core file size (KB) */ + retval |= setrlimit_value (RLIMIT_CORE, pp, 1024); + break; +#endif +#ifdef RLIMIT_DATA + case 'd': + case 'D': + /* RLIMIT_DATA - max data size (KB) */ + retval |= setrlimit_value (RLIMIT_DATA, pp, 1024); + break; +#endif +#ifdef RLIMIT_FSIZE + case 'f': + case 'F': + /* RLIMIT_FSIZE - Maximum filesize (KB) */ + retval |= setrlimit_value (RLIMIT_FSIZE, pp, 1024); + break; +#endif +#ifdef RLIMIT_NICE + case 'i': + case 'I': + /* RLIMIT_NICE - max scheduling priority (0..39) */ + retval |= setrlimit_value (RLIMIT_NICE, pp, 1); + break; +#endif + case 'k': + case 'K': + retval |= set_umask (pp); + break; + case 'l': + case 'L': + /* LIMIT the number of concurrent logins */ + retval |= check_logins (name, pp); + break; +#ifdef RLIMIT_MEMLOCK + case 'm': + case 'M': + /* RLIMIT_MEMLOCK - max locked-in-memory address space (KB) */ + retval |= setrlimit_value (RLIMIT_MEMLOCK, pp, 1024); + break; +#endif +#ifdef RLIMIT_NOFILE + case 'n': + case 'N': + /* RLIMIT_NOFILE - max number of open files */ + retval |= setrlimit_value (RLIMIT_NOFILE, pp, 1); + break; +#endif +#ifdef RLIMIT_RTPRIO + case 'o': + case 'O': + /* RLIMIT_RTPRIO - max real time priority (0..MAX_RT_PRIO) */ + retval |= setrlimit_value (RLIMIT_RTPRIO, pp, 1); + break; +#endif + case 'p': + case 'P': + retval |= set_prio (pp); + break; +#ifdef RLIMIT_RSS + case 'r': + case 'R': + /* RLIMIT_RSS - max resident set size (KB) */ + retval |= setrlimit_value (RLIMIT_RSS, pp, 1024); + break; +#endif +#ifdef RLIMIT_STACK + case 's': + case 'S': + /* RLIMIT_STACK - max stack size (KB) */ + retval |= setrlimit_value (RLIMIT_STACK, pp, 1024); + break; +#endif +#ifdef RLIMIT_CPU + case 't': + case 'T': + /* RLIMIT_CPU - max CPU time (MIN) */ + retval |= setrlimit_value (RLIMIT_CPU, pp, 60); + break; +#endif +#ifdef RLIMIT_NPROC + case 'u': + case 'U': + /* RLIMIT_NPROC - max number of processes */ + retval |= setrlimit_value (RLIMIT_NPROC, pp, 1); + break; +#endif + default: + /* Only report invalid strings once */ + /* Note: A string can be invalid just because a + * specific (theoretically valid) setting is not + * supported by this build. + * It is just a warning in syslog anyway. The line + * is still processed + */ + if (!reported) { + SYSLOG ((LOG_WARN, + "Invalid limit string: '%s'", + pp-1)); + reported = true; + retval |= LOGIN_ERROR_RLIMIT; + } + } + /* After parsing one limit setting (or just complaining + * about it), one still needs to skip its argument to + * prevent a bogus warning on trying to parse that as + * limit specification. + * So, let's skip all digits, "-" and our limited set of + * whitespace. + */ + while ( isdigit (*pp) + || ('-' == *pp) + || (' ' == *pp) + || ('\t' ==*pp)) { + pp++; + } + } + return retval; +} + +/* Check if user uname is in the group gname. + * Can I be sure that gr_mem contains no UID as string? + * Returns true when user is in the group, false when not. + * Any error is treated as false. + */ +static bool user_in_group (const char *uname, const char *gname) +{ + struct group *groupdata; + + if (uname == NULL || gname == NULL){ + return false; + } + + /* We are not claiming to be re-entrant! + * In case of paranoia or a multithreaded login program, + * one needs to add some mess for getgrnam_r. */ + groupdata = getgrnam (gname); + if (NULL == groupdata) { + SYSLOG ((LOG_WARN, "Nonexisting group `%s' in limits file.", + gname)); + return false; + } + + return is_on_list (groupdata->gr_mem, uname); +} + +static int setup_user_limits (const char *uname) +{ + FILE *fil; + char buf[1024]; + char name[1024]; + char limits[1024]; + char deflimits[1024]; + char tempbuf[1024]; + + /* init things */ + memzero (buf, sizeof (buf)); + memzero (name, sizeof (name)); + memzero (limits, sizeof (limits)); + memzero (deflimits, sizeof (deflimits)); + memzero (tempbuf, sizeof (tempbuf)); + + /* start the checks */ + fil = fopen (LIMITS_FILE, "r"); + if (fil == NULL) { + return 0; + } + /* The limits file have the following format: + * - '#' (comment) chars only as first chars on a line; + * - username must start on first column (or *, or @group) + * + * FIXME: A better (smarter) checking should be done + */ + while (fgets (buf, 1024, fil) != NULL) { + if (('#' == buf[0]) || ('\n' == buf[0])) { + continue; + } + memzero (tempbuf, sizeof (tempbuf)); + /* a valid line should have a username, then spaces, + * then limits + * we allow the format: + * username L2 D2048 R4096 + * where spaces={' ',\t}. Also, we reject invalid limits. + * Imposing a limit should be done with care, so a wrong + * entry means no care anyway :-). + * + * A '-' as a limits strings means no limits + * + * The username can also be: + * '*': the default limits (only the last is taken into + * account) + * @group: the limit applies to the members of the group + * + * To clarify: The first entry with matching user name rules, + * everything after it is ignored. If there is no user entry, + * the last encountered entry for a matching group rules. + * If there is no matching group entry, the default limits rule. + */ + if (sscanf (buf, "%s%[ACDFIKLMNOPRSTUacdfiklmnoprstu0-9 \t-]", + name, tempbuf) == 2) { + if (strcmp (name, uname) == 0) { + strcpy (limits, tempbuf); + break; + } else if (strcmp (name, "*") == 0) { + strcpy (deflimits, tempbuf); + } else if (name[0] == '@') { + /* If the user is in the group, the group + * limits apply unless later a line for + * the specific user is found. + */ + if (user_in_group (uname, name+1)) { + strcpy (limits, tempbuf); + } + } + } + } + (void) fclose (fil); + if (limits[0] == '\0') { + /* no user specific limits */ + if (deflimits[0] == '\0') { /* no default limits */ + return 0; + } + strcpy (limits, deflimits); /* use the default limits */ + } + return do_user_limits (limits, uname); +} +#endif /* LIMITS */ + + +static void setup_usergroups (const struct passwd *info) +{ + const struct group *grp; + +/* + * if not root, and UID == GID, and username is the same as primary + * group name, set umask group bits to be the same as owner bits + * (examples: 022 -> 002, 077 -> 007). + */ + if ((0 != info->pw_uid) && (info->pw_uid == info->pw_gid)) { + /* local, no need for xgetgrgid */ + grp = getgrgid (info->pw_gid); + if ( (NULL != grp) + && (strcmp (info->pw_name, grp->gr_name) == 0)) { + mode_t tmpmask; + tmpmask = umask (0777); + tmpmask = (tmpmask & ~070) | ((tmpmask >> 3) & 070); + (void) umask (tmpmask); + } + } +} + +/* + * set the process nice, ulimit, and umask from the password file entry + */ + +void setup_limits (const struct passwd *info) +{ + char *cp; + + if (getdef_bool ("USERGROUPS_ENAB")) { + setup_usergroups (info); + } + + /* + * See if the GECOS field contains values for NICE, UMASK or ULIMIT. + * If this feature is enabled in /etc/login.defs, we make those + * values the defaults for this login session. + */ + + if (getdef_bool ("QUOTAS_ENAB")) { +#ifdef LIMITS + if (info->pw_uid != 0) { + if ((setup_user_limits (info->pw_name) & LOGIN_ERROR_LOGIN) != 0) { + (void) fputs (_("Too many logins.\n"), stderr); + (void) sleep (2); /* XXX: Should be FAIL_DELAY */ + exit (EXIT_FAILURE); + } + } +#endif + for (cp = info->pw_gecos; cp != NULL; cp = strchr (cp, ',')) { + if (',' == *cp) { + cp++; + } + + if (strncmp (cp, "pri=", 4) == 0) { + long int inc; + if ( (getlong (cp + 4, &inc) == 1) + && (inc >= -20) && (inc <= 20)) { + errno = 0; + if ( (nice ((int) inc) != -1) + || (0 != errno)) { + continue; + } + } + + /* Failed to parse or failed to nice() */ + SYSLOG ((LOG_WARN, + "Can't set the nice value for user %s", + info->pw_name)); + + continue; + } + if (strncmp (cp, "ulimit=", 7) == 0) { + long int blocks; + if ( (getlong (cp + 7, &blocks) == 0) + || (blocks != (int) blocks) + || (set_filesize_limit ((int) blocks) != 0)) { + SYSLOG ((LOG_WARN, + "Can't set the ulimit for user %s", + info->pw_name)); + } + continue; + } + if (strncmp (cp, "umask=", 6) == 0) { + unsigned long int mask; + if ( (getulong (cp + 6, &mask) == 0) + || (mask != (mode_t) mask)) { + SYSLOG ((LOG_WARN, + "Can't set umask value for user %s", + info->pw_name)); + } else { + (void) umask ((mode_t) mask); + } + + continue; + } + } + } +} + +#else /* !USE_PAM */ +extern int errno; /* warning: ANSI C forbids an empty source file */ +#endif /* !USE_PAM */ + diff --git a/libmisc/list.c b/libmisc/list.c new file mode 100644 index 00000000..39f6a706 --- /dev/null +++ b/libmisc/list.c @@ -0,0 +1,271 @@ +/* + * Copyright (c) 1991 - 1994, Julianne Frances Haugh + * Copyright (c) 1996 - 1997, Marek Michałkiewicz + * Copyright (c) 2003 - 2005, Tomasz Kłoczko + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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> + +#ident "$Id: list.c 3224 2010-08-21 15:32:53Z nekral-guest $" + +#include <assert.h> +#include "prototypes.h" +#include "defines.h" +/* + * add_list - add a member to a list of group members + * + * the array of member names is searched for the new member + * name, and if not present it is added to a freshly allocated + * list of users. + */ +/*@only@*/ /*@out@*/char **add_list (/*@returned@*/ /*@only@*/char **list, const char *member) +{ + int i; + char **tmp; + + assert (NULL != member); + assert (NULL != list); + + /* + * Scan the list for the new name. Return the original list + * pointer if it is present. + */ + + for (i = 0; list[i] != (char *) 0; i++) { + if (strcmp (list[i], member) == 0) { + return list; + } + } + + /* + * Allocate a new list pointer large enough to hold all the + * old entries, and the new entries as well. + */ + + tmp = (char **) xmalloc ((i + 2) * sizeof member); + + /* + * Copy the original list to the new list, then append the + * new member and NULL terminate the result. This new list + * is returned to the invoker. + */ + + for (i = 0; list[i] != (char *) 0; i++) { + tmp[i] = list[i]; + } + + tmp[i] = xstrdup (member); + tmp[i+1] = (char *) 0; + + return tmp; +} + +/* + * del_list - delete a member from a list of group members + * + * the array of member names is searched for the old member + * name, and if present it is deleted from a freshly allocated + * list of users. + */ + +/*@only@*/ /*@out@*/char **del_list (/*@returned@*/ /*@only@*/char **list, const char *member) +{ + int i, j; + char **tmp; + + assert (NULL != member); + assert (NULL != list); + + /* + * Scan the list for the old name. Return the original list + * pointer if it is not present. + */ + + for (i = j = 0; list[i] != (char *) 0; i++) { + if (strcmp (list[i], member) != 0) { + j++; + } + } + + if (j == i) { + return list; + } + + /* + * Allocate a new list pointer large enough to hold all the + * old entries. + */ + + tmp = (char **) xmalloc ((j + 1) * sizeof member); + + /* + * Copy the original list except the deleted members to the + * new list, then NULL terminate the result. This new list + * is returned to the invoker. + */ + + for (i = j = 0; list[i] != (char *) 0; i++) { + if (strcmp (list[i], member) != 0) { + tmp[j] = list[i]; + j++; + } + } + + tmp[j] = (char *) 0; + + return tmp; +} + +/* + * Duplicate a list. + * The input list is not modified, but in order to allow the use of this + * function with list of members, the list elements are not enforced to be + * constant strings here. + */ +/*@only@*/ /*@out@*/char **dup_list (char *const *list) +{ + int i; + char **tmp; + + assert (NULL != list); + + for (i = 0; NULL != list[i]; i++); + + tmp = (char **) xmalloc ((i + 1) * sizeof (char *)); + + i = 0; + while (NULL != *list) { + tmp[i] = xstrdup (*list); + i++; + list++; + } + + tmp[i] = (char *) 0; + return tmp; +} + +/* + * Check if member is part of the input list + * The input list is not modified, but in order to allow the use of this + * function with list of members, the list elements are not enforced to be + * constant strings here. + */ +bool is_on_list (char *const *list, const char *member) +{ + assert (NULL != member); + assert (NULL != list); + + while (NULL != *list) { + if (strcmp (*list, member) == 0) { + return true; + } + list++; + } + + return false; +} + +/* + * comma_to_list - convert comma-separated list to (char *) array + */ + +/*@only@*/char **comma_to_list (const char *comma) +{ + char *members; + char **array; + int i; + char *cp; + char *cp2; + + assert (NULL != comma); + + /* + * Make a copy since we are going to be modifying the list + */ + + members = xstrdup (comma); + + /* + * Count the number of commas in the list + */ + + for (cp = members, i = 0;; i++) { + cp2 = strchr (cp, ','); + if (NULL != cp2) { + cp = cp2 + 1; + } else { + break; + } + } + + /* + * Add 2 - one for the ending NULL, the other for the last item + */ + + i += 2; + + /* + * Allocate the array we're going to store the pointers into. + */ + + array = (char **) xmalloc (sizeof (char *) * i); + + /* + * Empty list is special - 0 members, not 1 empty member. --marekm + */ + + if ('\0' == *members) { + *array = (char *) 0; + return array; + } + + /* + * Now go walk that list all over again, this time building the + * array of pointers. + */ + + for (cp = members, i = 0;; i++) { + array[i] = cp; + cp2 = strchr (cp, ','); + if (NULL != cp2) { + *cp2 = '\0'; + cp2++; + cp = cp2; + } else { + array[i + 1] = (char *) 0; + break; + } + } + + /* + * Return the new array of pointers + */ + + return array; +} + diff --git a/libmisc/log.c b/libmisc/log.c new file mode 100644 index 00000000..92a01609 --- /dev/null +++ b/libmisc/log.c @@ -0,0 +1,116 @@ +/* + * Copyright (c) 1989 - 1994, Julianne Frances Haugh + * Copyright (c) 1996 - 1998, Marek Michałkiewicz + * Copyright (c) 2003 - 2005, Tomasz Kłoczko + * Copyright (c) 2008 , Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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> + +#ident "$Id: log.c 2820 2009-04-27 20:15:09Z nekral-guest $" + +#include <sys/types.h> +#include <pwd.h> +#include <fcntl.h> +#include <time.h> +#include "defines.h" +#include <lastlog.h> +#include "prototypes.h" + +/* + * dolastlog - create lastlog entry + * + * A "last login" entry is created for the user being logged in. The + * UID is extracted from the global (struct passwd) entry and the + * TTY information is gotten from the (struct utmp). + */ +void dolastlog ( + struct lastlog *ll, + const struct passwd *pw, + /*@unique@*/const char *line, + /*@unique@*/const char *host) +{ + int fd; + off_t offset; + struct lastlog newlog; + time_t ll_time; + + /* + * If the file does not exist, don't create it. + */ + + fd = open (LASTLOG_FILE, O_RDWR); + if (-1 == fd) { + return; + } + + /* + * The file is indexed by UID number. Seek to the record + * for this UID. Negative UID's will create problems, but ... + */ + + offset = (off_t) pw->pw_uid * sizeof newlog; + + if (lseek (fd, offset, SEEK_SET) != offset) { + SYSLOG ((LOG_WARN, + "Can't read last lastlog entry for UID %lu in %s. Entry not updated.", + (unsigned long) pw->pw_uid, LASTLOG_FILE)); + (void) close (fd); + return; + } + + /* + * Read the old entry so we can tell the user when they last + * logged in. Then construct the new entry and write it out + * the way we read the old one in. + */ + + if (read (fd, (void *) &newlog, sizeof newlog) != (ssize_t) sizeof newlog) { + memzero (&newlog, sizeof newlog); + } + if (NULL != ll) { + *ll = newlog; + } + + ll_time = newlog.ll_time; + (void) time (&ll_time); + newlog.ll_time = ll_time; + strncpy (newlog.ll_line, line, sizeof newlog.ll_line); +#if HAVE_LL_HOST + strncpy (newlog.ll_host, host, sizeof newlog.ll_host); +#endif + if ( (lseek (fd, offset, SEEK_SET) != offset) + || (write (fd, (const void *) &newlog, sizeof newlog) != (ssize_t) sizeof newlog) + || (close (fd) != 0)) { + SYSLOG ((LOG_WARN, + "Can't write lastlog entry for UID %lu in %s.", + (unsigned long) pw->pw_uid, LASTLOG_FILE)); + (void) close (fd); + } +} + diff --git a/libmisc/loginprompt.c b/libmisc/loginprompt.c new file mode 100644 index 00000000..ccfb5dee --- /dev/null +++ b/libmisc/loginprompt.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 1989 - 1993, Julianne Frances Haugh + * Copyright (c) 1996 - 2000, Marek Michałkiewicz + * Copyright (c) 2003 - 2005, Tomasz Kłoczko + * Copyright (c) 2008 - 2011, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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> + +#ident "$Id: loginprompt.c 3490 2011-09-18 20:41:38Z nekral-guest $" + +#include <assert.h> +#include <stdio.h> +#include <signal.h> +#include <ctype.h> +#include "prototypes.h" +#include "defines.h" +#include "getdef.h" + +static void login_exit (unused int sig) +{ + exit (EXIT_FAILURE); +} + +/* + * login_prompt - prompt the user for their login name + * + * login_prompt() displays the standard login prompt. If ISSUE_FILE + * is set in login.defs, this file is displayed before the prompt. + */ + +void login_prompt (const char *prompt, char *name, int namesize) +{ + char buf[1024]; + +#define MAX_ENV 32 + char *envp[MAX_ENV]; + char *cp; + int i; + FILE *fp; + + RETSIGTYPE (*sigquit) (int); +#ifdef SIGTSTP + RETSIGTYPE (*sigtstp) (int); +#endif + + /* + * There is a small chance that a QUIT character will be part of + * some random noise during a prompt. Deal with this by exiting + * instead of core dumping. If SIGTSTP is defined, do the same + * thing for that signal. + */ + + sigquit = signal (SIGQUIT, login_exit); +#ifdef SIGTSTP + sigtstp = signal (SIGTSTP, login_exit); +#endif + + /* + * See if the user has configured the issue file to + * be displayed and display it before the prompt. + */ + + if (NULL != prompt) { + const char *fname = getdef_str ("ISSUE_FILE"); + if (NULL != fname) { + fp = fopen (fname, "r"); + if (NULL != fp) { + while ((i = getc (fp)) != EOF) { + (void) putc (i, stdout); + } + + (void) fclose (fp); + } + } + (void) gethostname (buf, sizeof buf); + printf (prompt, buf); + (void) fflush (stdout); + } + + /* + * Read the user's response. The trailing newline will be + * removed. + */ + + memzero (buf, sizeof buf); + if (fgets (buf, (int) sizeof buf, stdin) != buf) { + exit (EXIT_FAILURE); + } + + cp = strchr (buf, '\n'); + if (NULL == cp) { + exit (EXIT_FAILURE); + } + *cp = '\0'; /* remove \n [ must be there ] */ + + /* + * Skip leading whitespace. This makes " username" work right. + * Then copy the rest (up to the end or the first "non-graphic" + * character into the username. + */ + + for (cp = buf; *cp == ' ' || *cp == '\t'; cp++); + + for (i = 0; i < namesize - 1 && isgraph (*cp); name[i++] = *cp++); + while (isgraph (*cp)) { + cp++; + } + + if ('\0' != *cp) { + cp++; + } + + name[i] = '\0'; + + /* + * This is a disaster, at best. The user may have entered extra + * environmental variables at the prompt. There are several ways + * to do this, and I just take the easy way out. + */ + + if ('\0' != *cp) { /* process new variables */ + char *nvar; + int count = 1; + int envc; + + for (envc = 0; envc < MAX_ENV; envc++) { + nvar = strtok ((0 != envc) ? (char *) 0 : cp, " \t,"); + if (NULL == nvar) { + break; + } + if (strchr (nvar, '=') != NULL) { + envp[envc] = nvar; + } else { + size_t len = strlen (nvar) + 32; + envp[envc] = xmalloc (len); + (void) snprintf (envp[envc], len, + "L%d=%s", count++, nvar); + } + } + set_env (envc, envp); + } + + /* + * Set the SIGQUIT handler back to its original value + */ + + (void) signal (SIGQUIT, sigquit); +#ifdef SIGTSTP + (void) signal (SIGTSTP, sigtstp); +#endif +} + diff --git a/libmisc/mail.c b/libmisc/mail.c new file mode 100644 index 00000000..e5de320a --- /dev/null +++ b/libmisc/mail.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 1989 - 1991, Julianne Frances Haugh + * Copyright (c) 1996 - 1998, Marek Michałkiewicz + * Copyright (c) 2003 - 2005, Tomasz Kłoczko + * Copyright (c) 2008 , Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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 "prototypes.h" +#include "defines.h" +#include <assert.h> +#include <sys/stat.h> +#include <stdio.h> +#include <string.h> + +#include "getdef.h" + +#ident "$Id: mail.c 2819 2009-04-27 20:09:18Z nekral-guest $" + + +void mailcheck (void) +{ + struct stat statbuf; + char *mailbox; + + if (!getdef_bool ("MAIL_CHECK_ENAB")) { + return; + } + + /* + * Check incoming mail in Maildir format - J. + */ + mailbox = getenv ("MAILDIR"); + if (NULL != mailbox) { + char *newmail; + size_t len = strlen (mailbox) + 5; + int wlen; + + newmail = xmalloc (len); + wlen = snprintf (newmail, len, "%s/new", mailbox); + assert (wlen == (int) len - 1); + + if (stat (newmail, &statbuf) != -1 && statbuf.st_size != 0) { + if (statbuf.st_mtime > statbuf.st_atime) { + free (newmail); + (void) puts (_("You have new mail.")); + return; + } + } + free (newmail); + } + + mailbox = getenv ("MAIL"); + if (NULL == mailbox) { + return; + } + + if ( (stat (mailbox, &statbuf) == -1) + || (statbuf.st_size == 0)) { + (void) puts (_("No mail.")); + } else if (statbuf.st_atime > statbuf.st_mtime) { + (void) puts (_("You have mail.")); + } else { + (void) puts (_("You have new mail.")); + } +} + diff --git a/libmisc/motd.c b/libmisc/motd.c new file mode 100644 index 00000000..27ee57d0 --- /dev/null +++ b/libmisc/motd.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 1989 - 1991, Julianne Frances Haugh + * Copyright (c) 1996 - 1997, Marek Michałkiewicz + * Copyright (c) 2003 - 2005, Tomasz Kłoczko + * Copyright (c) 2010 , Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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> + +#ident "$Id: motd.c 3224 2010-08-21 15:32:53Z nekral-guest $" + +#include <stdio.h> +#include "prototypes.h" +#include "defines.h" +#include "getdef.h" +/* + * motd -- output the /etc/motd file + * + * motd() determines the name of a login announcement file and outputs + * it to the user's terminal at login time. The MOTD_FILE configuration + * option is a colon-delimited list of filenames. + */ +void motd (void) +{ + FILE *fp; + char *motdlist; + const char *motdfile; + char *mb; + register int c; + + motdfile = getdef_str ("MOTD_FILE"); + if (NULL == motdfile) { + return; + } + + motdlist = xstrdup (motdfile); + + for (mb = motdlist; ;mb = NULL) { + motdfile = strtok (mb, ":"); + if (NULL == motdfile) { + break; + } + + fp = fopen (motdfile, "r"); + if (NULL != fp) { + while ((c = getc (fp)) != EOF) { + putchar (c); + } + fclose (fp); + } + } + fflush (stdout); + + free (motdlist); +} + diff --git a/libmisc/myname.c b/libmisc/myname.c new file mode 100644 index 00000000..092e5c64 --- /dev/null +++ b/libmisc/myname.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 1996 - 1997, Marek Michałkiewicz + * Copyright (c) 2003 - 2005, Tomasz Kłoczko + * Copyright (c) 2007 - 2009, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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. + */ + +/* + * myname.c - determine the current username and get the passwd entry + * + */ + +#include <config.h> + +#ident "$Id: myname.c 2813 2009-04-26 17:10:49Z nekral-guest $" + +#include "defines.h" +#include <pwd.h> +#include "prototypes.h" +/*@null@*/ /*@only@*/struct passwd *get_my_pwent (void) +{ + struct passwd *pw; + const char *cp = getlogin (); + uid_t ruid = getuid (); + + /* + * Try getlogin() first - if it fails or returns a non-existent + * username, or a username which doesn't match the real UID, fall + * back to getpwuid(getuid()). This should work reasonably with + * usernames longer than the utmp limit (8 characters), as well as + * shared UIDs - but not both at the same time... + * + * XXX - when running from su, will return the current user (not + * the original user, like getlogin() does). Does this matter? + */ + if ((NULL != cp) && ('\0' != *cp)) { + pw = xgetpwnam (cp); + if ((NULL != pw) && (pw->pw_uid == ruid)) { + return pw; + } + } + + return xgetpwuid (ruid); +} + diff --git a/libmisc/obscure.c b/libmisc/obscure.c new file mode 100644 index 00000000..91b2609c --- /dev/null +++ b/libmisc/obscure.c @@ -0,0 +1,323 @@ +/* + * Copyright (c) 1989 - 1994, Julianne Frances Haugh + * Copyright (c) 1996 - 1999, Marek Michałkiewicz + * Copyright (c) 2003 - 2005, Tomasz Kłoczko + * Copyright (c) 2007 - 2010, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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 USE_PAM + +#ident "$Id: obscure.c 3232 2010-08-22 19:13:53Z nekral-guest $" + + +/* + * This version of obscure.c contains modifications to support "cracklib" + * by Alec Muffet (alec.muffett@uk.sun.com). You must obtain the Cracklib + * library source code for this function to operate. + */ +#include <ctype.h> +#include <stdio.h> +#include "prototypes.h" +#include "defines.h" +#include "getdef.h" +/* + * can't be a palindrome - like `R A D A R' or `M A D A M' + */ +static bool palindrome (unused const char *old, const char *new) +{ + size_t i, j; + + i = strlen (new); + + for (j = 0; j < i; j++) { + if (new[i - j - 1] != new[j]) { + return false; + } + } + + return true; +} + +/* + * more than half of the characters are different ones. + */ + +static bool similar (/*@notnull@*/const char *old, /*@notnull@*/const char *new) +{ + int i, j; + + /* + * XXX - sometimes this fails when changing from a simple password + * to a really long one (MD5). For now, I just return success if + * the new password is long enough. Please feel free to suggest + * something better... --marekm + */ + if (strlen (new) >= 8) { + return false; + } + + for (i = j = 0; ('\0' != new[i]) && ('\0' != old[i]); i++) { + if (strchr (new, old[i]) != NULL) { + j++; + } + } + + if (i >= j * 2) { + return false; + } + + return true; +} + +/* + * a nice mix of characters. + */ + +static bool simple (unused const char *old, const char *new) +{ + bool digits = false; + bool uppers = false; + bool lowers = false; + bool others = false; + int size; + int i; + + for (i = 0; '\0' != new[i]; i++) { + if (isdigit (new[i])) { + digits = true; + } else if (isupper (new[i])) { + uppers = true; + } else if (islower (new[i])) { + lowers = true; + } else { + others = true; + } + } + + /* + * The scam is this - a password of only one character type + * must be 8 letters long. Two types, 7, and so on. + */ + + size = 9; + if (digits) { + size--; + } + if (uppers) { + size--; + } + if (lowers) { + size--; + } + if (others) { + size--; + } + + if (size <= i) { + return false; + } + + return true; +} + +static char *str_lower (/*@returned@*/char *string) +{ + char *cp; + + for (cp = string; '\0' != *cp; cp++) { + *cp = tolower (*cp); + } + return string; +} + +static /*@observer@*//*@null@*/const char *password_check ( + /*@notnull@*/const char *old, + /*@notnull@*/const char *new, + /*@notnull@*/const struct passwd *pwdp) +{ + const char *msg = NULL; + char *oldmono, *newmono, *wrapped; + +#ifdef HAVE_LIBCRACK + char *dictpath; + +#ifdef HAVE_LIBCRACK_PW + char *FascistCheckPw (); +#else + char *FascistCheck (); +#endif +#endif + + if (strcmp (new, old) == 0) { + return _("no change"); + } + + newmono = str_lower (xstrdup (new)); + oldmono = str_lower (xstrdup (old)); + wrapped = xmalloc (strlen (oldmono) * 2 + 1); + strcpy (wrapped, oldmono); + strcat (wrapped, oldmono); + + if (palindrome (oldmono, newmono)) { + msg = _("a palindrome"); + } else if (strcmp (oldmono, newmono) == 0) { + msg = _("case changes only"); + } else if (similar (oldmono, newmono)) { + msg = _("too similar"); + } else if (simple (old, new)) { + msg = _("too simple"); + } else if (strstr (wrapped, newmono) != NULL) { + msg = _("rotated"); + } else { +#ifdef HAVE_LIBCRACK + /* + * Invoke Alec Muffett's cracklib routines. + */ + + dictpath = getdef_str ("CRACKLIB_DICTPATH"); + if (NULL != dictpath) { +#ifdef HAVE_LIBCRACK_PW + msg = FascistCheckPw (new, dictpath, pwdp); +#else + msg = FascistCheck (new, dictpath); +#endif + } +#endif + } + strzero (newmono); + strzero (oldmono); + strzero (wrapped); + free (newmono); + free (oldmono); + free (wrapped); + + return msg; +} + +static /*@observer@*//*@null@*/const char *obscure_msg ( + /*@notnull@*/const char *old, + /*@notnull@*/const char *new, + /*@notnull@*/const struct passwd *pwdp) +{ + size_t maxlen, oldlen, newlen; + char *new1, *old1; + const char *msg; + const char *result; + + oldlen = strlen (old); + newlen = strlen (new); + + if (newlen < (size_t) getdef_num ("PASS_MIN_LEN", 0)) { + return _("too short"); + } + + /* + * Remaining checks are optional. + */ + if (!getdef_bool ("OBSCURE_CHECKS_ENAB")) { + return NULL; + } + + msg = password_check (old, new, pwdp); + if (NULL != msg) { + return msg; + } + + result = getdef_str ("ENCRYPT_METHOD"); + if (NULL == result) { + /* The traditional crypt() truncates passwords to 8 chars. It is + possible to circumvent the above checks by choosing an easy + 8-char password and adding some random characters to it... + Example: "password$%^&*123". So check it again, this time + truncated to the maximum length. Idea from npasswd. --marekm */ + + if (getdef_bool ("MD5_CRYPT_ENAB")) { + return NULL; + } + + } else { + + if ( (strcmp (result, "MD5") == 0) +#ifdef USE_SHA_CRYPT + || (strcmp (result, "SHA256") == 0) + || (strcmp (result, "SHA512") == 0) +#endif + ) { + return NULL; + } + + } + maxlen = (size_t) getdef_num ("PASS_MAX_LEN", 8); + if ( (oldlen <= maxlen) + && (newlen <= maxlen)) { + return NULL; + } + + new1 = xstrdup (new); + old1 = xstrdup (old); + if (newlen > maxlen) { + new1[maxlen] = '\0'; + } + if (oldlen > maxlen) { + old1[maxlen] = '\0'; + } + + msg = password_check (old1, new1, pwdp); + + memzero (new1, newlen); + memzero (old1, oldlen); + free (new1); + free (old1); + + return msg; +} + +/* + * Obscure - see if password is obscure enough. + * + * The programmer is encouraged to add as much complexity to this + * routine as desired. Included are some of my favorite ways to + * check passwords. + */ + +bool obscure (const char *old, const char *new, const struct passwd *pwdp) +{ + const char *msg = obscure_msg (old, new, pwdp); + + if (NULL != msg) { + printf (_("Bad password: %s. "), msg); + return false; + } + return true; +} + +#else /* !USE_PAM */ +extern int errno; /* warning: ANSI C forbids an empty source file */ +#endif /* !USE_PAM */ diff --git a/libmisc/pam_pass.c b/libmisc/pam_pass.c new file mode 100644 index 00000000..303a974a --- /dev/null +++ b/libmisc/pam_pass.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) 1997 - 1999, Marek Michałkiewicz + * Copyright (c) 2001 - 2005, Tomasz Kłoczko + * Copyright (c) 2008 , Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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> + +#ifdef USE_PAM + +#ident "$Id: pam_pass.c 2870 2009-05-09 13:15:17Z nekral-guest $" + + +/* + * Change the user's password using PAM. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include "defines.h" +#include "pam_defs.h" +#include "prototypes.h" + +void do_pam_passwd (const char *user, bool silent, bool change_expired) +{ + pam_handle_t *pamh = NULL; + int flags = 0, ret; + + if (silent) + flags |= PAM_SILENT; + if (change_expired) + flags |= PAM_CHANGE_EXPIRED_AUTHTOK; + + ret = pam_start ("passwd", user, &conv, &pamh); + if (ret != PAM_SUCCESS) { + fprintf (stderr, + _("passwd: pam_start() failed, error %d\n"), ret); + exit (10); /* XXX */ + } + + ret = pam_chauthtok (pamh, flags); + if (ret != PAM_SUCCESS) { + fprintf (stderr, _("passwd: %s\n"), pam_strerror (pamh, ret)); + fputs (_("passwd: password unchanged\n"), stderr); + pam_end (pamh, ret); + exit (10); /* XXX */ + } + + fputs (_("passwd: password updated successfully\n"), stderr); + (void) pam_end (pamh, PAM_SUCCESS); +} +#else /* !USE_PAM */ +extern int errno; /* warning: ANSI C forbids an empty source file */ +#endif /* !USE_PAM */ diff --git a/libmisc/pam_pass_non_interractive.c b/libmisc/pam_pass_non_interractive.c new file mode 100644 index 00000000..e50648b6 --- /dev/null +++ b/libmisc/pam_pass_non_interractive.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2009 - 2010, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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> + +#ident "$Id:$" + +#ifdef USE_PAM +#include <assert.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <security/pam_appl.h> +#include "prototypes.h" + +/*@null@*/ /*@only@*/static const char *non_interactive_password = NULL; +static int ni_conv (int num_msg, + const struct pam_message **msg, + struct pam_response **resp, + unused void *appdata_ptr); +static struct pam_conv non_interactive_pam_conv = { + ni_conv, + NULL +}; + + + +static int ni_conv (int num_msg, + const struct pam_message **msg, + struct pam_response **resp, + unused void *appdata_ptr) +{ + struct pam_response *responses; + int count; + + assert (NULL != non_interactive_password); + + if (num_msg <= 0) { + return PAM_CONV_ERR; + } + + responses = (struct pam_response *) calloc ((size_t) num_msg, + sizeof (*responses)); + if (NULL == responses) { + return PAM_CONV_ERR; + } + + for (count=0; count < num_msg; count++) { + responses[count].resp_retcode = 0; + + switch (msg[count]->msg_style) { + case PAM_PROMPT_ECHO_ON: + fprintf (stderr, + _("%s: PAM modules requesting echoing are not supported.\n"), + Prog); + goto failed_conversation; + case PAM_PROMPT_ECHO_OFF: + responses[count].resp = strdup (non_interactive_password); + if (NULL == responses[count].resp) { + goto failed_conversation; + } + break; + case PAM_ERROR_MSG: + if ( (NULL == msg[count]->msg) + || (fprintf (stderr, "%s\n", msg[count]->msg) <0)) { + goto failed_conversation; + } + responses[count].resp = NULL; + break; + case PAM_TEXT_INFO: + if ( (NULL == msg[count]->msg) + || (fprintf (stdout, "%s\n", msg[count]->msg) <0)) { + goto failed_conversation; + } + responses[count].resp = NULL; + break; + default: + (void) fprintf (stderr, + _("%s: conversation type %d not supported.\n"), + Prog, msg[count]->msg_style); + goto failed_conversation; + } + } + + *resp = responses; + + return PAM_SUCCESS; + +failed_conversation: + for (count=0; count < num_msg; count++) { + if (NULL != responses[count].resp) { + memset (responses[count].resp, 0, + strlen (responses[count].resp)); + free (responses[count].resp); + responses[count].resp = NULL; + } + } + + free (responses); + *resp = NULL; + + return PAM_CONV_ERR; +} + + +/* + * Change non interactively the user's password using PAM. + * + * Return 0 on success, 1 on failure. + */ +int do_pam_passwd_non_interractive (const char *pam_service, + const char *username, + const char* password) +{ + pam_handle_t *pamh = NULL; + int ret; + + ret = pam_start (pam_service, username, &non_interactive_pam_conv, &pamh); + if (ret != PAM_SUCCESS) { + fprintf (stderr, + _("%s: (user %s) pam_start failure %d\n"), + Prog, username, ret); + return 1; + } + + non_interactive_password = password; + ret = pam_chauthtok (pamh, 0); + if (ret != PAM_SUCCESS) { + fprintf (stderr, + _("%s: (user %s) pam_chauthtok() failed, error:\n" + "%s\n"), + Prog, username, pam_strerror (pamh, ret)); + } + + (void) pam_end (pamh, PAM_SUCCESS); + + return ((PAM_SUCCESS == ret) ? 0 : 1); +} +#else /* !USE_PAM */ +extern int errno; /* warning: ANSI C forbids an empty source file */ +#endif /* !USE_PAM */ diff --git a/libmisc/pwd2spwd.c b/libmisc/pwd2spwd.c new file mode 100644 index 00000000..dc4fbcf1 --- /dev/null +++ b/libmisc/pwd2spwd.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 1989 - 1994, Julianne Frances Haugh + * Copyright (c) 1996 - 1997, Marek Michałkiewicz + * Copyright (c) 2003 - 2005, Tomasz Kłoczko + * Copyright (c) 2007 - 2009, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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> + +#ident "$Id: pwd2spwd.c 2595 2009-04-05 22:29:42Z nekral-guest $" + +#ifndef USE_PAM + +#include <sys/types.h> +#include "prototypes.h" +#include "defines.h" +#include <pwd.h> +extern time_t time (time_t *); + +/* + * pwd_to_spwd - create entries for new spwd structure + * + * pwd_to_spwd() creates a new (struct spwd) containing the + * information in the pointed-to (struct passwd). + */ + +struct spwd *pwd_to_spwd (const struct passwd *pw) +{ + static struct spwd sp; + + /* + * Nice, easy parts first. The name and passwd map directly + * from the old password structure to the new one. + */ + sp.sp_namp = pw->pw_name; + sp.sp_pwdp = pw->pw_passwd; + + { + /* + * Defaults used if there is no pw_age information. + */ + sp.sp_min = 0; + sp.sp_max = (10000L * DAY) / SCALE; + sp.sp_lstchg = (long) time ((time_t *) 0) / SCALE; + if (0 == sp.sp_lstchg) { + /* Better disable aging than requiring a password + * change */ + sp.sp_lstchg = -1; + } + } + + /* + * These fields have no corresponding information in the password + * file. They are set to uninitialized values. + */ + sp.sp_warn = -1; + sp.sp_expire = -1; + sp.sp_inact = -1; + sp.sp_flag = SHADOW_SP_FLAG_UNSET; + + return &sp; +} +#else /* USE_PAM */ +extern int errno; /* warning: ANSI C forbids an empty source file */ +#endif /* !USE_PAM */ + diff --git a/libmisc/pwd_init.c b/libmisc/pwd_init.c new file mode 100644 index 00000000..cbc8cb44 --- /dev/null +++ b/libmisc/pwd_init.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 1990 - 1994, Julianne Frances Haugh + * Copyright (c) 1997 , Marek Michałkiewicz + * Copyright (c) 2003 - 2005, Tomasz Kłoczko + * Copyright (c) 2008 , Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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> + +#ident "$Id: pwd_init.c 1980 2008-04-27 00:40:09Z nekral-guest $" + +#include "defines.h" +#include <signal.h> +#include <sys/types.h> +#include <sys/stat.h> +#ifdef HAVE_SYS_RESOURCE_H +#include <sys/resource.h> +#endif + +#include "prototypes.h" + +/* + * pwd_init - ignore signals, and set resource limits to safe + * values. Call this before modifying password files, so that + * it is less likely to fail in the middle of operation. + */ +void pwd_init (void) +{ +#ifdef HAVE_SYS_RESOURCE_H + struct rlimit rlim; + +#ifdef RLIMIT_CORE + rlim.rlim_cur = rlim.rlim_max = 0; + setrlimit (RLIMIT_CORE, &rlim); +#endif + rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY; +#ifdef RLIMIT_AS + setrlimit (RLIMIT_AS, &rlim); +#endif +#ifdef RLIMIT_CPU + setrlimit (RLIMIT_CPU, &rlim); +#endif +#ifdef RLIMIT_DATA + setrlimit (RLIMIT_DATA, &rlim); +#endif +#ifdef RLIMIT_FSIZE + setrlimit (RLIMIT_FSIZE, &rlim); +#endif +#ifdef RLIMIT_NOFILE + setrlimit (RLIMIT_NOFILE, &rlim); +#endif +#ifdef RLIMIT_RSS + setrlimit (RLIMIT_RSS, &rlim); +#endif +#ifdef RLIMIT_STACK + setrlimit (RLIMIT_STACK, &rlim); +#endif +#else /* !HAVE_SYS_RESOURCE_H */ + set_filesize_limit (30000); + /* don't know how to set the other limits... */ +#endif /* !HAVE_SYS_RESOURCE_H */ + + signal (SIGALRM, SIG_IGN); + signal (SIGHUP, SIG_IGN); + signal (SIGINT, SIG_IGN); + signal (SIGPIPE, SIG_IGN); + signal (SIGQUIT, SIG_IGN); + signal (SIGTERM, SIG_IGN); +#ifdef SIGTSTP + signal (SIGTSTP, SIG_IGN); +#endif +#ifdef SIGTTOU + signal (SIGTTOU, SIG_IGN); +#endif + + umask (077); +} diff --git a/libmisc/pwdcheck.c b/libmisc/pwdcheck.c new file mode 100644 index 00000000..0131380b --- /dev/null +++ b/libmisc/pwdcheck.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2000 - 2005, Tomasz Kłoczko + * Copyright (c) 2007 - 2008, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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> + +#ident "$Id: pwdcheck.c 2779 2009-04-23 20:17:02Z nekral-guest $" + +#ifndef USE_PAM + +#include <stdio.h> +#include <shadow.h> +#include "prototypes.h" +#include "defines.h" +#include "pwauth.h" + +void passwd_check (const char *user, const char *passwd, unused const char *progname) +{ + struct spwd *sp; + + sp = getspnam (user); /* !USE_PAM, no need for xgetspnam */ + if (NULL != sp) { + passwd = sp->sp_pwdp; + } + if (pw_auth (passwd, user, PW_LOGIN, (char *) 0) != 0) { + SYSLOG ((LOG_WARN, "incorrect password for `%s'", user)); + (void) sleep (1); + fprintf (stderr, _("Incorrect password for %s.\n"), user); + exit (EXIT_FAILURE); + } +} +#else /* USE_PAM */ +extern int errno; /* warning: ANSI C forbids an empty source file */ +#endif /* USE_PAM */ diff --git a/libmisc/remove_tree.c b/libmisc/remove_tree.c new file mode 100644 index 00000000..1e527e3e --- /dev/null +++ b/libmisc/remove_tree.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 1991 - 1994, Julianne Frances Haugh + * Copyright (c) 1996 - 2001, Marek Michałkiewicz + * Copyright (c) 2003 - 2006, Tomasz Kłoczko + * Copyright (c) 2007 - 2010, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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> + +#ident "$Id: remove_tree.c 3283 2010-09-05 15:34:42Z nekral-guest $" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <unistd.h> +#include "prototypes.h" +#include "defines.h" + +/* + * remove_tree - delete a directory tree + * + * remove_tree() walks a directory tree and deletes all the files + * and directories. + * At the end, it deletes the root directory itself. + */ + +int remove_tree (const char *root, bool remove_root) +{ + char *new_name = NULL; + int err = 0; + struct DIRECT *ent; + struct stat sb; + DIR *dir; + + /* + * Open the source directory and read each entry. Every file + * entry in the directory is copied with the UID and GID set + * to the provided values. As an added security feature only + * regular files (and directories ...) are copied, and no file + * is made set-ID. + */ + dir = opendir (root); + if (NULL == dir) { + return -1; + } + + while ((ent = readdir (dir))) { + size_t new_len = strlen (root) + strlen (ent->d_name) + 2; + + /* + * Skip the "." and ".." entries + */ + + if (strcmp (ent->d_name, ".") == 0 || + strcmp (ent->d_name, "..") == 0) { + continue; + } + + /* + * Make the filename for the current entry. + */ + + free (new_name); + new_name = (char *) malloc (new_len); + if (NULL == new_name) { + err = -1; + break; + } + (void) snprintf (new_name, new_len, "%s/%s", root, ent->d_name); + if (LSTAT (new_name, &sb) == -1) { + continue; + } + + if (S_ISDIR (sb.st_mode)) { + /* + * Recursively delete this directory. + */ + if (remove_tree (new_name, true) != 0) { + err = -1; + break; + } + } else { + /* + * Delete the file. + */ + if (unlink (new_name) != 0) { + err = -1; + break; + } + } + } + if (NULL != new_name) { + free (new_name); + } + (void) closedir (dir); + + if (remove_root && (0 == err)) { + if (rmdir (root) != 0) { + err = -1; + } + } + + return err; +} + diff --git a/libmisc/rlogin.c b/libmisc/rlogin.c new file mode 100644 index 00000000..1364c9c7 --- /dev/null +++ b/libmisc/rlogin.c @@ -0,0 +1,198 @@ +/* + * Copyright (c) 1989 - 1994, Julianne Frances Haugh + * Copyright (c) 1996 - 1999, Marek Michałkiewicz + * Copyright (c) 2003 - 2005, Tomasz Kłoczko + * Copyright (c) 2007 - 2008, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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> + +#ifdef RLOGIN + +#ident "$Id: rlogin.c 2849 2009-04-30 21:08:49Z nekral-guest $" + +#include "prototypes.h" +#include "defines.h" +#include <stdio.h> +#include <pwd.h> +#include <netdb.h> +static struct { + int spd_name; + int spd_baud; +} speed_table[] = +{ +#ifdef B50 + { + B50, 50}, +#endif +#ifdef B75 + { + B75, 75}, +#endif +#ifdef B110 + { + B110, 110}, +#endif +#ifdef B134 + { + B134, 134}, +#endif +#ifdef B150 + { + B150, 150}, +#endif +#ifdef B200 + { + B200, 200}, +#endif +#ifdef B300 + { + B300, 300}, +#endif +#ifdef B600 + { + B600, 600}, +#endif +#ifdef B1200 + { + B1200, 1200}, +#endif +#ifdef B1800 + { + B1800, 1800}, +#endif +#ifdef B2400 + { + B2400, 2400}, +#endif +#ifdef B4800 + { + B4800, 4800}, +#endif +#ifdef B9600 + { + B9600, 9600}, +#endif +#ifdef B19200 + { + B19200, 19200}, +#endif +#ifdef B38400 + { + B38400, 38400}, +#endif + { + -1, -1} +}; + +static void get_remote_string (char *buf, size_t size) +{ + for (;;) { + if (read (0, buf, 1) != 1) { + exit (EXIT_FAILURE); + } + if ('\0' == *buf) { + return; + } + --size; + if (size > 0) { + ++buf; + } + } + /*NOTREACHED*/} + +int +do_rlogin (const char *remote_host, char *name, size_t namelen, char *term, + size_t termlen) +{ + struct passwd *pwd; + char remote_name[32]; + char *cp; + unsigned long remote_speed = 9600; + int speed_name = B9600; + int i; + TERMIO termio; + + get_remote_string (remote_name, sizeof remote_name); + get_remote_string (name, namelen); + get_remote_string (term, termlen); + + cp = strchr (term, '/'); + if (NULL != cp) { + *cp = '\0'; + cp++; + + if (getulong (cp, &remote_speed) == 0) { + remote_speed = 9600; + } + } + for (i = 0; + ( (speed_table[i].spd_baud != remote_speed) + && (speed_table[i].spd_name != -1)); + i++); + + if (-1 != speed_table[i].spd_name) { + speed_name = speed_table[i].spd_name; + } + + /* + * Put the terminal in cooked mode with echo turned on. + */ + + GTTY (0, &termio); + termio.c_iflag |= ICRNL | IXON; + termio.c_oflag |= OPOST | ONLCR; + termio.c_lflag |= ICANON | ECHO | ECHOE; +#ifdef CBAUD + termio.c_cflag = (termio.c_cflag & ~CBAUD) | speed_name; +#else + termio.c_cflag = (termio.c_cflag) | speed_name; +#endif + STTY (0, &termio); + + pwd = getpwnam (name); /* local, no need for xgetpwnam */ + if (NULL == pwd) { + return 0; + } + + /* + * ruserok() returns 0 for success on modern systems, and 1 on + * older ones. If you are having trouble with people logging + * in without giving a required password, THIS is the culprit - + * go fix the #define in config.h. + */ + +#ifndef RUSEROK + return 0; +#else + return ruserok (remote_host, pwd->pw_uid == 0, + remote_name, name) == RUSEROK; +#endif +} +#endif /* RLOGIN */ + diff --git a/libmisc/root_flag.c b/libmisc/root_flag.c new file mode 100644 index 00000000..717de51d --- /dev/null +++ b/libmisc/root_flag.c @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2011 , Julian Pidancet + * Copyright (c) 2011 , Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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> + +#ident "$Id$" + +#include <stdio.h> +#include <assert.h> +#include "defines.h" +#include "prototypes.h" +/*@-exitarg@*/ +#include "exitcodes.h" + +static void change_root (const char* newroot); + +/* + * process_root_flag - chroot if given the --root option + * + * This shall be called before accessing the passwd, group, shadow, + * gshadow, useradd's default, login.defs files (non exhaustive list) + * or authenticating the caller. + * + * The audit, syslog, or locale files shall be open before + */ +extern void process_root_flag (const char* short_opt, int argc, char **argv) +{ + /* + * Parse the command line options. + */ + int i; + const char *newroot = NULL; + + for (i = 0; i < argc; i++) { + if ( (strcmp (argv[i], "--root") == 0) + || (strcmp (argv[i], short_opt) == 0)) { + if (NULL != newroot) { + fprintf (stderr, + _("%s: multiple --root options\n"), + Prog); + exit (E_BAD_ARG); + } + + if (i + 1 == argc) { + fprintf (stderr, + _("%s: option '%s' requires an argument\n"), + Prog, argv[i]); + exit (E_BAD_ARG); + } + newroot = argv[i + 1]; + } + } + + if (NULL != newroot) { + change_root (newroot); + } +} + +static void change_root (const char* newroot) +{ + /* Drop privileges */ + if ( (setregid (getgid (), getgid ()) != 0) + || (setreuid (getuid (), getuid ()) != 0)) { + fprintf (stderr, _("%s: failed to drop privileges (%s)\n"), + Prog, strerror (errno)); + exit (EXIT_FAILURE); + } + + if ('/' != newroot[0]) { + fprintf (stderr, + _("%s: invalid chroot path '%s'\n"), + Prog, newroot); + exit (E_BAD_ARG); + } + + if (access (newroot, F_OK) != 0) { + fprintf(stderr, + _("%s: cannot access chroot directory %s: %s\n"), + Prog, newroot, strerror (errno)); + exit (E_BAD_ARG); + } + if (chroot (newroot) != 0) { + fprintf(stderr, + _("%s: unable to chroot to directory %s: %s\n"), + Prog, newroot, strerror (errno)); + exit (E_BAD_ARG); + } +} + diff --git a/libmisc/salt.c b/libmisc/salt.c new file mode 100644 index 00000000..e0f278ee --- /dev/null +++ b/libmisc/salt.c @@ -0,0 +1,252 @@ +/* + * salt.c - generate a random salt string for crypt() + * + * Written by Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>, + * it is in the public domain. + * + * l64a was Written by J.T. Conklin <jtc@netbsd.org>. Public domain. + */ + +#include <config.h> + +#ident "$Id: salt.c 3489 2011-09-18 20:40:50Z nekral-guest $" + +#include <sys/time.h> +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> +#include "prototypes.h" +#include "defines.h" +#include "getdef.h" + +/* local function prototypes */ +static void seedRNG (void); +static /*@observer@*/const char *gensalt (size_t salt_size); +#ifdef USE_SHA_CRYPT +static size_t SHA_salt_size (void); +static /*@observer@*/const char *SHA_salt_rounds (/*@null@*/int *prefered_rounds); +#endif /* USE_SHA_CRYPT */ + +#ifndef HAVE_L64A +static /*@observer@*/char *l64a(long value) +{ + static char buf[8]; + char *s = buf; + int digit; + int i; + + if (value < 0) { + errno = EINVAL; + return(NULL); + } + + for (i = 0; value != 0 && i < 6; i++) { + digit = value & 0x3f; + + if (digit < 2) { + *s = digit + '.'; + } else if (digit < 12) { + *s = digit + '0' - 2; + } else if (digit < 38) { + *s = digit + 'A' - 12; + } else { + *s = digit + 'a' - 38; + } + + value >>= 6; + s++; + } + + *s = '\0'; + + return(buf); +} +#endif /* !HAVE_L64A */ + +static void seedRNG (void) +{ + struct timeval tv; + static int seeded = 0; + + if (0 == seeded) { + (void) gettimeofday (&tv, NULL); + srandom (tv.tv_sec ^ tv.tv_usec ^ getpid ()); + seeded = 1; + } +} + +/* + * Add the salt prefix. + */ +#define MAGNUM(array,ch) (array)[0]=(array)[2]='$',(array)[1]=(ch),(array)[3]='\0' + +#ifdef USE_SHA_CRYPT +/* + * Return the salt size. + * The size of the salt string is between 8 and 16 bytes for the SHA crypt + * methods. + */ +static size_t SHA_salt_size (void) +{ + double rand_size; + seedRNG (); + rand_size = (double) 9.0 * random () / RAND_MAX; + return (size_t) (8 + rand_size); +} + +/* Default number of rounds if not explicitly specified. */ +#define ROUNDS_DEFAULT 5000 +/* Minimum number of rounds. */ +#define ROUNDS_MIN 1000 +/* Maximum number of rounds. */ +#define ROUNDS_MAX 999999999 + +/* + * Return a salt prefix specifying the rounds number for the SHA crypt methods. + */ +static /*@observer@*/const char *SHA_salt_rounds (/*@null@*/int *prefered_rounds) +{ + static char rounds_prefix[18]; /* Max size: rounds=999999999$ */ + long rounds; + + if (NULL == prefered_rounds) { + long min_rounds = getdef_long ("SHA_CRYPT_MIN_ROUNDS", -1); + long max_rounds = getdef_long ("SHA_CRYPT_MAX_ROUNDS", -1); + double rand_rounds; + + if ((-1 == min_rounds) && (-1 == max_rounds)) { + return ""; + } + + if (-1 == min_rounds) { + min_rounds = max_rounds; + } + + if (-1 == max_rounds) { + max_rounds = min_rounds; + } + + if (min_rounds > max_rounds) { + max_rounds = min_rounds; + } + + seedRNG (); + rand_rounds = (double) (max_rounds-min_rounds+1.0) * random (); + rand_rounds /= RAND_MAX; + rounds = min_rounds + rand_rounds; + } else if (0 == *prefered_rounds) { + return ""; + } else { + rounds = *prefered_rounds; + } + + /* Sanity checks. The libc should also check this, but this + * protects against a rounds_prefix overflow. */ + if (rounds < ROUNDS_MIN) { + rounds = ROUNDS_MIN; + } + + if (rounds > ROUNDS_MAX) { + rounds = ROUNDS_MAX; + } + + (void) snprintf (rounds_prefix, sizeof rounds_prefix, + "rounds=%ld$", rounds); + + return rounds_prefix; +} +#endif /* USE_SHA_CRYPT */ + +/* + * Generate salt of size salt_size. + */ +#define MAX_SALT_SIZE 16 +#define MIN_SALT_SIZE 8 + +static /*@observer@*/const char *gensalt (size_t salt_size) +{ + static char salt[32]; + + salt[0] = '\0'; + + assert (salt_size >= MIN_SALT_SIZE && + salt_size <= MAX_SALT_SIZE); + seedRNG (); + strcat (salt, l64a (random())); + do { + strcat (salt, l64a (random())); + } while (strlen (salt) < salt_size); + + salt[salt_size] = '\0'; + + return salt; +} + +/* + * Generate 8 base64 ASCII characters of random salt. If MD5_CRYPT_ENAB + * in /etc/login.defs is "yes", the salt string will be prefixed by "$1$" + * (magic) and pw_encrypt() will execute the MD5-based FreeBSD-compatible + * version of crypt() instead of the standard one. + * Other methods can be set with ENCRYPT_METHOD + * + * The method can be forced with the meth parameter. + * If NULL, the method will be defined according to the MD5_CRYPT_ENAB and + * ENCRYPT_METHOD login.defs variables. + * + * If meth is specified, an additional parameter can be provided. + * * For the SHA256 and SHA512 method, this specifies the number of rounds + * (if not NULL). + */ +/*@observer@*/const char *crypt_make_salt (/*@null@*//*@observer@*/const char *meth, /*@null@*/void *arg) +{ + /* Max result size for the SHA methods: + * +3 $5$ + * +17 rounds=999999999$ + * +16 salt + * +1 \0 + */ + static char result[40]; + size_t salt_len = 8; + const char *method; + + result[0] = '\0'; + + if (NULL != meth) + method = meth; + else { + method = getdef_str ("ENCRYPT_METHOD"); + if (NULL == method) { + method = getdef_bool ("MD5_CRYPT_ENAB") ? "MD5" : "DES"; + } + } + + if (0 == strcmp (method, "MD5")) { + MAGNUM(result, '1'); +#ifdef USE_SHA_CRYPT + } else if (0 == strcmp (method, "SHA256")) { + MAGNUM(result, '5'); + strcat(result, SHA_salt_rounds((int *)arg)); + salt_len = SHA_salt_size(); + } else if (0 == strcmp (method, "SHA512")) { + MAGNUM(result, '6'); + strcat(result, SHA_salt_rounds((int *)arg)); + salt_len = SHA_salt_size(); +#endif /* USE_SHA_CRYPT */ + } else if (0 != strcmp (method, "DES")) { + fprintf (stderr, + _("Invalid ENCRYPT_METHOD value: '%s'.\n" + "Defaulting to DES.\n"), + method); + result[0] = '\0'; + } + + /* + * Concatenate a pseudo random salt. + */ + assert (sizeof (result) > strlen (result) + salt_len); + strncat (result, gensalt (salt_len), + sizeof (result) - strlen (result) - 1); + + return result; +} + diff --git a/libmisc/setugid.c b/libmisc/setugid.c new file mode 100644 index 00000000..f680b58f --- /dev/null +++ b/libmisc/setugid.c @@ -0,0 +1,148 @@ +/* + * Copyright (c) 1989 - 1994, Julianne Frances Haugh + * Copyright (c) 1996 - 1998, Marek Michałkiewicz + * Copyright (c) 2003 - 2005, Tomasz Kłoczko + * Copyright (c) 2008 - 2010, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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. + */ + +/* + * Separated from setup.c. --marekm + */ + +#include <config.h> + +#ident "$Id: setugid.c 3231 2010-08-22 13:04:54Z nekral-guest $" + +#include <stdio.h> +#include <grp.h> +#include <errno.h> +#include "prototypes.h" +#include "defines.h" +#include <pwd.h> +#include "getdef.h" + +/* + * setup_groups - set the group credentials + * set the group ID to the value from the password file entry + * set the supplementary group IDs + * + * In case of PAM enabled configurations, this shall be called before + * pam_setcred. + * + * Returns 0 on success, or -1 on failure. + */ +int setup_groups (const struct passwd *info) +{ + /* + * Set the real group ID to the primary group ID in the password + * file. + */ + if (setgid (info->pw_gid) == -1) { + int err = errno; + perror ("setgid"); + SYSLOG ((LOG_ERR, "bad group ID `%d' for user `%s': %s\n", + info->pw_gid, info->pw_name, strerror (err))); + closelog (); + return -1; + } +#ifdef HAVE_INITGROUPS + /* + * For systems which support multiple concurrent groups, go get + * the group set from the /etc/group file. + */ + if (initgroups (info->pw_name, info->pw_gid) == -1) { + int err = errno; + perror ("initgroups"); + SYSLOG ((LOG_ERR, "initgroups failed for user `%s': %s\n", + info->pw_name, strerror (err))); + closelog (); + return -1; + } +#endif + return 0; +} + +/* + * change_uid - Set the real UID + * + * Returns 0 on success, or -1 on failure. + */ +int change_uid (const struct passwd *info) +{ + /* + * Set the real UID to the UID value in the password file. + */ + if (setuid (info->pw_uid) != 0) { + int err = errno; + perror ("setuid"); + SYSLOG ((LOG_ERR, "bad user ID `%d' for user `%s': %s\n", + (int) info->pw_uid, info->pw_name, strerror (err))); + closelog (); + return -1; + } + return 0; +} + +/* + * setup_uid_gid() performs the following steps - + * + * set the group ID to the value from the password file entry + * set the supplementary group IDs + * optionally call specified function which may add more groups + * set the user ID to the value from the password file entry + * + * Returns 0 on success, or -1 on failure. + */ + +#if defined (HAVE_INITGROUPS) && ! (defined USE_PAM) +int setup_uid_gid (const struct passwd *info, bool is_console) +#else +int setup_uid_gid (const struct passwd *info) +#endif +{ + if (setup_groups (info) < 0) { + return -1; + } + +#if defined (HAVE_INITGROUPS) && ! defined (USE_PAM) + if (is_console) { + const char *cp = getdef_str ("CONSOLE_GROUPS"); + + if ((NULL != cp) && (add_groups (cp) != 0)) { + perror ("Warning: add_groups"); + } + } +#endif /* HAVE_INITGROUPS && !USE_PAM*/ + + if (change_uid (info) < 0) { + return -1; + } + + return 0; +} + diff --git a/libmisc/setupenv.c b/libmisc/setupenv.c new file mode 100644 index 00000000..666b1c7b --- /dev/null +++ b/libmisc/setupenv.c @@ -0,0 +1,310 @@ +/* + * Copyright (c) 1989 - 1994, Julianne Frances Haugh + * Copyright (c) 1996 - 2000, Marek Michałkiewicz + * Copyright (c) 2001 - 2006, Tomasz Kłoczko + * Copyright (c) 2007 - 2010, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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. + */ + +/* + * Separated from setup.c. --marekm + */ + +#include <config.h> + +#ident "$Id: setupenv.c 3232 2010-08-22 19:13:53Z nekral-guest $" + +#include <assert.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <ctype.h> +#include "prototypes.h" +#include "defines.h" +#include <pwd.h> +#include "getdef.h" + +#ifndef USE_PAM +static void +addenv_path (const char *varname, const char *dirname, const char *filename) +{ + char *buf; + size_t len = strlen (dirname) + strlen (filename) + 2; + int wlen; + + buf = xmalloc (len); + wlen = snprintf (buf, len, "%s/%s", dirname, filename); + assert (wlen == (int) len - 1); + + addenv (varname, buf); + free (buf); +} + +static void read_env_file (const char *filename) +{ + FILE *fp; + char buf[1024]; + char *cp, *name, *val; + + fp = fopen (filename, "r"); + if (NULL == fp) { + return; + } + while (fgets (buf, (int)(sizeof buf), fp) == buf) { + cp = strrchr (buf, '\n'); + if (NULL == cp) { + break; + } + *cp = '\0'; + + cp = buf; + /* ignore whitespace and comments */ + while (('\0' != *cp) && isspace (*cp)) { + cp++; + } + if (('\0' == *cp) || ('#' == *cp)) { + continue; + } + /* + * ignore lines which don't follow the name=value format + * (for example, the "export NAME" shell commands) + */ + name = cp; + while (('\0' != *cp) && !isspace (*cp) && ('=' != *cp)) { + cp++; + } + if ('=' != *cp) { + continue; + } + /* NUL-terminate the name */ + *cp = '\0'; + cp++; + val = cp; +#if 0 /* XXX untested, and needs rewrite with fewer goto's :-) */ +/* + (state, char_type) -> (state, action) + + state: unquoted, single_quoted, double_quoted, escaped, double_quoted_escaped + char_type: normal, white, backslash, single, double + action: remove_curr, remove_curr_skip_next, remove_prev, finish XXX +*/ + no_quote: + if (*cp == '\\') { + /* remove the backslash */ + remove_char (cp); + /* skip over the next character */ + if (*cp) + cp++; + goto no_quote; + } else if (*cp == '\'') { + /* remove the quote */ + remove_char (cp); + /* now within single quotes */ + goto s_quote; + } else if (*cp == '"') { + /* remove the quote */ + remove_char (cp); + /* now within double quotes */ + goto d_quote; + } else if (*cp == '\0') { + /* end of string */ + goto finished; + } else if (isspace (*cp)) { + /* unescaped whitespace - end of string */ + *cp = '\0'; + goto finished; + } else { + cp++; + goto no_quote; + } + s_quote: + if (*cp == '\'') { + /* remove the quote */ + remove_char (cp); + /* unquoted again */ + goto no_quote; + } else if (*cp == '\0') { + /* end of string */ + goto finished; + } else { + /* preserve everything within single quotes */ + cp++; + goto s_quote; + } + d_quote: + if (*cp == '\"') { + /* remove the quote */ + remove_char (cp); + /* unquoted again */ + goto no_quote; + } else if (*cp == '\\') { + cp++; + /* if backslash followed by double quote, remove backslash + else skip over the backslash and following char */ + if (*cp == '"') + remove_char (cp - 1); + else if (*cp) + cp++; + goto d_quote; + } + eise if (*cp == '\0') { + /* end of string */ + goto finished; + } else { + /* preserve everything within double quotes */ + goto d_quote; + } + finished: +#endif /* 0 */ + /* + * XXX - should handle quotes, backslash escapes, etc. + * like the shell does. + */ + addenv (name, val); + } + (void) fclose (fp); +} +#endif /* USE_PAM */ + + +/* + * change to the user's home directory + * set the HOME, SHELL, MAIL, PATH, and LOGNAME or USER environmental + * variables. + */ + +void setup_env (struct passwd *info) +{ +#ifndef USE_PAM + const char *envf; +#endif + const char *cp; + + /* + * Change the current working directory to be the home directory + * of the user. It is a fatal error for this process to be unable + * to change to that directory. There is no "default" home + * directory. + * + * We no longer do it as root - should work better on NFS-mounted + * home directories. Some systems default to HOME=/, so we make + * this a configurable option. --marekm + */ + + if (chdir (info->pw_dir) == -1) { + static char temp_pw_dir[] = "/"; + + if (!getdef_bool ("DEFAULT_HOME") || chdir ("/") == -1) { + fprintf (stderr, _("Unable to cd to '%s'\n"), + info->pw_dir); + SYSLOG ((LOG_WARN, + "unable to cd to `%s' for user `%s'\n", + info->pw_dir, info->pw_name)); + closelog (); + exit (EXIT_FAILURE); + } + (void) puts (_("No directory, logging in with HOME=/")); + info->pw_dir = temp_pw_dir; + } + + /* + * Create the HOME environmental variable and export it. + */ + + addenv ("HOME", info->pw_dir); + + /* + * Create the SHELL environmental variable and export it. + */ + + if ((NULL == info->pw_shell) || ('\0' == *info->pw_shell)) { + static char temp_pw_shell[] = SHELL; + + info->pw_shell = temp_pw_shell; + } + + addenv ("SHELL", info->pw_shell); + + /* + * Export the user name. For BSD derived systems, it's "USER", for + * all others it's "LOGNAME". We set both of them. + */ + + addenv ("USER", info->pw_name); + addenv ("LOGNAME", info->pw_name); + + /* + * Create the PATH environmental variable and export it. + */ + + cp = getdef_str ((info->pw_uid == 0) ? "ENV_SUPATH" : "ENV_PATH"); + + if (NULL == cp) { + /* not specified, use a minimal default */ + addenv ((info->pw_uid == 0) ? "PATH=/sbin:/bin:/usr/sbin:/usr/bin" : "PATH=/bin:/usr/bin", NULL); + } else if (strchr (cp, '=')) { + /* specified as name=value (PATH=...) */ + addenv (cp, NULL); + } else { + /* only value specified without "PATH=" */ + addenv ("PATH", cp); + } + +#ifndef USE_PAM + /* + * Create the MAIL environmental variable and export it. login.defs + * knows the prefix. + */ + + if (getdef_bool ("MAIL_CHECK_ENAB")) { + cp = getdef_str ("MAIL_DIR"); + if (NULL != cp) { + addenv_path ("MAIL", cp, info->pw_name); + } else { + cp = getdef_str ("MAIL_FILE"); + if (NULL != cp) { + addenv_path ("MAIL", info->pw_dir, cp); + } else { +#if defined(MAIL_SPOOL_FILE) + addenv_path ("MAIL", info->pw_dir, MAIL_SPOOL_FILE); +#elif defined(MAIL_SPOOL_DIR) + addenv_path ("MAIL", MAIL_SPOOL_DIR, info->pw_name); +#endif + } + } + } + + /* + * Read environment from optional config file. --marekm + */ + envf = getdef_str ("ENVIRON_FILE"); + if (NULL != envf) { + read_env_file (envf); + } +#endif /* !USE_PAM */ +} + diff --git a/libmisc/shell.c b/libmisc/shell.c new file mode 100644 index 00000000..d815f2d4 --- /dev/null +++ b/libmisc/shell.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 1989 - 1991, Julianne Frances Haugh + * Copyright (c) 1996 - 1998, Marek Michałkiewicz + * Copyright (c) 2003 - 2006, Tomasz Kłoczko + * Copyright (c) 2009 , Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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> + +#ident "$Id: shell.c 3232 2010-08-22 19:13:53Z nekral-guest $" + +#include <stdio.h> +#include <errno.h> +#include "prototypes.h" +#include "defines.h" +extern char **newenvp; +extern size_t newenvc; + +/* + * shell - execute the named program + * + * shell begins by trying to figure out what argv[0] is going to + * be for the named process. The user may pass in that argument, + * or it will be the last pathname component of the file with a + * '-' prepended. + * Then, it executes the named file. + */ + +int shell (const char *file, /*@null@*/const char *arg, char *const envp[]) +{ + char arg0[1024]; + int err; + + if (file == (char *) 0) { + errno = EINVAL; + return errno; + } + + /* + * The argv[0]'th entry is usually the path name, but + * for various reasons the invoker may want to override + * that. So, we determine the 0'th entry only if they + * don't want to tell us what it is themselves. + */ + if (arg == (char *) 0) { + (void) snprintf (arg0, sizeof arg0, "-%s", Basename ((char *) file)); + arg0[sizeof arg0 - 1] = '\0'; + arg = arg0; + } + + /* + * First we try the direct approach. The system should be + * able to figure out what we are up to without too much + * grief. + */ + (void) execle (file, arg, (char *) 0, envp); + err = errno; + + if (access (file, R_OK|X_OK) == 0) { + /* + * Assume this is a shell script (with no shebang). + * Interpret it with /bin/sh + */ + (void) execle (SHELL, "sh", "-", file, (char *)0, envp); + err = errno; + } + + /* + * Obviously something is really wrong - I can't figure out + * how to execute this stupid shell, so I might as well give + * up in disgust ... + */ + (void) snprintf (arg0, sizeof arg0, _("Cannot execute %s"), file); + errno = err; + perror (arg0); + return err; +} + diff --git a/libmisc/strtoday.c b/libmisc/strtoday.c new file mode 100644 index 00000000..d706ef44 --- /dev/null +++ b/libmisc/strtoday.c @@ -0,0 +1,243 @@ +/* + * Copyright (c) 1991 - 1994, Julianne Frances Haugh + * Copyright (c) 1996 - 1999, Marek Michałkiewicz + * Copyright (c) 2003 - 2005, Tomasz Kłoczko + * Copyright (c) 2008 , Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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. + */ + +#if !defined(__GLIBC__) +#define _XOPEN_SOURCE 500 +#endif + +#include <config.h> + +#include <ctype.h> + +#ident "$Id: strtoday.c 3169 2010-03-20 10:19:50Z nekral-guest $" + +#include "defines.h" +#include "prototypes.h" + +#ifndef USE_GETDATE +#define USE_GETDATE 1 +#endif + +#if USE_GETDATE +#include "getdate.h" +/* + * strtoday() now uses get_date() (borrowed from GNU shellutils) + * which can handle many date formats, for example: + * 1970-09-17 # ISO 8601. + * 70-9-17 # This century assumed by default. + * 70-09-17 # Leading zeros are ignored. + * 9/17/72 # Common U.S. writing. + * 24 September 1972 + * 24 Sept 72 # September has a special abbreviation. + * 24 Sep 72 # Three-letter abbreviations always allowed. + * Sep 24, 1972 + * 24-sep-72 + * 24sep72 + */ +long strtoday (const char *str) +{ + time_t t; + bool isnum = true; + const char *s = str; + + /* + * get_date() interprets an empty string as the current date, + * which is not what we expect, unless you're a BOFH :-). + * (useradd sets sp_expire = current date for new lusers) + */ + if ((NULL == str) || ('\0' == *str)) { + return -1; + } + + /* If a numerical value is provided, this is already a number of + * days since EPOCH. + */ + if ('-' == *s) { + s++; + } + while (' ' == *s) { + s++; + } + while (isnum && ('\0' != *s)) { + if (!isdigit (*s)) { + isnum = false; + } + s++; + } + if (isnum) { + long retdate; + if (getlong (str, &retdate) == 0) { + return -2; + } + return retdate; + } + + t = get_date (str, NULL); + if ((time_t) - 1 == t) { + return -2; + } + /* convert seconds to days since 1970-01-01 */ + return (long) (t + DAY / 2) / DAY; +} + +#else /* !USE_GETDATE */ +/* + * Old code, just in case get_date() doesn't work as expected... + */ +#include <stdio.h> +#ifdef HAVE_STRPTIME +/* + * for now we allow just one format, but we can define more later + * (we try them all until one succeeds). --marekm + */ +static char *date_formats[] = { + "%Y-%m-%d", + (char *) 0 +}; +#else +/* + * days and juldays are used to compute the number of days in the + * current month, and the cummulative number of days in the preceding + * months. they are declared so that january is 1, not 0. + */ +static short days[13] = { 0, + 31, 28, 31, 30, 31, 30, /* JAN - JUN */ + 31, 31, 30, 31, 30, 31 +}; /* JUL - DEC */ + +static short juldays[13] = { 0, + 0, 31, 59, 90, 120, 151, /* JAN - JUN */ + 181, 212, 243, 273, 304, 334 +}; /* JUL - DEC */ +#endif + +/* + * strtoday - compute the number of days since 1970. + * + * the total number of days prior to the current date is + * computed. january 1, 1970 is used as the origin with + * it having a day number of 0. + */ + +long strtoday (const char *str) +{ +#ifdef HAVE_STRPTIME + struct tm tp; + char *const *fmt; + char *cp; + time_t result; + + memzero (&tp, sizeof tp); + for (fmt = date_formats; *fmt; fmt++) { + cp = strptime ((char *) str, *fmt, &tp); + if ((NULL == cp) || ('\0' != *cp)) { + continue; + } + + result = mktime (&tp); + if ((time_t) - 1 == result) { + continue; + } + + return (long) (result / DAY); /* success */ + } + return -1; +#else + char slop[2]; + int month; + int day; + int year; + long total; + + /* + * start by separating the month, day and year. the order + * is compiled in ... + */ + + if (sscanf (str, "%d/%d/%d%c", &year, &month, &day, slop) != 3) { + return -1; + } + + /* + * the month, day of the month, and year are checked for + * correctness and the year adjusted so it falls between + * 1970 and 2069. + */ + + if ((month < 1) || (month > 12)) { + return -1; + } + + if (day < 1) { + return -1; + } + + if ( ((2 != month) || ((year % 4) != 0)) + && (day > days[month])) { + return -1; + } else if ((month == 2) && ((year % 4) == 0) && (day > 29)) { + return -1; + } + + if (year < 0) { + return -1; + } else if (year <= 69) { + year += 2000; + } else if (year <= 99) { + year += 1900; + } + + /* + * On systems with 32-bit signed time_t, time wraps around in 2038 + * - for now we just limit the year to 2037 (instead of 2069). + * This limit can be removed once no one is using 32-bit systems + * anymore :-). --marekm + */ + if ((year < 1970) || (year > 2037)) { + return -1; + } + + /* + * the total number of days is the total number of days in all + * the whole years, plus the number of leap days, plus the + * number of days in the whole months preceding, plus the number + * of days so far in the month. + */ + + total = (long) ((year - 1970) * 365L) + (((year + 1) - 1970) / 4); + total += (long) juldays[month] + (month > 2 && (year % 4) == 0 ? 1 : 0); + total += (long) day - 1; + + return total; +#endif /* HAVE_STRPTIME */ +} +#endif /* !USE_GETDATE */ diff --git a/libmisc/sub.c b/libmisc/sub.c new file mode 100644 index 00000000..4a40e58a --- /dev/null +++ b/libmisc/sub.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 1989 - 1991, Julianne Frances Haugh + * Copyright (c) 1996 - 1999, Marek Michałkiewicz + * Copyright (c) 2003 - 2006, Tomasz Kłoczko + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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> + +#ident "$Id: sub.c 3232 2010-08-22 19:13:53Z nekral-guest $" + +#include <pwd.h> +#include <stdio.h> +#include <sys/types.h> +#include "prototypes.h" +#include "defines.h" +#define BAD_SUBROOT2 "invalid root `%s' for user `%s'\n" +#define NO_SUBROOT2 "no subsystem root `%s' for user `%s'\n" +/* + * subsystem - change to subsystem root + * + * A subsystem login is indicated by the presense of a "*" as + * the first character of the login shell. The given home + * directory will be used as the root of a new filesystem which + * the user is actually logged into. + */ +void subsystem (const struct passwd *pw) +{ + /* + * The new root directory must begin with a "/" character. + */ + + if (pw->pw_dir[0] != '/') { + printf (_("Invalid root directory '%s'\n"), pw->pw_dir); + SYSLOG ((LOG_WARN, BAD_SUBROOT2, pw->pw_dir, pw->pw_name)); + closelog (); + exit (EXIT_FAILURE); + } + + /* + * The directory must be accessible and the current process + * must be able to change into it. + */ + + if ( (chdir (pw->pw_dir) != 0) + || (chroot (pw->pw_dir) != 0)) { + (void) printf (_("Can't change root directory to '%s'\n"), + pw->pw_dir); + SYSLOG ((LOG_WARN, NO_SUBROOT2, pw->pw_dir, pw->pw_name)); + closelog (); + exit (EXIT_FAILURE); + } +} + diff --git a/libmisc/sulog.c b/libmisc/sulog.c new file mode 100644 index 00000000..5d366175 --- /dev/null +++ b/libmisc/sulog.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 1989 - 1992, Julianne Frances Haugh + * Copyright (c) 1996 - 2000, Marek Michałkiewicz + * Copyright (c) 2001 - 2005, Tomasz Kłoczko + * Copyright (c) 2008 - 2010, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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> + +#ident "$Id: sulog.c 3231 2010-08-22 13:04:54Z nekral-guest $" + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <time.h> +#include "prototypes.h" +#include "defines.h" +#include "getdef.h" + +/* + * sulog - log a SU command execution result + */ +void sulog (const char *tty, bool success, const char *oldname, const char *name) +{ + const char *sulog_file; + time_t now; + struct tm *tm; + FILE *fp; + mode_t oldmask; + gid_t oldgid = 0; + + if (success) { + SYSLOG ((LOG_INFO, + "Successful su for %s by %s",name,oldname)); + } else { + SYSLOG ((LOG_NOTICE, + "FAILED su for %s by %s",name,oldname)); + } + + sulog_file = getdef_str ("SULOG_FILE"); + if (NULL == sulog_file) { + return; + } + + oldgid = getgid (); + oldmask = umask (077); + /* Switch to group root to avoid creating the sulog file with + * the wrong group ownership. */ + if ((oldgid != 0) && (setgid (0) != 0)) { + SYSLOG ((LOG_INFO, + "su session not logged to %s", sulog_file)); + /* Continue, but do not switch back to oldgid later */ + oldgid = 0; + } + fp = fopen (sulog_file, "a+"); + (void) umask (oldmask); + if ((oldgid != 0) && (setgid (oldgid) != 0)) { + perror ("setgid"); + SYSLOG ((LOG_ERR, + "can't switch back to group `%d' in sulog", + oldgid)); + /* Do not return if the group permission were raised. */ + exit (EXIT_FAILURE); + } + if (fp == (FILE *) 0) { + return; /* can't open or create logfile */ + } + + (void) time (&now); + tm = localtime (&now); + + fprintf (fp, "SU %.02d/%.02d %.02d:%.02d %c %s %s-%s\n", + tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, + success ? '+' : '-', tty, oldname, name); + + (void) fflush (fp); + fsync (fileno (fp)); + fclose (fp); + /* TODO: log if failure */ +} + diff --git a/libmisc/ttytype.c b/libmisc/ttytype.c new file mode 100644 index 00000000..d505c122 --- /dev/null +++ b/libmisc/ttytype.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 1989 - 1994, Julianne Frances Haugh + * Copyright (c) 1996 - 1997, Marek Michałkiewicz + * Copyright (c) 2003 - 2005, Tomasz Kłoczko + * Copyright (c) 2008 - 2010, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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> + +#ident "$Id: ttytype.c 3232 2010-08-22 19:13:53Z nekral-guest $" + +#include <stdio.h> +#include "prototypes.h" +#include "defines.h" +#include "getdef.h" +/* + * ttytype - set ttytype from port to terminal type mapping database + */ +void ttytype (const char *line) +{ + FILE *fp; + char buf[BUFSIZ]; + const char *typefile; + char *cp; + char type[1024] = ""; + char port[1024]; + + if (getenv ("TERM") != NULL) { + return; + } + typefile = getdef_str ("TTYTYPE_FILE"); + if (NULL == typefile) { + return; + } + if (access (typefile, F_OK) != 0) { + return; + } + + fp = fopen (typefile, "r"); + if (NULL == fp) { + perror (typefile); + return; + } + while (fgets (buf, (int) sizeof buf, fp) == buf) { + if (buf[0] == '#') { + continue; + } + + cp = strchr (buf, '\n'); + if (NULL != cp) { + *cp = '\0'; + } + + if ( (sscanf (buf, "%1023s %1023s", type, port) == 2) + && (strcmp (line, port) == 0)) { + break; + } + } + if ((feof (fp) == 0) && (ferror (fp) == 0) && (type[0] != '\0')) { + addenv ("TERM", type); + } + + (void) fclose (fp); +} + diff --git a/libmisc/tz.c b/libmisc/tz.c new file mode 100644 index 00000000..3453f577 --- /dev/null +++ b/libmisc/tz.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) 1991 - 1994, Julianne Frances Haugh + * Copyright (c) 1991 - 1994, Chip Rosenthal + * Copyright (c) 1996 - 1998, Marek Michałkiewicz + * Copyright (c) 2003 - 2005, Tomasz Kłoczko + * Copyright (c) 2007 - 2010, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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 USE_PAM + +#ident "$Id: tz.c 3231 2010-08-22 13:04:54Z nekral-guest $" + +#include <stdio.h> +#include <string.h> +#include "defines.h" +#include "prototypes.h" +#include "getdef.h" + +/* + * tz - return local timezone name + * + * tz() determines the name of the local timezone by reading the + * contents of the file named by ``fname''. + */ +/*@observer@*/const char *tz (const char *fname) +{ + FILE *fp = NULL; + static char tzbuf[BUFSIZ]; + const char *def_tz = "TZ=CST6CDT"; + + fp = fopen (fname, "r"); + if ( (NULL == fp) + || (fgets (tzbuf, (int) sizeof (tzbuf), fp) == NULL)) { + def_tz = getdef_str ("ENV_TZ"); + if ((NULL == def_tz) || ('/' == def_tz[0])) { + def_tz = "TZ=CST6CDT"; + } + + strcpy (tzbuf, def_tz); + } else { + tzbuf[strlen (tzbuf) - 1] = '\0'; + } + + if (NULL != fp) { + (void) fclose (fp); + } + + return tzbuf; +} +#else /* !USE_PAM */ +extern int errno; /* warning: ANSI C forbids an empty source file */ +#endif /* !USE_PAM */ + diff --git a/libmisc/ulimit.c b/libmisc/ulimit.c new file mode 100644 index 00000000..d05b8400 --- /dev/null +++ b/libmisc/ulimit.c @@ -0,0 +1,70 @@ +/* + * Copyright (c) 1990 - 1994, Julianne Frances Haugh + * Copyright (c) 1996 - 1997, Marek Michałkiewicz + * Copyright (c) 2003 - 2005, Tomasz Kłoczko + * Copyright (c) 2008 , Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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> + +#ident "$Id: ulimit.c 2188 2008-06-15 21:59:41Z nekral-guest $" + +#if HAVE_ULIMIT_H +#include <ulimit.h> +#ifndef UL_SETFSIZE +#ifdef UL_SFILLIM +#define UL_SETFSIZE UL_SFILLIM +#else +#define UL_SETFSIZE 2 +#endif +#endif +#elif HAVE_SYS_RESOURCE_H +#include <sys/time.h> /* for struct timeval on sunos4 */ +/* XXX - is the above ok or should it be <time.h> on ultrix? */ +#include <sys/resource.h> +#endif +#include "prototypes.h" + +int set_filesize_limit (int blocks) +{ + int ret = -1; +#if HAVE_ULIMIT_H + if (ulimit (UL_SETFSIZE, blocks) != -1) { + ret = 0; + } +#elif defined(RLIMIT_FSIZE) + struct rlimit rlimit_fsize; + + rlimit_fsize.rlim_cur = 512L * blocks; + rlimit_fsize.rlim_max = rlimit_fsize.rlim_cur; + ret = setrlimit (RLIMIT_FSIZE, &rlimit_fsize); +#endif + + return ret; +} + diff --git a/libmisc/user_busy.c b/libmisc/user_busy.c new file mode 100644 index 00000000..168f9d55 --- /dev/null +++ b/libmisc/user_busy.c @@ -0,0 +1,237 @@ +/* + * Copyright (c) 1991 - 1994, Julianne Frances Haugh + * Copyright (c) 1996 - 2000, Marek Michałkiewicz + * Copyright (c) 2000 - 2006, Tomasz Kłoczko + * Copyright (c) 2007 - 2009, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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> + +#ident "$Id: $" + +#include <assert.h> +#include <stdio.h> +#include <sys/types.h> +#include <dirent.h> +#include "defines.h" +#include "prototypes.h" + +#ifdef __linux__ +static int check_status (const char *sname, uid_t uid); +static int user_busy_processes (const char *name, uid_t uid); +#else /* !__linux__ */ +static int user_busy_utmp (const char *name); +#endif /* !__linux__ */ + +/* + * user_busy - check if an user if currently running processes + */ +int user_busy (const char *name, uid_t uid) +{ + /* There are no standard ways to get the list of processes. + * An option could be to run an external tool (ps). + */ +#ifdef __linux__ + /* On Linux, directly parse /proc */ + return user_busy_processes (name, uid); +#else /* !__linux__ */ + /* If we cannot rely on /proc, check is there is a record in utmp + * indicating that the user is still logged in */ + return user_busy_utmp (name); +#endif /* !__linux__ */ +} + +#ifndef __linux__ +static int user_busy_utmp (const char *name) +{ +#ifdef USE_UTMPX + struct utmpx *utent; + + setutxent (); + while ((utent = getutxent ()) != NULL) +#else /* !USE_UTMPX */ + struct utmp *utent; + + setutent (); + while ((utent = getutent ()) != NULL) +#endif /* !USE_UTMPX */ + { + if (utent->ut_type != USER_PROCESS) { + continue; + } + if (strncmp (utent->ut_user, name, sizeof utent->ut_user) != 0) { + continue; + } + if (kill (utent->ut_pid, 0) != 0) { + continue; + } + + fprintf (stderr, + _("%s: user %s is currently logged in\n"), + Prog, name); + return 1; + } + + return 0; +} +#endif /* !__linux__ */ + +#ifdef __linux__ +static int check_status (const char *sname, uid_t uid) +{ + /* 40: /proc/xxxxxxxxxx/task/xxxxxxxxxx/status + \0 */ + char status[40]; + char line[1024]; + FILE *sfile; + + snprintf (status, 40, "/proc/%s/status", sname); + status[39] = '\0'; + + sfile = fopen (status, "r"); + if (NULL == sfile) { + return 0; + } + while (fgets (line, sizeof (line), sfile) == line) { + if (strncmp (line, "Uid:\t", 5) == 0) { + unsigned long ruid, euid, suid; + assert (uid == (unsigned long) uid); + if (sscanf (line, + "Uid:\t%lu\t%lu\t%lu\n", + &ruid, &euid, &suid) == 3) { + if ( (ruid == (unsigned long) uid) + || (euid == (unsigned long) uid) + || (suid == (unsigned long) uid)) { + (void) fclose (sfile); + return 1; + } + } else { + /* Ignore errors. This is just a best effort. */ + } + (void) fclose (sfile); + return 0; + } + } + (void) fclose (sfile); + return 0; +} + +static int user_busy_processes (const char *name, uid_t uid) +{ + DIR *proc; + struct dirent *ent; + char *tmp_d_name; + pid_t pid; + DIR *task_dir; + /* 22: /proc/xxxxxxxxxx/task + \0 */ + char task_path[22]; + char root_path[22]; + struct stat sbroot; + struct stat sbroot_process; + + proc = opendir ("/proc"); + if (proc == NULL) { + perror ("opendir /proc"); + return 0; + } + if (stat ("/", &sbroot) != 0) { + perror ("stat (\"/\")"); + (void) closedir (proc); + return 0; + } + + while ((ent = readdir (proc)) != NULL) { + tmp_d_name = ent->d_name; + /* + * Ingo Molnar's patch introducing NPTL for 2.4 hides + * threads in the /proc directory by prepending a period. + * This patch is applied by default in some RedHat + * kernels. + */ + if ( (strcmp (tmp_d_name, ".") == 0) + || (strcmp (tmp_d_name, "..") == 0)) { + continue; + } + if (*tmp_d_name == '.') { + tmp_d_name++; + } + + /* Check if this is a valid PID */ + if (get_pid (tmp_d_name, &pid) == 0) { + continue; + } + + /* Check if the process is in our chroot */ + snprintf (root_path, 22, "/proc/%lu/root", (unsigned long) pid); + root_path[21] = '\0'; + if (stat (root_path, &sbroot_process) != 0) { + continue; + } + if ( (sbroot.st_dev != sbroot_process.st_dev) + || (sbroot.st_ino != sbroot_process.st_ino)) { + continue; + } + + if (check_status (tmp_d_name, uid) != 0) { + (void) closedir (proc); + fprintf (stderr, + _("%s: user %s is currently used by process %d\n"), + Prog, name, pid); + return 1; + } + + snprintf (task_path, 22, "/proc/%lu/task", (unsigned long) pid); + task_path[21] = '\0'; + task_dir = opendir (task_path); + if (task_dir != NULL) { + while ((ent = readdir (task_dir)) != NULL) { + pid_t tid; + if (get_pid (ent->d_name, &tid) == 0) { + continue; + } + if (tid == pid) { + continue; + } + if (check_status (task_path+6, uid) != 0) { + (void) closedir (proc); + fprintf (stderr, + _("%s: user %s is currently used by process %d\n"), + Prog, name, pid); + return 1; + } + } + (void) closedir (task_dir); + } else { + /* Ignore errors. This is just a best effort */ + } + } + + (void) closedir (proc); + return 0; +} +#endif /* __linux__ */ + diff --git a/libmisc/utmp.c b/libmisc/utmp.c new file mode 100644 index 00000000..43bac476 --- /dev/null +++ b/libmisc/utmp.c @@ -0,0 +1,462 @@ +/* + * Copyright (c) 1989 - 1994, Julianne Frances Haugh + * Copyright (c) 1996 - 1999, Marek Michałkiewicz + * Copyright (c) 2001 - 2005, Tomasz Kłoczko + * Copyright (c) 2008 - 2009, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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 "defines.h" +#include "prototypes.h" + +#include <utmp.h> + +#ifdef USE_UTMPX +#include <utmpx.h> +#endif + +#include <assert.h> +#include <netdb.h> +#include <stdio.h> + +#ident "$Id: utmp.c 3720 2012-05-18 17:57:52Z nekral-guest $" + + +/* + * is_my_tty -- determine if "tty" is the same TTY stdin is using + */ +static bool is_my_tty (const char *tty) +{ + /* full_tty shall be at least sizeof utmp.ut_line + 5 */ + char full_tty[200]; + /* tmptty shall be bigger than full_tty */ + static char tmptty[sizeof (full_tty)+1]; + + if ('/' != *tty) { + (void) snprintf (full_tty, sizeof full_tty, "/dev/%s", tty); + tty = &full_tty[0]; + } + + if ('\0' == tmptty[0]) { + const char *tname = ttyname (STDIN_FILENO); + if (NULL != tname) { + (void) strncpy (tmptty, tname, sizeof tmptty); + tmptty[sizeof (tmptty) - 1] = '\0'; + } + } + + if ('\0' == tmptty[0]) { + (void) puts (_("Unable to determine your tty name.")); + exit (EXIT_FAILURE); + } else if (strncmp (tty, tmptty, sizeof (tmptty)) != 0) { + return false; + } else { + return true; + } +} + +/* + * get_current_utmp - return the most probable utmp entry for the current + * session + * + * The utmp file is scanned for an entry with the same process ID. + * The line enterred by the *getty / telnetd, etc. should also match + * the current terminal. + * + * When an entry is returned by get_current_utmp, and if the utmp + * structure has a ut_id field, this field should be used to update + * the entry information. + * + * Return NULL if no entries exist in utmp for the current process. + */ +/*@null@*/ /*@only@*/struct utmp *get_current_utmp (void) +{ + struct utmp *ut; + struct utmp *ret = NULL; + + setutent (); + + /* First, try to find a valid utmp entry for this process. */ + while ((ut = getutent ()) != NULL) { + if ( (ut->ut_pid == getpid ()) +#ifdef HAVE_STRUCT_UTMP_UT_ID + && ('\0' != ut->ut_id[0]) +#endif +#ifdef HAVE_STRUCT_UTMP_UT_TYPE + && ( (LOGIN_PROCESS == ut->ut_type) + || (USER_PROCESS == ut->ut_type)) +#endif + /* A process may have failed to close an entry + * Check if this entry refers to the current tty */ + && is_my_tty (ut->ut_line)) { + break; + } + } + + if (NULL != ut) { + ret = (struct utmp *) xmalloc (sizeof (*ret)); + memcpy (ret, ut, sizeof (*ret)); + } + + endutent (); + + return ret; +} + +#ifndef USE_PAM +/* + * Some systems already have updwtmp() and possibly updwtmpx(). Others + * don't, so we re-implement these functions if necessary. + */ +#ifndef HAVE_UPDWTMP +static void updwtmp (const char *filename, const struct utmp *ut) +{ + int fd; + + fd = open (filename, O_APPEND | O_WRONLY, 0); + if (fd >= 0) { + write (fd, (const char *) ut, sizeof (*ut)); + close (fd); + } +} +#endif /* ! HAVE_UPDWTMP */ + +#ifdef USE_UTMPX +#ifndef HAVE_UPDWTMPX +static void updwtmpx (const char *filename, const struct utmpx *utx) +{ + int fd; + + fd = open (filename, O_APPEND | O_WRONLY, 0); + if (fd >= 0) { + write (fd, (const char *) utx, sizeof (*utx)); + close (fd); + } +} +#endif /* ! HAVE_UPDWTMPX */ +#endif /* ! USE_UTMPX */ +#endif /* ! USE_PAM */ + + +/* + * prepare_utmp - prepare an utmp entry so that it can be logged in a + * utmp/wtmp file. + * + * It accepts an utmp entry in input (ut) to return an entry with + * the right ut_id. This is typically an entry returned by + * get_current_utmp + * If ut is NULL, ut_id will be forged based on the line argument. + * + * The ut_host field of the input structure may also be kept, and is + * used to define the ut_addr/ut_addr_v6 fields. (if these fields + * exist) + * + * Other fields are discarded and filed with new values (if they + * exist). + * + * The returned structure shall be freed by the caller. + */ +/*@only@*/struct utmp *prepare_utmp (const char *name, + const char *line, + const char *host, + /*@null@*/const struct utmp *ut) +{ + struct timeval tv; + char *hostname = NULL; + struct utmp *utent; + + assert (NULL != name); + assert (NULL != line); + + + + if ( (NULL != host) + && ('\0' != host[0])) { + hostname = (char *) xmalloc (strlen (host) + 1); + strcpy (hostname, host); +#ifdef HAVE_STRUCT_UTMP_UT_HOST + } else if ( (NULL != ut) + && ('\0' != ut->ut_host[0])) { + hostname = (char *) xmalloc (sizeof (ut->ut_host) + 1); + strncpy (hostname, ut->ut_host, sizeof (ut->ut_host)); + hostname[sizeof (ut->ut_host)] = '\0'; +#endif /* HAVE_STRUCT_UTMP_UT_HOST */ + } + + if (strncmp(line, "/dev/", 5) == 0) { + line += 5; + } + + + utent = (struct utmp *) xmalloc (sizeof (*utent)); + memzero (utent, sizeof (*utent)); + + + +#ifdef HAVE_STRUCT_UTMP_UT_TYPE + utent->ut_type = USER_PROCESS; +#endif /* HAVE_STRUCT_UTMP_UT_TYPE */ + utent->ut_pid = getpid (); + strncpy (utent->ut_line, line, sizeof (utent->ut_line)); +#ifdef HAVE_STRUCT_UTMP_UT_ID + if (NULL != ut) { + strncpy (utent->ut_id, ut->ut_id, sizeof (utent->ut_id)); + } else { + /* XXX - assumes /dev/tty?? */ + strncpy (utent->ut_id, line + 3, sizeof (utent->ut_id)); + } +#endif /* HAVE_STRUCT_UTMP_UT_ID */ +#ifdef HAVE_STRUCT_UTMP_UT_NAME + strncpy (utent->ut_name, name, sizeof (utent->ut_name)); +#endif /* HAVE_STRUCT_UTMP_UT_NAME */ +#ifdef HAVE_STRUCT_UTMP_UT_USER + strncpy (utent->ut_user, name, sizeof (utent->ut_user)); +#endif /* HAVE_STRUCT_UTMP_UT_USER */ + if (NULL != hostname) { + struct addrinfo *info = NULL; +#ifdef HAVE_STRUCT_UTMP_UT_HOST + strncpy (utent->ut_host, hostname, sizeof (utent->ut_host)); +#endif /* HAVE_STRUCT_UTMP_UT_HOST */ +#ifdef HAVE_STRUCT_UTMP_UT_SYSLEN + utent->ut_syslen = MIN (strlen (hostname), + sizeof (utent->ut_host)); +#endif /* HAVE_STRUCT_UTMP_UT_SYSLEN */ +#if defined(HAVE_STRUCT_UTMP_UT_ADDR) || defined(HAVE_STRUCT_UTMP_UT_ADDR_V6) + if (getaddrinfo (hostname, NULL, NULL, &info) == 0) { + /* getaddrinfo might not be reliable. + * Just try to log what may be useful. + */ + if (info->ai_family == AF_INET) { + struct sockaddr_in *sa = + (struct sockaddr_in *) info->ai_addr; +#ifdef HAVE_STRUCT_UTMP_UT_ADDR + memcpy (&(utent->ut_addr), + &(sa->sin_addr), + MIN (sizeof (utent->ut_addr), + sizeof (sa->sin_addr))); +#endif /* HAVE_STRUCT_UTMP_UT_ADDR */ +#ifdef HAVE_STRUCT_UTMP_UT_ADDR_V6 + memcpy (utent->ut_addr_v6, + &(sa->sin_addr), + MIN (sizeof (utent->ut_addr_v6), + sizeof (sa->sin_addr))); + } else if (info->ai_family == AF_INET6) { + struct sockaddr_in6 *sa = + (struct sockaddr_in6 *) info->ai_addr; + memcpy (utent->ut_addr_v6, + &(sa->sin6_addr), + MIN (sizeof (utent->ut_addr_v6), + sizeof (sa->sin6_addr))); +#endif /* HAVE_STRUCT_UTMP_UT_ADDR_V6 */ + } + freeaddrinfo (info); + } +#endif /* HAVE_STRUCT_UTMP_UT_ADDR || HAVE_STRUCT_UTMP_UT_ADDR_V6 */ + free (hostname); + } + /* ut_exit is only for DEAD_PROCESS */ + utent->ut_session = getsid (0); + if (gettimeofday (&tv, NULL) == 0) { +#ifdef HAVE_STRUCT_UTMP_UT_TIME + utent->ut_time = tv.tv_sec; +#endif /* HAVE_STRUCT_UTMP_UT_TIME */ +#ifdef HAVE_STRUCT_UTMP_UT_XTIME + utent->ut_xtime = tv.tv_usec; +#endif /* HAVE_STRUCT_UTMP_UT_XTIME */ +#ifdef HAVE_STRUCT_UTMP_UT_TV + utent->ut_tv.tv_sec = tv.tv_sec; + utent->ut_tv.tv_usec = tv.tv_usec; +#endif /* HAVE_STRUCT_UTMP_UT_TV */ + } + + return utent; +} + +/* + * setutmp - Update an entry in utmp and log an entry in wtmp + * + * Return 1 on failure and 0 on success. + */ +int setutmp (struct utmp *ut) +{ + int err = 0; + + assert (NULL != ut); + + setutent (); + if (pututline (ut) == NULL) { + err = 1; + } + endutent (); + +#ifndef USE_PAM + /* This is done by pam_lastlog */ + updwtmp (_WTMP_FILE, ut); +#endif /* ! USE_PAM */ + + return err; +} + +#ifdef USE_UTMPX +/* + * prepare_utmpx - the UTMPX version for prepare_utmp + */ +/*@only@*/struct utmpx *prepare_utmpx (const char *name, + const char *line, + const char *host, + /*@null@*/const struct utmp *ut) +{ + struct timeval tv; + char *hostname = NULL; + struct utmpx *utxent; + + assert (NULL != name); + assert (NULL != line); + + + + if ( (NULL != host) + && ('\0' != host[0])) { + hostname = (char *) xmalloc (strlen (host) + 1); + strcpy (hostname, host); +#ifdef HAVE_STRUCT_UTMP_UT_HOST + } else if ( (NULL != ut) + && (NULL != ut->ut_host) + && ('\0' != ut->ut_host[0])) { + hostname = (char *) xmalloc (sizeof (ut->ut_host) + 1); + strncpy (hostname, ut->ut_host, sizeof (ut->ut_host)); + hostname[sizeof (ut->ut_host)] = '\0'; +#endif /* HAVE_STRUCT_UTMP_UT_TYPE */ + } + + if (strncmp(line, "/dev/", 5) == 0) { + line += 5; + } + + utxent = (struct utmpx *) xmalloc (sizeof (*utxent)); + memzero (utxent, sizeof (*utxent)); + + + + utxent->ut_type = USER_PROCESS; + utxent->ut_pid = getpid (); + strncpy (utxent->ut_line, line, sizeof (utxent->ut_line)); + /* existence of ut->ut_id is enforced by configure */ + if (NULL != ut) { + strncpy (utxent->ut_id, ut->ut_id, sizeof (utxent->ut_id)); + } else { + /* XXX - assumes /dev/tty?? */ + strncpy (utxent->ut_id, line + 3, sizeof (utxent->ut_id)); + } +#ifdef HAVE_STRUCT_UTMPX_UT_NAME + strncpy (utxent->ut_name, name, sizeof (utxent->ut_name)); +#endif /* HAVE_STRUCT_UTMPX_UT_NAME */ + strncpy (utxent->ut_user, name, sizeof (utxent->ut_user)); + if (NULL != hostname) { + struct addrinfo *info = NULL; +#ifdef HAVE_STRUCT_UTMPX_UT_HOST + strncpy (utxent->ut_host, hostname, sizeof (utxent->ut_host)); +#endif /* HAVE_STRUCT_UTMPX_UT_HOST */ +#ifdef HAVE_STRUCT_UTMPX_UT_SYSLEN + utxent->ut_syslen = MIN (strlen (hostname), + sizeof (utxent->ut_host)); +#endif /* HAVE_STRUCT_UTMPX_UT_SYSLEN */ +#if defined(HAVE_STRUCT_UTMPX_UT_ADDR) || defined(HAVE_STRUCT_UTMPX_UT_ADDR_V6) + if (getaddrinfo (hostname, NULL, NULL, &info) == 0) { + /* getaddrinfo might not be reliable. + * Just try to log what may be useful. + */ + if (info->ai_family == AF_INET) { + struct sockaddr_in *sa = + (struct sockaddr_in *) info->ai_addr; +#ifdef HAVE_STRUCT_UTMPX_UT_ADDR + memcpy (utxent->ut_addr, + &(sa->sin_addr), + MIN (sizeof (utxent->ut_addr), + sizeof (sa->sin_addr))); +#endif /* HAVE_STRUCT_UTMPX_UT_ADDR */ +#ifdef HAVE_STRUCT_UTMPX_UT_ADDR_V6 + memcpy (utxent->ut_addr_v6, + &(sa->sin_addr), + MIN (sizeof (utxent->ut_addr_v6), + sizeof (sa->sin_addr))); + } else if (info->ai_family == AF_INET6) { + struct sockaddr_in6 *sa = + (struct sockaddr_in6 *) info->ai_addr; + memcpy (utxent->ut_addr_v6, + &(sa->sin6_addr), + MIN (sizeof (utxent->ut_addr_v6), + sizeof (sa->sin6_addr))); +#endif /* HAVE_STRUCT_UTMPX_UT_ADDR_V6 */ + } + freeaddrinfo (info); + } +#endif /* HAVE_STRUCT_UTMPX_UT_ADDR || HAVE_STRUCT_UTMPX_UT_ADDR_V6 */ + free (hostname); + } + /* ut_exit is only for DEAD_PROCESS */ + utxent->ut_session = getsid (0); + if (gettimeofday (&tv, NULL) == 0) { +#ifdef HAVE_STRUCT_UTMPX_UT_TIME + utxent->ut_time = tv.tv_sec; +#endif /* HAVE_STRUCT_UTMPX_UT_TIME */ +#ifdef HAVE_STRUCT_UTMPX_UT_XTIME + utxent->ut_xtime = tv.tv_usec; +#endif /* HAVE_STRUCT_UTMPX_UT_XTIME */ + utxent->ut_tv.tv_sec = tv.tv_sec; + utxent->ut_tv.tv_usec = tv.tv_usec; + } + + return utxent; +} + +/* + * setutmpx - the UTMPX version for setutmp + */ +int setutmpx (struct utmpx *utx) +{ + int err = 0; + + assert (NULL != utx); + + setutxent (); + if (pututxline (utx) == NULL) { + err = 1; + } + endutxent (); + +#ifndef USE_PAM + /* This is done by pam_lastlog */ + updwtmpx (_WTMP_FILE "x", utx); +#endif /* ! USE_PAM */ + + return err; +} +#endif /* USE_UTMPX */ + diff --git a/libmisc/valid.c b/libmisc/valid.c new file mode 100644 index 00000000..1cfe2392 --- /dev/null +++ b/libmisc/valid.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 1989 - 1993, Julianne Frances Haugh + * Copyright (c) 1996 - 1999, Marek Michałkiewicz + * Copyright (c) 2003 - 2005, Tomasz Kłoczko + * Copyright (c) 2007 - 2008, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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> + +#ident "$Id: valid.c 3232 2010-08-22 19:13:53Z nekral-guest $" + +#include <sys/types.h> +#include <stdio.h> +#include "prototypes.h" +#include "defines.h" +#include <pwd.h> +/* + * valid - compare encrypted passwords + * + * Valid() compares the DES encrypted password from the password file + * against the password which the user has entered after it has been + * encrypted using the same salt as the original. Entries which do + * not have a password file entry have a NULL pw_name field and this + * is used to indicate that a dummy salt must be used to encrypt the + * password anyway. + */ +bool valid (const char *password, const struct passwd *ent) +{ + const char *encrypted; + /*@observer@*/const char *salt; + + /* + * Start with blank or empty password entries. Always encrypt + * a password if no such user exists. Only if the ID exists and + * the password is really empty do you return quickly. This + * routine is meant to waste CPU time. + */ + + if ((NULL != ent->pw_name) && ('\0' == ent->pw_passwd[0])) { + if ('\0' == password[0]) { + return true; /* user entered nothing */ + } else { + return false; /* user entered something! */ + } + } + + /* + * If there is no entry then we need a salt to use. + */ + + if ((NULL == ent->pw_name) || ('\0' == ent->pw_passwd[0])) { + salt = "xx"; + } else { + salt = ent->pw_passwd; + } + + /* + * Now, perform the encryption using the salt from before on + * the users input. Since we always encrypt the string, it + * should be very difficult to determine if the user exists by + * looking at execution time. + */ + + encrypted = pw_encrypt (password, salt); + + /* + * One last time we must deal with there being no password file + * entry for the user. We use the pw_name == NULL idiom to + * cause non-existent users to not be validated. + */ + + if ( (NULL != ent->pw_name) + && (strcmp (encrypted, ent->pw_passwd) == 0)) { + return true; + } else { + return false; + } +} + diff --git a/libmisc/xgetXXbyYY.c b/libmisc/xgetXXbyYY.c new file mode 100644 index 00000000..1b0b0013 --- /dev/null +++ b/libmisc/xgetXXbyYY.c @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2007 - 2009, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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. + */ + +/* + * According to the Linux-PAM documentation: + * + * 4.1. Care about standard library calls + * + * In general, writers of authorization-granting applications should + * assume that each module is likely to call any or all 'libc' functions. + * For 'libc' functions that return pointers to static/dynamically + * allocated structures (ie. the library allocates the memory and the + * user is not expected to 'free()' it) any module call to this function + * is likely to corrupt a pointer previously obtained by the application. + * The application programmer should either re-call such a 'libc' + * function after a call to the Linux-PAM library, or copy the structure + * contents to some safe area of memory before passing control to the + * Linux-PAM library. + * + * Two important function classes that fall into this category are + * getpwnam(3) and syslog(3). + * + * This file provide wrapper to the getpwnam or getpwnam_r functions. + */ + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include "prototypes.h" + +#define XFUNCTION_NAME XPREFIX (FUNCTION_NAME) +#define XPREFIX(name) XPREFIX1 (name) +#define XPREFIX1(name) x##name +#define REENTRANT_NAME APPEND_R (FUNCTION_NAME) +#define APPEND_R(name) APPEND_R1 (name) +#define APPEND_R1(name) name##_r +#define STRINGIZE(name) STRINGIZE1 (name) +#define STRINGIZE1(name) #name + +/*@null@*/ /*@only@*/LOOKUP_TYPE *XFUNCTION_NAME (ARG_TYPE ARG_NAME) +{ +#if HAVE_FUNCTION_R + LOOKUP_TYPE *result=NULL; + char *buffer=NULL; + /* we have to start with something */ + size_t length = 0x100; + + result = malloc(sizeof(LOOKUP_TYPE)); + if (NULL == result) { + fprintf (stderr, _("%s: out of memory\n"), + "x" STRINGIZE(FUNCTION_NAME)); + exit (13); + } + + while (true) { + int status; + LOOKUP_TYPE *resbuf = NULL; + buffer = (char *)realloc (buffer, length); + if (NULL == buffer) { + fprintf (stderr, _("%s: out of memory\n"), + "x" STRINGIZE(FUNCTION_NAME)); + exit (13); + } + errno = 0; + status = REENTRANT_NAME(ARG_NAME, result, buffer, + length, &resbuf); + if ((0 == status) && (resbuf == result)) { + /* Build a result structure that can be freed by + * the shadow *_free functions. */ + LOOKUP_TYPE *ret_result = DUP_FUNCTION(result); + free(buffer); + free(result); + return ret_result; + } + + if (ERANGE != errno) { + free (buffer); + free (result); + return NULL; + } + + if (length <= ((size_t)-1 / 4)) { + length *= 4; + } else if (length == (size_t) -1) { + break; + } else { + length = (size_t) -1; + } + } + + free(buffer); + free(result); + return NULL; + +#else /* !HAVE_FUNCTION_R */ + + /* No reentrant function. + * Duplicate the structure to avoid other call to overwrite it. + * + * We should also restore the initial structure. But that would be + * overkill. + */ + LOOKUP_TYPE *result = FUNCTION_NAME(ARG_NAME); + + if (result) { + result = DUP_FUNCTION(result); + if (NULL == result) { + fprintf (stderr, _("%s: out of memory\n"), + "x" STRINGIZE(FUNCTION_NAME)); + exit (13); + } + } + + return result; +#endif +} + diff --git a/libmisc/xgetgrgid.c b/libmisc/xgetgrgid.c new file mode 100644 index 00000000..2ef171d1 --- /dev/null +++ b/libmisc/xgetgrgid.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2007 - 2009, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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. + */ + +/* + * According to the Linux-PAM documentation: + * + * 4.1. Care about standard library calls + * + * In general, writers of authorization-granting applications should + * assume that each module is likely to call any or all 'libc' functions. + * For 'libc' functions that return pointers to static/dynamically + * allocated structures (ie. the library allocates the memory and the + * user is not expected to 'free()' it) any module call to this function + * is likely to corrupt a pointer previously obtained by the application. + * The application programmer should either re-call such a 'libc' + * function after a call to the Linux-PAM library, or copy the structure + * contents to some safe area of memory before passing control to the + * Linux-PAM library. + * + * Two important function classes that fall into this category are + * getpwnam(3) and syslog(3). + * + * This file provide wrapper to the getpwnam or getpwnam_r functions. + */ + +#include <config.h> + +#include "groupio.h" + +#define LOOKUP_TYPE struct group +#define FUNCTION_NAME getgrgid +#define ARG_TYPE gid_t +#define ARG_NAME gid +#define DUP_FUNCTION __gr_dup +#define HAVE_FUNCTION_R (defined HAVE_GETGRGID_R) + +#include "xgetXXbyYY.c" + diff --git a/libmisc/xgetgrnam.c b/libmisc/xgetgrnam.c new file mode 100644 index 00000000..a07d0c33 --- /dev/null +++ b/libmisc/xgetgrnam.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2007 - 2009, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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. + */ + +/* + * According to the Linux-PAM documentation: + * + * 4.1. Care about standard library calls + * + * In general, writers of authorization-granting applications should + * assume that each module is likely to call any or all 'libc' functions. + * For 'libc' functions that return pointers to static/dynamically + * allocated structures (ie. the library allocates the memory and the + * user is not expected to 'free()' it) any module call to this function + * is likely to corrupt a pointer previously obtained by the application. + * The application programmer should either re-call such a 'libc' + * function after a call to the Linux-PAM library, or copy the structure + * contents to some safe area of memory before passing control to the + * Linux-PAM library. + * + * Two important function classes that fall into this category are + * getpwnam(3) and syslog(3). + * + * This file provide wrapper to the getpwnam or getpwnam_r functions. + */ + +#include <config.h> + +#include "groupio.h" + +#define LOOKUP_TYPE struct group +#define FUNCTION_NAME getgrnam +#define ARG_TYPE const char * +#define ARG_NAME name +#define DUP_FUNCTION __gr_dup +#define HAVE_FUNCTION_R (defined HAVE_GETGRNAM_R) + +#include "xgetXXbyYY.c" + diff --git a/libmisc/xgetpwnam.c b/libmisc/xgetpwnam.c new file mode 100644 index 00000000..db65abb7 --- /dev/null +++ b/libmisc/xgetpwnam.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2007 - 2009, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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. + */ + +/* + * According to the Linux-PAM documentation: + * + * 4.1. Care about standard library calls + * + * In general, writers of authorization-granting applications should + * assume that each module is likely to call any or all 'libc' functions. + * For 'libc' functions that return pointers to static/dynamically + * allocated structures (ie. the library allocates the memory and the + * user is not expected to 'free()' it) any module call to this function + * is likely to corrupt a pointer previously obtained by the application. + * The application programmer should either re-call such a 'libc' + * function after a call to the Linux-PAM library, or copy the structure + * contents to some safe area of memory before passing control to the + * Linux-PAM library. + * + * Two important function classes that fall into this category are + * getpwnam(3) and syslog(3). + * + * This file provide wrapper to the getpwnam or getpwnam_r functions. + */ + +#include <config.h> + +#include "pwio.h" + +#define LOOKUP_TYPE struct passwd +#define FUNCTION_NAME getpwnam +#define ARG_TYPE const char * +#define ARG_NAME name +#define DUP_FUNCTION __pw_dup +#define HAVE_FUNCTION_R (defined HAVE_GETPWNAM_R) + +#include "xgetXXbyYY.c" + diff --git a/libmisc/xgetpwuid.c b/libmisc/xgetpwuid.c new file mode 100644 index 00000000..89241344 --- /dev/null +++ b/libmisc/xgetpwuid.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2007 - 2009, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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. + */ + +/* + * According to the Linux-PAM documentation: + * + * 4.1. Care about standard library calls + * + * In general, writers of authorization-granting applications should + * assume that each module is likely to call any or all 'libc' functions. + * For 'libc' functions that return pointers to static/dynamically + * allocated structures (ie. the library allocates the memory and the + * user is not expected to 'free()' it) any module call to this function + * is likely to corrupt a pointer previously obtained by the application. + * The application programmer should either re-call such a 'libc' + * function after a call to the Linux-PAM library, or copy the structure + * contents to some safe area of memory before passing control to the + * Linux-PAM library. + * + * Two important function classes that fall into this category are + * getpwnam(3) and syslog(3). + * + * This file provide wrapper to the getpwnam or getpwnam_r functions. + */ + +#include <config.h> + +#include "pwio.h" + +#define LOOKUP_TYPE struct passwd +#define FUNCTION_NAME getpwuid +#define ARG_TYPE uid_t +#define ARG_NAME uid +#define DUP_FUNCTION __pw_dup +#define HAVE_FUNCTION_R (defined HAVE_GETPWUID_R) + +#include "xgetXXbyYY.c" + diff --git a/libmisc/xgetspnam.c b/libmisc/xgetspnam.c new file mode 100644 index 00000000..287e97f2 --- /dev/null +++ b/libmisc/xgetspnam.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2008 - 2009, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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. + */ + +/* + * According to the Linux-PAM documentation: + * + * 4.1. Care about standard library calls + * + * In general, writers of authorization-granting applications should + * assume that each module is likely to call any or all 'libc' functions. + * For 'libc' functions that return pointers to static/dynamically + * allocated structures (ie. the library allocates the memory and the + * user is not expected to 'free()' it) any module call to this function + * is likely to corrupt a pointer previously obtained by the application. + * The application programmer should either re-call such a 'libc' + * function after a call to the Linux-PAM library, or copy the structure + * contents to some safe area of memory before passing control to the + * Linux-PAM library. + * + * Two important function classes that fall into this category are + * getpwnam(3) and syslog(3). + * + * This file provide wrapper to the getpwnam or getpwnam_r functions. + */ + +#include <config.h> + +#include "shadowio.h" + +#define LOOKUP_TYPE struct spwd +#define FUNCTION_NAME getspnam +#define ARG_TYPE const char * +#define ARG_NAME name +#define DUP_FUNCTION __spw_dup +#define HAVE_FUNCTION_R (defined HAVE_GETSPNAM_R) + +#include "xgetXXbyYY.c" + diff --git a/libmisc/xmalloc.c b/libmisc/xmalloc.c new file mode 100644 index 00000000..f7183f69 --- /dev/null +++ b/libmisc/xmalloc.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 1990 - 1994, Julianne Frances Haugh + * Copyright (c) 1996 - 1998, Marek Michałkiewicz + * Copyright (c) 2003 - 2006, Tomasz Kłoczko + * Copyright (c) 2008 , Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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. + */ + +/* Replacements for malloc and strdup with error checking. Too trivial + to be worth copyrighting :-). I did that because a lot of code used + malloc and strdup without checking for NULL pointer, and I like some + message better than a core dump... --marekm + + Yeh, but. Remember that bailing out might leave the system in some + bizarre state. You really want to put in error checking, then add + some back-out failure recovery code. -- jfh */ + +#include <config.h> + +#ident "$Id: xmalloc.c 3320 2011-06-02 18:41:05Z nekral-guest $" + +#include <stdio.h> +#include <errno.h> +#include "defines.h" +#include "prototypes.h" + +/*@maynotreturn@*/ /*@only@*//*@out@*//*@notnull@*/char *xmalloc (size_t size) +{ + char *ptr; + + ptr = (char *) malloc (size); + if (NULL == ptr) { + (void) fprintf (stderr, + _("%s: failed to allocate memory: %s\n"), + Prog, strerror (errno)); + exit (13); + } + return ptr; +} + +/*@maynotreturn@*/ /*@only@*//*@notnull@*/char *xstrdup (const char *str) +{ + return strcpy (xmalloc (strlen (str) + 1), str); +} diff --git a/libmisc/yesno.c b/libmisc/yesno.c new file mode 100644 index 00000000..0605237c --- /dev/null +++ b/libmisc/yesno.c @@ -0,0 +1,79 @@ +/* + * Copyright (c) 1992 - 1994, Julianne Frances Haugh + * Copyright (c) 2007 - 2008, Nicolas François + * 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. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS 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. + */ + +/* + * Common code for yes/no prompting + * + * Used by pwck.c and grpck.c + */ + +#include <config.h> + +#ident "$Id: yesno.c 2765 2009-04-23 11:14:56Z nekral-guest $" + +#include <stdio.h> +#include "prototypes.h" + +/* + * yes_or_no - get answer to question from the user + * + * It returns false if no. + * + * If the read_only flag is set, it will print No, and will return + * false. + */ +bool yes_or_no (bool read_only) +{ + char buf[80]; + + /* + * In read-only mode all questions are answered "no". + */ + if (read_only) { + (void) puts (_("No")); + return false; + } + + /* + * Typically, there's a prompt on stdout, sometimes unflushed. + */ + (void) fflush (stdout); + + /* + * Get a line and see what the first character is. + */ + /* TODO: use gettext */ + if (fgets (buf, (int) sizeof buf, stdin) == buf) { + return buf[0] == 'y' || buf[0] == 'Y'; + } + + return false; +} + |