diff options
Diffstat (limited to '')
139 files changed, 27658 insertions, 0 deletions
diff --git a/lib/.indent.pro b/lib/.indent.pro new file mode 100644 index 0000000..fe572bb --- /dev/null +++ b/lib/.indent.pro @@ -0,0 +1,5 @@ +-kr +-i8 +-bad +-pcs +-l80 diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 0000000..3a50b46 --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,82 @@ + +AUTOMAKE_OPTIONS = 1.0 foreign + +DEFS = + +noinst_LTLIBRARIES = libshadow.la + +libshadow_la_CPPFLAGS = $(ECONF_CPPFLAGS) +if HAVE_VENDORDIR +libshadow_la_CPPFLAGS += -DVENDORDIR=\"$(VENDORDIR)\" +endif + +libshadow_la_CPPFLAGS += -I$(top_srcdir) + +libshadow_la_SOURCES = \ + commonio.c \ + commonio.h \ + defines.h \ + encrypt.c \ + exitcodes.h \ + faillog.h \ + fields.c \ + fputsx.c \ + getdef.c \ + getdef.h \ + get_gid.c \ + getlong.c \ + get_pid.c \ + get_uid.c \ + getulong.c \ + groupio.c \ + groupmem.c \ + groupio.h \ + gshadow.c \ + lockpw.c \ + nss.c \ + nscd.c \ + nscd.h \ + shadowlog.c \ + shadowlog.h \ + shadowlog_internal.h \ + sssd.c \ + sssd.h \ + pam_defs.h \ + port.c \ + port.h \ + prototypes.h \ + pwauth.c \ + pwauth.h \ + pwio.c \ + pwio.h \ + pwmem.c \ + run_part.h \ + run_part.c \ + subordinateio.h \ + subordinateio.c \ + selinux.c \ + semanage.c \ + sgetgrent.c \ + sgetpwent.c \ + sgetspent.c \ + sgroupio.c \ + sgroupio.h\ + shadow.c \ + shadowio.c \ + shadowio.h \ + shadowmem.c \ + spawn.c \ + utent.c + +if WITH_TCB +libshadow_la_SOURCES += tcbfuncs.c tcbfuncs.h +endif + +# These files are unneeded for some reason, listed in +# order of appearance: +# +# sources for dbm support (not yet used) + +EXTRA_DIST = \ + .indent.pro \ + gshadow_.h diff --git a/lib/Makefile.in b/lib/Makefile.in new file mode 100644 index 0000000..9eef01c --- /dev/null +++ b/lib/Makefile.in @@ -0,0 +1,1085 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 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__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +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@ +@HAVE_VENDORDIR_TRUE@am__append_1 = -DVENDORDIR=\"$(VENDORDIR)\" +@WITH_TCB_TRUE@am__append_2 = tcbfuncs.c tcbfuncs.h +subdir = lib +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/intlmacosx.m4 \ + $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \ + $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \ + $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +libshadow_la_LIBADD = +am__libshadow_la_SOURCES_DIST = commonio.c commonio.h defines.h \ + encrypt.c exitcodes.h faillog.h fields.c fputsx.c getdef.c \ + getdef.h get_gid.c getlong.c get_pid.c get_uid.c getulong.c \ + groupio.c groupmem.c groupio.h gshadow.c lockpw.c nss.c nscd.c \ + nscd.h shadowlog.c shadowlog.h shadowlog_internal.h sssd.c \ + sssd.h pam_defs.h port.c port.h prototypes.h pwauth.c pwauth.h \ + pwio.c pwio.h pwmem.c run_part.h run_part.c subordinateio.h \ + subordinateio.c selinux.c semanage.c sgetgrent.c sgetpwent.c \ + sgetspent.c sgroupio.c sgroupio.h shadow.c shadowio.c \ + shadowio.h shadowmem.c spawn.c utent.c tcbfuncs.c tcbfuncs.h +@WITH_TCB_TRUE@am__objects_1 = libshadow_la-tcbfuncs.lo +am_libshadow_la_OBJECTS = libshadow_la-commonio.lo \ + libshadow_la-encrypt.lo libshadow_la-fields.lo \ + libshadow_la-fputsx.lo libshadow_la-getdef.lo \ + libshadow_la-get_gid.lo libshadow_la-getlong.lo \ + libshadow_la-get_pid.lo libshadow_la-get_uid.lo \ + libshadow_la-getulong.lo libshadow_la-groupio.lo \ + libshadow_la-groupmem.lo libshadow_la-gshadow.lo \ + libshadow_la-lockpw.lo libshadow_la-nss.lo \ + libshadow_la-nscd.lo libshadow_la-shadowlog.lo \ + libshadow_la-sssd.lo libshadow_la-port.lo \ + libshadow_la-pwauth.lo libshadow_la-pwio.lo \ + libshadow_la-pwmem.lo libshadow_la-run_part.lo \ + libshadow_la-subordinateio.lo libshadow_la-selinux.lo \ + libshadow_la-semanage.lo libshadow_la-sgetgrent.lo \ + libshadow_la-sgetpwent.lo libshadow_la-sgetspent.lo \ + libshadow_la-sgroupio.lo libshadow_la-shadow.lo \ + libshadow_la-shadowio.lo libshadow_la-shadowmem.lo \ + libshadow_la-spawn.lo libshadow_la-utent.lo $(am__objects_1) +libshadow_la_OBJECTS = $(am_libshadow_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/libshadow_la-commonio.Plo \ + ./$(DEPDIR)/libshadow_la-encrypt.Plo \ + ./$(DEPDIR)/libshadow_la-fields.Plo \ + ./$(DEPDIR)/libshadow_la-fputsx.Plo \ + ./$(DEPDIR)/libshadow_la-get_gid.Plo \ + ./$(DEPDIR)/libshadow_la-get_pid.Plo \ + ./$(DEPDIR)/libshadow_la-get_uid.Plo \ + ./$(DEPDIR)/libshadow_la-getdef.Plo \ + ./$(DEPDIR)/libshadow_la-getlong.Plo \ + ./$(DEPDIR)/libshadow_la-getulong.Plo \ + ./$(DEPDIR)/libshadow_la-groupio.Plo \ + ./$(DEPDIR)/libshadow_la-groupmem.Plo \ + ./$(DEPDIR)/libshadow_la-gshadow.Plo \ + ./$(DEPDIR)/libshadow_la-lockpw.Plo \ + ./$(DEPDIR)/libshadow_la-nscd.Plo \ + ./$(DEPDIR)/libshadow_la-nss.Plo \ + ./$(DEPDIR)/libshadow_la-port.Plo \ + ./$(DEPDIR)/libshadow_la-pwauth.Plo \ + ./$(DEPDIR)/libshadow_la-pwio.Plo \ + ./$(DEPDIR)/libshadow_la-pwmem.Plo \ + ./$(DEPDIR)/libshadow_la-run_part.Plo \ + ./$(DEPDIR)/libshadow_la-selinux.Plo \ + ./$(DEPDIR)/libshadow_la-semanage.Plo \ + ./$(DEPDIR)/libshadow_la-sgetgrent.Plo \ + ./$(DEPDIR)/libshadow_la-sgetpwent.Plo \ + ./$(DEPDIR)/libshadow_la-sgetspent.Plo \ + ./$(DEPDIR)/libshadow_la-sgroupio.Plo \ + ./$(DEPDIR)/libshadow_la-shadow.Plo \ + ./$(DEPDIR)/libshadow_la-shadowio.Plo \ + ./$(DEPDIR)/libshadow_la-shadowlog.Plo \ + ./$(DEPDIR)/libshadow_la-shadowmem.Plo \ + ./$(DEPDIR)/libshadow_la-spawn.Plo \ + ./$(DEPDIR)/libshadow_la-sssd.Plo \ + ./$(DEPDIR)/libshadow_la-subordinateio.Plo \ + ./$(DEPDIR)/libshadow_la-tcbfuncs.Plo \ + ./$(DEPDIR)/libshadow_la-utent.Plo +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libshadow_la_SOURCES) +DIST_SOURCES = $(am__libshadow_la_SOURCES_DIST) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +ECONF_CPPFLAGS = @ECONF_CPPFLAGS@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +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@ +LIBECONF = @LIBECONF@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBMD = @LIBMD@ +LIBOBJS = @LIBOBJS@ +LIBPAM = @LIBPAM@ +LIBS = @LIBS@ +LIBSELINUX = @LIBSELINUX@ +LIBSEMANAGE = @LIBSEMANAGE@ +LIBSKEY = @LIBSKEY@ +LIBSUBID_ABI = @LIBSUBID_ABI@ +LIBSUBID_ABI_MAJOR = @LIBSUBID_ABI_MAJOR@ +LIBSUBID_ABI_MICRO = @LIBSUBID_ABI_MICRO@ +LIBSUBID_ABI_MINOR = @LIBSUBID_ABI_MINOR@ +LIBTCB = @LIBTCB@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LIYESCRYPT = @LIYESCRYPT@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +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@ +VENDORDIR = @VENDORDIR@ +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@ +capcmd = @capcmd@ +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@ +runstatedir = @runstatedir@ +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@ +AUTOMAKE_OPTIONS = 1.0 foreign +noinst_LTLIBRARIES = libshadow.la +libshadow_la_CPPFLAGS = $(ECONF_CPPFLAGS) $(am__append_1) \ + -I$(top_srcdir) +libshadow_la_SOURCES = commonio.c commonio.h defines.h encrypt.c \ + exitcodes.h faillog.h fields.c fputsx.c getdef.c getdef.h \ + get_gid.c getlong.c get_pid.c get_uid.c getulong.c groupio.c \ + groupmem.c groupio.h gshadow.c lockpw.c nss.c nscd.c nscd.h \ + shadowlog.c shadowlog.h shadowlog_internal.h sssd.c sssd.h \ + pam_defs.h port.c port.h prototypes.h pwauth.c pwauth.h pwio.c \ + pwio.h pwmem.c run_part.h run_part.c subordinateio.h \ + subordinateio.c selinux.c semanage.c sgetgrent.c sgetpwent.c \ + sgetspent.c sgroupio.c sgroupio.h shadow.c shadowio.c \ + shadowio.h shadowmem.c spawn.c utent.c $(am__append_2) + +# These files are unneeded for some reason, listed in +# order of appearance: +# +# sources for dbm support (not yet used) +EXTRA_DIST = \ + .indent.pro \ + gshadow_.h + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(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) --foreign lib/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign lib/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__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + 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-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libshadow.la: $(libshadow_la_OBJECTS) $(libshadow_la_DEPENDENCIES) $(EXTRA_libshadow_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libshadow_la_OBJECTS) $(libshadow_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-commonio.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-encrypt.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-fields.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-fputsx.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-get_gid.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-get_pid.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-get_uid.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-getdef.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-getlong.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-getulong.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-groupio.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-groupmem.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-gshadow.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-lockpw.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-nscd.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-nss.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-port.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-pwauth.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-pwio.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-pwmem.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-run_part.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-selinux.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-semanage.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-sgetgrent.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-sgetpwent.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-sgetspent.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-sgroupio.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-shadow.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-shadowio.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-shadowlog.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-shadowmem.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-spawn.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-sssd.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-subordinateio.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-tcbfuncs.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadow_la-utent.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +libshadow_la-commonio.lo: commonio.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-commonio.lo -MD -MP -MF $(DEPDIR)/libshadow_la-commonio.Tpo -c -o libshadow_la-commonio.lo `test -f 'commonio.c' || echo '$(srcdir)/'`commonio.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-commonio.Tpo $(DEPDIR)/libshadow_la-commonio.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='commonio.c' object='libshadow_la-commonio.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-commonio.lo `test -f 'commonio.c' || echo '$(srcdir)/'`commonio.c + +libshadow_la-encrypt.lo: encrypt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-encrypt.lo -MD -MP -MF $(DEPDIR)/libshadow_la-encrypt.Tpo -c -o libshadow_la-encrypt.lo `test -f 'encrypt.c' || echo '$(srcdir)/'`encrypt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-encrypt.Tpo $(DEPDIR)/libshadow_la-encrypt.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='encrypt.c' object='libshadow_la-encrypt.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-encrypt.lo `test -f 'encrypt.c' || echo '$(srcdir)/'`encrypt.c + +libshadow_la-fields.lo: fields.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-fields.lo -MD -MP -MF $(DEPDIR)/libshadow_la-fields.Tpo -c -o libshadow_la-fields.lo `test -f 'fields.c' || echo '$(srcdir)/'`fields.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-fields.Tpo $(DEPDIR)/libshadow_la-fields.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fields.c' object='libshadow_la-fields.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-fields.lo `test -f 'fields.c' || echo '$(srcdir)/'`fields.c + +libshadow_la-fputsx.lo: fputsx.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-fputsx.lo -MD -MP -MF $(DEPDIR)/libshadow_la-fputsx.Tpo -c -o libshadow_la-fputsx.lo `test -f 'fputsx.c' || echo '$(srcdir)/'`fputsx.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-fputsx.Tpo $(DEPDIR)/libshadow_la-fputsx.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fputsx.c' object='libshadow_la-fputsx.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-fputsx.lo `test -f 'fputsx.c' || echo '$(srcdir)/'`fputsx.c + +libshadow_la-getdef.lo: getdef.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-getdef.lo -MD -MP -MF $(DEPDIR)/libshadow_la-getdef.Tpo -c -o libshadow_la-getdef.lo `test -f 'getdef.c' || echo '$(srcdir)/'`getdef.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-getdef.Tpo $(DEPDIR)/libshadow_la-getdef.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getdef.c' object='libshadow_la-getdef.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-getdef.lo `test -f 'getdef.c' || echo '$(srcdir)/'`getdef.c + +libshadow_la-get_gid.lo: get_gid.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-get_gid.lo -MD -MP -MF $(DEPDIR)/libshadow_la-get_gid.Tpo -c -o libshadow_la-get_gid.lo `test -f 'get_gid.c' || echo '$(srcdir)/'`get_gid.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-get_gid.Tpo $(DEPDIR)/libshadow_la-get_gid.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='get_gid.c' object='libshadow_la-get_gid.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-get_gid.lo `test -f 'get_gid.c' || echo '$(srcdir)/'`get_gid.c + +libshadow_la-getlong.lo: getlong.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-getlong.lo -MD -MP -MF $(DEPDIR)/libshadow_la-getlong.Tpo -c -o libshadow_la-getlong.lo `test -f 'getlong.c' || echo '$(srcdir)/'`getlong.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-getlong.Tpo $(DEPDIR)/libshadow_la-getlong.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getlong.c' object='libshadow_la-getlong.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-getlong.lo `test -f 'getlong.c' || echo '$(srcdir)/'`getlong.c + +libshadow_la-get_pid.lo: get_pid.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-get_pid.lo -MD -MP -MF $(DEPDIR)/libshadow_la-get_pid.Tpo -c -o libshadow_la-get_pid.lo `test -f 'get_pid.c' || echo '$(srcdir)/'`get_pid.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-get_pid.Tpo $(DEPDIR)/libshadow_la-get_pid.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='get_pid.c' object='libshadow_la-get_pid.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-get_pid.lo `test -f 'get_pid.c' || echo '$(srcdir)/'`get_pid.c + +libshadow_la-get_uid.lo: get_uid.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-get_uid.lo -MD -MP -MF $(DEPDIR)/libshadow_la-get_uid.Tpo -c -o libshadow_la-get_uid.lo `test -f 'get_uid.c' || echo '$(srcdir)/'`get_uid.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-get_uid.Tpo $(DEPDIR)/libshadow_la-get_uid.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='get_uid.c' object='libshadow_la-get_uid.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-get_uid.lo `test -f 'get_uid.c' || echo '$(srcdir)/'`get_uid.c + +libshadow_la-getulong.lo: getulong.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-getulong.lo -MD -MP -MF $(DEPDIR)/libshadow_la-getulong.Tpo -c -o libshadow_la-getulong.lo `test -f 'getulong.c' || echo '$(srcdir)/'`getulong.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-getulong.Tpo $(DEPDIR)/libshadow_la-getulong.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getulong.c' object='libshadow_la-getulong.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-getulong.lo `test -f 'getulong.c' || echo '$(srcdir)/'`getulong.c + +libshadow_la-groupio.lo: groupio.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-groupio.lo -MD -MP -MF $(DEPDIR)/libshadow_la-groupio.Tpo -c -o libshadow_la-groupio.lo `test -f 'groupio.c' || echo '$(srcdir)/'`groupio.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-groupio.Tpo $(DEPDIR)/libshadow_la-groupio.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='groupio.c' object='libshadow_la-groupio.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-groupio.lo `test -f 'groupio.c' || echo '$(srcdir)/'`groupio.c + +libshadow_la-groupmem.lo: groupmem.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-groupmem.lo -MD -MP -MF $(DEPDIR)/libshadow_la-groupmem.Tpo -c -o libshadow_la-groupmem.lo `test -f 'groupmem.c' || echo '$(srcdir)/'`groupmem.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-groupmem.Tpo $(DEPDIR)/libshadow_la-groupmem.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='groupmem.c' object='libshadow_la-groupmem.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-groupmem.lo `test -f 'groupmem.c' || echo '$(srcdir)/'`groupmem.c + +libshadow_la-gshadow.lo: gshadow.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-gshadow.lo -MD -MP -MF $(DEPDIR)/libshadow_la-gshadow.Tpo -c -o libshadow_la-gshadow.lo `test -f 'gshadow.c' || echo '$(srcdir)/'`gshadow.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-gshadow.Tpo $(DEPDIR)/libshadow_la-gshadow.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gshadow.c' object='libshadow_la-gshadow.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-gshadow.lo `test -f 'gshadow.c' || echo '$(srcdir)/'`gshadow.c + +libshadow_la-lockpw.lo: lockpw.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-lockpw.lo -MD -MP -MF $(DEPDIR)/libshadow_la-lockpw.Tpo -c -o libshadow_la-lockpw.lo `test -f 'lockpw.c' || echo '$(srcdir)/'`lockpw.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-lockpw.Tpo $(DEPDIR)/libshadow_la-lockpw.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lockpw.c' object='libshadow_la-lockpw.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-lockpw.lo `test -f 'lockpw.c' || echo '$(srcdir)/'`lockpw.c + +libshadow_la-nss.lo: nss.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-nss.lo -MD -MP -MF $(DEPDIR)/libshadow_la-nss.Tpo -c -o libshadow_la-nss.lo `test -f 'nss.c' || echo '$(srcdir)/'`nss.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-nss.Tpo $(DEPDIR)/libshadow_la-nss.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nss.c' object='libshadow_la-nss.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-nss.lo `test -f 'nss.c' || echo '$(srcdir)/'`nss.c + +libshadow_la-nscd.lo: nscd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-nscd.lo -MD -MP -MF $(DEPDIR)/libshadow_la-nscd.Tpo -c -o libshadow_la-nscd.lo `test -f 'nscd.c' || echo '$(srcdir)/'`nscd.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-nscd.Tpo $(DEPDIR)/libshadow_la-nscd.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nscd.c' object='libshadow_la-nscd.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-nscd.lo `test -f 'nscd.c' || echo '$(srcdir)/'`nscd.c + +libshadow_la-shadowlog.lo: shadowlog.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-shadowlog.lo -MD -MP -MF $(DEPDIR)/libshadow_la-shadowlog.Tpo -c -o libshadow_la-shadowlog.lo `test -f 'shadowlog.c' || echo '$(srcdir)/'`shadowlog.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-shadowlog.Tpo $(DEPDIR)/libshadow_la-shadowlog.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='shadowlog.c' object='libshadow_la-shadowlog.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-shadowlog.lo `test -f 'shadowlog.c' || echo '$(srcdir)/'`shadowlog.c + +libshadow_la-sssd.lo: sssd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-sssd.lo -MD -MP -MF $(DEPDIR)/libshadow_la-sssd.Tpo -c -o libshadow_la-sssd.lo `test -f 'sssd.c' || echo '$(srcdir)/'`sssd.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-sssd.Tpo $(DEPDIR)/libshadow_la-sssd.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sssd.c' object='libshadow_la-sssd.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-sssd.lo `test -f 'sssd.c' || echo '$(srcdir)/'`sssd.c + +libshadow_la-port.lo: port.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-port.lo -MD -MP -MF $(DEPDIR)/libshadow_la-port.Tpo -c -o libshadow_la-port.lo `test -f 'port.c' || echo '$(srcdir)/'`port.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-port.Tpo $(DEPDIR)/libshadow_la-port.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='port.c' object='libshadow_la-port.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-port.lo `test -f 'port.c' || echo '$(srcdir)/'`port.c + +libshadow_la-pwauth.lo: pwauth.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-pwauth.lo -MD -MP -MF $(DEPDIR)/libshadow_la-pwauth.Tpo -c -o libshadow_la-pwauth.lo `test -f 'pwauth.c' || echo '$(srcdir)/'`pwauth.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-pwauth.Tpo $(DEPDIR)/libshadow_la-pwauth.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pwauth.c' object='libshadow_la-pwauth.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-pwauth.lo `test -f 'pwauth.c' || echo '$(srcdir)/'`pwauth.c + +libshadow_la-pwio.lo: pwio.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-pwio.lo -MD -MP -MF $(DEPDIR)/libshadow_la-pwio.Tpo -c -o libshadow_la-pwio.lo `test -f 'pwio.c' || echo '$(srcdir)/'`pwio.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-pwio.Tpo $(DEPDIR)/libshadow_la-pwio.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pwio.c' object='libshadow_la-pwio.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-pwio.lo `test -f 'pwio.c' || echo '$(srcdir)/'`pwio.c + +libshadow_la-pwmem.lo: pwmem.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-pwmem.lo -MD -MP -MF $(DEPDIR)/libshadow_la-pwmem.Tpo -c -o libshadow_la-pwmem.lo `test -f 'pwmem.c' || echo '$(srcdir)/'`pwmem.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-pwmem.Tpo $(DEPDIR)/libshadow_la-pwmem.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pwmem.c' object='libshadow_la-pwmem.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-pwmem.lo `test -f 'pwmem.c' || echo '$(srcdir)/'`pwmem.c + +libshadow_la-run_part.lo: run_part.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-run_part.lo -MD -MP -MF $(DEPDIR)/libshadow_la-run_part.Tpo -c -o libshadow_la-run_part.lo `test -f 'run_part.c' || echo '$(srcdir)/'`run_part.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-run_part.Tpo $(DEPDIR)/libshadow_la-run_part.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='run_part.c' object='libshadow_la-run_part.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-run_part.lo `test -f 'run_part.c' || echo '$(srcdir)/'`run_part.c + +libshadow_la-subordinateio.lo: subordinateio.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-subordinateio.lo -MD -MP -MF $(DEPDIR)/libshadow_la-subordinateio.Tpo -c -o libshadow_la-subordinateio.lo `test -f 'subordinateio.c' || echo '$(srcdir)/'`subordinateio.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-subordinateio.Tpo $(DEPDIR)/libshadow_la-subordinateio.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='subordinateio.c' object='libshadow_la-subordinateio.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-subordinateio.lo `test -f 'subordinateio.c' || echo '$(srcdir)/'`subordinateio.c + +libshadow_la-selinux.lo: selinux.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-selinux.lo -MD -MP -MF $(DEPDIR)/libshadow_la-selinux.Tpo -c -o libshadow_la-selinux.lo `test -f 'selinux.c' || echo '$(srcdir)/'`selinux.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-selinux.Tpo $(DEPDIR)/libshadow_la-selinux.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='selinux.c' object='libshadow_la-selinux.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-selinux.lo `test -f 'selinux.c' || echo '$(srcdir)/'`selinux.c + +libshadow_la-semanage.lo: semanage.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-semanage.lo -MD -MP -MF $(DEPDIR)/libshadow_la-semanage.Tpo -c -o libshadow_la-semanage.lo `test -f 'semanage.c' || echo '$(srcdir)/'`semanage.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-semanage.Tpo $(DEPDIR)/libshadow_la-semanage.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='semanage.c' object='libshadow_la-semanage.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-semanage.lo `test -f 'semanage.c' || echo '$(srcdir)/'`semanage.c + +libshadow_la-sgetgrent.lo: sgetgrent.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-sgetgrent.lo -MD -MP -MF $(DEPDIR)/libshadow_la-sgetgrent.Tpo -c -o libshadow_la-sgetgrent.lo `test -f 'sgetgrent.c' || echo '$(srcdir)/'`sgetgrent.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-sgetgrent.Tpo $(DEPDIR)/libshadow_la-sgetgrent.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sgetgrent.c' object='libshadow_la-sgetgrent.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-sgetgrent.lo `test -f 'sgetgrent.c' || echo '$(srcdir)/'`sgetgrent.c + +libshadow_la-sgetpwent.lo: sgetpwent.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-sgetpwent.lo -MD -MP -MF $(DEPDIR)/libshadow_la-sgetpwent.Tpo -c -o libshadow_la-sgetpwent.lo `test -f 'sgetpwent.c' || echo '$(srcdir)/'`sgetpwent.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-sgetpwent.Tpo $(DEPDIR)/libshadow_la-sgetpwent.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sgetpwent.c' object='libshadow_la-sgetpwent.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-sgetpwent.lo `test -f 'sgetpwent.c' || echo '$(srcdir)/'`sgetpwent.c + +libshadow_la-sgetspent.lo: sgetspent.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-sgetspent.lo -MD -MP -MF $(DEPDIR)/libshadow_la-sgetspent.Tpo -c -o libshadow_la-sgetspent.lo `test -f 'sgetspent.c' || echo '$(srcdir)/'`sgetspent.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-sgetspent.Tpo $(DEPDIR)/libshadow_la-sgetspent.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sgetspent.c' object='libshadow_la-sgetspent.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-sgetspent.lo `test -f 'sgetspent.c' || echo '$(srcdir)/'`sgetspent.c + +libshadow_la-sgroupio.lo: sgroupio.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-sgroupio.lo -MD -MP -MF $(DEPDIR)/libshadow_la-sgroupio.Tpo -c -o libshadow_la-sgroupio.lo `test -f 'sgroupio.c' || echo '$(srcdir)/'`sgroupio.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-sgroupio.Tpo $(DEPDIR)/libshadow_la-sgroupio.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sgroupio.c' object='libshadow_la-sgroupio.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-sgroupio.lo `test -f 'sgroupio.c' || echo '$(srcdir)/'`sgroupio.c + +libshadow_la-shadow.lo: shadow.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-shadow.lo -MD -MP -MF $(DEPDIR)/libshadow_la-shadow.Tpo -c -o libshadow_la-shadow.lo `test -f 'shadow.c' || echo '$(srcdir)/'`shadow.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-shadow.Tpo $(DEPDIR)/libshadow_la-shadow.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='shadow.c' object='libshadow_la-shadow.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-shadow.lo `test -f 'shadow.c' || echo '$(srcdir)/'`shadow.c + +libshadow_la-shadowio.lo: shadowio.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-shadowio.lo -MD -MP -MF $(DEPDIR)/libshadow_la-shadowio.Tpo -c -o libshadow_la-shadowio.lo `test -f 'shadowio.c' || echo '$(srcdir)/'`shadowio.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-shadowio.Tpo $(DEPDIR)/libshadow_la-shadowio.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='shadowio.c' object='libshadow_la-shadowio.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-shadowio.lo `test -f 'shadowio.c' || echo '$(srcdir)/'`shadowio.c + +libshadow_la-shadowmem.lo: shadowmem.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-shadowmem.lo -MD -MP -MF $(DEPDIR)/libshadow_la-shadowmem.Tpo -c -o libshadow_la-shadowmem.lo `test -f 'shadowmem.c' || echo '$(srcdir)/'`shadowmem.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-shadowmem.Tpo $(DEPDIR)/libshadow_la-shadowmem.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='shadowmem.c' object='libshadow_la-shadowmem.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-shadowmem.lo `test -f 'shadowmem.c' || echo '$(srcdir)/'`shadowmem.c + +libshadow_la-spawn.lo: spawn.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-spawn.lo -MD -MP -MF $(DEPDIR)/libshadow_la-spawn.Tpo -c -o libshadow_la-spawn.lo `test -f 'spawn.c' || echo '$(srcdir)/'`spawn.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-spawn.Tpo $(DEPDIR)/libshadow_la-spawn.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='spawn.c' object='libshadow_la-spawn.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-spawn.lo `test -f 'spawn.c' || echo '$(srcdir)/'`spawn.c + +libshadow_la-utent.lo: utent.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-utent.lo -MD -MP -MF $(DEPDIR)/libshadow_la-utent.Tpo -c -o libshadow_la-utent.lo `test -f 'utent.c' || echo '$(srcdir)/'`utent.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-utent.Tpo $(DEPDIR)/libshadow_la-utent.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utent.c' object='libshadow_la-utent.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-utent.lo `test -f 'utent.c' || echo '$(srcdir)/'`utent.c + +libshadow_la-tcbfuncs.lo: tcbfuncs.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libshadow_la-tcbfuncs.lo -MD -MP -MF $(DEPDIR)/libshadow_la-tcbfuncs.Tpo -c -o libshadow_la-tcbfuncs.lo `test -f 'tcbfuncs.c' || echo '$(srcdir)/'`tcbfuncs.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadow_la-tcbfuncs.Tpo $(DEPDIR)/libshadow_la-tcbfuncs.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tcbfuncs.c' object='libshadow_la-tcbfuncs.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshadow_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libshadow_la-tcbfuncs.lo `test -f 'tcbfuncs.c' || echo '$(srcdir)/'`tcbfuncs.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + 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-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + 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" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(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 $(LTLIBRARIES) +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." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/libshadow_la-commonio.Plo + -rm -f ./$(DEPDIR)/libshadow_la-encrypt.Plo + -rm -f ./$(DEPDIR)/libshadow_la-fields.Plo + -rm -f ./$(DEPDIR)/libshadow_la-fputsx.Plo + -rm -f ./$(DEPDIR)/libshadow_la-get_gid.Plo + -rm -f ./$(DEPDIR)/libshadow_la-get_pid.Plo + -rm -f ./$(DEPDIR)/libshadow_la-get_uid.Plo + -rm -f ./$(DEPDIR)/libshadow_la-getdef.Plo + -rm -f ./$(DEPDIR)/libshadow_la-getlong.Plo + -rm -f ./$(DEPDIR)/libshadow_la-getulong.Plo + -rm -f ./$(DEPDIR)/libshadow_la-groupio.Plo + -rm -f ./$(DEPDIR)/libshadow_la-groupmem.Plo + -rm -f ./$(DEPDIR)/libshadow_la-gshadow.Plo + -rm -f ./$(DEPDIR)/libshadow_la-lockpw.Plo + -rm -f ./$(DEPDIR)/libshadow_la-nscd.Plo + -rm -f ./$(DEPDIR)/libshadow_la-nss.Plo + -rm -f ./$(DEPDIR)/libshadow_la-port.Plo + -rm -f ./$(DEPDIR)/libshadow_la-pwauth.Plo + -rm -f ./$(DEPDIR)/libshadow_la-pwio.Plo + -rm -f ./$(DEPDIR)/libshadow_la-pwmem.Plo + -rm -f ./$(DEPDIR)/libshadow_la-run_part.Plo + -rm -f ./$(DEPDIR)/libshadow_la-selinux.Plo + -rm -f ./$(DEPDIR)/libshadow_la-semanage.Plo + -rm -f ./$(DEPDIR)/libshadow_la-sgetgrent.Plo + -rm -f ./$(DEPDIR)/libshadow_la-sgetpwent.Plo + -rm -f ./$(DEPDIR)/libshadow_la-sgetspent.Plo + -rm -f ./$(DEPDIR)/libshadow_la-sgroupio.Plo + -rm -f ./$(DEPDIR)/libshadow_la-shadow.Plo + -rm -f ./$(DEPDIR)/libshadow_la-shadowio.Plo + -rm -f ./$(DEPDIR)/libshadow_la-shadowlog.Plo + -rm -f ./$(DEPDIR)/libshadow_la-shadowmem.Plo + -rm -f ./$(DEPDIR)/libshadow_la-spawn.Plo + -rm -f ./$(DEPDIR)/libshadow_la-sssd.Plo + -rm -f ./$(DEPDIR)/libshadow_la-subordinateio.Plo + -rm -f ./$(DEPDIR)/libshadow_la-tcbfuncs.Plo + -rm -f ./$(DEPDIR)/libshadow_la-utent.Plo + -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 -f ./$(DEPDIR)/libshadow_la-commonio.Plo + -rm -f ./$(DEPDIR)/libshadow_la-encrypt.Plo + -rm -f ./$(DEPDIR)/libshadow_la-fields.Plo + -rm -f ./$(DEPDIR)/libshadow_la-fputsx.Plo + -rm -f ./$(DEPDIR)/libshadow_la-get_gid.Plo + -rm -f ./$(DEPDIR)/libshadow_la-get_pid.Plo + -rm -f ./$(DEPDIR)/libshadow_la-get_uid.Plo + -rm -f ./$(DEPDIR)/libshadow_la-getdef.Plo + -rm -f ./$(DEPDIR)/libshadow_la-getlong.Plo + -rm -f ./$(DEPDIR)/libshadow_la-getulong.Plo + -rm -f ./$(DEPDIR)/libshadow_la-groupio.Plo + -rm -f ./$(DEPDIR)/libshadow_la-groupmem.Plo + -rm -f ./$(DEPDIR)/libshadow_la-gshadow.Plo + -rm -f ./$(DEPDIR)/libshadow_la-lockpw.Plo + -rm -f ./$(DEPDIR)/libshadow_la-nscd.Plo + -rm -f ./$(DEPDIR)/libshadow_la-nss.Plo + -rm -f ./$(DEPDIR)/libshadow_la-port.Plo + -rm -f ./$(DEPDIR)/libshadow_la-pwauth.Plo + -rm -f ./$(DEPDIR)/libshadow_la-pwio.Plo + -rm -f ./$(DEPDIR)/libshadow_la-pwmem.Plo + -rm -f ./$(DEPDIR)/libshadow_la-run_part.Plo + -rm -f ./$(DEPDIR)/libshadow_la-selinux.Plo + -rm -f ./$(DEPDIR)/libshadow_la-semanage.Plo + -rm -f ./$(DEPDIR)/libshadow_la-sgetgrent.Plo + -rm -f ./$(DEPDIR)/libshadow_la-sgetpwent.Plo + -rm -f ./$(DEPDIR)/libshadow_la-sgetspent.Plo + -rm -f ./$(DEPDIR)/libshadow_la-sgroupio.Plo + -rm -f ./$(DEPDIR)/libshadow_la-shadow.Plo + -rm -f ./$(DEPDIR)/libshadow_la-shadowio.Plo + -rm -f ./$(DEPDIR)/libshadow_la-shadowlog.Plo + -rm -f ./$(DEPDIR)/libshadow_la-shadowmem.Plo + -rm -f ./$(DEPDIR)/libshadow_la-spawn.Plo + -rm -f ./$(DEPDIR)/libshadow_la-sssd.Plo + -rm -f ./$(DEPDIR)/libshadow_la-subordinateio.Plo + -rm -f ./$(DEPDIR)/libshadow_la-tcbfuncs.Plo + -rm -f ./$(DEPDIR)/libshadow_la-utent.Plo + -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 TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-noinstLTLIBRARIES \ + cscopelist-am ctags ctags-am 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 tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +# 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/lib/commonio.c b/lib/commonio.c new file mode 100644 index 0000000..9a02ce1 --- /dev/null +++ b/lib/commonio.c @@ -0,0 +1,1276 @@ +/* + * SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 2001, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2001 - 2006, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2007 - 2011, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#include "defines.h" +#include <assert.h> +#include <sys/stat.h> +#include <stdlib.h> +#include <limits.h> +#include <utime.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <signal.h> +#include "nscd.h" +#include "sssd.h" +#ifdef WITH_TCB +#include <tcb.h> +#endif /* WITH_TCB */ +#include "prototypes.h" +#include "commonio.h" +#include "shadowlog_internal.h" + +/* local function prototypes */ +static int lrename (const char *, const char *); +static int check_link_count (const char *file, bool log); +static int do_lock_file (const char *file, const char *lock, bool log); +static /*@null@*/ /*@dependent@*/FILE *fopen_set_perms ( + const char *name, + const char *mode, + const struct stat *sb); +static int create_backup (const char *, FILE *); +static void free_linked_list (struct commonio_db *); +static void add_one_entry ( + struct commonio_db *db, + /*@owned@*/struct commonio_entry *p); +static bool name_is_nis (const char *name); +static int write_all (const struct commonio_db *); +static /*@dependent@*/ /*@null@*/struct commonio_entry *find_entry_by_name ( + struct commonio_db *, + const char *); +static /*@dependent@*/ /*@null@*/struct commonio_entry *next_entry_by_name ( + struct commonio_db *, + /*@null@*/struct commonio_entry *pos, + const char *); + +static int lock_count = 0; +static bool nscd_need_reload = false; + +/* + * Simple rename(P) alternative that attempts to rename to symlink + * target. + */ +int lrename (const char *old, const char *new) +{ + int res; + char *r = NULL; + +#ifndef __GLIBC__ + char resolved_path[PATH_MAX]; +#endif /* !__GLIBC__ */ + struct stat sb; + if (lstat (new, &sb) == 0 && S_ISLNK (sb.st_mode)) { +#ifdef __GLIBC__ /* now a POSIX.1-2008 feature */ + r = realpath (new, NULL); +#else /* !__GLIBC__ */ + r = realpath (new, resolved_path); +#endif /* !__GLIBC__ */ + if (NULL == r) { + perror ("realpath in lrename()"); + } else { + new = r; + } + } + + res = rename (old, new); + +#ifdef __GLIBC__ + free (r); +#endif /* __GLIBC__ */ + + return res; +} + +static int check_link_count (const char *file, bool log) +{ + struct stat sb; + + if (stat (file, &sb) != 0) { + if (log) { + (void) fprintf (shadow_logfd, + "%s: %s file stat error: %s\n", + shadow_progname, file, strerror (errno)); + } + return 0; + } + + if (sb.st_nlink != 2) { + if (log) { + (void) fprintf (shadow_logfd, + "%s: %s: lock file already used (nlink: %u)\n", + shadow_progname, file, sb.st_nlink); + } + return 0; + } + + return 1; +} + + +static int do_lock_file (const char *file, const char *lock, bool log) +{ + int fd; + pid_t pid; + ssize_t len; + int retval; + char buf[32]; + + fd = open (file, O_CREAT | O_TRUNC | O_WRONLY, 0600); + if (-1 == fd) { + if (log) { + (void) fprintf (shadow_logfd, + "%s: %s: %s\n", + shadow_progname, file, strerror (errno)); + } + return 0; + } + + pid = getpid (); + snprintf (buf, sizeof buf, "%lu", (unsigned long) pid); + len = (ssize_t) strlen (buf) + 1; + if (write (fd, buf, (size_t) len) != len) { + if (log) { + (void) fprintf (shadow_logfd, + "%s: %s file write error: %s\n", + shadow_progname, file, strerror (errno)); + } + (void) close (fd); + unlink (file); + return 0; + } + if (fdatasync (fd) == -1) { + if (log) { + (void) fprintf (shadow_logfd, + "%s: %s file sync error: %s\n", + shadow_progname, file, strerror (errno)); + } + (void) close (fd); + unlink (file); + return 0; + } + close (fd); + + if (link (file, lock) == 0) { + retval = check_link_count (file, log); + unlink (file); + return retval; + } + + fd = open (lock, O_RDWR); + if (-1 == fd) { + if (log) { + (void) fprintf (shadow_logfd, + "%s: %s: %s\n", + shadow_progname, lock, strerror (errno)); + } + unlink (file); + errno = EINVAL; + return 0; + } + len = read (fd, buf, sizeof (buf) - 1); + close (fd); + if (len <= 0) { + if (log) { + (void) fprintf (shadow_logfd, + "%s: existing lock file %s without a PID\n", + shadow_progname, lock); + } + unlink (file); + errno = EINVAL; + return 0; + } + buf[len] = '\0'; + if (get_pid (buf, &pid) == 0) { + if (log) { + (void) fprintf (shadow_logfd, + "%s: existing lock file %s with an invalid PID '%s'\n", + shadow_progname, lock, buf); + } + unlink (file); + errno = EINVAL; + return 0; + } + if (kill (pid, 0) == 0) { + if (log) { + (void) fprintf (shadow_logfd, + "%s: lock %s already used by PID %lu\n", + shadow_progname, lock, (unsigned long) pid); + } + unlink (file); + errno = EEXIST; + return 0; + } + if (unlink (lock) != 0) { + if (log) { + (void) fprintf (shadow_logfd, + "%s: cannot get lock %s: %s\n", + shadow_progname, lock, strerror (errno)); + } + unlink (file); + return 0; + } + + retval = 0; + if (link (file, lock) == 0) { + retval = check_link_count (file, log); + } else { + if (log) { + (void) fprintf (shadow_logfd, + "%s: cannot get lock %s: %s\n", + shadow_progname, lock, strerror (errno)); + } + } + + unlink (file); + return retval; +} + + +static /*@null@*/ /*@dependent@*/FILE *fopen_set_perms ( + const char *name, + const char *mode, + const struct stat *sb) +{ + FILE *fp; + mode_t mask; + + mask = umask (0777); + fp = fopen (name, mode); + (void) umask (mask); + if (NULL == fp) { + return NULL; + } + +#ifdef HAVE_FCHOWN + if (fchown (fileno (fp), sb->st_uid, sb->st_gid) != 0) { + goto fail; + } +#else /* !HAVE_FCHOWN */ + if (chown (name, sb->st_mode) != 0) { + goto fail; + } +#endif /* !HAVE_FCHOWN */ + +#ifdef HAVE_FCHMOD + if (fchmod (fileno (fp), sb->st_mode & 0664) != 0) { + goto fail; + } +#else /* !HAVE_FCHMOD */ + if (chmod (name, sb->st_mode & 0664) != 0) { + goto fail; + } +#endif /* !HAVE_FCHMOD */ + return fp; + + fail: + (void) fclose (fp); + /* fopen_set_perms is used for intermediate files */ + (void) unlink (name); + return NULL; +} + + +static int create_backup (const char *backup, FILE * fp) +{ + struct stat sb; + struct utimbuf ub; + FILE *bkfp; + int c; + + if (fstat (fileno (fp), &sb) != 0) { + return -1; + } + + bkfp = fopen_set_perms (backup, "w", &sb); + if (NULL == bkfp) { + return -1; + } + + /* TODO: faster copy, not one-char-at-a-time. --marekm */ + c = 0; + if (fseek (fp, 0, SEEK_SET) == 0) { + while ((c = getc (fp)) != EOF) { + if (putc (c, bkfp) == EOF) { + break; + } + } + } + if ((c != EOF) || (ferror (fp) != 0) || (fflush (bkfp) != 0)) { + (void) fclose (bkfp); + /* FIXME: unlink the backup file? */ + return -1; + } + if (fsync (fileno (bkfp)) != 0) { + (void) fclose (bkfp); + /* FIXME: unlink the backup file? */ + return -1; + } + if (fclose (bkfp) != 0) { + /* FIXME: unlink the backup file? */ + return -1; + } + + ub.actime = sb.st_atime; + ub.modtime = sb.st_mtime; + (void) utime (backup, &ub); + return 0; +} + + +static void free_linked_list (struct commonio_db *db) +{ + struct commonio_entry *p; + + while (NULL != db->head) { + p = db->head; + db->head = p->next; + + free (p->line); + + if (NULL != p->eptr) { + db->ops->free (p->eptr); + } + + free (p); + } + db->tail = NULL; +} + + +int commonio_setname (struct commonio_db *db, const char *name) +{ + snprintf (db->filename, sizeof (db->filename), "%s", name); + db->setname = true; + return 1; +} + + +bool commonio_present (const struct commonio_db *db) +{ + return (access (db->filename, F_OK) == 0); +} + + +int commonio_lock_nowait (struct commonio_db *db, bool log) +{ + char* file = NULL; + char* lock = NULL; + size_t lock_file_len; + size_t file_len; + int err = 0; + + if (db->locked) { + return 1; + } + file_len = strlen(db->filename) + 11;/* %lu max size */ + lock_file_len = strlen(db->filename) + 6; /* sizeof ".lock" */ + file = (char*)malloc(file_len); + if (file == NULL) { + goto cleanup_ENOMEM; + } + lock = (char*)malloc(lock_file_len); + if (lock == NULL) { + goto cleanup_ENOMEM; + } + snprintf (file, file_len, "%s.%lu", + db->filename, (unsigned long) getpid ()); + snprintf (lock, lock_file_len, "%s.lock", db->filename); + if (do_lock_file (file, lock, log) != 0) { + db->locked = true; + lock_count++; + err = 1; + } +cleanup_ENOMEM: + free(file); + free(lock); + return err; +} + + +int commonio_lock (struct commonio_db *db) +{ + int i; + +#ifdef HAVE_LCKPWDF + /* + * Only if the system libc has a real lckpwdf() - the one from + * lockpw.c calls us and would cause infinite recursion! + * It is also not used with the prefix option. + */ + if (!db->setname) { + /* + * Call lckpwdf() on the first lock. + * If it succeeds, call *_lock() only once + * (no retries, it should always succeed). + */ + if (0 == lock_count) { + if (lckpwdf () == -1) { + if (geteuid () != 0) { + (void) fprintf (shadow_logfd, + "%s: Permission denied.\n", + shadow_progname); + } + return 0; /* failure */ + } + } + + if (commonio_lock_nowait (db, true) != 0) { + return 1; /* success */ + } + + ulckpwdf (); + return 0; /* failure */ + } +#endif /* !HAVE_LCKPWDF */ + + /* + * lckpwdf() not used - do it the old way. + */ +#ifndef LOCK_TRIES +#define LOCK_TRIES 15 +#endif + +#ifndef LOCK_SLEEP +#define LOCK_SLEEP 1 +#endif + for (i = 0; i < LOCK_TRIES; i++) { + if (i > 0) { + sleep (LOCK_SLEEP); /* delay between retries */ + } + if (commonio_lock_nowait (db, i==LOCK_TRIES-1) != 0) { + return 1; /* success */ + } + /* no unnecessary retries on "permission denied" errors */ + if (geteuid () != 0) { + (void) fprintf (shadow_logfd, "%s: Permission denied.\n", + shadow_progname); + return 0; + } + } + return 0; /* failure */ +} + +static void dec_lock_count (void) +{ + if (lock_count > 0) { + lock_count--; + if (lock_count == 0) { + /* Tell nscd when lock count goes to zero, + if any of the files were changed. */ + if (nscd_need_reload) { + nscd_flush_cache ("passwd"); + nscd_flush_cache ("group"); + sssd_flush_cache (SSSD_DB_PASSWD | SSSD_DB_GROUP); + nscd_need_reload = false; + } +#ifdef HAVE_LCKPWDF + ulckpwdf (); +#endif /* HAVE_LCKPWDF */ + } + } +} + + +int commonio_unlock (struct commonio_db *db) +{ + char lock[1024]; + + if (db->isopen) { + db->readonly = true; + if (commonio_close (db) == 0) { + if (db->locked) { + dec_lock_count (); + } + return 0; + } + } + if (db->locked) { + /* + * Unlock in reverse order: remove the lock file, + * then call ulckpwdf() (if used) on last unlock. + */ + db->locked = false; + snprintf (lock, sizeof lock, "%s.lock", db->filename); + unlink (lock); + dec_lock_count (); + return 1; + } + return 0; +} + + +/* + * Add an entry at the end. + * + * defines p->next, p->prev + * (unfortunately, owned special are not supported) + */ +static void add_one_entry (struct commonio_db *db, + /*@owned@*/struct commonio_entry *p) +{ + /*@-mustfreeonly@*/ + p->next = NULL; + p->prev = db->tail; + /*@=mustfreeonly@*/ + if (NULL == db->head) { + db->head = p; + } + if (NULL != db->tail) { + db->tail->next = p; + } + db->tail = p; +} + + +static bool name_is_nis (const char *name) +{ + return (('+' == name[0]) || ('-' == name[0])); +} + + +/* + * New entries are inserted before the first NIS entry. Order is preserved + * when db is written out. + */ +#ifndef KEEP_NIS_AT_END +#define KEEP_NIS_AT_END 1 +#endif + +#if KEEP_NIS_AT_END +static void add_one_entry_nis (struct commonio_db *db, + /*@owned@*/struct commonio_entry *newp); + +/* + * Insert an entry between the regular entries, and the NIS entries. + * + * defines newp->next, newp->prev + * (unfortunately, owned special are not supported) + */ +static void add_one_entry_nis (struct commonio_db *db, + /*@owned@*/struct commonio_entry *newp) +{ + struct commonio_entry *p; + + for (p = db->head; NULL != p; p = p->next) { + if (name_is_nis (p->eptr ? db->ops->getname (p->eptr) + : p->line)) { + /*@-mustfreeonly@*/ + newp->next = p; + newp->prev = p->prev; + /*@=mustfreeonly@*/ + if (NULL != p->prev) { + p->prev->next = newp; + } else { + db->head = newp; + } + p->prev = newp; + return; + } + } + add_one_entry (db, newp); +} +#endif /* KEEP_NIS_AT_END */ + +/* Initial buffer size, as well as increment if not sufficient + (for reading very long lines in group files). */ +#define BUFLEN 4096 + +int commonio_open (struct commonio_db *db, int mode) +{ + char *buf; + char *cp; + char *line; + struct commonio_entry *p; + void *eptr = NULL; + int flags = mode; + size_t buflen; + int fd; + int saved_errno; + + mode &= ~O_CREAT; + + if ( db->isopen + || ( (O_RDONLY != mode) + && (O_RDWR != mode))) { + errno = EINVAL; + return 0; + } + db->readonly = (mode == O_RDONLY); + if (!db->readonly && !db->locked) { + errno = EACCES; + return 0; + } + + db->head = NULL; + db->tail = NULL; + db->cursor = NULL; + db->changed = false; + + fd = open (db->filename, + (db->readonly ? O_RDONLY : O_RDWR) + | O_NOCTTY | O_NONBLOCK | O_NOFOLLOW); + saved_errno = errno; + db->fp = NULL; + if (fd >= 0) { +#ifdef WITH_TCB + if (tcb_is_suspect (fd) != 0) { + (void) close (fd); + errno = EINVAL; + return 0; + } +#endif /* WITH_TCB */ + db->fp = fdopen (fd, db->readonly ? "r" : "r+"); + saved_errno = errno; + if (NULL == db->fp) { + (void) close (fd); + } + } + errno = saved_errno; + + /* + * If O_CREAT was specified and the file didn't exist, it will be + * created by commonio_close(). We have no entries to read yet. --marekm + */ + if (NULL == db->fp) { + if (((flags & O_CREAT) != 0) && (ENOENT == errno)) { + db->isopen = true; + return 1; + } + return 0; + } + + /* Do not inherit fd in spawned processes (e.g. nscd) */ + fcntl (fileno (db->fp), F_SETFD, FD_CLOEXEC); + + buflen = BUFLEN; + buf = (char *) malloc (buflen); + if (NULL == buf) { + goto cleanup_ENOMEM; + } + + while (db->ops->fgets (buf, (int) buflen, db->fp) == buf) { + while ( ((cp = strrchr (buf, '\n')) == NULL) + && (feof (db->fp) == 0)) { + size_t len; + + buflen += BUFLEN; + cp = (char *) realloc (buf, buflen); + if (NULL == cp) { + goto cleanup_buf; + } + buf = cp; + len = strlen (buf); + if (db->ops->fgets (buf + len, + (int) (buflen - len), + db->fp) == NULL) { + goto cleanup_buf; + } + } + cp = strrchr (buf, '\n'); + if (NULL != cp) { + *cp = '\0'; + } + + line = strdup (buf); + if (NULL == line) { + goto cleanup_buf; + } + + if (name_is_nis (line)) { + eptr = NULL; + } else { + eptr = db->ops->parse (line); + if (NULL != eptr) { + eptr = db->ops->dup (eptr); + if (NULL == eptr) { + goto cleanup_line; + } + } + } + + p = (struct commonio_entry *) malloc (sizeof *p); + if (NULL == p) { + goto cleanup_entry; + } + + p->eptr = eptr; + p->line = line; + p->changed = false; + + add_one_entry (db, p); + } + + free (buf); + + if (ferror (db->fp) != 0) { + goto cleanup_errno; + } + + if ((NULL != db->ops->open_hook) && (db->ops->open_hook () == 0)) { + goto cleanup_errno; + } + + db->isopen = true; + return 1; + + cleanup_entry: + if (NULL != eptr) { + db->ops->free (eptr); + } + cleanup_line: + free (line); + cleanup_buf: + free (buf); + cleanup_ENOMEM: + errno = ENOMEM; + cleanup_errno: + saved_errno = errno; + free_linked_list (db); + fclose (db->fp); + db->fp = NULL; + errno = saved_errno; + return 0; +} + +/* + * Sort given db according to cmp function (usually compares uids) + */ +int +commonio_sort (struct commonio_db *db, int (*cmp) (const void *, const void *)) +{ + struct commonio_entry **entries, *ptr; + size_t n = 0, i; +#if KEEP_NIS_AT_END + struct commonio_entry *nis = NULL; +#endif + + for (ptr = db->head; + (NULL != ptr) +#if KEEP_NIS_AT_END + && ((NULL == ptr->line) + || (('+' != ptr->line[0]) + && ('-' != ptr->line[0]))) +#endif + ; + ptr = ptr->next) { + n++; + } +#if KEEP_NIS_AT_END + if (NULL != ptr) { + nis = ptr; + } +#endif + + if (n <= 1) { + return 0; + } + + entries = malloc (n * sizeof (struct commonio_entry *)); + if (entries == NULL) { + return -1; + } + + n = 0; + for (ptr = db->head; +#if KEEP_NIS_AT_END + nis != ptr; +#else + NULL != ptr; +#endif +/*@ -nullderef @*/ + ptr = ptr->next +/*@ +nullderef @*/ + ) { + entries[n] = ptr; + n++; + } + qsort (entries, n, sizeof (struct commonio_entry *), cmp); + + /* Take care of the head and tail separately */ + db->head = entries[0]; + n--; +#if KEEP_NIS_AT_END + if (NULL == nis) +#endif + { + db->tail = entries[n]; + } + db->head->prev = NULL; + db->head->next = entries[1]; + entries[n]->prev = entries[n - 1]; +#if KEEP_NIS_AT_END + entries[n]->next = nis; +#else + entries[n]->next = NULL; +#endif + + /* Now other elements have prev and next entries */ + for (i = 1; i < n; i++) { + entries[i]->prev = entries[i - 1]; + entries[i]->next = entries[i + 1]; + } + + free (entries); + db->changed = true; + + return 0; +} + +/* + * Sort entries in db according to order in another. + */ +int commonio_sort_wrt (struct commonio_db *shadow, + const struct commonio_db *passwd) +{ + struct commonio_entry *head = NULL, *pw_ptr, *spw_ptr; + const char *name; + + if ((NULL == shadow) || (NULL == shadow->head)) { + return 0; + } + + for (pw_ptr = passwd->head; NULL != pw_ptr; pw_ptr = pw_ptr->next) { + if (NULL == pw_ptr->eptr) { + continue; + } + name = passwd->ops->getname (pw_ptr->eptr); + for (spw_ptr = shadow->head; + NULL != spw_ptr; + spw_ptr = spw_ptr->next) { + if (NULL == spw_ptr->eptr) { + continue; + } + if (strcmp (name, shadow->ops->getname (spw_ptr->eptr)) + == 0) { + break; + } + } + if (NULL == spw_ptr) { + continue; + } + commonio_del_entry (shadow, spw_ptr); + spw_ptr->next = head; + head = spw_ptr; + } + + for (spw_ptr = head; NULL != spw_ptr; spw_ptr = head) { + head = head->next; + + if (NULL != shadow->head) { + shadow->head->prev = spw_ptr; + } + spw_ptr->next = shadow->head; + shadow->head = spw_ptr; + } + + shadow->head->prev = NULL; + shadow->changed = true; + + return 0; +} + +/* + * write_all - Write the database to its file. + * + * It returns 0 if all the entries could be written correctly. + */ +static int write_all (const struct commonio_db *db) + /*@requires notnull db->fp@*/ +{ + const struct commonio_entry *p; + void *eptr; + + for (p = db->head; NULL != p; p = p->next) { + if (p->changed) { + eptr = p->eptr; + assert (NULL != eptr); + if (db->ops->put (eptr, db->fp) != 0) { + return -1; + } + } else if (NULL != p->line) { + if (db->ops->fputs (p->line, db->fp) == EOF) { + return -1; + } + if (putc ('\n', db->fp) == EOF) { + return -1; + } + } + } + return 0; +} + + +int commonio_close (struct commonio_db *db) +{ + char buf[1024]; + int errors = 0; + struct stat sb; + + if (!db->isopen) { + errno = EINVAL; + return 0; + } + db->isopen = false; + + if (!db->changed || db->readonly) { + if (NULL != db->fp) { + (void) fclose (db->fp); + db->fp = NULL; + } + goto success; + } + + if ((NULL != db->ops->close_hook) && (db->ops->close_hook () == 0)) { + goto fail; + } + + memzero (&sb, sizeof sb); + if (NULL != db->fp) { + if (fstat (fileno (db->fp), &sb) != 0) { + (void) fclose (db->fp); + db->fp = NULL; + goto fail; + } + + /* + * Create backup file. + */ + snprintf (buf, sizeof buf, "%s-", db->filename); + +#ifdef WITH_SELINUX + if (set_selinux_file_context (db->filename, S_IFREG) != 0) { + errors++; + } +#endif + if (create_backup (buf, db->fp) != 0) { + errors++; + } + + if (fclose (db->fp) != 0) { + errors++; + } + +#ifdef WITH_SELINUX + if (reset_selinux_file_context () != 0) { + errors++; + } +#endif + if (errors != 0) { + db->fp = NULL; + goto fail; + } + } else { + /* + * Default permissions for new [g]shadow files. + */ + sb.st_mode = db->st_mode; + sb.st_uid = db->st_uid; + sb.st_gid = db->st_gid; + } + + snprintf (buf, sizeof buf, "%s+", db->filename); + +#ifdef WITH_SELINUX + if (set_selinux_file_context (db->filename, S_IFREG) != 0) { + errors++; + } +#endif + + db->fp = fopen_set_perms (buf, "w", &sb); + if (NULL == db->fp) { + goto fail; + } + + if (write_all (db) != 0) { + errors++; + } + + if (fflush (db->fp) != 0) { + errors++; + } +#ifdef HAVE_FSYNC + if (fsync (fileno (db->fp)) != 0) { + errors++; + } +#else /* !HAVE_FSYNC */ + sync (); +#endif /* !HAVE_FSYNC */ + if (fclose (db->fp) != 0) { + errors++; + } + + db->fp = NULL; + + if (errors != 0) { + unlink (buf); + goto fail; + } + + if (lrename (buf, db->filename) != 0) { + goto fail; + } + +#ifdef WITH_SELINUX + if (reset_selinux_file_context () != 0) { + goto fail; + } +#endif + + nscd_need_reload = true; + goto success; + fail: + errors++; + success: + + free_linked_list (db); + return errors == 0; +} + +static /*@dependent@*/ /*@null@*/struct commonio_entry *next_entry_by_name ( + struct commonio_db *db, + /*@null@*/struct commonio_entry *pos, + const char *name) +{ + struct commonio_entry *p; + void *ep; + + if (NULL == pos) { + return NULL; + } + + for (p = pos; NULL != p; p = p->next) { + ep = p->eptr; + if ( (NULL != ep) + && (strcmp (db->ops->getname (ep), name) == 0)) { + break; + } + } + return p; +} + +static /*@dependent@*/ /*@null@*/struct commonio_entry *find_entry_by_name ( + struct commonio_db *db, + const char *name) +{ + return next_entry_by_name (db, db->head, name); +} + + +int commonio_update (struct commonio_db *db, const void *eptr) +{ + struct commonio_entry *p; + void *nentry; + + if (!db->isopen || db->readonly) { + errno = EINVAL; + return 0; + } + nentry = db->ops->dup (eptr); + if (NULL == nentry) { + errno = ENOMEM; + return 0; + } + p = find_entry_by_name (db, db->ops->getname (eptr)); + if (NULL != p) { + if (next_entry_by_name (db, p->next, db->ops->getname (eptr)) != NULL) { + fprintf (shadow_logfd, _("Multiple entries named '%s' in %s. Please fix this with pwck or grpck.\n"), db->ops->getname (eptr), db->filename); + db->ops->free (nentry); + return 0; + } + db->ops->free (p->eptr); + p->eptr = nentry; + p->changed = true; + db->cursor = p; + + db->changed = true; + return 1; + } + /* not found, new entry */ + p = (struct commonio_entry *) malloc (sizeof *p); + if (NULL == p) { + db->ops->free (nentry); + errno = ENOMEM; + return 0; + } + + p->eptr = nentry; + p->line = NULL; + p->changed = true; + +#if KEEP_NIS_AT_END + add_one_entry_nis (db, p); +#else /* !KEEP_NIS_AT_END */ + add_one_entry (db, p); +#endif /* !KEEP_NIS_AT_END */ + + db->changed = true; + return 1; +} + +#ifdef ENABLE_SUBIDS +int commonio_append (struct commonio_db *db, const void *eptr) +{ + struct commonio_entry *p; + void *nentry; + + if (!db->isopen || db->readonly) { + errno = EINVAL; + return 0; + } + nentry = db->ops->dup (eptr); + if (NULL == nentry) { + errno = ENOMEM; + return 0; + } + /* new entry */ + p = (struct commonio_entry *) malloc (sizeof *p); + if (NULL == p) { + db->ops->free (nentry); + errno = ENOMEM; + return 0; + } + + p->eptr = nentry; + p->line = NULL; + p->changed = true; + add_one_entry (db, p); + + db->changed = true; + return 1; +} +#endif /* ENABLE_SUBIDS */ + +void commonio_del_entry (struct commonio_db *db, const struct commonio_entry *p) +{ + if (p == db->cursor) { + db->cursor = p->next; + } + + if (NULL != p->prev) { + p->prev->next = p->next; + } else { + db->head = p->next; + } + + if (NULL != p->next) { + p->next->prev = p->prev; + } else { + db->tail = p->prev; + } + + db->changed = true; +} + +/* + * commonio_remove - Remove the entry of the given name from the database. + */ +int commonio_remove (struct commonio_db *db, const char *name) +{ + struct commonio_entry *p; + + if (!db->isopen || db->readonly) { + errno = EINVAL; + return 0; + } + p = find_entry_by_name (db, name); + if (NULL == p) { + errno = ENOENT; + return 0; + } + if (next_entry_by_name (db, p->next, name) != NULL) { + fprintf (shadow_logfd, _("Multiple entries named '%s' in %s. Please fix this with pwck or grpck.\n"), name, db->filename); + return 0; + } + + commonio_del_entry (db, p); + + free (p->line); + + if (NULL != p->eptr) { + db->ops->free (p->eptr); + } + + return 1; +} + +/* + * commonio_locate - Find the first entry with the specified name in + * the database. + * + * If found, it returns the entry and set the cursor of the database to + * that entry. + * + * Otherwise, it returns NULL. + */ +/*@observer@*/ /*@null@*/const void *commonio_locate (struct commonio_db *db, const char *name) +{ + struct commonio_entry *p; + + if (!db->isopen) { + errno = EINVAL; + return NULL; + } + p = find_entry_by_name (db, name); + if (NULL == p) { + errno = ENOENT; + return NULL; + } + db->cursor = p; + return p->eptr; +} + +/* + * commonio_rewind - Restore the database cursor to the first entry. + * + * It returns 0 on error, 1 on success. + */ +int commonio_rewind (struct commonio_db *db) +{ + if (!db->isopen) { + errno = EINVAL; + return 0; + } + db->cursor = NULL; + return 1; +} + +/* + * commonio_next - Return the next entry of the specified database + * + * It returns the next entry, or NULL if no other entries could be found. + */ +/*@observer@*/ /*@null@*/const void *commonio_next (struct commonio_db *db) +{ + void *eptr; + + if (!db->isopen) { + errno = EINVAL; + return 0; + } + if (NULL == db->cursor) { + db->cursor = db->head; + } else { + db->cursor = db->cursor->next; + } + + while (NULL != db->cursor) { + eptr = db->cursor->eptr; + if (NULL != eptr) { + return eptr; + } + + db->cursor = db->cursor->next; + } + return NULL; +} + diff --git a/lib/commonio.h b/lib/commonio.h new file mode 100644 index 0000000..e63c5b4 --- /dev/null +++ b/lib/commonio.h @@ -0,0 +1,144 @@ +/* + * SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 2000, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2001 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2007 - 2010, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* $Id$ */ +#ifndef COMMONIO_H +#define COMMONIO_H + +#include "defines.h" /* bool */ + +/* + * Linked list entry. + */ +struct commonio_entry { + /*@null@*/char *line; + /*@null@*/void *eptr; /* struct passwd, struct spwd, ... */ + /*@dependent@*/ /*@null@*/struct commonio_entry *prev; + /*@owned@*/ /*@null@*/struct commonio_entry *next; + bool changed:1; +}; + +/* + * Operations depending on database type: passwd, group, shadow etc. + */ +struct commonio_ops { + /* + * Make a copy of the object (for example, struct passwd) + * and all strings pointed by it, in malloced memory. + */ + /*@null@*/ /*@only@*/void *(*dup) (const void *); + + /* + * free() the object including any strings pointed by it. + */ + void (*free) (/*@out@*/ /*@only@*/void *); + + /* + * Return the name of the object (for example, pw_name + * for struct passwd). + */ + const char *(*getname) (const void *); + + /* + * Parse a string, return object (in static area - + * should be copied using the dup operation above). + */ + void *(*parse) (const char *); + + /* + * Write the object to the file (this calls putpwent() + * for struct passwd, for example). + */ + int (*put) (const void *, FILE *); + + /* + * fgets and fputs (can be replaced by versions that + * understand line continuation conventions). + */ + /*@null@*/char *(*fgets) (/*@returned@*/ /*@out@*/char *s, int n, FILE *stream); + int (*fputs) (const char *, FILE *); + + /* + * open_hook and close_hook. + * If non NULL, these functions will be called after the database + * is open or before it is closed. + * They return 0 on failure and 1 on success. + */ + /*@null@*/int (*open_hook) (void); + /*@null@*/int (*close_hook) (void); +}; + +/* + * Database structure. + */ +struct commonio_db { + /* + * Name of the data file. + */ + char filename[1024]; + + /* + * Operations from above. + */ + /*@observer@*/const struct commonio_ops *ops; + + /* + * Currently open file stream. + */ + /*@dependent@*/ /*@null@*/FILE *fp; + +#ifdef WITH_SELINUX + /*@null@*/char *scontext; +#endif + /* + * Default permissions and owner for newly created data file. + */ + mode_t st_mode; + uid_t st_uid; + gid_t st_gid; + /* + * Head, tail, current position in linked list. + */ + /*@owned@*/ /*@null@*/struct commonio_entry *head; + /*@dependent@*/ /*@null@*/struct commonio_entry *tail; + /*@dependent@*/ /*@null@*/struct commonio_entry *cursor; + + /* + * Various flags. + */ + bool changed:1; + bool isopen:1; + bool locked:1; + bool readonly:1; + bool setname:1; +}; + +extern int commonio_setname (struct commonio_db *, const char *); +extern bool commonio_present (const struct commonio_db *db); +extern int commonio_lock (struct commonio_db *); +extern int commonio_lock_nowait (struct commonio_db *, bool log); +extern int commonio_open (struct commonio_db *, int); +extern /*@observer@*/ /*@null@*/const void *commonio_locate (struct commonio_db *, const char *); +extern int commonio_update (struct commonio_db *, const void *); +#ifdef ENABLE_SUBIDS +extern int commonio_append (struct commonio_db *, const void *); +#endif /* ENABLE_SUBIDS */ +extern int commonio_remove (struct commonio_db *, const char *); +extern int commonio_rewind (struct commonio_db *); +extern /*@observer@*/ /*@null@*/const void *commonio_next (struct commonio_db *); +extern int commonio_close (struct commonio_db *); +extern int commonio_unlock (struct commonio_db *); +extern void commonio_del_entry (struct commonio_db *, + const struct commonio_entry *); +extern int commonio_sort_wrt (struct commonio_db *shadow, + const struct commonio_db *passwd); +extern int commonio_sort (struct commonio_db *db, + int (*cmp) (const void *, const void *)); + +#endif diff --git a/lib/defines.h b/lib/defines.h new file mode 100644 index 0000000..d01f691 --- /dev/null +++ b/lib/defines.h @@ -0,0 +1,339 @@ +/* $Id$ */ +/* some useful defines */ + +#ifndef _DEFINES_H_ +#define _DEFINES_H_ + +#include "config.h" + +#if HAVE_STDBOOL_H +# include <stdbool.h> +#else +# if ! HAVE__BOOL +# ifdef __cplusplus +typedef bool _Bool; +# else +typedef unsigned char _Bool; +# endif +# endif +# define bool _Bool +# define false (0) +# define true (1) +# define __bool_true_false_are_defined 1 +#endif + +/* Take care of NLS matters. */ +#ifdef S_SPLINT_S +extern char *setlocale(int categories, const char *locale); +# define LC_ALL (6) +extern char * bindtextdomain (const char * domainname, const char * dirname); +extern char * textdomain (const char * domainname); +# define _(Text) Text +# define ngettext(Msgid1, Msgid2, N) \ + ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) +#else +#ifdef HAVE_LOCALE_H +# include <locale.h> +#else +# undef setlocale +# define setlocale(category, locale) (NULL) +# ifndef LC_ALL +# define LC_ALL 6 +# endif +#endif + +#define gettext_noop(String) (String) +/* #define gettext_def(String) "#define String" */ + +#ifdef ENABLE_NLS +# include <libintl.h> +# define _(Text) gettext (Text) +#else +# undef bindtextdomain +# define bindtextdomain(Domain, Directory) (NULL) +# undef textdomain +# define textdomain(Domain) (NULL) +# define _(Text) Text +# define ngettext(Msgid1, Msgid2, N) \ + ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) +#endif +#endif + +#include <stdlib.h> +#include <string.h> + +#if HAVE_ERRNO_H +# include <errno.h> +#endif + +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> + +#if HAVE_UNISTD_H +# include <unistd.h> +#endif + +/* + * crypt(3), crypt_gensalt(3), and their + * feature test macros may be defined in here. + */ +#if HAVE_CRYPT_H +# include <crypt.h> +#endif + +#include <sys/time.h> +#include <time.h> + +#ifdef HAVE_MEMSET_S +# define memzero(ptr, size) memset_s((ptr), 0, (size)) +#elif defined HAVE_EXPLICIT_BZERO /* !HAVE_MEMSET_S */ +# define memzero(ptr, size) explicit_bzero((ptr), (size)) +#else /* !HAVE_MEMSET_S && HAVE_EXPLICIT_BZERO */ +static inline void memzero(void *ptr, size_t size) +{ + volatile unsigned char * volatile p = ptr; + while (size--) { + *p++ = '\0'; + } +} +#endif /* !HAVE_MEMSET_S && !HAVE_EXPLICIT_BZERO */ + +#define strzero(s) memzero(s, strlen(s)) /* warning: evaluates twice */ + +#include <dirent.h> + +/* + * Possible cases: + * - /usr/include/shadow.h exists and includes the shadow group stuff. + * - /usr/include/shadow.h exists, but we use our own gshadow.h. + */ +#include <shadow.h> +#if defined(SHADOWGRP) && !defined(GSHADOW) +#include "gshadow_.h" +#endif + +#include <limits.h> + +#ifndef NGROUPS_MAX +#ifdef NGROUPS +#define NGROUPS_MAX NGROUPS +#else +#define NGROUPS_MAX 64 +#endif +#endif + +#ifdef USE_SYSLOG +#include <syslog.h> + +#ifndef LOG_WARN +#define LOG_WARN LOG_WARNING +#endif + +/* LOG_NOWAIT is deprecated */ +#ifndef LOG_NOWAIT +#define LOG_NOWAIT 0 +#endif + +/* LOG_AUTH is deprecated, use LOG_AUTHPRIV instead */ +#ifndef LOG_AUTHPRIV +#define LOG_AUTHPRIV LOG_AUTH +#endif + +/* cleaner than lots of #ifdefs everywhere - use this as follows: + SYSLOG((LOG_CRIT, "user %s cracked root", user)); */ +#ifdef ENABLE_NLS +/* Temporarily set LC_TIME to "C" to avoid strange dates in syslog. + This is a workaround for a more general syslog(d) design problem - + syslogd should log the current system time for each event, and not + trust the formatted time received from the unix domain (or worse, + UDP) socket. -MM */ +/* Avoid translated PAM error messages: Set LC_ALL to "C". + * --Nekral */ +#define SYSLOG(x) \ + do { \ + char *old_locale = setlocale (LC_ALL, NULL); \ + char *saved_locale = NULL; \ + if (NULL != old_locale) { \ + saved_locale = strdup (old_locale); \ + } \ + if (NULL != saved_locale) { \ + (void) setlocale (LC_ALL, "C"); \ + } \ + syslog x ; \ + if (NULL != saved_locale) { \ + (void) setlocale (LC_ALL, saved_locale); \ + free (saved_locale); \ + } \ + } while (false) +#else /* !ENABLE_NLS */ +#define SYSLOG(x) syslog x +#endif /* !ENABLE_NLS */ + +#else /* !USE_SYSLOG */ + +#define SYSLOG(x) /* empty */ +#define openlog(a,b,c) /* empty */ +#define closelog() /* empty */ + +#endif /* !USE_SYSLOG */ + +/* The default syslog settings can now be changed here, + in just one place. */ + +#ifndef SYSLOG_OPTIONS +/* #define SYSLOG_OPTIONS (LOG_PID | LOG_CONS | LOG_NOWAIT) */ +#define SYSLOG_OPTIONS (LOG_PID) +#endif + +#ifndef SYSLOG_FACILITY +#define SYSLOG_FACILITY LOG_AUTHPRIV +#endif + +#define OPENLOG(progname) openlog(progname, SYSLOG_OPTIONS, SYSLOG_FACILITY) + +#ifndef F_OK +# define F_OK 0 +# define X_OK 1 +# define W_OK 2 +# define R_OK 4 +#endif + +#ifndef SEEK_SET +# define SEEK_SET 0 +# define SEEK_CUR 1 +# define SEEK_END 2 +#endif + +#if HAVE_TERMIOS_H +# include <termios.h> +# define STTY(fd, termio) tcsetattr(fd, TCSANOW, termio) +# define GTTY(fd, termio) tcgetattr(fd, termio) +# define TERMIO struct termios +# define USE_TERMIOS +#else /* assumed HAVE_TERMIO_H */ +# include <sys/ioctl.h> +# include <termio.h> +# define STTY(fd, termio) ioctl(fd, TCSETA, termio) +# define GTTY(fd, termio) ioctl(fd, TCGETA, termio) +# define TEMRIO struct termio +# define USE_TERMIO +#endif + +/* + * Password aging constants + * + * DAY - seconds / day + * WEEK - seconds / week + * SCALE - seconds / aging unit + */ + +/* Solaris defines this in shadow.h */ +#ifndef DAY +#define DAY (24L*3600L) +#endif + +#define WEEK (7*DAY) + +#ifdef ITI_AGING +#define SCALE 1 +#else +#define SCALE DAY +#endif + +/* Copy string pointed by B to array A with size checking. It was originally + in lmain.c but is _very_ useful elsewhere. Some setuid root programs with + very sloppy coding used to assume that BUFSIZ will always be enough... */ + + /* danger - side effects */ +#define STRFCPY(A,B) \ + (strncpy((A), (B), sizeof(A) - 1), (A)[sizeof(A) - 1] = '\0') + +#ifndef PASSWD_FILE +#define PASSWD_FILE "/etc/passwd" +#endif + +#ifndef GROUP_FILE +#define GROUP_FILE "/etc/group" +#endif + +#ifndef SHADOW_FILE +#define SHADOW_FILE "/etc/shadow" +#endif + +#ifdef SHADOWGRP +#ifndef SGROUP_FILE +#define SGROUP_FILE "/etc/gshadow" +#endif +#endif + +#ifndef NULL +#define NULL ((void *) 0) +#endif + +#ifdef sun /* hacks for compiling on SunOS */ +# ifndef SOLARIS +extern int fputs (); +extern char *strdup (); +extern char *strerror (); +# endif +#endif + +/* + * string to use for the pw_passwd field in /etc/passwd when using + * shadow passwords - most systems use "x" but there are a few + * exceptions, so it can be changed here if necessary. --marekm + */ +#ifndef SHADOW_PASSWD_STRING +#define SHADOW_PASSWD_STRING "x" +#endif + +#define SHADOW_SP_FLAG_UNSET ((unsigned long int)-1) + +#ifdef WITH_AUDIT +#ifdef __u8 /* in case we use pam < 0.80 */ +#undef __u8 +#endif +#ifdef __u32 +#undef __u32 +#endif + +#include <libaudit.h> +#endif + +/* To be used for verified unused parameters */ +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) +# define unused __attribute__((unused)) +# define format_attr(type, index, check) __attribute__((format (type, index, check))) +#else +# define unused +# define format_attr(type, index, check) +#endif + +/* Maximum length of usernames */ +#ifdef HAVE_UTMPX_H +# include <utmpx.h> +# define USER_NAME_MAX_LENGTH (sizeof (((struct utmpx *)NULL)->ut_user)) +#else +# include <utmp.h> +# ifdef HAVE_STRUCT_UTMP_UT_USER +# define USER_NAME_MAX_LENGTH (sizeof (((struct utmp *)NULL)->ut_user)) +# else +# ifdef HAVE_STRUCT_UTMP_UT_NAME +# define USER_NAME_MAX_LENGTH (sizeof (((struct utmp *)NULL)->ut_name)) +# else +# define USER_NAME_MAX_LENGTH 32 +# endif +# endif +#endif + +/* Maximum length of passwd entry */ +#define PASSWD_ENTRY_MAX_LENGTH 32768 + +#ifdef HAVE_SECURE_GETENV +# define shadow_getenv(name) secure_getenv(name) +# else +# define shadow_getenv(name) getenv(name) +#endif + +#endif /* _DEFINES_H_ */ diff --git a/lib/encrypt.c b/lib/encrypt.c new file mode 100644 index 0000000..c84a255 --- /dev/null +++ b/lib/encrypt.c @@ -0,0 +1,79 @@ +/* + * SPDX-FileCopyrightText: 1990 - 1993, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 2000, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2005 , Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2007 - 2010, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#include <unistd.h> +#include <stdio.h> + +#include "prototypes.h" +#include "defines.h" +#include "shadowlog_internal.h" + +/*@exposed@*//*@null@*/char *pw_encrypt (const char *clear, const char *salt) +{ + static char cipher[128]; + char *cp; + + cp = crypt (clear, salt); + if (NULL == cp) { + /* + * Single Unix Spec: crypt() may return a null pointer, + * and set errno to indicate an error. In this case return + * the NULL so the caller can handle appropriately. + */ + return NULL; + } + + /* Some crypt() do not return NULL if the algorithm is not + * supported, and return a DES encrypted password. */ + if ((NULL != salt) && (salt[0] == '$') && (strlen (cp) <= 13)) + { + /*@observer@*/const char *method; + switch (salt[1]) + { + case '1': + method = "MD5"; + break; + case '2': + method = "BCRYPT"; + break; + case '5': + method = "SHA256"; + break; + case '6': + method = "SHA512"; + break; + case 'y': + method = "YESCRYPT"; + break; + default: + { + static char nummethod[4] = "$x$"; + nummethod[1] = salt[1]; + method = &nummethod[0]; + } + } + (void) fprintf (shadow_logfd, + _("crypt method not supported by libcrypt? (%s)\n"), + method); + exit (EXIT_FAILURE); + } + + if (strlen (cp) != 13) { + return cp; /* nonstandard crypt() in libc, better bail out */ + } + + strcpy (cipher, cp); + + return cipher; +} + diff --git a/lib/exitcodes.h b/lib/exitcodes.h new file mode 100644 index 0000000..7dbe340 --- /dev/null +++ b/lib/exitcodes.h @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2005 - 2006, Tomasz KÅ‚oczko + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* $Id$ */ + +/* + * Exit codes used by shadow programs + */ +#define E_SUCCESS EXIT_SUCCESS /* success */ +/* + * FIXME: other values should differ from EXIT_FAILURE (and EXIT_SUCCESS). + * + * FIXME: reserve EXIT_FAILURE for internal failures. + */ +#define E_NOPERM 1 /* permission denied */ +#define E_USAGE 2 /* invalid command syntax */ +#define E_BAD_ARG 3 /* invalid argument to option */ +#define E_PASSWD_NOTFOUND 14 /* not found password file */ +#define E_SHADOW_NOTFOUND 15 /* not found shadow password file */ +#define E_GROUP_NOTFOUND 16 /* not found group file */ +#define E_GSHADOW_NOTFOUND 17 /* not found shadow group file */ +#define E_CMD_NOEXEC 126 /* can't run command/shell */ +#define E_CMD_NOTFOUND 127 /* can't find command/shell to run */ diff --git a/lib/faillog.h b/lib/faillog.h new file mode 100644 index 0000000..38476ea --- /dev/null +++ b/lib/faillog.h @@ -0,0 +1,34 @@ +/* + * SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1997, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2005 , Tomasz KÅ‚oczko + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * faillog.h - login failure logging file format + * + * $Id$ + * + * The login failure file is maintained by login(1) and faillog(8) + * Each record in the file represents a separate UID and the file + * is indexed in that fashion. + */ + +#ifndef _FAILLOG_H +#define _FAILLOG_H + +struct faillog { + short fail_cnt; /* failures since last success */ + short fail_max; /* failures before turning account off */ + char fail_line[12]; /* last failure occurred here */ + time_t fail_time; /* last failure occurred then */ + /* + * If nonzero, the account will be re-enabled if there are no + * failures for fail_locktime seconds since last failure. + */ + long fail_locktime; +}; + +#endif diff --git a/lib/fields.c b/lib/fields.c new file mode 100644 index 0000000..8a56035 --- /dev/null +++ b/lib/fields.c @@ -0,0 +1,107 @@ +/* + * SPDX-FileCopyrightText: 1990 , Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1997, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2007 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#include <ctype.h> +#include <string.h> +#include <stdio.h> +#include "prototypes.h" + +/* + * valid_field - insure that a field contains all legal characters + * + * The supplied field is scanned for non-printable and other illegal + * characters. + * + -1 is returned if an illegal character is present. + * + 1 is returned if no illegal characters are present, but the field + * contains a non-printable character. + * + 0 is returned otherwise. + */ +int valid_field (const char *field, const char *illegal) +{ + const char *cp; + int err = 0; + + if (NULL == field) { + return -1; + } + + /* For each character of field, search if it appears in the list + * of illegal characters. */ + for (cp = field; '\0' != *cp; cp++) { + if (strchr (illegal, *cp) != NULL) { + err = -1; + break; + } + } + + if (0 == err) { + /* Search if there are some non-printable characters */ + for (cp = field; '\0' != *cp; cp++) { + if (!isprint (*cp)) { + err = 1; + break; + } + } + } + + return err; +} + +/* + * change_field - change a single field if a new value is given. + * + * prompt the user with the name of the field being changed and the + * current value. + */ +void change_field (char *buf, size_t maxsize, const char *prompt) +{ + char newf[200]; + char *cp; + + if (maxsize > sizeof (newf)) { + maxsize = sizeof (newf); + } + + printf ("\t%s [%s]: ", prompt, buf); + (void) fflush (stdout); + if (fgets (newf, (int) maxsize, stdin) != newf) { + return; + } + + cp = strchr (newf, '\n'); + if (NULL == cp) { + return; + } + *cp = '\0'; + + if ('\0' != newf[0]) { + /* + * Remove leading and trailing whitespace. This also + * makes it possible to change the field to empty, by + * entering a space. --marekm + */ + + while (--cp >= newf && isspace (*cp)); + cp++; + *cp = '\0'; + + cp = newf; + while (('\0' != *cp) && isspace (*cp)) { + cp++; + } + + strncpy (buf, cp, maxsize - 1); + buf[maxsize - 1] = '\0'; + } +} + diff --git a/lib/fputsx.c b/lib/fputsx.c new file mode 100644 index 0000000..bb71ab2 --- /dev/null +++ b/lib/fputsx.c @@ -0,0 +1,67 @@ +/* + * SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1999, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2005 , Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2008 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#include <stdio.h> +#include "defines.h" +#include "prototypes.h" + +#ident "$Id$" + + +/*@null@*/char *fgetsx (/*@returned@*/ /*@out@*/char *buf, int cnt, FILE * f) +{ + char *cp = buf; + char *ep; + + while (cnt > 0) { + if (fgets (cp, cnt, f) != cp) { + if (cp == buf) { + return 0; + } else { + break; + } + } + ep = strrchr (cp, '\\'); + if ((NULL != ep) && (*(ep + 1) == '\n')) { + cnt -= ep - cp; + if (cnt > 0) { + cp = ep; + *cp = '\0'; + } + } else { + break; + } + } + return buf; +} + +int fputsx (const char *s, FILE * stream) +{ + int i; + + for (i = 0; '\0' != *s; i++, s++) { + if (putc (*s, stream) == EOF) { + return EOF; + } + +#if 0 /* The standard getgr*() can't handle that. --marekm */ + if (i > (BUFSIZ / 2)) { + if (putc ('\\', stream) == EOF || + putc ('\n', stream) == EOF) + return EOF; + + i = 0; + } +#endif + } + return 0; +} + diff --git a/lib/get_gid.c b/lib/get_gid.c new file mode 100644 index 0000000..cbcd6f4 --- /dev/null +++ b/lib/get_gid.c @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2009 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#include "prototypes.h" +#include "defines.h" + +int get_gid (const char *gidstr, gid_t *gid) +{ + long long int val; + char *endptr; + + errno = 0; + val = strtoll (gidstr, &endptr, 10); + if ( ('\0' == *gidstr) + || ('\0' != *endptr) + || (ERANGE == errno) + || (/*@+longintegral@*/val != (gid_t)val)/*@=longintegral@*/) { + return 0; + } + + *gid = (gid_t)val; + return 1; +} + diff --git a/lib/get_pid.c b/lib/get_pid.c new file mode 100644 index 0000000..383eb69 --- /dev/null +++ b/lib/get_pid.c @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2009 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#include "prototypes.h" +#include "defines.h" + +int get_pid (const char *pidstr, pid_t *pid) +{ + long long int val; + char *endptr; + + errno = 0; + val = strtoll (pidstr, &endptr, 10); + if ( ('\0' == *pidstr) + || ('\0' != *endptr) + || (ERANGE == errno) + || (/*@+longintegral@*/val != (pid_t)val)/*@=longintegral@*/) { + return 0; + } + + *pid = (pid_t)val; + return 1; +} + diff --git a/lib/get_uid.c b/lib/get_uid.c new file mode 100644 index 0000000..50f9922 --- /dev/null +++ b/lib/get_uid.c @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2009 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#include "prototypes.h" +#include "defines.h" + +int get_uid (const char *uidstr, uid_t *uid) +{ + long long int val; + char *endptr; + + errno = 0; + val = strtoll (uidstr, &endptr, 10); + if ( ('\0' == *uidstr) + || ('\0' != *endptr) + || (ERANGE == errno) + || (/*@+longintegral@*/val != (uid_t)val)/*@=longintegral@*/) { + return 0; + } + + *uid = (uid_t)val; + return 1; +} + diff --git a/lib/getdef.c b/lib/getdef.c new file mode 100644 index 0000000..dcd1fe7 --- /dev/null +++ b/lib/getdef.c @@ -0,0 +1,623 @@ +/* + * SPDX-FileCopyrightText: 1991 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 2000, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2002 - 2006, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2007 - 2008, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#include "prototypes.h" +#include "defines.h" +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> +#ifdef USE_ECONF +#include <libeconf.h> +#endif +#include "getdef.h" +#include "shadowlog_internal.h" +/* + * A configuration item definition. + */ +struct itemdef { + /*@null@*/const char *name; /* name of the item */ + /*@null@*/char *value; /* value given, or NULL if no value */ +}; + +#define PAMDEFS \ + {"CHFN_AUTH", NULL}, \ + {"CHSH_AUTH", NULL}, \ + {"CRACKLIB_DICTPATH", NULL}, \ + {"ENV_HZ", NULL}, \ + {"ENVIRON_FILE", NULL}, \ + {"ENV_TZ", NULL}, \ + {"FAILLOG_ENAB", NULL}, \ + {"FTMP_FILE", NULL}, \ + {"HMAC_CRYPTO_ALGO", NULL}, \ + {"ISSUE_FILE", NULL}, \ + {"LASTLOG_ENAB", NULL}, \ + {"LOGIN_STRING", NULL}, \ + {"MAIL_CHECK_ENAB", NULL}, \ + {"MOTD_FILE", NULL}, \ + {"NOLOGINS_FILE", NULL}, \ + {"OBSCURE_CHECKS_ENAB", NULL}, \ + {"PASS_ALWAYS_WARN", NULL}, \ + {"PASS_CHANGE_TRIES", NULL}, \ + {"PASS_MAX_LEN", NULL}, \ + {"PASS_MIN_LEN", NULL}, \ + {"PORTTIME_CHECKS_ENAB", NULL}, \ + {"QUOTAS_ENAB", NULL}, \ + {"SU_WHEEL_ONLY", NULL}, \ + {"ULIMIT", NULL}, + +/* + * Items used in other tools (util-linux, etc.) + */ +#define FOREIGNDEFS \ + {"ALWAYS_SET_PATH", NULL}, \ + {"ENV_ROOTPATH", NULL}, \ + {"LOGIN_KEEP_USERNAME", NULL}, \ + {"LOGIN_PLAIN_PROMPT", NULL}, \ + {"MOTD_FIRSTONLY", NULL}, \ + + +#define NUMDEFS (sizeof(def_table)/sizeof(def_table[0])) +static struct itemdef def_table[] = { + {"CHFN_RESTRICT", NULL}, + {"CONSOLE_GROUPS", NULL}, + {"CONSOLE", NULL}, + {"CREATE_HOME", NULL}, + {"DEFAULT_HOME", NULL}, + {"ENCRYPT_METHOD", NULL}, + {"ENV_PATH", NULL}, + {"ENV_SUPATH", NULL}, + {"ERASECHAR", NULL}, + {"FAIL_DELAY", NULL}, + {"FAKE_SHELL", NULL}, + {"GID_MAX", NULL}, + {"GID_MIN", NULL}, + {"HOME_MODE", NULL}, + {"HUSHLOGIN_FILE", NULL}, + {"KILLCHAR", NULL}, + {"LASTLOG_UID_MAX", NULL}, + {"LOGIN_RETRIES", NULL}, + {"LOGIN_TIMEOUT", NULL}, + {"LOG_OK_LOGINS", NULL}, + {"LOG_UNKFAIL_ENAB", NULL}, + {"MAIL_DIR", NULL}, + {"MAIL_FILE", NULL}, + {"MAX_MEMBERS_PER_GROUP", NULL}, + {"MD5_CRYPT_ENAB", NULL}, + {"NONEXISTENT", NULL}, + {"PASS_MAX_DAYS", NULL}, + {"PASS_MIN_DAYS", NULL}, + {"PASS_WARN_AGE", NULL}, +#ifdef USE_SHA_CRYPT + {"SHA_CRYPT_MAX_ROUNDS", NULL}, + {"SHA_CRYPT_MIN_ROUNDS", NULL}, +#endif +#ifdef USE_BCRYPT + {"BCRYPT_MAX_ROUNDS", NULL}, + {"BCRYPT_MIN_ROUNDS", NULL}, +#endif +#ifdef USE_YESCRYPT + {"YESCRYPT_COST_FACTOR", NULL}, +#endif + {"SUB_GID_COUNT", NULL}, + {"SUB_GID_MAX", NULL}, + {"SUB_GID_MIN", NULL}, + {"SUB_UID_COUNT", NULL}, + {"SUB_UID_MAX", NULL}, + {"SUB_UID_MIN", NULL}, + {"SULOG_FILE", NULL}, + {"SU_NAME", NULL}, + {"SYS_GID_MAX", NULL}, + {"SYS_GID_MIN", NULL}, + {"SYS_UID_MAX", NULL}, + {"SYS_UID_MIN", NULL}, + {"TTYGROUP", NULL}, + {"TTYPERM", NULL}, + {"TTYTYPE_FILE", NULL}, + {"UID_MAX", NULL}, + {"UID_MIN", NULL}, + {"UMASK", NULL}, + {"USERDEL_CMD", NULL}, + {"USERGROUPS_ENAB", NULL}, +#ifndef USE_PAM + PAMDEFS +#endif +#ifdef USE_SYSLOG + {"SYSLOG_SG_ENAB", NULL}, + {"SYSLOG_SU_ENAB", NULL}, +#endif +#ifdef WITH_TCB + {"TCB_AUTH_GROUP", NULL}, + {"TCB_SYMLINKS", NULL}, + {"USE_TCB", NULL}, +#endif + {"FORCE_SHADOW", NULL}, + {"GRANT_AUX_GROUP_SUBIDS", NULL}, + {"PREVENT_NO_AUTH", NULL}, + {NULL, NULL} +}; + +#define NUMKNOWNDEFS (sizeof(knowndef_table)/sizeof(knowndef_table[0])) +static struct itemdef knowndef_table[] = { +#ifdef USE_PAM + PAMDEFS +#endif + FOREIGNDEFS + {NULL, NULL} +}; + +#ifdef USE_ECONF +#ifdef VENDORDIR +static const char* vendordir = VENDORDIR; +#else +static const char* vendordir = NULL; +#endif +static const char* sysconfdir = "/etc"; +#else +#ifndef LOGINDEFS +#define LOGINDEFS "/etc/login.defs" +#endif + +static const char* def_fname = LOGINDEFS; /* login config defs file */ +#endif +static bool def_loaded = false; /* are defs already loaded? */ + +/* local function prototypes */ +static /*@observer@*/ /*@null@*/struct itemdef *def_find (const char *); +static void def_load (void); + + +/* + * getdef_str - get string value from table of definitions. + * + * Return point to static data for specified item, or NULL if item is not + * defined. First time invoked, will load definitions from the file. + */ + +/*@observer@*/ /*@null@*/const char *getdef_str (const char *item) +{ + struct itemdef *d; + + if (!def_loaded) { + def_load (); + } + + d = def_find (item); + return ((NULL == d)? (const char *) NULL : d->value); +} + + +/* + * getdef_bool - get boolean value from table of definitions. + * + * Return TRUE if specified item is defined as "yes", else FALSE. + */ + +bool getdef_bool (const char *item) +{ + struct itemdef *d; + + if (!def_loaded) { + def_load (); + } + + d = def_find (item); + if ((NULL == d) || (NULL == d->value)) { + return false; + } + + return (strcasecmp (d->value, "yes") == 0); +} + + +/* + * getdef_num - get numerical value from table of definitions + * + * Returns numeric value of specified item, else the "dflt" value if + * the item is not defined. Octal (leading "0") and hex (leading "0x") + * values are handled. + */ + +int getdef_num (const char *item, int dflt) +{ + struct itemdef *d; + long val; + + if (!def_loaded) { + def_load (); + } + + d = def_find (item); + if ((NULL == d) || (NULL == d->value)) { + return dflt; + } + + if ( (getlong (d->value, &val) == 0) + || (val > INT_MAX) + || (val < INT_MIN)) { + fprintf (shadow_logfd, + _("configuration error - cannot parse %s value: '%s'"), + item, d->value); + return dflt; + } + + return (int) val; +} + + +/* + * getdef_unum - get unsigned numerical value from table of definitions + * + * Returns numeric value of specified item, else the "dflt" value if + * the item is not defined. Octal (leading "0") and hex (leading "0x") + * values are handled. + */ + +unsigned int getdef_unum (const char *item, unsigned int dflt) +{ + struct itemdef *d; + long val; + + if (!def_loaded) { + def_load (); + } + + d = def_find (item); + if ((NULL == d) || (NULL == d->value)) { + return dflt; + } + + if ( (getlong (d->value, &val) == 0) + || (val < 0) + || (val > INT_MAX)) { + fprintf (shadow_logfd, + _("configuration error - cannot parse %s value: '%s'"), + item, d->value); + return dflt; + } + + return (unsigned int) val; +} + + +/* + * getdef_long - get long integer value from table of definitions + * + * Returns numeric value of specified item, else the "dflt" value if + * the item is not defined. Octal (leading "0") and hex (leading "0x") + * values are handled. + */ + +long getdef_long (const char *item, long dflt) +{ + struct itemdef *d; + long val; + + if (!def_loaded) { + def_load (); + } + + d = def_find (item); + if ((NULL == d) || (NULL == d->value)) { + return dflt; + } + + if (getlong (d->value, &val) == 0) { + fprintf (shadow_logfd, + _("configuration error - cannot parse %s value: '%s'"), + item, d->value); + return dflt; + } + + return val; +} + +/* + * getdef_ulong - get unsigned long numerical value from table of definitions + * + * Returns numeric value of specified item, else the "dflt" value if + * the item is not defined. Octal (leading "0") and hex (leading "0x") + * values are handled. + */ + +unsigned long getdef_ulong (const char *item, unsigned long dflt) +{ + struct itemdef *d; + unsigned long val; + + if (!def_loaded) { + def_load (); + } + + d = def_find (item); + if ((NULL == d) || (NULL == d->value)) { + return dflt; + } + + if (getulong (d->value, &val) == 0) { + fprintf (shadow_logfd, + _("configuration error - cannot parse %s value: '%s'"), + item, d->value); + return dflt; + } + + return val; +} + +/* + * putdef_str - override the value read from /etc/login.defs + * (also used when loading the initial defaults) + */ + +int putdef_str (const char *name, const char *value) +{ + struct itemdef *d; + char *cp; + + if (!def_loaded) { + def_load (); + } + + /* + * Locate the slot to save the value. If this parameter + * is unknown then "def_find" will print an err message. + */ + d = def_find (name); + if (NULL == d) { + return -1; + } + + /* + * Save off the value. + */ + cp = strdup (value); + if (NULL == cp) { + (void) fputs (_("Could not allocate space for config info.\n"), + shadow_logfd); + SYSLOG ((LOG_ERR, "could not allocate space for config info")); + return -1; + } + + free (d->value); + d->value = cp; + return 0; +} + + +/* + * def_find - locate named item in table + * + * Search through a table of configurable items to locate the + * specified configuration option. + */ + +static /*@observer@*/ /*@null@*/struct itemdef *def_find (const char *name) +{ + struct itemdef *ptr; + + /* + * Search into the table. + */ + + for (ptr = def_table; NULL != ptr->name; ptr++) { + if (strcmp (ptr->name, name) == 0) { + return ptr; + } + } + + /* + * Item was never found. + */ + + for (ptr = knowndef_table; NULL != ptr->name; ptr++) { + if (strcmp (ptr->name, name) == 0) { + goto out; + } + } + fprintf (shadow_logfd, + _("configuration error - unknown item '%s' (notify administrator)\n"), + name); + SYSLOG ((LOG_CRIT, "unknown configuration item `%s'", name)); + +out: + return (struct itemdef *) NULL; +} + +/* + * setdef_config_file - set the default configuration file path + * + * must be called prior to any def* calls. + */ + +void setdef_config_file (const char* file) +{ +#ifdef USE_ECONF + size_t len; + char* cp; + + len = strlen(file) + strlen(sysconfdir) + 2; + cp = malloc(len); + if (cp == NULL) + exit (13); + snprintf(cp, len, "%s/%s", file, sysconfdir); + sysconfdir = cp; +#ifdef VENDORDIR + len = strlen(file) + strlen(vendordir) + 2; + cp = malloc(len); + if (cp == NULL) + exit (13); + snprintf(cp, len, "%s/%s", file, vendordir); + vendordir = cp; +#endif +#else + def_fname = file; +#endif +} + +/* + * def_load - load configuration table + * + * Loads the user-configured options from the default configuration file + */ + +static void def_load (void) +{ +#ifdef USE_ECONF + econf_file *defs_file = NULL; + econf_err error; + char **keys; + size_t key_number; +#else + int i; + FILE *fp; + char buf[1024], *name, *value, *s; +#endif + + /* + * Set the initialized flag. + * (do it early to prevent recursion in putdef_str()) + */ + def_loaded = true; + +#ifdef USE_ECONF + + error = econf_readDirs (&defs_file, vendordir, sysconfdir, "login", "defs", " \t", "#"); + if (error) { + if (error == ECONF_NOFILE) + return; + + SYSLOG ((LOG_CRIT, "cannot open login definitions [%s]", + econf_errString(error))); + exit (EXIT_FAILURE); + } + + if ((error = econf_getKeys(defs_file, NULL, &key_number, &keys))) { + SYSLOG ((LOG_CRIT, "cannot read login definitions [%s]", + econf_errString(error))); + exit (EXIT_FAILURE); + } + + for (size_t i = 0; i < key_number; i++) { + char *value; + + econf_getStringValue(defs_file, NULL, keys[i], &value); + + /* + * Store the value in def_table. + * + * Ignore failures to load the login.defs file. + * The error was already reported to the user and to + * syslog. The tools will just use their default values. + */ + (void)putdef_str (keys[i], value); + } + + econf_free (keys); + econf_free (defs_file); +#else + /* + * Open the configuration definitions file. + */ + fp = fopen (def_fname, "r"); + if (NULL == fp) { + if (errno == ENOENT) + return; + + int err = errno; + SYSLOG ((LOG_CRIT, "cannot open login definitions %s [%s]", + def_fname, strerror (err))); + exit (EXIT_FAILURE); + } + + /* + * Go through all of the lines in the file. + */ + while (fgets (buf, (int) sizeof (buf), fp) != NULL) { + + /* + * Trim trailing whitespace. + */ + for (i = (int) strlen (buf) - 1; i >= 0; --i) { + if (!isspace (buf[i])) { + break; + } + } + i++; + buf[i] = '\0'; + + /* + * Break the line into two fields. + */ + name = buf + strspn (buf, " \t"); /* first nonwhite */ + if (*name == '\0' || *name == '#') + continue; /* comment or empty */ + + s = name + strcspn (name, " \t"); /* end of field */ + if (*s == '\0') + continue; /* only 1 field?? */ + + *s++ = '\0'; + value = s + strspn (s, " \"\t"); /* next nonwhite */ + *(value + strcspn (value, "\"")) = '\0'; + + /* + * Store the value in def_table. + * + * Ignore failures to load the login.defs file. + * The error was already reported to the user and to + * syslog. The tools will just use their default values. + */ + (void)putdef_str (name, value); + } + + if (ferror (fp) != 0) { + int err = errno; + SYSLOG ((LOG_CRIT, "cannot read login definitions %s [%s]", + def_fname, strerror (err))); + exit (EXIT_FAILURE); + } + + (void) fclose (fp); +#endif +} + + +#ifdef CKDEFS +int main (int argc, char **argv) +{ + int i; + char *cp; + struct itemdef *d; + + def_load (); + + for (i = 0; i < NUMDEFS; ++i) { + d = def_find (def_table[i].name); + if (NULL == d) { + printf ("error - lookup '%s' failed\n", + def_table[i].name); + } else { + printf ("%4d %-24s %s\n", i + 1, d->name, d->value); + } + } + for (i = 1; i < argc; i++) { + cp = getdef_str (argv[1]); + if (NULL != cp) { + printf ("%s `%s'\n", argv[1], cp); + } else { + printf ("%s not found\n", argv[1]); + } + } + exit (EXIT_SUCCESS); +} +#endif diff --git a/lib/getdef.h b/lib/getdef.h new file mode 100644 index 0000000..2bd3fc5 --- /dev/null +++ b/lib/getdef.h @@ -0,0 +1,25 @@ +/* + * SPDX-FileCopyrightText: 1991 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 2000, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2002 - 2006, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2008 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef _GETDEF_H +#define _GETDEF_H + +/* getdef.c */ +extern bool getdef_bool (const char *); +extern long getdef_long (const char *, long); +extern int getdef_num (const char *, int); +extern unsigned long getdef_ulong (const char *, unsigned long); +extern unsigned int getdef_unum (const char *, unsigned int); +extern /*@observer@*/ /*@null@*/const char *getdef_str (const char *); +extern int putdef_str (const char *, const char *); +extern void setdef_config_file (const char* file); + +/* default UMASK value if not specified in /etc/login.defs */ +#define GETDEF_DEFAULT_UMASK 022 + +#endif /* _GETDEF_H */ diff --git a/lib/getlong.c b/lib/getlong.c new file mode 100644 index 0000000..ec4aa54 --- /dev/null +++ b/lib/getlong.c @@ -0,0 +1,36 @@ +/* + * SPDX-FileCopyrightText: 2007 - 2009, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#include <stdlib.h> +#include <errno.h> +#include "prototypes.h" + +/* + * getlong - extract a long integer provided by the numstr string in *result + * + * It supports decimal, hexadecimal or octal representations. + * + * Returns 0 on failure, 1 on success. + */ +int getlong (const char *numstr, /*@out@*/long int *result) +{ + long val; + char *endptr; + + errno = 0; + val = strtol (numstr, &endptr, 0); + if (('\0' == *numstr) || ('\0' != *endptr) || (ERANGE == errno)) { + return 0; + } + + *result = val; + return 1; +} + diff --git a/lib/getulong.c b/lib/getulong.c new file mode 100644 index 0000000..33250e3 --- /dev/null +++ b/lib/getulong.c @@ -0,0 +1,39 @@ +/* + * SPDX-FileCopyrightText: 2007 - 2009, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id: getlong.c 2763 2009-04-23 09:57:03Z nekral-guest $" + +#include <stdlib.h> +#include <errno.h> +#include "prototypes.h" + +/* + * getulong - extract an unsigned long integer provided by the numstr string in *result + * + * It supports decimal, hexadecimal or octal representations. + * + * Returns 0 on failure, 1 on success. + */ +int getulong (const char *numstr, /*@out@*/unsigned long int *result) +{ + unsigned long int val; + char *endptr; + + errno = 0; + val = strtoul (numstr, &endptr, 0); + if ( ('\0' == *numstr) + || ('\0' != *endptr) + || (ERANGE == errno) + ) { + return 0; + } + + *result = val; + return 1; +} + diff --git a/lib/groupio.c b/lib/groupio.c new file mode 100644 index 0000000..357a30e --- /dev/null +++ b/lib/groupio.c @@ -0,0 +1,438 @@ +/* + * SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 2000, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2001 , MichaÅ‚ Moskal + * SPDX-FileCopyrightText: 2005 , Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2007 - 2010, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#include <assert.h> +#include <stdio.h> + +#include "prototypes.h" +#include "defines.h" +#include "commonio.h" +#include "getdef.h" +#include "groupio.h" + +static /*@null@*/struct commonio_entry *merge_group_entries ( + /*@null@*/ /*@returned@*/struct commonio_entry *gr1, + /*@null@*/struct commonio_entry *gr2); +static int split_groups (unsigned int max_members); +static int group_open_hook (void); + +static /*@null@*/ /*@only@*/void *group_dup (const void *ent) +{ + const struct group *gr = ent; + + return __gr_dup (gr); +} + +static void group_free (/*@out@*/ /*@only@*/void *ent) +{ + struct group *gr = ent; + + gr_free (gr); +} + +static const char *group_getname (const void *ent) +{ + const struct group *gr = ent; + + return gr->gr_name; +} + +static void *group_parse (const char *line) +{ + return (void *) sgetgrent (line); +} + +static int group_put (const void *ent, FILE * file) +{ + const struct group *gr = ent; + + if ( (NULL == gr) + || (valid_field (gr->gr_name, ":\n") == -1) + || (valid_field (gr->gr_passwd, ":\n") == -1) + || (gr->gr_gid == (gid_t)-1)) { + return -1; + } + + /* FIXME: fail also if gr->gr_mem == NULL ?*/ + if (NULL != gr->gr_mem) { + size_t i; + for (i = 0; NULL != gr->gr_mem[i]; i++) { + if (valid_field (gr->gr_mem[i], ",:\n") == -1) { + return -1; + } + } + } + + return (putgrent (gr, file) == -1) ? -1 : 0; +} + +static int group_close_hook (void) +{ + unsigned int max_members = getdef_unum("MAX_MEMBERS_PER_GROUP", 0); + + if (0 == max_members) { + return 1; + } + + return split_groups (max_members); +} + +static struct commonio_ops group_ops = { + group_dup, + group_free, + group_getname, + group_parse, + group_put, + fgetsx, + fputsx, + group_open_hook, + group_close_hook +}; + +static /*@owned@*/struct commonio_db group_db = { + GROUP_FILE, /* filename */ + &group_ops, /* ops */ + NULL, /* fp */ +#ifdef WITH_SELINUX + NULL, /* scontext */ +#endif + 0644, /* st_mode */ + 0, /* st_uid */ + 0, /* st_gid */ + NULL, /* head */ + NULL, /* tail */ + NULL, /* cursor */ + false, /* changed */ + false, /* isopen */ + false, /* locked */ + false, /* readonly */ + false /* setname */ +}; + +int gr_setdbname (const char *filename) +{ + return commonio_setname (&group_db, filename); +} + +/*@observer@*/const char *gr_dbname (void) +{ + return group_db.filename; +} + +int gr_lock (void) +{ + return commonio_lock (&group_db); +} + +int gr_open (int mode) +{ + return commonio_open (&group_db, mode); +} + +/*@observer@*/ /*@null@*/const struct group *gr_locate (const char *name) +{ + return commonio_locate (&group_db, name); +} + +/*@observer@*/ /*@null@*/const struct group *gr_locate_gid (gid_t gid) +{ + const struct group *grp; + + gr_rewind (); + while ( ((grp = gr_next ()) != NULL) + && (grp->gr_gid != gid)) { + } + + return grp; +} + +int gr_update (const struct group *gr) +{ + return commonio_update (&group_db, (const void *) gr); +} + +int gr_remove (const char *name) +{ + return commonio_remove (&group_db, name); +} + +int gr_rewind (void) +{ + return commonio_rewind (&group_db); +} + +/*@observer@*/ /*@null@*/const struct group *gr_next (void) +{ + return commonio_next (&group_db); +} + +int gr_close (void) +{ + return commonio_close (&group_db); +} + +int gr_unlock (void) +{ + return commonio_unlock (&group_db); +} + +void __gr_set_changed (void) +{ + group_db.changed = true; +} + +/*@dependent@*/ /*@null@*/struct commonio_entry *__gr_get_head (void) +{ + return group_db.head; +} + +/*@observer@*/const struct commonio_db *__gr_get_db (void) +{ + return &group_db; +} + +void __gr_del_entry (const struct commonio_entry *ent) +{ + commonio_del_entry (&group_db, ent); +} + +static int gr_cmp (const void *p1, const void *p2) +{ + gid_t u1, u2; + + if ((*(struct commonio_entry **) p1)->eptr == NULL) { + return 1; + } + if ((*(struct commonio_entry **) p2)->eptr == NULL) { + return -1; + } + + u1 = ((struct group *) (*(struct commonio_entry **) p1)->eptr)->gr_gid; + u2 = ((struct group *) (*(struct commonio_entry **) p2)->eptr)->gr_gid; + + if (u1 < u2) { + return -1; + } else if (u1 > u2) { + return 1; + } else { + return 0; + } +} + +/* Sort entries by GID */ +int gr_sort () +{ + return commonio_sort (&group_db, gr_cmp); +} + +static int group_open_hook (void) +{ + unsigned int max_members = getdef_unum("MAX_MEMBERS_PER_GROUP", 0); + struct commonio_entry *gr1, *gr2; + + if (0 == max_members) { + return 1; + } + + for (gr1 = group_db.head; NULL != gr1; gr1 = gr1->next) { + for (gr2 = gr1->next; NULL != gr2; gr2 = gr2->next) { + struct group *g1 = (struct group *)gr1->eptr; + struct group *g2 = (struct group *)gr2->eptr; + if (NULL != g1 && + NULL != g2 && + 0 == strcmp (g1->gr_name, g2->gr_name) && + 0 == strcmp (g1->gr_passwd, g2->gr_passwd) && + g1->gr_gid == g2->gr_gid) { + /* Both group entries refer to the same + * group. It is a split group. Merge the + * members. */ + gr1 = merge_group_entries (gr1, gr2); + if (NULL == gr1) + return 0; + /* Unlink gr2 */ + if (NULL != gr2->next) { + gr2->next->prev = gr2->prev; + } + /* gr2 does not start with head */ + assert (NULL != gr2->prev); + gr2->prev->next = gr2->next; + } + } + assert (NULL != gr1); + } + + return 1; +} + +/* + * Merge the list of members of the two group entries. + * + * The commonio_entry arguments shall be group entries. + * + * You should not merge the members of two groups if they don't have the + * same name, password and gid. + * + * It merge the members of the second entry in the first one, and return + * the modified first entry on success, or NULL on failure (with errno + * set). + */ +static /*@null@*/struct commonio_entry *merge_group_entries ( + /*@null@*/ /*@returned@*/struct commonio_entry *gr1, + /*@null@*/struct commonio_entry *gr2) +{ + struct group *gptr1; + struct group *gptr2; + char **new_members; + size_t members = 0; + char *new_line; + size_t new_line_len, i; + if (NULL == gr2 || NULL == gr1) { + errno = EINVAL; + return NULL; + } + + gptr1 = (struct group *)gr1->eptr; + gptr2 = (struct group *)gr2->eptr; + if (NULL == gptr2 || NULL == gptr1) { + errno = EINVAL; + return NULL; + } + + /* Concatenate the 2 lines */ + new_line_len = strlen (gr1->line) + strlen (gr2->line) +1; + new_line = (char *)malloc (new_line_len + 1); + if (NULL == new_line) { + errno = ENOMEM; + return NULL; + } + snprintf(new_line, new_line_len + 1, "%s\n%s", gr1->line, gr2->line); + + /* Concatenate the 2 list of members */ + for (i=0; NULL != gptr1->gr_mem[i]; i++); + members += i; + for (i=0; NULL != gptr2->gr_mem[i]; i++) { + char **pmember = gptr1->gr_mem; + while (NULL != *pmember) { + if (0 == strcmp(*pmember, gptr2->gr_mem[i])) { + break; + } + pmember++; + } + if (NULL == *pmember) { + members++; + } + } + new_members = (char **)calloc ( (members+1), sizeof(char*) ); + if (NULL == new_members) { + free (new_line); + errno = ENOMEM; + return NULL; + } + for (i=0; NULL != gptr1->gr_mem[i]; i++) { + new_members[i] = gptr1->gr_mem[i]; + } + /* NULL termination enforced by above calloc */ + + members = i; + for (i=0; NULL != gptr2->gr_mem[i]; i++) { + char **pmember = new_members; + while (NULL != *pmember) { + if (0 == strcmp(*pmember, gptr2->gr_mem[i])) { + break; + } + pmember++; + } + if (NULL == *pmember) { + new_members[members] = gptr2->gr_mem[i]; + members++; + new_members[members] = NULL; + } + } + + gr1->line = new_line; + gptr1->gr_mem = new_members; + + return gr1; +} + +/* + * Scan the group database and split the groups which have more members + * than specified, if this is the result from a current change. + * + * Return 0 on failure (errno set) and 1 on success. + */ +static int split_groups (unsigned int max_members) +{ + struct commonio_entry *gr; + + for (gr = group_db.head; NULL != gr; gr = gr->next) { + struct group *gptr = (struct group *)gr->eptr; + struct commonio_entry *new; + struct group *new_gptr; + unsigned int members = 0; + unsigned int i; + + /* Check if this group must be split */ + if (!gr->changed) { + continue; + } + if (NULL == gptr) { + continue; + } + for (members = 0; NULL != gptr->gr_mem[members]; members++); + if (members <= max_members) { + continue; + } + + new = (struct commonio_entry *) malloc (sizeof *new); + if (NULL == new) { + errno = ENOMEM; + return 0; + } + new->eptr = group_dup(gr->eptr); + if (NULL == new->eptr) { + free (new); + errno = ENOMEM; + return 0; + } + new_gptr = (struct group *)new->eptr; + new->line = NULL; + new->changed = true; + + /* Enforce the maximum number of members on gptr */ + for (i = max_members; NULL != gptr->gr_mem[i]; i++) { + free (gptr->gr_mem[i]); + gptr->gr_mem[i] = NULL; + } + /* Shift all the members */ + /* The number of members in new_gptr will be check later */ + for (i = 0; NULL != new_gptr->gr_mem[i + max_members]; i++) { + free (new_gptr->gr_mem[i]); + new_gptr->gr_mem[i] = new_gptr->gr_mem[i + max_members]; + new_gptr->gr_mem[i + max_members] = NULL; + } + for (; NULL != new_gptr->gr_mem[i]; i++) { + free (new_gptr->gr_mem[i]); + new_gptr->gr_mem[i] = NULL; + } + + /* insert the new entry in the list */ + new->prev = gr; + new->next = gr->next; + gr->next = new; + } + + return 1; +} + diff --git a/lib/groupio.h b/lib/groupio.h new file mode 100644 index 0000000..2014de0 --- /dev/null +++ b/lib/groupio.h @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 2000, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2001 , MichaÅ‚ Moskal + * SPDX-FileCopyrightText: 2005 , Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2008 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* $Id$ */ +#ifndef _GROUPIO_H +#define _GROUPIO_H + +#include <sys/types.h> +#include <grp.h> + +extern int gr_close (void); +extern /*@observer@*/ /*@null@*/const struct group *gr_locate (const char *name); +extern /*@observer@*/ /*@null@*/const struct group *gr_locate_gid (gid_t gid); +extern int gr_lock (void); +extern int gr_setdbname (const char *filename); +extern /*@observer@*/const char *gr_dbname (void); +extern /*@observer@*/ /*@null@*/const struct group *gr_next (void); +extern int gr_open (int mode); +extern int gr_remove (const char *name); +extern int gr_rewind (void); +extern int gr_unlock (void); +extern int gr_update (const struct group *gr); +extern int gr_sort (void); + +#endif diff --git a/lib/groupmem.c b/lib/groupmem.c new file mode 100644 index 0000000..c858b72 --- /dev/null +++ b/lib/groupmem.c @@ -0,0 +1,118 @@ +/* + * SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 2000, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2001 , MichaÅ‚ Moskal + * SPDX-FileCopyrightText: 2005 , Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2007 - 2013, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#include "prototypes.h" +#include "defines.h" +#include "groupio.h" + +/*@null@*/ /*@only@*/struct group *__gr_dup (const struct group *grent) +{ + struct group *gr; + int i; + + gr = (struct group *) malloc (sizeof *gr); + if (NULL == gr) { + return NULL; + } + /* The libc might define other fields. They won't be copied. */ + memset (gr, 0, sizeof *gr); + gr->gr_gid = grent->gr_gid; + /*@-mustfreeonly@*/ + gr->gr_name = strdup (grent->gr_name); + /*@=mustfreeonly@*/ + if (NULL == gr->gr_name) { + gr_free(gr); + return NULL; + } + /*@-mustfreeonly@*/ + gr->gr_passwd = strdup (grent->gr_passwd); + /*@=mustfreeonly@*/ + if (NULL == gr->gr_passwd) { + gr_free(gr); + return NULL; + } + + for (i = 0; grent->gr_mem[i]; i++); + + /*@-mustfreeonly@*/ + gr->gr_mem = (char **) malloc ((i + 1) * sizeof (char *)); + /*@=mustfreeonly@*/ + if (NULL == gr->gr_mem) { + gr_free(gr); + return NULL; + } + for (i = 0; grent->gr_mem[i]; i++) { + gr->gr_mem[i] = strdup (grent->gr_mem[i]); + if (NULL == gr->gr_mem[i]) { + gr_free(gr); + return NULL; + } + } + gr->gr_mem[i] = NULL; + + return gr; +} + +void gr_free_members (struct group *grent) +{ + if (NULL != grent->gr_mem) { + size_t i; + for (i = 0; NULL != grent->gr_mem[i]; i++) { + free (grent->gr_mem[i]); + } + free (grent->gr_mem); + grent->gr_mem = NULL; + } +} + +void gr_free (/*@out@*/ /*@only@*/struct group *grent) +{ + free (grent->gr_name); + if (NULL != grent->gr_passwd) { + strzero (grent->gr_passwd); + free (grent->gr_passwd); + } + gr_free_members(grent); + free (grent); +} + +bool gr_append_member(struct group *grp, char *member) +{ + int i; + + if (NULL == grp->gr_mem || grp->gr_mem[0] == NULL) { + grp->gr_mem = (char **)malloc(2 * sizeof(char *)); + if (!grp->gr_mem) { + return false; + } + grp->gr_mem[0] = strdup(member); + if (!grp->gr_mem[0]) { + return false; + } + grp->gr_mem[1] = NULL; + return true; + } + + for (i = 0; grp->gr_mem[i]; i++) ; + grp->gr_mem = realloc(grp->gr_mem, (i + 2) * sizeof(char *)); + if (NULL == grp->gr_mem) { + return false; + } + grp->gr_mem[i] = strdup(member); + if (NULL == grp->gr_mem[i]) { + return false; + } + grp->gr_mem[i + 1] = NULL; + return true; +} diff --git a/lib/gshadow.c b/lib/gshadow.c new file mode 100644 index 0000000..2e12923 --- /dev/null +++ b/lib/gshadow.c @@ -0,0 +1,506 @@ +/* + * SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1998, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2005 , Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2008 - 2009, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +/* Newer versions of Linux libc already have shadow support. */ +#if defined(SHADOWGRP) && !defined(HAVE_SHADOWGRP) /*{ */ + +#ident "$Id$" + +#include <stdio.h> +#include "prototypes.h" +#include "defines.h" +static /*@null@*/FILE *shadow; +static /*@null@*//*@only@*/char **members = NULL; +static size_t nmembers = 0; +static /*@null@*//*@only@*/char **admins = NULL; +static size_t nadmins = 0; +static struct sgrp sgroup; + +#define FIELDS 4 + +#ifdef USE_NIS +static bool nis_used; +static bool nis_ignore; +static enum { native, start, middle, native2 } nis_state; +static bool nis_bound; +static char *nis_domain; +static char *nis_key; +static int nis_keylen; +static char *nis_val; +static int nis_vallen; + +#define IS_NISCHAR(c) ((c)=='+') +#endif + +#ifdef USE_NIS +/* + * bind_nis - bind to NIS server + */ + +static int bind_nis (void) +{ + if (yp_get_default_domain (&nis_domain)) + return -1; + + nis_bound = true; + return 0; +} +#endif + +static /*@null@*/char **build_list (char *s, char **list[], size_t * nlist) +{ + char **ptr = *list; + size_t nelem = *nlist, size; + + while (s != NULL && *s != '\0') { + size = (nelem + 1) * sizeof (ptr); + ptr = realloc (*list, size); + if (NULL != ptr) { + ptr[nelem] = s; + nelem++; + *list = ptr; + *nlist = nelem; + s = strchr (s, ','); + if (NULL != s) { + *s = '\0'; + s++; + } + } + } + size = (nelem + 1) * sizeof (ptr); + ptr = realloc (*list, size); + if (NULL != ptr) { + ptr[nelem] = NULL; + *list = ptr; + } + return ptr; +} + +void setsgent (void) +{ +#ifdef USE_NIS + nis_state = native; +#endif + if (NULL != shadow) { + rewind (shadow); + } else { + shadow = fopen (SGROUP_FILE, "r"); + } +} + +void endsgent (void) +{ + if (NULL != shadow) { + (void) fclose (shadow); + } + + shadow = (FILE *) 0; +} + +/*@observer@*//*@null@*/struct sgrp *sgetsgent (const char *string) +{ + static char *sgrbuf = NULL; + static size_t sgrbuflen = 0; + + char *fields[FIELDS]; + char *cp; + int i; + size_t len = strlen (string) + 1; + + if (len > sgrbuflen) { + char *buf = (char *) realloc (sgrbuf, sizeof (char) * len); + if (NULL == buf) { + return NULL; + } + sgrbuf = buf; + sgrbuflen = len; + } + + strncpy (sgrbuf, string, len); + sgrbuf[len-1] = '\0'; + + cp = strrchr (sgrbuf, '\n'); + if (NULL != cp) { + *cp = '\0'; + } + + /* + * There should be exactly 4 colon separated fields. Find + * all 4 of them and save the starting addresses in fields[]. + */ + + for (cp = sgrbuf, i = 0; (i < FIELDS) && (NULL != cp); i++) { + fields[i] = cp; + cp = strchr (cp, ':'); + if (NULL != cp) { + *cp++ = '\0'; + } + } + + /* + * If there was an extra field somehow, or perhaps not enough, + * the line is invalid. + */ + + if ((NULL != cp) || (i != FIELDS)) { +#ifdef USE_NIS + if (!IS_NISCHAR (fields[0][0])) { + return 0; + } else { + nis_used = true; + } +#else + return 0; +#endif + } + + sgroup.sg_name = fields[0]; + sgroup.sg_passwd = fields[1]; + if (0 != nadmins) { + nadmins = 0; + free (admins); + admins = NULL; + } + if (0 != nmembers) { + nmembers = 0; + free (members); + members = NULL; + } + sgroup.sg_adm = build_list (fields[2], &admins, &nadmins); + sgroup.sg_mem = build_list (fields[3], &members, &nmembers); + + return &sgroup; +} + +/* + * fgetsgent - convert next line in stream to (struct sgrp) + * + * fgetsgent() reads the next line from the provided stream and + * converts it to a (struct sgrp). NULL is returned on EOF. + */ + +/*@observer@*//*@null@*/struct sgrp *fgetsgent (/*@null@*/FILE * fp) +{ + static size_t buflen = 0; + static char *buf = NULL; + + char *cp; + + if (0 == buflen) { + buf = (char *) malloc (BUFSIZ); + if (NULL == buf) { + return NULL; + } + buflen = BUFSIZ; + } + + if (NULL == fp) { + return NULL; + } + +#ifdef USE_NIS + while (fgetsx (buf, (int) buflen, fp) == buf) +#else + if (fgetsx (buf, (int) buflen, fp) == buf) +#endif + { + while ( ((cp = strrchr (buf, '\n')) == NULL) + && (feof (fp) == 0)) { + size_t len; + + cp = (char *) realloc (buf, buflen*2); + if (NULL == cp) { + return NULL; + } + buf = cp; + buflen *= 2; + + len = strlen (buf); + if (fgetsx (&buf[len], + (int) (buflen - len), + fp) != &buf[len]) { + return NULL; + } + } + cp = strrchr (buf, '\n'); + if (NULL != cp) { + *cp = '\0'; + } +#ifdef USE_NIS + if (nis_ignore && IS_NISCHAR (buf[0])) { + continue; + } +#endif + return (sgetsgent (buf)); + } + return NULL; +} + +/* + * getsgent - get a single shadow group entry + */ + +/*@observer@*//*@null@*/struct sgrp *getsgent (void) +{ +#ifdef USE_NIS + bool nis_1_group = false; + struct sgrp *val; +#endif + if (NULL == shadow) { + setsgent (); + } + +#ifdef USE_NIS + again: + /* + * See if we are reading from the local file. + */ + + if (nis_state == native || nis_state == native2) { + + /* + * Get the next entry from the shadow group file. Return + * NULL right away if there is none. + */ + + val = fgetsgent (shadow); + if (NULL == val) { + return 0; + } + + /* + * If this entry began with a NIS escape character, we have + * to see if this is just a single group, or if the entire + * map is being asked for. + */ + + if (IS_NISCHAR (val->sg_name[0])) { + if ('\0' != val->sg_name[1]) { + nis_1_group = true; + } else { + nis_state = start; + } + } + + /* + * If this isn't a NIS group and this isn't an escape to go + * use a NIS map, it must be a regular local group. + */ + + if (!nis_1_group && (nis_state != start)) { + return val; + } + + /* + * If this is an escape to use an NIS map, switch over to + * that bunch of code. + */ + + if (nis_state == start) { + goto again; + } + + /* + * NEEDSWORK. Here we substitute pieces-parts of this entry. + */ + + return 0; + } else { + if (!nis_bound) { + if (bind_nis ()) { + nis_state = native2; + goto again; + } + } + if (nis_state == start) { + if (yp_first (nis_domain, "gshadow.byname", &nis_key, + &nis_keylen, &nis_val, &nis_vallen)) { + nis_state = native2; + goto again; + } + nis_state = middle; + } else if (nis_state == middle) { + if (yp_next (nis_domain, "gshadow.byname", nis_key, + nis_keylen, &nis_key, &nis_keylen, + &nis_val, &nis_vallen)) { + nis_state = native2; + goto again; + } + } + return sgetsgent (nis_val); + } +#else + return (fgetsgent (shadow)); +#endif +} + +/* + * getsgnam - get a shadow group entry by name + */ + +/*@observer@*//*@null@*/struct sgrp *getsgnam (const char *name) +{ + struct sgrp *sgrp; + +#ifdef USE_NIS + static char save_name[16]; + int nis_disabled = 0; +#endif + + setsgent (); + +#ifdef USE_NIS + if (nis_used) { + again: + + /* + * Search the gshadow.byname map for this group. + */ + + if (!nis_bound) { + bind_nis (); + } + + if (nis_bound) { + char *cp; + + if (yp_match (nis_domain, "gshadow.byname", name, + strlen (name), &nis_val, + &nis_vallen) == 0) { + cp = strchr (nis_val, '\n'); + if (NULL != cp) { + *cp = '\0'; + } + + nis_state = middle; + sgrp = sgetsgent (nis_val); + if (NULL != sgrp) { + strcpy (save_name, sgrp->sg_name); + nis_key = save_name; + nis_keylen = strlen (save_name); + } + return sgrp; + } + } + nis_state = native2; + } +#endif +#ifdef USE_NIS + if (nis_used) { + nis_ignore = true; + nis_disabled = true; + } +#endif + while ((sgrp = getsgent ()) != (struct sgrp *) 0) { + if (strcmp (name, sgrp->sg_name) == 0) { + break; + } + } +#ifdef USE_NIS + nis_ignore = false; +#endif + return sgrp; +} + +/* + * putsgent - output shadow group entry in text form + * + * putsgent() converts the contents of a (struct sgrp) to text and + * writes the result to the given stream. This is the logical + * opposite of fgetsgent. + */ + +int putsgent (const struct sgrp *sgrp, FILE * fp) +{ + char *buf, *cp; + int i; + size_t size; + + if ((NULL == fp) || (NULL == sgrp)) { + return -1; + } + + /* calculate the required buffer size */ + size = strlen (sgrp->sg_name) + strlen (sgrp->sg_passwd) + 10; + for (i = 0; (NULL != sgrp->sg_adm) && (NULL != sgrp->sg_adm[i]); i++) { + size += strlen (sgrp->sg_adm[i]) + 1; + } + for (i = 0; (NULL != sgrp->sg_mem) && (NULL != sgrp->sg_mem[i]); i++) { + size += strlen (sgrp->sg_mem[i]) + 1; + } + + buf = malloc (size); + if (NULL == buf) { + return -1; + } + cp = buf; + + /* + * Copy the group name and passwd. + */ + + strcpy (cp, sgrp->sg_name); + cp += strlen (cp); + *cp++ = ':'; + + strcpy (cp, sgrp->sg_passwd); + cp += strlen (cp); + *cp++ = ':'; + + /* + * Copy the administrators, separating each from the other + * with a ",". + */ + + for (i = 0; NULL != sgrp->sg_adm[i]; i++) { + if (i > 0) { + *cp++ = ','; + } + + strcpy (cp, sgrp->sg_adm[i]); + cp += strlen (cp); + } + *cp = ':'; + cp++; + + /* + * Now do likewise with the group members. + */ + + for (i = 0; NULL != sgrp->sg_mem[i]; i++) { + if (i > 0) { + *cp = ','; + cp++; + } + + strcpy (cp, sgrp->sg_mem[i]); + cp += strlen (cp); + } + *cp = '\n'; + cp++; + *cp = '\0'; + + /* + * Output using the function which understands the line + * continuation conventions. + */ + + if (fputsx (buf, fp) == EOF) { + free (buf); + return -1; + } + + free (buf); + return 0; +} +#else +extern int errno; /* warning: ANSI C forbids an empty source file */ +#endif /*} SHADOWGRP */ diff --git a/lib/gshadow_.h b/lib/gshadow_.h new file mode 100644 index 0000000..68a0bb6 --- /dev/null +++ b/lib/gshadow_.h @@ -0,0 +1,52 @@ +/* + * SPDX-FileCopyrightText: 1988 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1997, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2005, Tomasz KÅ‚oczko + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * $Id$ + */ + +#ifndef _H_GSHADOW +#define _H_GSHADOW + +/* + * Shadow group security file structure + */ + +struct sgrp { + char *sg_name; /* group name */ + char *sg_passwd; /* group password */ + char **sg_adm; /* group administrator list */ + char **sg_mem; /* group membership list */ +}; + +/* + * Shadow group security file functions. + */ + +#include <stdio.h> /* for FILE */ + +#if __STDC__ +/*@observer@*//*@null@*/struct sgrp *getsgent (void); +/*@observer@*//*@null@*/struct sgrp *getsgnam (const char *); +/*@observer@*//*@null@*/struct sgrp *sgetsgent (const char *); +/*@observer@*//*@null@*/struct sgrp *fgetsgent (/*@null@*/FILE *); +void setsgent (void); +void endsgent (void); +int putsgent (const struct sgrp *, FILE *); +#else +/*@observer@*//*@null@*/struct sgrp *getsgent (); +/*@observer@*//*@null@*/struct sgrp *getsgnam (); +/*@observer@*//*@null@*/struct sgrp *sgetsgent (); +/*@observer@*//*@null@*/struct sgrp *fgetsgent (); +void setsgent (); +void endsgent (); +int putsgent (); +#endif + +#define GSHADOW "/etc/gshadow" +#endif /* ifndef _H_GSHADOW */ diff --git a/lib/lockpw.c b/lib/lockpw.c new file mode 100644 index 0000000..aaa317f --- /dev/null +++ b/lib/lockpw.c @@ -0,0 +1,85 @@ +/* + * SPDX-FileCopyrightText: 1992 , Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1998, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2005 , Tomasz KÅ‚oczko + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ifndef HAVE_LCKPWDF + +#ident "$Id$" + +#include "prototypes.h" +#include "defines.h" +#include "pwio.h" +#include "shadowio.h" +/* + * lckpwdf - lock the password files + */ +int lckpwdf (void) +{ + int i; + + /* + * We have 15 seconds to lock the whole mess + */ + + for (i = 0; i < 15; i++) + if (pw_lock ()) + break; + else + sleep (1); + + /* + * Did we run out of time? + */ + + if (i == 15) + return -1; + + /* + * Nope, use any remaining time to lock the shadow password + * file. + */ + + for (; i < 15; i++) + if (spw_lock ()) + break; + else + sleep (1); + + /* + * Out of time yet? + */ + + if (i == 15) { + pw_unlock (); + return -1; + } + + /* + * Nope - and both files are now locked. + */ + + return 0; +} + +/* + * ulckpwdf - unlock the password files + */ + +int ulckpwdf (void) +{ + + /* + * Unlock both files. + */ + + return (pw_unlock () && spw_unlock ())? 0 : -1; +} +#else +extern int errno; /* warning: ANSI C forbids an empty source file */ +#endif diff --git a/lib/nscd.c b/lib/nscd.c new file mode 100644 index 0000000..2c2251a --- /dev/null +++ b/lib/nscd.c @@ -0,0 +1,58 @@ +/* Author: Peter Vrabec <pvrabec@redhat.com> */ + +#include <config.h> +#ifdef USE_NSCD + +#include <stdio.h> +#include <sys/wait.h> +#include <sys/types.h> +#include "exitcodes.h" +#include "defines.h" +#include "prototypes.h" +#include "nscd.h" +#include "shadowlog_internal.h" + +#define MSG_NSCD_FLUSH_CACHE_FAILED "%s: Failed to flush the nscd cache.\n" + +/* + * nscd_flush_cache - flush specified service buffer in nscd cache + */ +int nscd_flush_cache (const char *service) +{ + int status, code; + const char *cmd = "/usr/sbin/nscd"; + const char *spawnedArgs[] = {"nscd", "-i", service, NULL}; + const char *spawnedEnv[] = {NULL}; + + if (run_command (cmd, spawnedArgs, spawnedEnv, &status) != 0) { + /* run_command writes its own more detailed message. */ + (void) fprintf (shadow_logfd, _(MSG_NSCD_FLUSH_CACHE_FAILED), shadow_progname); + return -1; + } + + code = WEXITSTATUS (status); + if (!WIFEXITED (status)) { + (void) fprintf (shadow_logfd, + _("%s: nscd did not terminate normally (signal %d)\n"), + shadow_progname, WTERMSIG (status)); + return -1; + } else if (code == E_CMD_NOTFOUND) { + /* nscd is not installed, or it is installed but uses an + interpreter that is missing. Probably the former. */ + return 0; + } else if (code == 1) { + /* nscd is installed, but it isn't active. */ + return 0; + } else if (code != 0) { + (void) fprintf (shadow_logfd, _("%s: nscd exited with status %d\n"), + shadow_progname, code); + (void) fprintf (shadow_logfd, _(MSG_NSCD_FLUSH_CACHE_FAILED), shadow_progname); + return -1; + } + + return 0; +} +#else /* USE_NSCD */ +extern int errno; /* warning: ANSI C forbids an empty source file */ +#endif /* USE_NSCD */ + diff --git a/lib/nscd.h b/lib/nscd.h new file mode 100644 index 0000000..a430b00 --- /dev/null +++ b/lib/nscd.h @@ -0,0 +1,13 @@ +#ifndef _NSCD_H_ +#define _NSCD_H_ + +/* + * nscd_flush_cache - flush specified service buffer in nscd cache + */ +#ifdef USE_NSCD +extern int nscd_flush_cache (const char *service); +#else +#define nscd_flush_cache(service) (0) +#endif + +#endif diff --git a/lib/nss.c b/lib/nss.c new file mode 100644 index 0000000..23d0518 --- /dev/null +++ b/lib/nss.c @@ -0,0 +1,151 @@ +#include <stdio.h> +#include <stdlib.h> +#include <dlfcn.h> +#include <stdbool.h> +#include <string.h> +#include <strings.h> +#include <ctype.h> +#include <stdatomic.h> +#include "prototypes.h" +#include "../libsubid/subid.h" +#include "shadowlog_internal.h" +#include "shadowlog.h" + +#define NSSWITCH "/etc/nsswitch.conf" + +// NSS plugin handling for subids +// If nsswitch has a line like +// subid: sssd +// then sssd will be consulted for subids. Unlike normal NSS dbs, +// only one db is supported at a time. That's open to debate, but +// the subids are a pretty limited resource, and local files seem +// bound to step on any other allocations leading to insecure +// conditions. +static atomic_flag nss_init_started; +static atomic_bool nss_init_completed; + +static struct subid_nss_ops *subid_nss; + +bool nss_is_initialized() { + return atomic_load(&nss_init_completed); +} + +static void nss_exit(void) { + if (nss_is_initialized() && subid_nss) { + dlclose(subid_nss->handle); + free(subid_nss); + subid_nss = NULL; + } +} + +// nsswitch_path is an argument only to support testing. +void nss_init(const char *nsswitch_path) { + FILE *nssfp = NULL; + char *line = NULL, *p, *token, *saveptr; + size_t len = 0; + FILE *shadow_logfd = log_get_logfd(); + + if (atomic_flag_test_and_set(&nss_init_started)) { + // Another thread has started nss_init, wait for it to complete + while (!atomic_load(&nss_init_completed)) + usleep(100); + return; + } + + if (!nsswitch_path) + nsswitch_path = NSSWITCH; + + // read nsswitch.conf to check for a line like: + // subid: files + nssfp = fopen(nsswitch_path, "r"); + if (!nssfp) { + atomic_store(&nss_init_completed, true); + return; + } + while ((getline(&line, &len, nssfp)) != -1) { + if (line[0] == '\0' || line[0] == '#') + continue; + if (strlen(line) < 8) + continue; + if (strncasecmp(line, "subid:", 6) != 0) + continue; + p = &line[6]; + while ((*p) && isspace(*p)) + p++; + if (!*p) + continue; + for (token = strtok_r(p, " \n\t", &saveptr); + token; + token = strtok_r(NULL, " \n\t", &saveptr)) { + char libname[65]; + void *h; + if (strcmp(token, "files") == 0) { + subid_nss = NULL; + goto done; + } + if (strlen(token) > 50) { + fprintf(shadow_logfd, "Subid NSS module name too long (longer than 50 characters): %s\n", token); + fprintf(shadow_logfd, "Using files\n"); + subid_nss = NULL; + goto done; + } + snprintf(libname, 64, "libsubid_%s.so", token); + h = dlopen(libname, RTLD_LAZY); + if (!h) { + fprintf(shadow_logfd, "Error opening %s: %s\n", libname, dlerror()); + fprintf(shadow_logfd, "Using files\n"); + subid_nss = NULL; + goto done; + } + subid_nss = malloc(sizeof(*subid_nss)); + if (!subid_nss) { + dlclose(h); + goto done; + } + subid_nss->has_range = dlsym(h, "shadow_subid_has_range"); + if (!subid_nss->has_range) { + fprintf(shadow_logfd, "%s did not provide @has_range@\n", libname); + dlclose(h); + free(subid_nss); + subid_nss = NULL; + goto done; + } + subid_nss->list_owner_ranges = dlsym(h, "shadow_subid_list_owner_ranges"); + if (!subid_nss->list_owner_ranges) { + fprintf(shadow_logfd, "%s did not provide @list_owner_ranges@\n", libname); + dlclose(h); + free(subid_nss); + subid_nss = NULL; + goto done; + } + subid_nss->find_subid_owners = dlsym(h, "shadow_subid_find_subid_owners"); + if (!subid_nss->find_subid_owners) { + fprintf(shadow_logfd, "%s did not provide @find_subid_owners@\n", libname); + dlclose(h); + free(subid_nss); + subid_nss = NULL; + goto done; + } + subid_nss->handle = h; + goto done; + } + fprintf(shadow_logfd, "No usable subid NSS module found, using files\n"); + // subid_nss has to be null here, but to ease reviews: + free(subid_nss); + subid_nss = NULL; + goto done; + } + +done: + atomic_store(&nss_init_completed, true); + free(line); + if (nssfp) { + atexit(nss_exit); + fclose(nssfp); + } +} + +struct subid_nss_ops *get_subid_nss_handle() { + nss_init(NULL); + return subid_nss; +} diff --git a/lib/pam_defs.h b/lib/pam_defs.h new file mode 100644 index 0000000..2dcda3c --- /dev/null +++ b/lib/pam_defs.h @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: 1999 , Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2001 - 2005, Tomasz KÅ‚oczko + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> +#include <security/pam_appl.h> +#ifdef HAVE_SECURITY_PAM_MISC_H +# include <security/pam_misc.h> +#endif +#ifdef HAVE_SECURITY_OPENPAM_H +# include <security/openpam.h> +#endif + + +static struct pam_conv conv = { + SHADOW_PAM_CONVERSATION, + NULL +}; + +/* compatibility with different versions of Linux-PAM */ +#if !HAVE_DECL_PAM_ESTABLISH_CRED +#define PAM_ESTABLISH_CRED PAM_CRED_ESTABLISH +#endif +#if !HAVE_DECL_PAM_DELETE_CRED +#define PAM_DELETE_CRED PAM_CRED_DELETE +#endif +#if !HAVE_DECL_PAM_NEW_AUTHTOK_REQD +#define PAM_NEW_AUTHTOK_REQD PAM_AUTHTOKEN_REQD +#endif +#if !HAVE_DECL_PAM_DATA_SILENT +#define PAM_DATA_SILENT 0 +#endif diff --git a/lib/port.c b/lib/port.c new file mode 100644 index 0000000..0bea2ef --- /dev/null +++ b/lib/port.c @@ -0,0 +1,454 @@ +/* + * SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1997, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2005 , Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2008 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#include <stdio.h> +#include <ctype.h> +#include <errno.h> +#include "defines.h" +#include "prototypes.h" +#include "port.h" + +static FILE *ports; + +/* + * portcmp - compare the name of a port to a /etc/porttime entry + * + * portcmp works like strcmp, except that if the last character + * in a failing match is a '*', the match is considered to have + * passed. The "*" match is suppressed whenever the port is "SU", + * which is the token the "su" command uses to validate access. + * A match returns 0, failure returns non-zero. + */ + +static int portcmp (const char *pattern, const char *port) +{ + const char *orig = port; + + while (('\0' != *pattern) && (*pattern == *port)) { + pattern++; + port++; + } + + if (('\0' == *pattern) && ('\0' == *port)) { + return 0; + } + if (('S' == orig[0]) && ('U' == orig[1]) && ('\0' == orig[2])) { + return 1; + } + + return (*pattern == '*') ? 0 : 1; +} + +/* + * setportent - open /etc/porttime file or rewind + * + * the /etc/porttime file is rewound if already open, or + * opened for reading. + */ + +static void setportent (void) +{ + if (NULL != ports) { + rewind (ports); + } else { + ports = fopen (PORTS, "r"); + } +} + +/* + * endportent - close the /etc/porttime file + * + * the /etc/porttime file is closed and the ports variable set + * to NULL to indicate that the /etc/porttime file is no longer + * open. + */ + +static void endportent (void) +{ + if (NULL != ports) { + (void) fclose (ports); + } + + ports = (FILE *) 0; +} + +/* + * getportent - read a single entry from /etc/porttime + * + * the next line in /etc/porttime is converted to a (struct port) + * and a pointer to a static (struct port) is returned to the + * invoker. NULL is returned on either EOF or error. errno is + * set to EINVAL on error to distinguish the two conditions. + */ + +static struct port *getportent (void) +{ + static struct port port; /* static struct to point to */ + static char buf[BUFSIZ]; /* some space for stuff */ + static char *ttys[PORT_TTY + 1]; /* some pointers to tty names */ + static char *users[PORT_IDS + 1]; /* some pointers to user ids */ + static struct pt_time ptimes[PORT_TIMES + 1]; /* time ranges */ + char *cp; /* pointer into line */ + int dtime; /* scratch time of day */ + int i, j; + int saveerr = errno; /* errno value on entry */ + + /* + * If the ports file is not open, open the file. Do not rewind + * since we want to search from the beginning each time. + */ + + if (NULL == ports) { + setportent (); + } + + if (NULL == ports) { + errno = saveerr; + return 0; + } + + /* + * Common point for beginning a new line - + * + * - read a line, and NUL terminate + * - skip lines which begin with '#' + * - parse off the tty names + * - parse off a list of user names + * - parse off a list of days and times + */ + + again: + + /* + * Get the next line and remove the last character, which + * is a '\n'. Lines which begin with '#' are all ignored. + */ + + if (fgets (buf, (int) sizeof buf, ports) == 0) { + errno = saveerr; + return 0; + } + if ('#' == buf[0]) { + goto again; + } + + /* + * Get the name of the TTY device. It is the first colon + * separated field, and is the name of the TTY with no + * leading "/dev". The entry '*' is used to specify all + * TTY devices. + */ + + buf[strlen (buf) - 1] = 0; + + port.pt_names = ttys; + for (cp = buf, j = 0; j < PORT_TTY; j++) { + port.pt_names[j] = cp; + while (('\0' != *cp) && (':' != *cp) && (',' != *cp)) { + cp++; + } + + if ('\0' == *cp) { + goto again; /* line format error */ + } + + if (':' == *cp) { /* end of tty name list */ + break; + } + + if (',' == *cp) { /* end of current tty name */ + *cp++ = '\0'; + } + } + *cp = '\0'; + cp++; + port.pt_names[j + 1] = (char *) 0; + + /* + * Get the list of user names. It is the second colon + * separated field, and is a comma separated list of user + * names. The entry '*' is used to specify all usernames. + * The last entry in the list is a (char *) 0 pointer. + */ + + if (':' != *cp) { + port.pt_users = users; + port.pt_users[0] = cp; + + for (j = 1; ':' != *cp; cp++) { + if ((',' == *cp) && (j < PORT_IDS)) { + *cp = '\0'; + cp++; + port.pt_users[j] = cp; + j++; + } + } + port.pt_users[j] = 0; + } else { + port.pt_users = 0; + } + + if (':' != *cp) { + goto again; + } + + *cp = '\0'; + cp++; + + /* + * Get the list of valid times. The times field is the third + * colon separated field and is a list of days of the week and + * times during which this port may be used by this user. The + * valid days are 'Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', and 'Sa'. + * + * In addition, the value 'Al' represents all 7 days, and 'Wk' + * represents the 5 weekdays. + * + * Times are given as HHMM-HHMM. The ending time may be before + * the starting time. Days are presumed to wrap at 0000. + */ + + if ('\0' == *cp) { + port.pt_times = 0; + return &port; + } + + port.pt_times = ptimes; + + /* + * Get the next comma separated entry + */ + + for (j = 0; ('\0' != *cp) && (j < PORT_TIMES); j++) { + + /* + * Start off with no days of the week + */ + + port.pt_times[j].t_days = 0; + + /* + * Check each two letter sequence to see if it is + * one of the abbreviations for the days of the + * week or the other two values. + */ + + for (i = 0; + ('\0' != cp[i]) && ('\0' != cp[i + 1]) && isalpha (cp[i]); + i += 2) { + switch ((cp[i] << 8) | (cp[i + 1])) { + case ('S' << 8) | 'u': + port.pt_times[j].t_days |= 01; + break; + case ('M' << 8) | 'o': + port.pt_times[j].t_days |= 02; + break; + case ('T' << 8) | 'u': + port.pt_times[j].t_days |= 04; + break; + case ('W' << 8) | 'e': + port.pt_times[j].t_days |= 010; + break; + case ('T' << 8) | 'h': + port.pt_times[j].t_days |= 020; + break; + case ('F' << 8) | 'r': + port.pt_times[j].t_days |= 040; + break; + case ('S' << 8) | 'a': + port.pt_times[j].t_days |= 0100; + break; + case ('W' << 8) | 'k': + port.pt_times[j].t_days |= 076; + break; + case ('A' << 8) | 'l': + port.pt_times[j].t_days |= 0177; + break; + default: + errno = EINVAL; + return 0; + } + } + + /* + * The default is 'Al' if no days were seen. + */ + + if (0 == i) { + port.pt_times[j].t_days = 0177; + } + + /* + * The start and end times are separated from each + * other by a '-'. The times are four digit numbers + * representing the times of day. + */ + + for (dtime = 0; ('\0' != cp[i]) && isdigit (cp[i]); i++) { + dtime = dtime * 10 + cp[i] - '0'; + } + + if (('-' != cp[i]) || (dtime > 2400) || ((dtime % 100) > 59)) { + goto again; + } + port.pt_times[j].t_start = dtime; + cp = cp + i + 1; + + for (dtime = 0, i = 0; + ('\0' != cp[i]) && isdigit (cp[i]); + i++) { + dtime = dtime * 10 + cp[i] - '0'; + } + + if ( ((',' != cp[i]) && ('\0' != cp[i])) + || (dtime > 2400) + || ((dtime % 100) > 59)) { + goto again; + } + + port.pt_times[j].t_end = dtime; + cp = cp + i + 1; + } + + /* + * The end of the list is indicated by a pair of -1's for the + * start and end times. + */ + + port.pt_times[j].t_start = port.pt_times[j].t_end = -1; + + return &port; +} + +/* + * getttyuser - get ports information for user and tty + * + * getttyuser() searches the ports file for an entry with a TTY + * and user field both of which match the supplied TTY and + * user name. The file is searched from the beginning, so the + * entries are treated as an ordered list. + */ + +static struct port *getttyuser (const char *tty, const char *user) +{ + int i, j; + struct port *port; + + setportent (); + + while ((port = getportent ()) != NULL) { + if ( (0 == port->pt_names) + || (0 == port->pt_users)) { + continue; + } + + for (i = 0; NULL != port->pt_names[i]; i++) { + if (portcmp (port->pt_names[i], tty) == 0) { + break; + } + } + + if (port->pt_names[i] == 0) { + continue; + } + + for (j = 0; NULL != port->pt_users[j]; j++) { + if ( (strcmp (user, port->pt_users[j]) == 0) + || (strcmp (port->pt_users[j], "*") == 0)) { + break; + } + } + + if (port->pt_users[j] != 0) { + break; + } + } + endportent (); + return port; +} + +/* + * isttytime - tell if a given user may login at a particular time + * + * isttytime searches the ports file for an entry which matches + * the user name and TTY given. + */ + +bool isttytime (const char *id, const char *port, time_t when) +{ + int i; + int dtime; + struct port *pp; + struct tm *tm; + + /* + * Try to find a matching entry for this user. Default to + * letting the user in - there are plenty of ways to have an + * entry to match all users. + */ + + pp = getttyuser (port, id); + if (NULL == pp) { + return true; + } + + /* + * The entry is there, but has no time entries - don't + * ever let them login. + */ + + if (0 == pp->pt_times) { + return false; + } + + /* + * The current time is converted to HHMM format for + * comparison against the time values in the TTY entry. + */ + + tm = localtime (&when); + dtime = tm->tm_hour * 100 + tm->tm_min; + + /* + * Each time entry is compared against the current + * time. For entries with the start after the end time, + * the comparison is made so that the time is between + * midnight and either the start or end time. + */ + + for (i = 0; pp->pt_times[i].t_start != -1; i++) { + if (!(pp->pt_times[i].t_days & PORT_DAY (tm->tm_wday))) { + continue; + } + + if (pp->pt_times[i].t_start <= pp->pt_times[i].t_end) { + if ( (dtime >= pp->pt_times[i].t_start) + && (dtime <= pp->pt_times[i].t_end)) { + return true; + } + } else { + if ( (dtime >= pp->pt_times[i].t_start) + || (dtime <= pp->pt_times[i].t_end)) { + return true; + } + } + } + + /* + * No matching time entry was found, user shouldn't + * be let in right now. + */ + + return false; +} + diff --git a/lib/port.h b/lib/port.h new file mode 100644 index 0000000..c19421d --- /dev/null +++ b/lib/port.h @@ -0,0 +1,60 @@ +/* + * SPDX-FileCopyrightText: 1989 - 1991, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1997, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2005 , Tomasz KÅ‚oczko + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * port.h - structure of /etc/porttime + * + * $Id$ + * + * Each entry in /etc/porttime consists of a TTY device + * name or "*" to indicate all TTY devices, followed by + * a list of 1 or more user IDs or "*" to indicate all + * user names, followed by a list of zero or more valid + * login times. Login time entries consist of zero or + * more day names (Su, Mo, Tu, We, Th, Fr, Sa, Wk, Al) + * followed by a pair of time values in HHMM format + * separated by a "-". + */ + +/* + * PORTS - Name of system port access time file. + * PORT_IDS - Allowable number of IDs per entry. + * PORT_TTY - Allowable number of TTYs per entry. + * PORT_TIMES - Allowable number of time entries per entry. + * PORT_DAY - Day of the week to a bit value (0 = Sunday). + */ + +#define PORTS "/etc/porttime" +#define PORT_IDS 64 +#define PORT_TTY 64 +#define PORT_TIMES 24 +#define PORT_DAY(day) (1<<(day)) + +/* + * pt_names - pointer to array of device names in /dev/ + * pt_users - pointer to array of applicable user IDs. + * pt_times - pointer to list of allowable time periods. + */ + +struct port { + char **pt_names; + char **pt_users; + struct pt_time *pt_times; +}; + +/* + * t_days - bit array for each day of the week (0 = Sunday) + * t_start - starting time for this entry + * t_end - ending time for this entry + */ + +struct pt_time { + short t_days; + short t_start; + short t_end; +}; diff --git a/lib/prototypes.h b/lib/prototypes.h new file mode 100644 index 0000000..1172b5d --- /dev/null +++ b/lib/prototypes.h @@ -0,0 +1,501 @@ +/* + * SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 2000, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2006, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2007 - 2010, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * prototypes.h + * + * prototypes of libmisc functions, and private lib functions. + * + * $Id$ + * + */ + +#ifndef _PROTOTYPES_H +#define _PROTOTYPES_H + +#include <config.h> + +#include <sys/stat.h> +#ifdef USE_UTMPX +#include <utmpx.h> +#else +#include <utmp.h> +#endif +#include <sys/types.h> +#include <pwd.h> +#include <grp.h> +#include <shadow.h> +#include <lastlog.h> + +#include "defines.h" +#include "commonio.h" + +/* addgrps.c */ +#if defined (HAVE_SETGROUPS) && ! defined (USE_PAM) +extern int add_groups (const char *); +#endif + +/* age.c */ +extern void agecheck (/*@null@*/const struct spwd *); +extern int expire (const struct passwd *, /*@null@*/const struct spwd *); +/* isexpired.c */ +extern int isexpired (const struct passwd *, /*@null@*/const struct spwd *); + +/* btrfs.c */ +#ifdef WITH_BTRFS +extern int btrfs_create_subvolume(const char *path); +extern int btrfs_remove_subvolume(const char *path); +extern int btrfs_is_subvolume(const char *path); +extern int is_btrfs(const char *path); +#endif + +/* basename() renamed to Basename() to avoid libc name space confusion */ +/* basename.c */ +extern /*@observer@*/const char *Basename (const char *str); + +/* chowndir.c */ +extern int chown_tree (const char *root, + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid); + +/* chowntty.c */ +extern void chown_tty (const struct passwd *); + +/* cleanup.c */ +typedef /*@null@*/void (*cleanup_function) (/*@null@*/void *arg); +void add_cleanup (/*@notnull@*/cleanup_function pcf, /*@null@*/void *arg); +void del_cleanup (/*@notnull@*/cleanup_function pcf); +void do_cleanups (void); + +/* cleanup_group.c */ +struct cleanup_info_mod { + char *audit_msg; + char *action; + /*@observer@*/const char *name; +}; +void cleanup_report_add_group (void *group_name); +void cleanup_report_add_group_group (void *group_name); +#ifdef SHADOWGRP +void cleanup_report_add_group_gshadow (void *group_name); +#endif +void cleanup_report_del_group (void *group_name); +void cleanup_report_del_group_group (void *group_name); +#ifdef SHADOWGRP +void cleanup_report_del_group_gshadow (void *group_name); +#endif +void cleanup_report_mod_passwd (void *cleanup_info); +void cleanup_report_mod_group (void *cleanup_info); +void cleanup_report_mod_gshadow (void *cleanup_info); +void cleanup_unlock_group (/*@null@*/void *unused); +#ifdef SHADOWGRP +void cleanup_unlock_gshadow (/*@null@*/void *unused); +#endif +void cleanup_unlock_passwd (/*@null@*/void *unused); + +/* console.c */ +extern bool console (const char *); + +/* copydir.c */ +extern 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); + +/* date_to_str.c */ +extern void date_to_str (size_t size, char buf[size], long date); + +/* encrypt.c */ +extern /*@exposed@*//*@null@*/char *pw_encrypt (const char *, const char *); + +/* entry.c */ +extern void pw_entry (const char *, struct passwd *); + +/* env.c */ +extern void addenv (const char *, /*@null@*/const char *); +extern void initenv (void); +extern void set_env (int, char *const *); +extern void sanitize_env (void); + +/* fields.c */ +extern void change_field (char *, size_t, const char *); +extern int valid_field (const char *, const char *); + +/* find_new_gid.c */ +extern int find_new_gid (bool sys_group, + gid_t *gid, + /*@null@*/gid_t const *preferred_gid); + +/* find_new_uid.c */ +extern int find_new_uid (bool sys_user, + uid_t *uid, + /*@null@*/uid_t const *preferred_uid); + +#ifdef ENABLE_SUBIDS +/* find_new_sub_gids.c */ +extern int find_new_sub_gids (gid_t *range_start, unsigned long *range_count); + +/* find_new_sub_uids.c */ +extern int find_new_sub_uids (uid_t *range_start, unsigned long *range_count); +#endif /* ENABLE_SUBIDS */ + + +/* get_gid.c */ +extern int get_gid (const char *gidstr, gid_t *gid); + +/* getgr_nam_gid.c */ +extern /*@only@*//*@null@*/struct group *getgr_nam_gid (/*@null@*/const char *grname); + +/* getlong.c */ +extern int getlong (const char *numstr, /*@out@*/long int *result); + +/* get_pid.c */ +extern int get_pid (const char *pidstr, pid_t *pid); + +/* getrange */ +extern int getrange (const char *range, + unsigned long *min, bool *has_min, + unsigned long *max, bool *has_max); + +/* gettime.c */ +extern time_t gettime (void); + +/* get_uid.c */ +extern int get_uid (const char *uidstr, uid_t *uid); + +/* getulong.c */ +extern int getulong (const char *numstr, /*@out@*/unsigned long int *result); + +/* fputsx.c */ +extern /*@null@*/char *fgetsx (/*@returned@*/ /*@out@*/char *, int, FILE *); +extern int fputsx (const char *, FILE *); + +/* groupio.c */ +extern void __gr_del_entry (const struct commonio_entry *ent); +extern /*@observer@*/const struct commonio_db *__gr_get_db (void); +extern /*@dependent@*/ /*@null@*/struct commonio_entry *__gr_get_head (void); +extern void __gr_set_changed (void); + +/* groupmem.c */ +extern /*@null@*/ /*@only@*/struct group *__gr_dup (const struct group *grent); +extern void gr_free_members (struct group *grent); +extern void gr_free (/*@out@*/ /*@only@*/struct group *grent); +extern bool gr_append_member (struct group *grp, char *member); + +/* hushed.c */ +extern bool hushed (const char *username); + +/* audit_help.c */ +#ifdef WITH_AUDIT +extern int audit_fd; +extern void audit_help_open (void); +/* Use AUDIT_NO_ID when a name is provided to audit_logger instead of an ID */ +#define AUDIT_NO_ID ((unsigned int) -1) +typedef enum { + SHADOW_AUDIT_FAILURE = 0, + SHADOW_AUDIT_SUCCESS = 1} shadow_audit_result; +extern void audit_logger (int type, const char *pgname, const char *op, + const char *name, unsigned int id, + shadow_audit_result result); +void audit_logger_message (const char *message, shadow_audit_result result); +#endif + +/* limits.c */ +#ifndef USE_PAM +extern void setup_limits (const struct passwd *); +#endif + +/* list.c */ +extern /*@only@*/ /*@out@*/char **add_list (/*@returned@*/ /*@only@*/char **, const char *); +extern /*@only@*/ /*@out@*/char **del_list (/*@returned@*/ /*@only@*/char **, const char *); +extern /*@only@*/ /*@out@*/char **dup_list (char *const *); +extern bool is_on_list (char *const *list, const char *member); +extern /*@only@*/char **comma_to_list (const char *); + +/* log.c */ +extern void dolastlog ( + struct lastlog *ll, + const struct passwd *pw, + /*@unique@*/const char *line, + /*@unique@*/const char *host); + +/* login_nopam.c */ +extern int login_access (const char *user, const char *from); + +/* loginprompt.c */ +extern void login_prompt (const char *, char *, int); + +/* mail.c */ +extern void mailcheck (void); + +/* motd.c */ +extern void motd (void); + +/* myname.c */ +extern /*@null@*//*@only@*/struct passwd *get_my_pwent (void); + +/* nss.c */ +#include <libsubid/subid.h> +extern void nss_init(const char *nsswitch_path); +extern bool nss_is_initialized(void); + +struct subid_nss_ops { + /* + * nss_has_range: does a user own a given subid range + * + * @owner: username + * @start: first subid in queried range + * @count: number of subids in queried range + * @idtype: subuid or subgid + * @result: true if @owner has been allocated the subid range. + * + * returns success if the module was able to determine an answer (true or false), + * else an error status. + */ + enum subid_status (*has_range)(const char *owner, unsigned long start, unsigned long count, enum subid_type idtype, bool *result); + + /* + * nss_list_owner_ranges: list the subid ranges delegated to a user. + * + * @owner - string representing username being queried + * @id_type - subuid or subgid + * @ranges - pointer to an array of struct subid_range, or NULL. The + * returned array must be freed by the caller. + * @count - pointer to an integer into which the number of returned ranges + * is written. + + * returns success if the module was able to determine an answer, + * else an error status. + */ + enum subid_status (*list_owner_ranges)(const char *owner, enum subid_type id_type, struct subid_range **ranges, int *count); + + /* + * nss_find_subid_owners: find uids who own a given subuid or subgid. + * + * @id - the delegated id (subuid or subgid) being queried + * @id_type - subuid or subgid + * @uids - pointer to an array of uids which will be allocated by + * nss_find_subid_owners() + * @count - number of uids found + * + * returns success if the module was able to determine an answer, + * else an error status. + */ + enum subid_status (*find_subid_owners)(unsigned long id, enum subid_type id_type, uid_t **uids, int *count); + + /* The dlsym handle to close */ + void *handle; +}; + +extern struct subid_nss_ops *get_subid_nss_handle(void); + + +/* pam_pass_non_interactive.c */ +#ifdef USE_PAM +extern int do_pam_passwd_non_interactive (const char *pam_service, + const char *username, + const char* password); +#endif /* USE_PAM */ + +/* obscure.c */ +#ifndef USE_PAM +extern bool obscure (const char *, const char *, const struct passwd *); +#endif + +/* pam_pass.c */ +#ifdef USE_PAM +extern void do_pam_passwd (const char *user, bool silent, bool change_expired); +#endif + +/* port.c */ +extern bool isttytime (const char *, const char *, time_t); + +/* prefix_flag.c */ +extern const char* process_prefix_flag (const char* short_opt, int argc, char **argv); +extern struct group *prefix_getgrnam(const char *name); +extern struct group *prefix_getgrgid(gid_t gid); +extern struct passwd *prefix_getpwuid(uid_t uid); +extern struct passwd *prefix_getpwnam(const char* name); +extern struct spwd *prefix_getspnam(const char* name); +extern struct group *prefix_getgr_nam_gid(const char *grname); +extern void prefix_setpwent(void); +extern struct passwd* prefix_getpwent(void); +extern void prefix_endpwent(void); +extern void prefix_setgrent(void); +extern struct group* prefix_getgrent(void); +extern void prefix_endgrent(void); + +/* pwd2spwd.c */ +#ifndef USE_PAM +extern struct spwd *pwd_to_spwd (const struct passwd *); +#endif + +/* pwdcheck.c */ +#ifndef USE_PAM +extern void passwd_check (const char *, const char *, const char *); +#endif + +/* pwd_init.c */ +extern void pwd_init (void); + +/* pwio.c */ +extern void __pw_del_entry (const struct commonio_entry *ent); +extern struct commonio_db *__pw_get_db (void); +extern /*@dependent@*/ /*@null@*/struct commonio_entry *__pw_get_head (void); + +/* pwmem.c */ +extern /*@null@*/ /*@only@*/struct passwd *__pw_dup (const struct passwd *pwent); +extern void pw_free (/*@out@*/ /*@only@*/struct passwd *pwent); + +/* remove_tree.c */ +extern int remove_tree (const char *root, bool remove_root); + +/* rlogin.c */ +extern int do_rlogin (const char *remote_host, char *name, size_t namelen, + char *term, size_t termlen); + +/* root_flag.c */ +extern void process_root_flag (const char* short_opt, int argc, char **argv); + +/* salt.c */ +extern /*@observer@*/const char *crypt_make_salt (/*@null@*//*@observer@*/const char *meth, /*@null@*/void *arg); + +/* selinux.c */ +#ifdef WITH_SELINUX +extern int set_selinux_file_context (const char *dst_name, mode_t mode); +extern void reset_selinux_handle (void); +extern int reset_selinux_file_context (void); +extern int check_selinux_permit (const char *perm_name); +#endif + +/* semanage.c */ +#ifdef WITH_SELINUX +extern int set_seuser(const char *login_name, const char *seuser_name); +extern int del_seuser(const char *login_name); +#endif + +/* setugid.c */ +extern int setup_groups (const struct passwd *info); +extern int change_uid (const struct passwd *info); +#if (defined HAVE_INITGROUPS) && (! defined USE_PAM) +extern int setup_uid_gid (const struct passwd *info, bool is_console); +#else +extern int setup_uid_gid (const struct passwd *info); +#endif + +/* setup.c */ +extern void setup (struct passwd *); + +/* setupenv.c */ +extern void setup_env (struct passwd *); + +/* sgetgrent.c */ +extern struct group *sgetgrent (const char *buf); + +/* sgetpwent.c */ +extern struct passwd *sgetpwent (const char *buf); + +/* sgetspent.c */ +#ifndef HAVE_SGETSPENT +extern struct spwd *sgetspent (const char *string); +#endif + +/* sgroupio.c */ +extern void __sgr_del_entry (const struct commonio_entry *ent); +extern /*@null@*/ /*@only@*/struct sgrp *__sgr_dup (const struct sgrp *sgent); +extern void sgr_free (/*@out@*/ /*@only@*/struct sgrp *sgent); +extern /*@dependent@*/ /*@null@*/struct commonio_entry *__sgr_get_head (void); +extern void __sgr_set_changed (void); + +/* shadowio.c */ +extern /*@dependent@*/ /*@null@*/struct commonio_entry *__spw_get_head (void); +extern void __spw_del_entry (const struct commonio_entry *ent); + +/* shadowmem.c */ +extern /*@null@*/ /*@only@*/struct spwd *__spw_dup (const struct spwd *spent); +extern void spw_free (/*@out@*/ /*@only@*/struct spwd *spent); + +/* shell.c */ +extern int shell (const char *file, /*@null@*/const char *arg, char *const envp[]); + +/* spawn.c */ +extern int run_command (const char *cmd, const char *argv[], + /*@null@*/const char *envp[], /*@out@*/int *status); + +/* strtoday.c */ +extern long strtoday (const char *); + +/* suauth.c */ +extern int check_su_auth (const char *actual_id, + const char *wanted_id, + bool su_to_root); + +/* sulog.c */ +extern void sulog (const char *tty, + bool success, + const char *oldname, + const char *name); + +/* sub.c */ +extern void subsystem (const struct passwd *); + +/* ttytype.c */ +extern void ttytype (const char *); + +/* tz.c */ +#ifndef USE_PAM +extern /*@observer@*/const char *tz (const char *); +#endif + +/* ulimit.c */ +extern int set_filesize_limit (int blocks); + +/* user_busy.c */ +extern int user_busy (const char *name, uid_t uid); + +/* utmp.c */ +#ifndef USE_UTMPX +extern /*@null@*/struct utmp *get_current_utmp (void); +extern struct utmp *prepare_utmp (const char *name, + const char *line, + const char *host, + /*@null@*/const struct utmp *ut); +extern int setutmp (struct utmp *ut); +#else +extern /*@null@*/struct utmpx *get_current_utmp (void); +extern struct utmpx *prepare_utmpx (const char *name, + const char *line, + const char *host, + /*@null@*/const struct utmpx *ut); +extern int setutmpx (struct utmpx *utx); +#endif /* USE_UTMPX */ + +/* valid.c */ +extern bool valid (const char *, const struct passwd *); + +/* xmalloc.c */ +extern /*@maynotreturn@*/ /*@only@*//*@out@*//*@notnull@*/void *xmalloc (size_t size) + /*@ensures MaxSet(result) == (size - 1); @*/; +extern /*@maynotreturn@*/ /*@only@*//*@notnull@*/char *xstrdup (const char *); + +/* xgetpwnam.c */ +extern /*@null@*/ /*@only@*/struct passwd *xgetpwnam (const char *); +/* xgetpwuid.c */ +extern /*@null@*/ /*@only@*/struct passwd *xgetpwuid (uid_t); +/* xgetgrnam.c */ +extern /*@null@*/ /*@only@*/struct group *xgetgrnam (const char *); +/* xgetgrgid.c */ +extern /*@null@*/ /*@only@*/struct group *xgetgrgid (gid_t); +/* xgetspnam.c */ +extern /*@null@*/ /*@only@*/struct spwd *xgetspnam(const char *); + +/* yesno.c */ +extern bool yes_or_no (bool read_only); + +#endif /* _PROTOTYPES_H */ diff --git a/lib/pwauth.c b/lib/pwauth.c new file mode 100644 index 0000000..62de472 --- /dev/null +++ b/lib/pwauth.c @@ -0,0 +1,211 @@ +/* + * SPDX-FileCopyrightText: 1992 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 2000, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2006, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2008 - 2009, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ifndef USE_PAM +#ident "$Id$" + +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <sys/types.h> +#include <unistd.h> +#include "prototypes.h" +#include "defines.h" +#include "pwauth.h" +#include "getdef.h" +#ifdef SKEY +#include <skey.h> +#endif +#ifdef __linux__ /* standard password prompt by default */ +static const char *PROMPT = gettext_noop ("Password: "); +#else +static const char *PROMPT = gettext_noop ("%s's Password: "); +#endif + +bool wipe_clear_pass = true; +/*@null@*/char *clear_pass = NULL; + +/* + * pw_auth - perform getpass/crypt authentication + * + * pw_auth gets the user's cleartext password and encrypts it + * using the salt in the encrypted password. The results are + * compared. + */ + +int pw_auth (const char *cipher, + const char *user, + int reason, + /*@null@*/const char *input) +{ + char prompt[1024]; + char *clear = NULL; + const char *cp; + const char *encrypted; + int retval; + +#ifdef SKEY + bool use_skey = false; + char challenge_info[40]; + struct skey skey; +#endif + + /* + * There are programs for adding and deleting authentication data. + */ + + if ((PW_ADD == reason) || (PW_DELETE == reason)) { + return 0; + } + + /* + * There are even programs for changing the user name ... + */ + + if ((PW_CHANGE == reason) && (NULL != input)) { + return 0; + } + + /* + * WARNING: + * + * When we change a password and we are root, we don't prompt. + * This is so root can change any password without having to + * know it. This is a policy decision that might have to be + * revisited. + */ + + if ((PW_CHANGE == reason) && (getuid () == 0)) { + return 0; + } + + /* + * WARNING: + * + * When we are logging in a user with no ciphertext password, + * we don't prompt for the password or anything. In reality + * the user could just hit <ENTER>, so it doesn't really + * matter. + */ + + if ((NULL == cipher) || ('\0' == *cipher)) { + return 0; + } + +#ifdef SKEY + /* + * If the user has an S/KEY entry show them the pertinent info + * and then we can try validating the created ciphertext and the SKEY. + * If there is no SKEY information we default to not using SKEY. + */ + +# ifdef SKEY_BSD_STYLE + /* + * Some BSD updates to the S/KEY API adds a fourth parameter; the + * sizeof of the challenge info buffer. + */ +# define skeychallenge(s,u,c) skeychallenge(s,u,c,sizeof(c)) +# endif + + if (skeychallenge (&skey, user, challenge_info) == 0) { + use_skey = true; + } +#endif + + /* + * Prompt for the password as required. FTPD and REXECD both + * get the cleartext password for us. + */ + + if ((PW_FTP != reason) && (PW_REXEC != reason) && (NULL == input)) { + cp = getdef_str ("LOGIN_STRING"); + if (NULL == cp) { + cp = _(PROMPT); + } +#ifdef SKEY + if (use_skey) { + printf ("[%s]\n", challenge_info); + } +#endif + + snprintf (prompt, sizeof prompt, cp, user); + clear = getpass (prompt); + if (NULL == clear) { + static char c[1]; + + c[0] = '\0'; + clear = c; + } + input = clear; + } + + /* + * Convert the cleartext password into a ciphertext string. + * If the two match, the return value will be zero, which is + * SUCCESS. Otherwise we see if SKEY is being used and check + * the results there as well. + */ + + encrypted = pw_encrypt (input, cipher); + if (NULL != encrypted) { + retval = strcmp (encrypted, cipher); + } else { + retval = -1; + } + +#ifdef SKEY + /* + * If (1) The password fails to match, and + * (2) The password is empty and + * (3) We are using OPIE or S/Key, then + * ...Re-prompt, with echo on. + * -- AR 8/22/1999 + */ + if ((0 != retval) && ('\0' == input[0]) && use_skey) { + clear = getpass (prompt); + if (NULL == clear) { + static char c[1]; + + c[0] = '\0'; + clear = c; + } + input = clear; + } + + if ((0 != retval) && use_skey) { + int passcheck = -1; + + if (skeyverify (&skey, input) == 0) { + passcheck = skey.n; + } + if (passcheck > 0) { + retval = 0; + } + } +#endif + + /* + * Things like RADIUS authentication may need the password - + * if the external variable wipe_clear_pass is zero, we will + * not wipe it (the caller should wipe clear_pass when it is + * no longer needed). --marekm + */ + + clear_pass = clear; + if (wipe_clear_pass && (NULL != clear) && ('\0' != *clear)) { + strzero (clear); + } + return retval; +} +#else /* !USE_PAM */ +extern int errno; /* warning: ANSI C forbids an empty source file */ +#endif /* !USE_PAM */ diff --git a/lib/pwauth.h b/lib/pwauth.h new file mode 100644 index 0000000..b610025 --- /dev/null +++ b/lib/pwauth.h @@ -0,0 +1,48 @@ +/* + * SPDX-FileCopyrightText: 1992 - 1993, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1997, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2009 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * $Id$ + */ + +#ifndef _PWAUTH_H +#define _PWAUTH_H + +#ifndef USE_PAM +int pw_auth (const char *cipher, + const char *user, + int flag, + /*@null@*/const char *input); +#endif /* !USE_PAM */ + +/* + * Local access + */ + +#define PW_SU 1 +#define PW_LOGIN 2 + +/* + * Administrative functions + */ + +#define PW_ADD 101 +#define PW_CHANGE 102 +#define PW_DELETE 103 + +/* + * Network access + */ + +#define PW_TELNET 201 +#define PW_RLOGIN 202 +#define PW_FTP 203 +#define PW_REXEC 204 + +#endif /* _PWAUTH_H */ diff --git a/lib/pwio.c b/lib/pwio.c new file mode 100644 index 0000000..e59b473 --- /dev/null +++ b/lib/pwio.c @@ -0,0 +1,207 @@ +/* + * SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 2000, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2001 , MichaÅ‚ Moskal + * SPDX-FileCopyrightText: 2003 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2007 - 2009, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#include "prototypes.h" +#include "defines.h" +#include <pwd.h> +#include <stdio.h> +#include "commonio.h" +#include "pwio.h" + +static /*@null@*/ /*@only@*/void *passwd_dup (const void *ent) +{ + const struct passwd *pw = ent; + + return __pw_dup (pw); +} + +static void passwd_free (/*@out@*/ /*@only@*/void *ent) +{ + struct passwd *pw = ent; + + pw_free (pw); +} + +static const char *passwd_getname (const void *ent) +{ + const struct passwd *pw = ent; + + return pw->pw_name; +} + +static void *passwd_parse (const char *line) +{ + return (void *) sgetpwent (line); +} + +static int passwd_put (const void *ent, FILE * file) +{ + const struct passwd *pw = ent; + + if ( (NULL == pw) + || (valid_field (pw->pw_name, ":\n") == -1) + || (valid_field (pw->pw_passwd, ":\n") == -1) + || (pw->pw_uid == (uid_t)-1) + || (pw->pw_gid == (gid_t)-1) + || (valid_field (pw->pw_gecos, ":\n") == -1) + || (valid_field (pw->pw_dir, ":\n") == -1) + || (valid_field (pw->pw_shell, ":\n") == -1) + || (strlen (pw->pw_name) + strlen (pw->pw_passwd) + + strlen (pw->pw_gecos) + strlen (pw->pw_dir) + + strlen (pw->pw_shell) + 100 > PASSWD_ENTRY_MAX_LENGTH)) { + return -1; + } + + return (putpwent (pw, file) == -1) ? -1 : 0; +} + +static struct commonio_ops passwd_ops = { + passwd_dup, + passwd_free, + passwd_getname, + passwd_parse, + passwd_put, + fgets, + fputs, + NULL, /* open_hook */ + NULL /* close_hook */ +}; + +static struct commonio_db passwd_db = { + PASSWD_FILE, /* filename */ + &passwd_ops, /* ops */ + NULL, /* fp */ +#ifdef WITH_SELINUX + NULL, /* scontext */ +#endif + 0644, /* st_mode */ + 0, /* st_uid */ + 0, /* st_gid */ + NULL, /* head */ + NULL, /* tail */ + NULL, /* cursor */ + false, /* changed */ + false, /* isopen */ + false, /* locked */ + false, /* readonly */ + false /* setname */ +}; + +int pw_setdbname (const char *filename) +{ + return commonio_setname (&passwd_db, filename); +} + +/*@observer@*/const char *pw_dbname (void) +{ + return passwd_db.filename; +} + +int pw_lock (void) +{ + return commonio_lock (&passwd_db); +} + +int pw_open (int mode) +{ + return commonio_open (&passwd_db, mode); +} + +/*@observer@*/ /*@null@*/const struct passwd *pw_locate (const char *name) +{ + return commonio_locate (&passwd_db, name); +} + +/*@observer@*/ /*@null@*/const struct passwd *pw_locate_uid (uid_t uid) +{ + const struct passwd *pwd; + + pw_rewind (); + while ( ((pwd = pw_next ()) != NULL) + && (pwd->pw_uid != uid)) { + } + + return pwd; +} + +int pw_update (const struct passwd *pw) +{ + return commonio_update (&passwd_db, (const void *) pw); +} + +int pw_remove (const char *name) +{ + return commonio_remove (&passwd_db, name); +} + +int pw_rewind (void) +{ + return commonio_rewind (&passwd_db); +} + +/*@observer@*/ /*@null@*/const struct passwd *pw_next (void) +{ + return commonio_next (&passwd_db); +} + +int pw_close (void) +{ + return commonio_close (&passwd_db); +} + +int pw_unlock (void) +{ + return commonio_unlock (&passwd_db); +} + +/*@null@*/struct commonio_entry *__pw_get_head (void) +{ + return passwd_db.head; +} + +void __pw_del_entry (const struct commonio_entry *ent) +{ + commonio_del_entry (&passwd_db, ent); +} + +struct commonio_db *__pw_get_db (void) +{ + return &passwd_db; +} + +static int pw_cmp (const void *p1, const void *p2) +{ + uid_t u1, u2; + + if ((*(struct commonio_entry **) p1)->eptr == NULL) + return 1; + if ((*(struct commonio_entry **) p2)->eptr == NULL) + return -1; + + u1 = ((struct passwd *) (*(struct commonio_entry **) p1)->eptr)->pw_uid; + u2 = ((struct passwd *) (*(struct commonio_entry **) p2)->eptr)->pw_uid; + + if (u1 < u2) + return -1; + else if (u1 > u2) + return 1; + else + return 0; +} + +/* Sort entries by UID */ +int pw_sort () +{ + return commonio_sort (&passwd_db, pw_cmp); +} diff --git a/lib/pwio.h b/lib/pwio.h new file mode 100644 index 0000000..882a7c7 --- /dev/null +++ b/lib/pwio.h @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 2000, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2005 , MichaÅ‚ Moskal + * SPDX-FileCopyrightText: 2005 , Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2008 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* $Id$ */ +#ifndef _PWIO_H +#define _PWIO_H + +#include <sys/types.h> +#include <pwd.h> + +extern int pw_close (void); +extern /*@observer@*/ /*@null@*/const struct passwd *pw_locate (const char *name); +extern /*@observer@*/ /*@null@*/const struct passwd *pw_locate_uid (uid_t uid); +extern int pw_lock (void); +extern int pw_setdbname (const char *filename); +extern /*@observer@*/const char *pw_dbname (void); +extern /*@observer@*/ /*@null@*/const struct passwd *pw_next (void); +extern int pw_open (int mode); +extern int pw_remove (const char *name); +extern int pw_rewind (void); +extern int pw_unlock (void); +extern int pw_update (const struct passwd *pw); +extern int pw_sort (void); + +#endif diff --git a/lib/pwmem.c b/lib/pwmem.c new file mode 100644 index 0000000..867e3f7 --- /dev/null +++ b/lib/pwmem.c @@ -0,0 +1,85 @@ +/* + * SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 2000, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2001 , MichaÅ‚ Moskal + * SPDX-FileCopyrightText: 2003 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2007 - 2013, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#include <stdio.h> +#include "defines.h" +#include "prototypes.h" +#include "pwio.h" + +/*@null@*/ /*@only@*/struct passwd *__pw_dup (const struct passwd *pwent) +{ + struct passwd *pw; + + pw = (struct passwd *) malloc (sizeof *pw); + if (NULL == pw) { + return NULL; + } + /* The libc might define other fields. They won't be copied. */ + memset (pw, 0, sizeof *pw); + pw->pw_uid = pwent->pw_uid; + pw->pw_gid = pwent->pw_gid; + /*@-mustfreeonly@*/ + pw->pw_name = strdup (pwent->pw_name); + /*@=mustfreeonly@*/ + if (NULL == pw->pw_name) { + pw_free(pw); + return NULL; + } + /*@-mustfreeonly@*/ + pw->pw_passwd = strdup (pwent->pw_passwd); + /*@=mustfreeonly@*/ + if (NULL == pw->pw_passwd) { + pw_free(pw); + return NULL; + } + /*@-mustfreeonly@*/ + pw->pw_gecos = strdup (pwent->pw_gecos); + /*@=mustfreeonly@*/ + if (NULL == pw->pw_gecos) { + pw_free(pw); + return NULL; + } + /*@-mustfreeonly@*/ + pw->pw_dir = strdup (pwent->pw_dir); + /*@=mustfreeonly@*/ + if (NULL == pw->pw_dir) { + pw_free(pw); + return NULL; + } + /*@-mustfreeonly@*/ + pw->pw_shell = strdup (pwent->pw_shell); + /*@=mustfreeonly@*/ + if (NULL == pw->pw_shell) { + pw_free(pw); + return NULL; + } + + return pw; +} + +void pw_free (/*@out@*/ /*@only@*/struct passwd *pwent) +{ + if (pwent != NULL) { + free (pwent->pw_name); + if (pwent->pw_passwd) { + strzero (pwent->pw_passwd); + free (pwent->pw_passwd); + } + free (pwent->pw_gecos); + free (pwent->pw_dir); + free (pwent->pw_shell); + free (pwent); + } +} + diff --git a/lib/run_part.c b/lib/run_part.c new file mode 100644 index 0000000..bce11d3 --- /dev/null +++ b/lib/run_part.c @@ -0,0 +1,104 @@ +#include <dirent.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include <lib/prototypes.h> +#include "run_part.h" +#include "shadowlog_internal.h" + +int run_part (char *script_path, const char *name, const char *action) +{ + int pid; + int wait_status; + int pid_status; + char *args[] = { script_path, NULL }; + + pid=fork(); + if (pid==-1) { + perror ("Could not fork"); + return 1; + } + if (pid==0) { + setenv ("ACTION",action,1); + setenv ("SUBJECT",name,1); + execv (script_path,args); + perror ("execv"); + exit(1); + } + + pid_status = wait (&wait_status); + if (pid_status == pid) { + return (wait_status); + } + + perror ("waitpid"); + return (1); +} + +int run_parts (const char *directory, const char *name, const char *action) +{ + struct dirent **namelist; + int scanlist; + int n; + int execute_result = 0; + + scanlist = scandir (directory, &namelist, 0, alphasort); + if (scanlist<=0) { + return (0); + } + + for (n=0; n<scanlist; n++) { + int path_length; + struct stat sb; + + path_length=strlen(directory) + strlen(namelist[n]->d_name) + 2; + char *s = (char*)malloc(path_length); + if (!s) { + printf ("could not allocate memory\n"); + for (; n<scanlist; n++) { + free (namelist[n]); + } + free (namelist); + return (1); + } + snprintf (s, path_length, "%s/%s", directory, namelist[n]->d_name); + + execute_result = 0; + if (stat (s, &sb) == -1) { + perror ("stat"); + free (s); + for (; n<scanlist; n++) { + free (namelist[n]); + } + free (namelist); + return (1); + } + + if (S_ISREG (sb.st_mode) || S_ISLNK (sb.st_mode)) { + execute_result = run_part (s, name, action); + } + + free (s); + + if (execute_result!=0) { + fprintf (shadow_logfd, + "%s: did not exit cleanly.\n", + namelist[n]->d_name); + for (; n<scanlist; n++) { + free (namelist[n]); + } + break; + } + + free (namelist[n]); + } + free (namelist); + + return (execute_result); +} + diff --git a/lib/run_part.h b/lib/run_part.h new file mode 100644 index 0000000..6422134 --- /dev/null +++ b/lib/run_part.h @@ -0,0 +1,7 @@ +#ifndef _RUN_PART_H +#define _RUN_PART_H + +int run_part (char *script_path, const char *name, const char *action); +int run_parts (const char *directory, const char *name, const char *action); + +#endif /* _RUN_PART_H */ diff --git a/lib/selinux.c b/lib/selinux.c new file mode 100644 index 0000000..ad639bd --- /dev/null +++ b/lib/selinux.c @@ -0,0 +1,210 @@ +/* + * SPDX-FileCopyrightText: 2011 , Peter Vrabec <pvrabec@redhat.com> + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ifdef WITH_SELINUX + +#include <stdio.h> +#include "defines.h" + +#include <selinux/selinux.h> +#include <selinux/label.h> +#include "prototypes.h" + +#include "shadowlog_internal.h" + +static bool selinux_checked = false; +static bool selinux_enabled; +static /*@null@*/struct selabel_handle *selabel_hnd = NULL; + +static void cleanup(void) +{ + if (selabel_hnd) { + selabel_close(selabel_hnd); + selabel_hnd = NULL; + } +} + +void reset_selinux_handle (void) +{ + cleanup(); +} + +/* + * set_selinux_file_context - Set the security context before any file or + * directory creation. + * + * set_selinux_file_context () should be called before any creation + * of file, symlink, directory, ... + * + * Callers may have to Reset SELinux to create files with default + * contexts with reset_selinux_file_context + */ +int set_selinux_file_context (const char *dst_name, mode_t mode) +{ + if (!selinux_checked) { + selinux_enabled = is_selinux_enabled () > 0; + selinux_checked = true; + } + + if (selinux_enabled) { + /* Get the default security context for this file */ + + /*@null@*/char *fcontext_raw = NULL; + int r; + + if (selabel_hnd == NULL) { + selabel_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0); + if (selabel_hnd == NULL) { + return security_getenforce () != 0; + } + (void) atexit(cleanup); + } + + r = selabel_lookup_raw(selabel_hnd, &fcontext_raw, dst_name, mode); + if (r < 0) { + /* No context specified for the searched path */ + if (errno == ENOENT) { + return 0; + } + + return security_getenforce () != 0; + } + + /* Set the security context for the next created file */ + r = setfscreatecon_raw (fcontext_raw); + freecon (fcontext_raw); + if (r < 0) { + return security_getenforce () != 0; + } + } + return 0; +} + +/* + * reset_selinux_file_context - Reset the security context to the default + * policy behavior + * + * reset_selinux_file_context () should be called after the context + * was changed with set_selinux_file_context () + */ +int reset_selinux_file_context (void) +{ + if (!selinux_checked) { + selinux_enabled = is_selinux_enabled () > 0; + selinux_checked = true; + } + if (selinux_enabled) { + if (setfscreatecon_raw (NULL) != 0) { + return security_getenforce () != 0; + } + } + return 0; +} + +/* + * Log callback for libselinux internal error reporting. + */ +format_attr(printf, 2, 3) +static int selinux_log_cb (int type, const char *fmt, ...) { + va_list ap; + char *buf; + int r; +#ifdef WITH_AUDIT + static int selinux_audit_fd = -2; +#endif + + va_start (ap, fmt); + r = vasprintf (&buf, fmt, ap); + va_end (ap); + + if (r < 0) { + return 0; + } + +#ifdef WITH_AUDIT + if (-2 == selinux_audit_fd) { + selinux_audit_fd = audit_open (); + + if (-1 == selinux_audit_fd) { + /* You get these only when the kernel doesn't have + * audit compiled in. */ + if ( (errno != EINVAL) + && (errno != EPROTONOSUPPORT) + && (errno != EAFNOSUPPORT)) { + + (void) fputs (_("Cannot open audit interface.\n"), + shadow_logfd); + SYSLOG ((LOG_WARN, "Cannot open audit interface.")); + } + } + } + + if (-1 != selinux_audit_fd) { + if (SELINUX_AVC == type) { + if (audit_log_user_avc_message (selinux_audit_fd, + AUDIT_USER_AVC, buf, NULL, NULL, + NULL, 0) > 0) { + goto skip_syslog; + } + } else if (SELINUX_ERROR == type) { + if (audit_log_user_avc_message (selinux_audit_fd, + AUDIT_USER_SELINUX_ERR, buf, NULL, NULL, + NULL, 0) > 0) { + goto skip_syslog; + } + } + } +#endif + + SYSLOG ((LOG_WARN, "libselinux: %s", buf)); + +skip_syslog: + free (buf); + + return 0; +} + +/* + * check_selinux_permit - Check whether SELinux grants the given + * operation + * + * Parameter is the SELinux permission name, e.g. rootok + * + * Returns 0 when permission is granted + * or something failed but running in + * permissive mode + */ +int check_selinux_permit (const char *perm_name) +{ + char *user_context_raw; + int r; + + if (0 == is_selinux_enabled ()) { + return 0; + } + + selinux_set_callback (SELINUX_CB_LOG, (union selinux_callback) selinux_log_cb); + + if (getprevcon_raw (&user_context_raw) != 0) { + fprintf (shadow_logfd, + _("%s: can not get previous SELinux process context: %s\n"), + shadow_progname, strerror (errno)); + SYSLOG ((LOG_WARN, + "can not get previous SELinux process context: %s", + strerror (errno))); + return (security_getenforce () != 0); + } + + r = selinux_check_access (user_context_raw, user_context_raw, "passwd", perm_name, NULL); + freecon (user_context_raw); + return r; +} + +#else /* !WITH_SELINUX */ +extern int errno; /* warning: ANSI C forbids an empty source file */ +#endif /* !WITH_SELINUX */ diff --git a/lib/semanage.c b/lib/semanage.c new file mode 100644 index 0000000..082a6e8 --- /dev/null +++ b/lib/semanage.c @@ -0,0 +1,361 @@ +/* + * SPDX-FileCopyrightText: 2010 , Jakub Hrozek <jhrozek@redhat.com> + * SPDX-FileCopyrightText: 2011 , Peter Vrabec <pvrabec@redhat.com> + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ifdef WITH_SELINUX + +#include "defines.h" + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include <stdio.h> +#include <stdarg.h> +#include <selinux/selinux.h> +#include <semanage/semanage.h> +#include "prototypes.h" + +#include "shadowlog_internal.h" + +#ifndef DEFAULT_SERANGE +#define DEFAULT_SERANGE "s0" +#endif + + +format_attr(printf, 3, 4) +static void semanage_error_callback (unused void *varg, + semanage_handle_t *handle, + const char *fmt, ...) +{ + int ret; + char * message = NULL; + va_list ap; + + + va_start (ap, fmt); + ret = vasprintf (&message, fmt, ap); + va_end (ap); + if (ret < 0) { + /* ENOMEM */ + return; + } + + switch (semanage_msg_get_level (handle)) { + case SEMANAGE_MSG_ERR: + case SEMANAGE_MSG_WARN: + fprintf (shadow_logfd, _("[libsemanage]: %s\n"), message); + break; + case SEMANAGE_MSG_INFO: + /* nop */ + break; + } + + free (message); +} + + +static semanage_handle_t *semanage_init (void) +{ + int ret; + semanage_handle_t *handle = NULL; + + handle = semanage_handle_create (); + if (NULL == handle) { + fprintf (shadow_logfd, + _("Cannot create SELinux management handle\n")); + return NULL; + } + + semanage_msg_set_callback (handle, semanage_error_callback, NULL); + + ret = semanage_is_managed (handle); + if (ret != 1) { + fprintf (shadow_logfd, _("SELinux policy not managed\n")); + goto fail; + } + + ret = semanage_access_check (handle); + if (ret < SEMANAGE_CAN_READ) { + fprintf (shadow_logfd, _("Cannot read SELinux policy store\n")); + goto fail; + } + + ret = semanage_connect (handle); + if (ret != 0) { + fprintf (shadow_logfd, + _("Cannot establish SELinux management connection\n")); + goto fail; + } + + ret = semanage_begin_transaction (handle); + if (ret != 0) { + fprintf (shadow_logfd, _("Cannot begin SELinux transaction\n")); + goto fail; + } + + return handle; + +fail: + semanage_handle_destroy (handle); + return NULL; +} + + +static int semanage_user_mod (semanage_handle_t *handle, + semanage_seuser_key_t *key, + const char *login_name, + const char *seuser_name) +{ + int ret; + semanage_seuser_t *seuser = NULL; + + semanage_seuser_query (handle, key, &seuser); + if (NULL == seuser) { + fprintf (shadow_logfd, + _("Could not query seuser for %s\n"), login_name); + ret = 1; + goto done; + } + + if (semanage_mls_enabled(handle)) { + ret = semanage_seuser_set_mlsrange (handle, seuser, DEFAULT_SERANGE); + if (ret != 0) { + fprintf (shadow_logfd, + _("Could not set serange for %s\n"), login_name); + ret = 1; + goto done; + } + } + + ret = semanage_seuser_set_sename (handle, seuser, seuser_name); + if (ret != 0) { + fprintf (shadow_logfd, + _("Could not set sename for %s\n"), + login_name); + ret = 1; + goto done; + } + + ret = semanage_seuser_modify_local (handle, key, seuser); + if (ret != 0) { + fprintf (shadow_logfd, + _("Could not modify login mapping for %s\n"), + login_name); + ret = 1; + goto done; + } + + ret = 0; +done: + semanage_seuser_free (seuser); + return ret; +} + + +static int semanage_user_add (semanage_handle_t *handle, + semanage_seuser_key_t *key, + const char *login_name, + const char *seuser_name) +{ + int ret; + semanage_seuser_t *seuser = NULL; + + ret = semanage_seuser_create (handle, &seuser); + if (ret != 0) { + fprintf (shadow_logfd, + _("Cannot create SELinux login mapping for %s\n"), + login_name); + ret = 1; + goto done; + } + + ret = semanage_seuser_set_name (handle, seuser, login_name); + if (ret != 0) { + fprintf (shadow_logfd, _("Could not set name for %s\n"), login_name); + ret = 1; + goto done; + } + + if (semanage_mls_enabled(handle)) { + ret = semanage_seuser_set_mlsrange (handle, seuser, DEFAULT_SERANGE); + if (ret != 0) { + fprintf (shadow_logfd, + _("Could not set serange for %s\n"), login_name); + ret = 1; + goto done; + } + } + + ret = semanage_seuser_set_sename (handle, seuser, seuser_name); + if (ret != 0) { + fprintf (shadow_logfd, + _("Could not set SELinux user for %s\n"), + login_name); + ret = 1; + goto done; + } + + ret = semanage_seuser_modify_local (handle, key, seuser); + if (ret != 0) { + fprintf (shadow_logfd, + _("Could not add login mapping for %s\n"), + login_name); + ret = 1; + goto done; + } + + ret = 0; +done: + semanage_seuser_free (seuser); + return ret; +} + + +int set_seuser (const char *login_name, const char *seuser_name) +{ + semanage_handle_t *handle = NULL; + semanage_seuser_key_t *key = NULL; + int ret; + int seuser_exists = 0; + + if (NULL == seuser_name) { + /* don't care, just let system pick the defaults */ + return 0; + } + + handle = semanage_init (); + if (NULL == handle) { + fprintf (shadow_logfd, _("Cannot init SELinux management\n")); + ret = 1; + goto done; + } + + ret = semanage_seuser_key_create (handle, login_name, &key); + if (ret != 0) { + fprintf (shadow_logfd, _("Cannot create SELinux user key\n")); + ret = 1; + goto done; + } + + ret = semanage_seuser_exists (handle, key, &seuser_exists); + if (ret < 0) { + fprintf (shadow_logfd, _("Cannot verify the SELinux user\n")); + ret = 1; + goto done; + } + + if (0 != seuser_exists) { + ret = semanage_user_mod (handle, key, login_name, seuser_name); + if (ret != 0) { + fprintf (shadow_logfd, + _("Cannot modify SELinux user mapping\n")); + ret = 1; + goto done; + } + } else { + ret = semanage_user_add (handle, key, login_name, seuser_name); + if (ret != 0) { + fprintf (shadow_logfd, + _("Cannot add SELinux user mapping\n")); + ret = 1; + goto done; + } + } + + ret = semanage_commit (handle); + if (ret < 0) { + fprintf (shadow_logfd, _("Cannot commit SELinux transaction\n")); + ret = 1; + goto done; + } + + ret = 0; + reset_selinux_handle(); + +done: + semanage_seuser_key_free (key); + semanage_handle_destroy (handle); + return ret; +} + + +int del_seuser (const char *login_name) +{ + semanage_handle_t *handle = NULL; + semanage_seuser_key_t *key = NULL; + int ret; + int exists = 0; + + handle = semanage_init (); + if (NULL == handle) { + fprintf (shadow_logfd, _("Cannot init SELinux management\n")); + ret = 1; + goto done; + } + + ret = semanage_seuser_key_create (handle, login_name, &key); + if (ret != 0) { + fprintf (shadow_logfd, _("Cannot create SELinux user key\n")); + ret = 1; + goto done; + } + + ret = semanage_seuser_exists (handle, key, &exists); + if (ret < 0) { + fprintf (shadow_logfd, _("Cannot verify the SELinux user\n")); + ret = 1; + goto done; + } + + if (0 == exists) { + fprintf (shadow_logfd, + _("Login mapping for %s is not defined, OK if default mapping was used\n"), + login_name); + ret = 0; /* probably default mapping */ + goto done; + } + + ret = semanage_seuser_exists_local (handle, key, &exists); + if (ret < 0) { + fprintf (shadow_logfd, _("Cannot verify the SELinux user\n")); + ret = 1; + goto done; + } + + if (0 == exists) { + fprintf (shadow_logfd, + _("Login mapping for %s is defined in policy, cannot be deleted\n"), + login_name); + ret = 0; /* Login mapping defined in policy can't be deleted */ + goto done; + } + + ret = semanage_seuser_del_local (handle, key); + if (ret != 0) { + fprintf (shadow_logfd, + _("Could not delete login mapping for %s"), + login_name); + ret = 1; + goto done; + } + + ret = semanage_commit (handle); + if (ret < 0) { + fprintf (shadow_logfd, _("Cannot commit SELinux transaction\n")); + ret = 1; + goto done; + } + + ret = 0; +done: + semanage_handle_destroy (handle); + return ret; +} +#else /* !WITH_SELINUX */ +extern int errno; /* warning: ANSI C forbids an empty source file */ +#endif /* !WITH_SELINUX */ diff --git a/lib/sgetgrent.c b/lib/sgetgrent.c new file mode 100644 index 0000000..ad4fcc8 --- /dev/null +++ b/lib/sgetgrent.c @@ -0,0 +1,129 @@ +/* + * SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1998, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2005 , Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2008 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#include <stdio.h> +#include <sys/types.h> +#include <grp.h> +#include "defines.h" +#include "prototypes.h" + +#define NFIELDS 4 + +/* + * list - turn a comma-separated string into an array of (char *)'s + * + * list() converts the comma-separated list of member names into + * an array of character pointers. + * + * WARNING: I profiled this once with and without strchr() calls + * and found that using a register variable and an explicit loop + * works best. For large /etc/group files, this is a major win. + * + * FINALLY added dynamic allocation. Still need to fix sgetsgent(). + * --marekm + */ +static char **list (char *s) +{ + static char **members = 0; + static int size = 0; /* max members + 1 */ + int i; + char **rbuf; + + i = 0; + for (;;) { + /* check if there is room for another pointer (to a group + member name, or terminating NULL). */ + if (i >= size) { + size = i + 100; /* at least: i + 1 */ + if (members) { + rbuf = + realloc (members, size * sizeof (char *)); + } else { + /* for old (before ANSI C) implementations of + realloc() that don't handle NULL properly */ + rbuf = malloc (size * sizeof (char *)); + } + if (!rbuf) { + free (members); + members = 0; + size = 0; + return (char **) 0; + } + members = rbuf; + } + if (!s || s[0] == '\0') + break; + members[i++] = s; + while (('\0' != *s) && (',' != *s)) { + s++; + } + if ('\0' != *s) { + *s++ = '\0'; + } + } + members[i] = (char *) 0; + return members; +} + + +struct group *sgetgrent (const char *buf) +{ + static char *grpbuf = 0; + static size_t size = 0; + static char *grpfields[NFIELDS]; + static struct group grent; + int i; + char *cp; + + if (strlen (buf) + 1 > size) { + /* no need to use realloc() here - just free it and + allocate a larger block */ + free (grpbuf); + size = strlen (buf) + 1000; /* at least: strlen(buf) + 1 */ + grpbuf = malloc (size); + if (!grpbuf) { + size = 0; + return 0; + } + } + strcpy (grpbuf, buf); + + cp = strrchr (grpbuf, '\n'); + if (NULL != cp) { + *cp = '\0'; + } + + for (cp = grpbuf, i = 0; (i < NFIELDS) && (NULL != cp); i++) { + grpfields[i] = cp; + cp = strchr (cp, ':'); + if (NULL != cp) { + *cp = '\0'; + cp++; + } + } + if (i < (NFIELDS - 1) || *grpfields[2] == '\0' || cp != NULL) { + return (struct group *) 0; + } + grent.gr_name = grpfields[0]; + grent.gr_passwd = grpfields[1]; + if (get_gid (grpfields[2], &grent.gr_gid) == 0) { + return (struct group *) 0; + } + grent.gr_mem = list (grpfields[3]); + if (NULL == grent.gr_mem) { + return (struct group *) 0; /* out of memory */ + } + + return &grent; +} + diff --git a/lib/sgetpwent.c b/lib/sgetpwent.c new file mode 100644 index 0000000..1c8c63e --- /dev/null +++ b/lib/sgetpwent.c @@ -0,0 +1,109 @@ +/* + * SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1998, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2008 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#include <sys/types.h> +#include "defines.h" +#include <stdio.h> +#include <pwd.h> +#include "prototypes.h" +#include "shadowlog_internal.h" + +#define NFIELDS 7 + +/* + * sgetpwent - convert a string to a (struct passwd) + * + * sgetpwent() parses a string into the parts required for a password + * structure. Strict checking is made for the UID and GID fields and + * presence of the correct number of colons. Any failing tests result + * in a NULL pointer being returned. + * + * NOTE: This function uses hard-coded string scanning functions for + * performance reasons. I am going to come up with some conditional + * compilation glarp to improve on this in the future. + */ +struct passwd *sgetpwent (const char *buf) +{ + static struct passwd pwent; + static char pwdbuf[PASSWD_ENTRY_MAX_LENGTH]; + int i; + char *cp; + char *fields[NFIELDS]; + + /* + * Copy the string to a static buffer so the pointers into + * the password structure remain valid. + */ + + if (strlen (buf) >= sizeof pwdbuf) { + fprintf (shadow_logfd, + "%s: Too long passwd entry encountered, file corruption?\n", + shadow_progname); + return 0; /* fail if too long */ + } + strcpy (pwdbuf, buf); + + /* + * Save a pointer to the start of each colon separated + * field. The fields are converted into NUL terminated strings. + */ + + for (cp = pwdbuf, i = 0; (i < NFIELDS) && (NULL != cp); i++) { + fields[i] = cp; + while (('\0' != *cp) && (':' != *cp)) { + cp++; + } + + if ('\0' != *cp) { + *cp = '\0'; + cp++; + } else { + cp = NULL; + } + } + + /* something at the end, columns over shot */ + if ( cp != NULL ) { + return( NULL ); + } + + /* + * There must be exactly NFIELDS colon separated fields or + * the entry is invalid. Also, the UID and GID must be non-blank. + */ + + if (i != NFIELDS || *fields[2] == '\0' || *fields[3] == '\0') + return NULL; + + /* + * Each of the fields is converted the appropriate data type + * and the result assigned to the password structure. If the + * UID or GID does not convert to an integer value, a NULL + * pointer is returned. + */ + + pwent.pw_name = fields[0]; + pwent.pw_passwd = fields[1]; + if (get_uid (fields[2], &pwent.pw_uid) == 0) { + return NULL; + } + if (get_gid (fields[3], &pwent.pw_gid) == 0) { + return NULL; + } + pwent.pw_gecos = fields[4]; + pwent.pw_dir = fields[5]; + pwent.pw_shell = fields[6]; + + return &pwent; +} + diff --git a/lib/sgetspent.c b/lib/sgetspent.c new file mode 100644 index 0000000..f1d4b20 --- /dev/null +++ b/lib/sgetspent.c @@ -0,0 +1,187 @@ +/* + * SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1998, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2009 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +/* Newer versions of Linux libc already have shadow support. */ +#ifndef HAVE_SGETSPENT + +#ident "$Id$" + +#include <sys/types.h> +#include "prototypes.h" +#include "shadowlog_internal.h" +#include "defines.h" +#include <stdio.h> +#define FIELDS 9 +#define OFIELDS 5 +/* + * sgetspent - convert string in shadow file format to (struct spwd *) + */ +struct spwd *sgetspent (const char *string) +{ + static char spwbuf[PASSWD_ENTRY_MAX_LENGTH]; + static struct spwd spwd; + char *fields[FIELDS]; + char *cp; + int i; + + /* + * Copy string to local buffer. It has to be tokenized and we + * have to do that to our private copy. + */ + + if (strlen (string) >= sizeof spwbuf) { + fprintf (shadow_logfd, + "%s: Too long passwd entry encountered, file corruption?\n", + shadow_progname); + return 0; /* fail if too long */ + } + strcpy (spwbuf, string); + + cp = strrchr (spwbuf, '\n'); + if (NULL != cp) { + *cp = '\0'; + } + + /* + * Tokenize the string into colon separated fields. Allow up to + * FIELDS different fields. + */ + + for (cp = spwbuf, i = 0; ('\0' != *cp) && (i < FIELDS); i++) { + fields[i] = cp; + while (('\0' != *cp) && (':' != *cp)) { + cp++; + } + + if ('\0' != *cp) { + *cp = '\0'; + cp++; + } + } + + if (i == (FIELDS - 1)) { + fields[i++] = cp; + } + + if ( ((NULL != cp) && ('\0' != *cp)) || + ((i != FIELDS) && (i != OFIELDS)) ) { + return 0; + } + + /* + * Start populating the structure. The fields are all in + * static storage, as is the structure we pass back. + */ + + spwd.sp_namp = fields[0]; + spwd.sp_pwdp = fields[1]; + + /* + * Get the last changed date. For all of the integer fields, + * we check for proper format. It is an error to have an + * incorrectly formatted number. + */ + + if (fields[2][0] == '\0') { + spwd.sp_lstchg = -1; + } else if ( (getlong (fields[2], &spwd.sp_lstchg) == 0) + || (spwd.sp_lstchg < 0)) { + return 0; + } + + /* + * Get the minimum period between password changes. + */ + + if (fields[3][0] == '\0') { + spwd.sp_min = -1; + } else if ( (getlong (fields[3], &spwd.sp_min) == 0) + || (spwd.sp_min < 0)) { + return 0; + } + + /* + * Get the maximum number of days a password is valid. + */ + + if (fields[4][0] == '\0') { + spwd.sp_max = -1; + } else if ( (getlong (fields[4], &spwd.sp_max) == 0) + || (spwd.sp_max < 0)) { + return 0; + } + + /* + * If there are only OFIELDS fields (this is a SVR3.2 /etc/shadow + * formatted file), initialize the other field members to -1. + */ + + if (i == OFIELDS) { + spwd.sp_warn = -1; + spwd.sp_inact = -1; + spwd.sp_expire = -1; + spwd.sp_flag = SHADOW_SP_FLAG_UNSET; + + return &spwd; + } + + /* + * Get the number of days of password expiry warning. + */ + + if (fields[5][0] == '\0') { + spwd.sp_warn = -1; + } else if ( (getlong (fields[5], &spwd.sp_warn) == 0) + || (spwd.sp_warn < 0)) { + return 0; + } + + /* + * Get the number of days of inactivity before an account is + * disabled. + */ + + if (fields[6][0] == '\0') { + spwd.sp_inact = -1; + } else if ( (getlong (fields[6], &spwd.sp_inact) == 0) + || (spwd.sp_inact < 0)) { + return 0; + } + + /* + * Get the number of days after the epoch before the account is + * set to expire. + */ + + if (fields[7][0] == '\0') { + spwd.sp_expire = -1; + } else if ( (getlong (fields[7], &spwd.sp_expire) == 0) + || (spwd.sp_expire < 0)) { + return 0; + } + + /* + * This field is reserved for future use. But it isn't supposed + * to have anything other than a valid integer in it. + */ + + if (fields[8][0] == '\0') { + spwd.sp_flag = SHADOW_SP_FLAG_UNSET; + } else if (getulong (fields[8], &spwd.sp_flag) == 0) { + return 0; + } + + return (&spwd); +} +#else +extern int errno; /* warning: ANSI C forbids an empty source file */ +#endif + diff --git a/lib/sgroupio.c b/lib/sgroupio.c new file mode 100644 index 0000000..871749b --- /dev/null +++ b/lib/sgroupio.c @@ -0,0 +1,306 @@ +/* + * SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 2000, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2001 , MichaÅ‚ Moskal + * SPDX-FileCopyrightText: 2005 , Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2007 - 2013, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ifdef SHADOWGRP + +#ident "$Id$" + +#include "prototypes.h" +#include "defines.h" +#include "commonio.h" +#include "getdef.h" +#include "sgroupio.h" + +/*@null@*/ /*@only@*/struct sgrp *__sgr_dup (const struct sgrp *sgent) +{ + struct sgrp *sg; + int i; + + sg = (struct sgrp *) malloc (sizeof *sg); + if (NULL == sg) { + return NULL; + } + /* Do the same as the other _dup function, even if we know the + * structure. */ + memset (sg, 0, sizeof *sg); + /*@-mustfreeonly@*/ + sg->sg_name = strdup (sgent->sg_name); + /*@=mustfreeonly@*/ + if (NULL == sg->sg_name) { + free (sg); + return NULL; + } + /*@-mustfreeonly@*/ + sg->sg_passwd = strdup (sgent->sg_passwd); + /*@=mustfreeonly@*/ + if (NULL == sg->sg_passwd) { + free (sg->sg_name); + free (sg); + return NULL; + } + + for (i = 0; NULL != sgent->sg_adm[i]; i++); + /*@-mustfreeonly@*/ + sg->sg_adm = (char **) malloc ((i + 1) * sizeof (char *)); + /*@=mustfreeonly@*/ + if (NULL == sg->sg_adm) { + free (sg->sg_passwd); + free (sg->sg_name); + free (sg); + return NULL; + } + for (i = 0; NULL != sgent->sg_adm[i]; i++) { + sg->sg_adm[i] = strdup (sgent->sg_adm[i]); + if (NULL == sg->sg_adm[i]) { + for (i = 0; NULL != sg->sg_adm[i]; i++) { + free (sg->sg_adm[i]); + } + free (sg->sg_adm); + free (sg->sg_passwd); + free (sg->sg_name); + free (sg); + return NULL; + } + } + sg->sg_adm[i] = NULL; + + for (i = 0; NULL != sgent->sg_mem[i]; i++); + /*@-mustfreeonly@*/ + sg->sg_mem = (char **) malloc ((i + 1) * sizeof (char *)); + /*@=mustfreeonly@*/ + if (NULL == sg->sg_mem) { + for (i = 0; NULL != sg->sg_adm[i]; i++) { + free (sg->sg_adm[i]); + } + free (sg->sg_adm); + free (sg->sg_passwd); + free (sg->sg_name); + free (sg); + return NULL; + } + for (i = 0; NULL != sgent->sg_mem[i]; i++) { + sg->sg_mem[i] = strdup (sgent->sg_mem[i]); + if (NULL == sg->sg_mem[i]) { + for (i = 0; NULL != sg->sg_mem[i]; i++) { + free (sg->sg_mem[i]); + } + free (sg->sg_mem); + for (i = 0; NULL != sg->sg_adm[i]; i++) { + free (sg->sg_adm[i]); + } + free (sg->sg_adm); + free (sg->sg_passwd); + free (sg->sg_name); + free (sg); + return NULL; + } + } + sg->sg_mem[i] = NULL; + + return sg; +} + +static /*@null@*/ /*@only@*/void *gshadow_dup (const void *ent) +{ + const struct sgrp *sg = ent; + + return __sgr_dup (sg); +} + +static void gshadow_free (/*@out@*/ /*@only@*/void *ent) +{ + struct sgrp *sg = ent; + + sgr_free (sg); +} + +void sgr_free (/*@out@*/ /*@only@*/struct sgrp *sgent) +{ + size_t i; + free (sgent->sg_name); + if (NULL != sgent->sg_passwd) { + strzero (sgent->sg_passwd); + free (sgent->sg_passwd); + } + for (i = 0; NULL != sgent->sg_adm[i]; i++) { + free (sgent->sg_adm[i]); + } + free (sgent->sg_adm); + for (i = 0; NULL != sgent->sg_mem[i]; i++) { + free (sgent->sg_mem[i]); + } + free (sgent->sg_mem); + free (sgent); +} + +static const char *gshadow_getname (const void *ent) +{ + const struct sgrp *gr = ent; + + return gr->sg_name; +} + +static void *gshadow_parse (const char *line) +{ + return (void *) sgetsgent (line); +} + +static int gshadow_put (const void *ent, FILE * file) +{ + const struct sgrp *sg = ent; + + if ( (NULL == sg) + || (valid_field (sg->sg_name, ":\n") == -1) + || (valid_field (sg->sg_passwd, ":\n") == -1)) { + return -1; + } + + /* FIXME: fail also if sg->sg_adm == NULL ?*/ + if (NULL != sg->sg_adm) { + size_t i; + for (i = 0; NULL != sg->sg_adm[i]; i++) { + if (valid_field (sg->sg_adm[i], ",:\n") == -1) { + return -1; + } + } + } + + /* FIXME: fail also if sg->sg_mem == NULL ?*/ + if (NULL != sg->sg_mem) { + size_t i; + for (i = 0; NULL != sg->sg_mem[i]; i++) { + if (valid_field (sg->sg_mem[i], ",:\n") == -1) { + return -1; + } + } + } + + return (putsgent (sg, file) == -1) ? -1 : 0; +} + +static struct commonio_ops gshadow_ops = { + gshadow_dup, + gshadow_free, + gshadow_getname, + gshadow_parse, + gshadow_put, + fgetsx, + fputsx, + NULL, /* open_hook */ + NULL /* close_hook */ +}; + +static struct commonio_db gshadow_db = { + SGROUP_FILE, /* filename */ + &gshadow_ops, /* ops */ + NULL, /* fp */ +#ifdef WITH_SELINUX + NULL, /* scontext */ +#endif + 0400, /* st_mode */ + 0, /* st_uid */ + 0, /* st_gid */ + NULL, /* head */ + NULL, /* tail */ + NULL, /* cursor */ + false, /* changed */ + false, /* isopen */ + false, /* locked */ + false, /* readonly */ + false /* setname */ +}; + +int sgr_setdbname (const char *filename) +{ + return commonio_setname (&gshadow_db, filename); +} + +/*@observer@*/const char *sgr_dbname (void) +{ + return gshadow_db.filename; +} + +bool sgr_file_present (void) +{ + if (getdef_bool ("FORCE_SHADOW")) + return true; + return commonio_present (&gshadow_db); +} + +int sgr_lock (void) +{ + return commonio_lock (&gshadow_db); +} + +int sgr_open (int mode) +{ + return commonio_open (&gshadow_db, mode); +} + +/*@observer@*/ /*@null@*/const struct sgrp *sgr_locate (const char *name) +{ + return commonio_locate (&gshadow_db, name); +} + +int sgr_update (const struct sgrp *sg) +{ + return commonio_update (&gshadow_db, (const void *) sg); +} + +int sgr_remove (const char *name) +{ + return commonio_remove (&gshadow_db, name); +} + +int sgr_rewind (void) +{ + return commonio_rewind (&gshadow_db); +} + +/*@null@*/const struct sgrp *sgr_next (void) +{ + return commonio_next (&gshadow_db); +} + +int sgr_close (void) +{ + return commonio_close (&gshadow_db); +} + +int sgr_unlock (void) +{ + return commonio_unlock (&gshadow_db); +} + +void __sgr_set_changed (void) +{ + gshadow_db.changed = true; +} + +/*@dependent@*/ /*@null@*/struct commonio_entry *__sgr_get_head (void) +{ + return gshadow_db.head; +} + +void __sgr_del_entry (const struct commonio_entry *ent) +{ + commonio_del_entry (&gshadow_db, ent); +} + +/* Sort with respect to group ordering. */ +int sgr_sort () +{ + return commonio_sort_wrt (&gshadow_db, __gr_get_db ()); +} +#else +extern int errno; /* warning: ANSI C forbids an empty source file */ +#endif diff --git a/lib/sgroupio.h b/lib/sgroupio.h new file mode 100644 index 0000000..3474a98 --- /dev/null +++ b/lib/sgroupio.h @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 2000, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2001 , MichaÅ‚ Moskal + * SPDX-FileCopyrightText: 2005 , Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2008 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* $Id$ */ +#ifndef _SGROUPIO_H +#define _SGROUPIO_H + +extern int sgr_close (void); +extern bool sgr_file_present (void); +extern /*@observer@*/ /*@null@*/const struct sgrp *sgr_locate (const char *name); +extern int sgr_lock (void); +extern int sgr_setdbname (const char *filename); +extern /*@observer@*/const char *sgr_dbname (void); +extern /*@null@*/const struct sgrp *sgr_next (void); +extern int sgr_open (int mode); +extern int sgr_remove (const char *name); +extern int sgr_rewind (void); +extern int sgr_unlock (void); +extern int sgr_update (const struct sgrp *sg); +extern int sgr_sort (void); + +#endif diff --git a/lib/shadow.c b/lib/shadow.c new file mode 100644 index 0000000..b628b65 --- /dev/null +++ b/lib/shadow.c @@ -0,0 +1,530 @@ +/* + * SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1998, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2009 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +/* Newer versions of Linux libc already have shadow support. */ +#ifndef HAVE_GETSPNAM + +#ident "$Id$" + +#include <sys/types.h> +#include "prototypes.h" +#include "defines.h" +#include <stdio.h> +#ifdef USE_NIS +static bool nis_used; +static bool nis_ignore; +static enum { native, start, middle, native2 } nis_state; +static bool nis_bound; +static char *nis_domain; +static char *nis_key; +static int nis_keylen; +static char *nis_val; +static int nis_vallen; + +#define IS_NISCHAR(c) ((c)=='+') +#endif + +static FILE *shadow; + +#define FIELDS 9 +#define OFIELDS 5 + +#ifdef USE_NIS + +/* + * __setspNIS - turn on or off NIS searches + */ + +void __setspNIS (bool flag) +{ + nis_ignore = !flag; + + if (nis_ignore) { + nis_used = false; + } +} + +/* + * bind_nis - bind to NIS server + */ + +static int bind_nis (void) +{ + if (yp_get_default_domain (&nis_domain)) { + return -1; + } + + nis_bound = true; + return 0; +} +#endif + +/* + * setspent - initialize access to shadow text and DBM files + */ + +void setspent (void) +{ + if (NULL != shadow) { + rewind (shadow); + }else { + shadow = fopen (SHADOW_FILE, "r"); + } + +#ifdef USE_NIS + nis_state = native; +#endif +} + +/* + * endspent - terminate access to shadow text and DBM files + */ + +void endspent (void) +{ + if (NULL != shadow) { + (void) fclose (shadow); + } + + shadow = (FILE *) 0; +} + +/* + * my_sgetspent - convert string in shadow file format to (struct spwd *) + */ + +static struct spwd *my_sgetspent (const char *string) +{ + static char spwbuf[BUFSIZ]; + static struct spwd spwd; + char *fields[FIELDS]; + char *cp; + int i; + + /* + * Copy string to local buffer. It has to be tokenized and we + * have to do that to our private copy. + */ + + if (strlen (string) >= sizeof spwbuf) + return 0; + strcpy (spwbuf, string); + + cp = strrchr (spwbuf, '\n'); + if (NULL != cp) + *cp = '\0'; + + /* + * Tokenize the string into colon separated fields. Allow up to + * FIELDS different fields. + */ + + for (cp = spwbuf, i = 0; *cp && i < FIELDS; i++) { + fields[i] = cp; + while (*cp && *cp != ':') + cp++; + + if (*cp) + *cp++ = '\0'; + } + + if (i == (FIELDS - 1)) + fields[i++] = cp; + + if ((cp && *cp) || (i != FIELDS && i != OFIELDS)) + return 0; + + /* + * Start populating the structure. The fields are all in + * static storage, as is the structure we pass back. If we + * ever see a name with '+' as the first character, we try + * to turn on NIS processing. + */ + + spwd.sp_namp = fields[0]; +#ifdef USE_NIS + if (IS_NISCHAR (fields[0][0])) { + nis_used = true; + } +#endif + spwd.sp_pwdp = fields[1]; + + /* + * Get the last changed date. For all of the integer fields, + * we check for proper format. It is an error to have an + * incorrectly formatted number, unless we are using NIS. + */ + + if (fields[2][0] == '\0') { + spwd.sp_lstchg = -1; + } else { + if (getlong (fields[2], &spwd.sp_lstchg) == 0) { +#ifdef USE_NIS + if (nis_used) { + spwd.sp_lstchg = -1; + } else +#endif + return 0; + } else if (spwd.sp_lstchg < 0) { + return 0; + } + } + + /* + * Get the minimum period between password changes. + */ + + if (fields[3][0] == '\0') { + spwd.sp_min = -1; + } else { + if (getlong (fields[3], &spwd.sp_min) == 0) { +#ifdef USE_NIS + if (nis_used) { + spwd.sp_min = -1; + } else +#endif + { + return 0; + } + } else if (spwd.sp_min < 0) { + return 0; + } + } + + /* + * Get the maximum number of days a password is valid. + */ + + if (fields[4][0] == '\0') { + spwd.sp_max = -1; + } else { + if (getlong (fields[4], &spwd.sp_max) == 0) { +#ifdef USE_NIS + if (nis_used) { + spwd.sp_max = -1; + } else +#endif + return 0; + } else if (spwd.sp_max < 0) { + return 0; + } + } + + /* + * If there are only OFIELDS fields (this is a SVR3.2 /etc/shadow + * formatted file), initialize the other field members to -1. + */ + + if (i == OFIELDS) { + spwd.sp_warn = -1; + spwd.sp_inact = -1; + spwd.sp_expire = -1; + spwd.sp_flag = SHADOW_SP_FLAG_UNSET; + + return &spwd; + } + + /* + * Get the number of days of password expiry warning. + */ + + if (fields[5][0] == '\0') { + spwd.sp_warn = -1; + } else { + if (getlong (fields[5], &spwd.sp_warn) == 0) { +#ifdef USE_NIS + if (nis_used) { + spwd.sp_warn = -1; + } else +#endif + { + return 0; + } + } else if (spwd.sp_warn < 0) { + return 0; + } + } + + /* + * Get the number of days of inactivity before an account is + * disabled. + */ + + if (fields[6][0] == '\0') { + spwd.sp_inact = -1; + } else { + if (getlong (fields[6], &spwd.sp_inact) == 0) { +#ifdef USE_NIS + if (nis_used) { + spwd.sp_inact = -1; + } else +#endif + { + return 0; + } + } else if (spwd.sp_inact < 0) { + return 0; + } + } + + /* + * Get the number of days after the epoch before the account is + * set to expire. + */ + + if (fields[7][0] == '\0') { + spwd.sp_expire = -1; + } else { + if (getlong (fields[7], &spwd.sp_expire) == 0) { +#ifdef USE_NIS + if (nis_used) { + spwd.sp_expire = -1; + } else +#endif + { + return 0; + } + } else if (spwd.sp_expire < 0) { + return 0; + } + } + + /* + * This field is reserved for future use. But it isn't supposed + * to have anything other than a valid integer in it. + */ + + if (fields[8][0] == '\0') { + spwd.sp_flag = SHADOW_SP_FLAG_UNSET; + } else { + if (getulong (fields[8], &spwd.sp_flag) == 0) { +#ifdef USE_NIS + if (nis_used) { + spwd.sp_flag = SHADOW_SP_FLAG_UNSET; + } else +#endif + { + return 0; + } + } else if (spwd.sp_flag < 0) { + return 0; + } + } + + return (&spwd); +} + +/* + * fgetspent - get an entry from a /etc/shadow formatted stream + */ + +struct spwd *fgetspent (FILE * fp) +{ + char buf[BUFSIZ]; + char *cp; + + if (NULL == fp) { + return (0); + } + +#ifdef USE_NIS + while (fgets (buf, (int) sizeof buf, fp) != (char *) 0) +#else + if (fgets (buf, (int) sizeof buf, fp) != (char *) 0) +#endif + { + cp = strchr (buf, '\n'); + if (NULL != cp) { + *cp = '\0'; + } +#ifdef USE_NIS + if (nis_ignore && IS_NISCHAR (buf[0])) { + continue; + } +#endif + return my_sgetspent (buf); + } + return 0; +} + +/* + * getspent - get a (struct spwd *) from the current shadow file + */ + +struct spwd *getspent (void) +{ +#ifdef USE_NIS + int nis_1_user = 0; + struct spwd *val; +#endif + if (NULL == shadow) { + setspent (); + } + +#ifdef USE_NIS + again: + /* + * See if we are reading from the local file. + */ + + if (nis_state == native || nis_state == native2) { + + /* + * Get the next entry from the shadow file. Return NULL + * right away if there is none. + */ + + val = fgetspent (shadow); + if (NULL == val) + return 0; + + /* + * If this entry began with a NIS escape character, we have + * to see if this is just a single user, or if the entire + * map is being asked for. + */ + + if (IS_NISCHAR (val->sp_namp[0])) { + if (val->sp_namp[1]) + nis_1_user = 1; + else + nis_state = start; + } + + /* + * If this isn't a NIS user and this isn't an escape to go + * use a NIS map, it must be a regular local user. + */ + + if (nis_1_user == 0 && nis_state != start) + return val; + + /* + * If this is an escape to use an NIS map, switch over to + * that bunch of code. + */ + + if (nis_state == start) + goto again; + + /* + * NEEDSWORK. Here we substitute pieces-parts of this entry. + */ + + return 0; + } else { + if (!nis_bound) { + if (bind_nis ()) { + nis_state = native2; + goto again; + } + } + if (nis_state == start) { + if (yp_first (nis_domain, "shadow.bynam", &nis_key, + &nis_keylen, &nis_val, &nis_vallen)) { + nis_state = native2; + goto again; + } + nis_state = middle; + } else if (nis_state == middle) { + if (yp_next (nis_domain, "shadow.bynam", nis_key, + nis_keylen, &nis_key, &nis_keylen, + &nis_val, &nis_vallen)) { + nis_state = native2; + goto again; + } + } + return my_sgetspent (nis_val); + } +#else + return (fgetspent (shadow)); +#endif +} + +/* + * getspnam - get a shadow entry by name + */ + +struct spwd *getspnam (const char *name) +{ + struct spwd *sp; + +#ifdef USE_NIS + static char save_name[16]; + bool nis_disabled = false; +#endif + + setspent (); + +#ifdef USE_NIS + /* + * Search the shadow.byname map for this user. + */ + + if (!nis_ignore && !nis_bound) { + bind_nis (); + } + + if (!nis_ignore && nis_bound) { + char *cp; + + if (yp_match (nis_domain, "shadow.byname", name, + strlen (name), &nis_val, &nis_vallen) == 0) { + + cp = strchr (nis_val, '\n'); + if (NULL != cp) { + *cp = '\0'; + } + + nis_state = middle; + sp = my_sgetspent (nis_val); + if (NULL != sp) { + strcpy (save_name, sp->sp_namp); + nis_key = save_name; + nis_keylen = strlen (save_name); + } + endspent (); + return sp; + } else { + nis_state = native2; + } + } +#endif +#ifdef USE_NIS + /* + * NEEDSWORK -- this is a mess, and it is the same mess in the + * other three files. I can't just blindly turn off NIS because + * this might be the first pass through the local files. In + * that case, I never discover that NIS is present. + */ + + if (nis_used) { + nis_ignore = true; + nis_disabled = true; + } +#endif + while ((sp = getspent ()) != (struct spwd *) 0) { + if (strcmp (name, sp->sp_namp) == 0) { + break; + } + } +#ifdef USE_NIS + if (nis_disabled) { + nis_ignore = false; + } +#endif + endspent (); + return (sp); +} +#else +extern int errno; /* warning: ANSI C forbids an empty source file */ +#endif + diff --git a/lib/shadowio.c b/lib/shadowio.c new file mode 100644 index 0000000..683b6c8 --- /dev/null +++ b/lib/shadowio.c @@ -0,0 +1,247 @@ +/* + * SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 2000, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2001 , MichaÅ‚ Moskal + * SPDX-FileCopyrightText: 2005 , Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2007 - 2009, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#include "prototypes.h" +#include "defines.h" +#include <shadow.h> +#include <stdio.h> +#include "commonio.h" +#include "getdef.h" +#include "shadowio.h" +#ifdef WITH_TCB +#include <tcb.h> +#include "tcbfuncs.h" +#endif /* WITH_TCB */ + +static /*@null@*/ /*@only@*/void *shadow_dup (const void *ent) +{ + const struct spwd *sp = ent; + + return __spw_dup (sp); +} + +static void shadow_free (/*@out@*//*@only@*/void *ent) +{ + struct spwd *sp = ent; + + spw_free (sp); +} + +static const char *shadow_getname (const void *ent) +{ + const struct spwd *sp = ent; + + return sp->sp_namp; +} + +static void *shadow_parse (const char *line) +{ + return (void *) sgetspent (line); +} + +static int shadow_put (const void *ent, FILE * file) +{ + const struct spwd *sp = ent; + + if ( (NULL == sp) + || (valid_field (sp->sp_namp, ":\n") == -1) + || (valid_field (sp->sp_pwdp, ":\n") == -1) + || (strlen (sp->sp_namp) + strlen (sp->sp_pwdp) + + 1000 > PASSWD_ENTRY_MAX_LENGTH)) { + return -1; + } + + return (putspent (sp, file) == -1) ? -1 : 0; +} + +static struct commonio_ops shadow_ops = { + shadow_dup, + shadow_free, + shadow_getname, + shadow_parse, + shadow_put, + fgets, + fputs, + NULL, /* open_hook */ + NULL /* close_hook */ +}; + +static struct commonio_db shadow_db = { + SHADOW_FILE, /* filename */ + &shadow_ops, /* ops */ + NULL, /* fp */ +#ifdef WITH_SELINUX + NULL, /* scontext */ +#endif /* WITH_SELINUX */ + 0400, /* st_mode */ + 0, /* st_uid */ + 0, /* st_gid */ + NULL, /* head */ + NULL, /* tail */ + NULL, /* cursor */ + false, /* changed */ + false, /* isopen */ + false, /* locked */ + false, /* readonly */ + false /* setname */ +}; + +int spw_setdbname (const char *filename) +{ + return commonio_setname (&shadow_db, filename); +} + +/*@observer@*/const char *spw_dbname (void) +{ + return shadow_db.filename; +} + +bool spw_file_present (void) +{ + if (getdef_bool ("FORCE_SHADOW")) + return true; + return commonio_present (&shadow_db); +} + +int spw_lock (void) +{ +#ifdef WITH_TCB + int retval = 0; + + if (!getdef_bool ("USE_TCB")) { +#endif /* WITH_TCB */ + return commonio_lock (&shadow_db); +#ifdef WITH_TCB + } + if (shadowtcb_drop_priv () == SHADOWTCB_FAILURE) { + return 0; + } + if (lckpwdf_tcb (shadow_db.filename) == 0) { + shadow_db.locked = 1; + retval = 1; + } + if (shadowtcb_gain_priv () == SHADOWTCB_FAILURE) { + return 0; + } + return retval; +#endif /* WITH_TCB */ +} + +int spw_open (int mode) +{ + int retval = 0; +#ifdef WITH_TCB + bool use_tcb = getdef_bool ("USE_TCB"); + + if (use_tcb && (shadowtcb_drop_priv () == SHADOWTCB_FAILURE)) { + return 0; + } +#endif /* WITH_TCB */ + retval = commonio_open (&shadow_db, mode); +#ifdef WITH_TCB + if (use_tcb && (shadowtcb_gain_priv () == SHADOWTCB_FAILURE)) { + return 0; + } +#endif /* WITH_TCB */ + return retval; +} + +/*@observer@*/ /*@null@*/const struct spwd *spw_locate (const char *name) +{ + return commonio_locate (&shadow_db, name); +} + +int spw_update (const struct spwd *sp) +{ + return commonio_update (&shadow_db, (const void *) sp); +} + +int spw_remove (const char *name) +{ + return commonio_remove (&shadow_db, name); +} + +int spw_rewind (void) +{ + return commonio_rewind (&shadow_db); +} + +/*@observer@*/ /*@null@*/const struct spwd *spw_next (void) +{ + return commonio_next (&shadow_db); +} + +int spw_close (void) +{ + int retval = 0; +#ifdef WITH_TCB + bool use_tcb = getdef_bool ("USE_TCB"); + + if (use_tcb && (shadowtcb_drop_priv () == SHADOWTCB_FAILURE)) { + return 0; + } +#endif /* WITH_TCB */ + retval = commonio_close (&shadow_db); +#ifdef WITH_TCB + if (use_tcb && (shadowtcb_gain_priv () == SHADOWTCB_FAILURE)) { + return 0; + } +#endif /* WITH_TCB */ + return retval; +} + +int spw_unlock (void) +{ +#ifdef WITH_TCB + int retval = 0; + + if (!getdef_bool ("USE_TCB")) { +#endif /* WITH_TCB */ + return commonio_unlock (&shadow_db); +#ifdef WITH_TCB + } + if (shadowtcb_drop_priv () == SHADOWTCB_FAILURE) { + return 0; + } + if (ulckpwdf_tcb () == 0) { + shadow_db.locked = 0; + retval = 1; + } + if (shadowtcb_gain_priv () == SHADOWTCB_FAILURE) { + return 0; + } + return retval; +#endif /* WITH_TCB */ +} + +struct commonio_entry *__spw_get_head (void) +{ + return shadow_db.head; +} + +void __spw_del_entry (const struct commonio_entry *ent) +{ + commonio_del_entry (&shadow_db, ent); +} + +/* Sort with respect to passwd ordering. */ +int spw_sort () +{ +#ifdef WITH_TCB + if (getdef_bool ("USE_TCB")) { + return 0; + } +#endif /* WITH_TCB */ + return commonio_sort_wrt (&shadow_db, __pw_get_db ()); +} diff --git a/lib/shadowio.h b/lib/shadowio.h new file mode 100644 index 0000000..4dbeb6d --- /dev/null +++ b/lib/shadowio.h @@ -0,0 +1,30 @@ +/* + * SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 2000, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2001 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2008 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* $Id$ */ +#ifndef SHADOWIO_H +#define SHADOWIO_H + +#include "defines.h" + +extern int spw_close (void); +extern bool spw_file_present (void); +extern /*@observer@*/ /*@null@*/const struct spwd *spw_locate (const char *name); +extern int spw_lock (void); +extern int spw_setdbname (const char *filename); +extern /*@observer@*/const char *spw_dbname (void); +extern /*@observer@*/ /*@null@*/const struct spwd *spw_next (void); +extern int spw_open (int mode); +extern int spw_remove (const char *name); +extern int spw_rewind (void); +extern int spw_unlock (void); +extern int spw_update (const struct spwd *sp); +extern int spw_sort (void); + +#endif diff --git a/lib/shadowlog.c b/lib/shadowlog.c new file mode 100644 index 0000000..7bcc63c --- /dev/null +++ b/lib/shadowlog.c @@ -0,0 +1,31 @@ +#include "shadowlog.h" + +#include "lib/shadowlog_internal.h" + +const char *shadow_progname = "libshadow"; +FILE *shadow_logfd = NULL; + +void log_set_progname(const char *progname) +{ + shadow_progname = progname; +} + +const char *log_get_progname(void) +{ + return shadow_progname; +} + +void log_set_logfd(FILE *fd) +{ + if (NULL != fd) + shadow_logfd = fd; + else + shadow_logfd = stderr; +} + +FILE *log_get_logfd(void) +{ + if (shadow_logfd != NULL) + return shadow_logfd; + return stderr; +} diff --git a/lib/shadowlog.h b/lib/shadowlog.h new file mode 100644 index 0000000..52a0912 --- /dev/null +++ b/lib/shadowlog.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 , Serge Hallyn + * 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$ */ +#ifndef _LOG_H +#define _LOG_H +#include <stdio.h> + +extern void log_set_progname(const char *); +extern const char *log_get_progname(void); +extern void log_set_logfd(FILE *fd); +extern FILE *log_get_logfd(void); + +#endif diff --git a/lib/shadowlog_internal.h b/lib/shadowlog_internal.h new file mode 100644 index 0000000..72a0e0c --- /dev/null +++ b/lib/shadowlog_internal.h @@ -0,0 +1,7 @@ +#ifndef _SHADOWLOG_INTERNAL_H +#define _SHADOWLOG_INTERNAL_H + +extern const char *shadow_progname; /* Program name showed in error messages */ +extern FILE *shadow_logfd; /* file descripter to which error messages are printed */ + +#endif /* _SHADOWLOG_INTERNAL_H */ diff --git a/lib/shadowmem.c b/lib/shadowmem.c new file mode 100644 index 0000000..82f99e7 --- /dev/null +++ b/lib/shadowmem.c @@ -0,0 +1,68 @@ +/* + * SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 2000, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2001 , MichaÅ‚ Moskal + * SPDX-FileCopyrightText: 2005 , Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2007 - 2013, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#include "prototypes.h" +#include "defines.h" +#include <shadow.h> +#include <stdio.h> +#include "shadowio.h" + +/*@null@*/ /*@only@*/struct spwd *__spw_dup (const struct spwd *spent) +{ + struct spwd *sp; + + sp = (struct spwd *) malloc (sizeof *sp); + if (NULL == sp) { + return NULL; + } + /* The libc might define other fields. They won't be copied. */ + memset (sp, 0, sizeof *sp); + sp->sp_lstchg = spent->sp_lstchg; + sp->sp_min = spent->sp_min; + sp->sp_max = spent->sp_max; + sp->sp_warn = spent->sp_warn; + sp->sp_inact = spent->sp_inact; + sp->sp_expire = spent->sp_expire; + sp->sp_flag = spent->sp_flag; + /*@-mustfreeonly@*/ + sp->sp_namp = strdup (spent->sp_namp); + /*@=mustfreeonly@*/ + if (NULL == sp->sp_namp) { + free(sp); + return NULL; + } + /*@-mustfreeonly@*/ + sp->sp_pwdp = strdup (spent->sp_pwdp); + /*@=mustfreeonly@*/ + if (NULL == sp->sp_pwdp) { + free(sp->sp_namp); + free(sp); + return NULL; + } + + return sp; +} + +void spw_free (/*@out@*/ /*@only@*/struct spwd *spent) +{ + if (spent != NULL) { + free (spent->sp_namp); + if (NULL != spent->sp_pwdp) { + strzero (spent->sp_pwdp); + free (spent->sp_pwdp); + } + free (spent); + } +} + diff --git a/lib/spawn.c b/lib/spawn.c new file mode 100644 index 0000000..ce1a97d --- /dev/null +++ b/lib/spawn.c @@ -0,0 +1,63 @@ +/* + * SPDX-FileCopyrightText: 2011 , Jonathan Nieder + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#include <stdio.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include "exitcodes.h" +#include "prototypes.h" + +#include "shadowlog_internal.h" + +int run_command (const char *cmd, const char *argv[], + /*@null@*/const char *envp[], /*@out@*/int *status) +{ + pid_t pid, wpid; + + if (NULL == envp) { + envp = (const char **)environ; + } + + (void) fflush (stdout); + (void) fflush (shadow_logfd); + + pid = fork (); + if (0 == pid) { + (void) execve (cmd, (char * const *) argv, + (char * const *) envp); + if (ENOENT == errno) { + exit (E_CMD_NOTFOUND); + } + fprintf (shadow_logfd, "%s: cannot execute %s: %s\n", + shadow_progname, cmd, strerror (errno)); + exit (E_CMD_NOEXEC); + } else if ((pid_t)-1 == pid) { + fprintf (shadow_logfd, "%s: cannot execute %s: %s\n", + shadow_progname, cmd, strerror (errno)); + return -1; + } + + do { + wpid = waitpid (pid, status, 0); + if ((pid_t)-1 == wpid && errno == ECHILD) + break; + } while ( ((pid_t)-1 == wpid && errno == EINTR) + || ((pid_t)-1 != wpid && wpid != pid)); + + if ((pid_t)-1 == wpid) { + fprintf (shadow_logfd, "%s: waitpid (status: %d): %s\n", + shadow_progname, *status, strerror (errno)); + return -1; + } + + return 0; +} + diff --git a/lib/sssd.c b/lib/sssd.c new file mode 100644 index 0000000..786ccd6 --- /dev/null +++ b/lib/sssd.c @@ -0,0 +1,75 @@ +/* Author: Peter Vrabec <pvrabec@redhat.com> */ + +#include <config.h> +#ifdef USE_SSSD + +#include <stdio.h> +#include <sys/wait.h> +#include <sys/types.h> +#include "exitcodes.h" +#include "defines.h" +#include "prototypes.h" +#include "sssd.h" + +#include "shadowlog_internal.h" + +#define MSG_SSSD_FLUSH_CACHE_FAILED "%s: Failed to flush the sssd cache." + +int sssd_flush_cache (int dbflags) +{ + int status, code, rv; + const char *cmd = "/usr/sbin/sss_cache"; + char *sss_cache_args = NULL; + const char *spawnedArgs[] = {"sss_cache", NULL, NULL}; + const char *spawnedEnv[] = {NULL}; + int i = 0; + + sss_cache_args = malloc(4); + if (sss_cache_args == NULL) { + return -1; + } + + sss_cache_args[i++] = '-'; + if (dbflags & SSSD_DB_PASSWD) { + sss_cache_args[i++] = 'U'; + } + if (dbflags & SSSD_DB_GROUP) { + sss_cache_args[i++] = 'G'; + } + sss_cache_args[i++] = '\0'; + if (i == 2) { + /* Neither passwd nor group, nothing to do */ + free(sss_cache_args); + return 0; + } + spawnedArgs[1] = sss_cache_args; + + rv = run_command (cmd, spawnedArgs, spawnedEnv, &status); + free(sss_cache_args); + if (rv != 0) { + /* run_command writes its own more detailed message. */ + SYSLOG ((LOG_WARN, MSG_SSSD_FLUSH_CACHE_FAILED, shadow_progname)); + return -1; + } + + code = WEXITSTATUS (status); + if (!WIFEXITED (status)) { + SYSLOG ((LOG_WARN, "%s: sss_cache did not terminate normally (signal %d)", + shadow_progname, WTERMSIG (status))); + return -1; + } else if (code == E_CMD_NOTFOUND) { + /* sss_cache is not installed, or it is installed but uses an + interpreter that is missing. Probably the former. */ + return 0; + } else if (code != 0) { + SYSLOG ((LOG_WARN, "%s: sss_cache exited with status %d", shadow_progname, code)); + SYSLOG ((LOG_WARN, MSG_SSSD_FLUSH_CACHE_FAILED, shadow_progname)); + return -1; + } + + return 0; +} +#else /* USE_SSSD */ +extern int errno; /* warning: ANSI C forbids an empty source file */ +#endif /* USE_SSSD */ + diff --git a/lib/sssd.h b/lib/sssd.h new file mode 100644 index 0000000..00ff2a8 --- /dev/null +++ b/lib/sssd.h @@ -0,0 +1,17 @@ +#ifndef _SSSD_H_ +#define _SSSD_H_ + +#define SSSD_DB_PASSWD 0x001 +#define SSSD_DB_GROUP 0x002 + +/* + * sssd_flush_cache - flush specified service buffer in sssd cache + */ +#ifdef USE_SSSD +extern int sssd_flush_cache (int dbflags); +#else +#define sssd_flush_cache(service) (0) +#endif + +#endif + diff --git a/lib/subordinateio.c b/lib/subordinateio.c new file mode 100644 index 0000000..bd1af26 --- /dev/null +++ b/lib/subordinateio.c @@ -0,0 +1,1102 @@ +/* + * SPDX-FileCopyrightText: 2012 - Eric Biederman + */ + +#include <config.h> + +#ifdef ENABLE_SUBIDS + +#include "prototypes.h" +#include "defines.h" +#include <stdio.h> +#include "commonio.h" +#include "subordinateio.h" +#include "../libsubid/subid.h" +#include <sys/types.h> +#include <pwd.h> +#include <ctype.h> +#include <fcntl.h> + +#define ID_SIZE 31 + +/* + * subordinate_dup: create a duplicate range + * + * @ent: a pointer to a subordinate_range struct + * + * Returns a pointer to a newly allocated duplicate subordinate_range struct + * or NULL on failure + */ +static /*@null@*/ /*@only@*/void *subordinate_dup (const void *ent) +{ + const struct subordinate_range *rangeent = ent; + struct subordinate_range *range; + + range = (struct subordinate_range *) malloc (sizeof *range); + if (NULL == range) { + return NULL; + } + range->owner = strdup (rangeent->owner); + if (NULL == range->owner) { + free(range); + return NULL; + } + range->start = rangeent->start; + range->count = rangeent->count; + + return range; +} + +/* + * subordinate_free: free a subordinate_range struct + * + * @ent: pointer to a subordinate_range struct to free. + */ +static void subordinate_free (/*@out@*/ /*@only@*/void *ent) +{ + struct subordinate_range *rangeent = ent; + + free ((void *)(rangeent->owner)); + free (rangeent); +} + +/* + * subordinate_parse: + * + * @line: a line to parse + * + * Returns a pointer to a subordinate_range struct representing the values + * in @line, or NULL on failure. Note that the returned value should not + * be freed by the caller. + */ +static void *subordinate_parse (const char *line) +{ + static struct subordinate_range range; + static char rangebuf[1024]; + int i; + char *cp; + char *fields[SUBID_NFIELDS]; + + /* + * Copy the string to a temporary buffer so the substrings can + * be modified to be NULL terminated. + */ + if (strlen (line) >= sizeof rangebuf) + return NULL; /* fail if too long */ + strcpy (rangebuf, line); + + /* + * Save a pointer to the start of each colon separated + * field. The fields are converted into NUL terminated strings. + */ + + for (cp = rangebuf, i = 0; (i < SUBID_NFIELDS) && (NULL != cp); i++) { + fields[i] = cp; + while (('\0' != *cp) && (':' != *cp)) { + cp++; + } + + if ('\0' != *cp) { + *cp = '\0'; + cp++; + } else { + cp = NULL; + } + } + + /* + * There must be exactly SUBID_NFIELDS colon separated fields or + * the entry is invalid. Also, fields must be non-blank. + */ + if (i != SUBID_NFIELDS || *fields[0] == '\0' || *fields[1] == '\0' || *fields[2] == '\0') + return NULL; + range.owner = fields[0]; + if (getulong (fields[1], &range.start) == 0) + return NULL; + if (getulong (fields[2], &range.count) == 0) + return NULL; + + return ⦥ +} + +/* + * subordinate_put: print a subordinate_range value to a file + * + * @ent: a pointer to a subordinate_range struct to print out. + * @file: file to which to print. + * + * Returns 0 on success, -1 on error. + */ +static int subordinate_put (const void *ent, FILE * file) +{ + const struct subordinate_range *range = ent; + + return fprintf(file, "%s:%lu:%lu\n", + range->owner, + range->start, + range->count) < 0 ? -1 : 0; +} + +static struct commonio_ops subordinate_ops = { + subordinate_dup, /* dup */ + subordinate_free, /* free */ + NULL, /* getname */ + subordinate_parse, /* parse */ + subordinate_put, /* put */ + fgets, /* fgets */ + fputs, /* fputs */ + NULL, /* open_hook */ + NULL, /* close_hook */ +}; + +/* + * range_exists: Check whether @owner owns any ranges + * + * @db: database to query + * @owner: owner being queried + * + * Returns true if @owner owns any subuid ranges, false otherwise. + */ +static bool range_exists(struct commonio_db *db, const char *owner) +{ + const struct subordinate_range *range; + commonio_rewind(db); + while ((range = commonio_next(db)) != NULL) { + if (0 == strcmp(range->owner, owner)) + return true; + } + return false; +} + +/* + * find_range: find a range which @owner is authorized to use which includes + * subuid @val. + * + * @db: database to query + * @owner: owning uid being queried + * @val: subuid being searched for. + * + * Returns a range of subuids belonging to @owner and including the subuid + * @val, or NULL if no such range exists. + */ +static const struct subordinate_range *find_range(struct commonio_db *db, + const char *owner, unsigned long val) +{ + const struct subordinate_range *range; + + /* + * Search for exact username/group specification + * + * This is the original method - go fast through the db, doing only + * exact username/group string comparison. Therefore we leave it as-is + * for the time being, in order to keep it equally fast as it was + * before. + */ + commonio_rewind(db); + while ((range = commonio_next(db)) != NULL) { + unsigned long first = range->start; + unsigned long last = first + range->count - 1; + + if (0 != strcmp(range->owner, owner)) + continue; + + if ((val >= first) && (val <= last)) + return range; + } + + + /* + * We only do special handling for these two files + */ + if ((0 != strcmp(db->filename, "/etc/subuid")) && (0 != strcmp(db->filename, "/etc/subgid"))) + return NULL; + + /* + * Search loop above did not produce any result. Let's rerun it, + * but this time try to match actual UIDs. The first entry that + * matches is considered a success. + * (It may be specified as literal UID or as another username which + * has the same UID as the username we are looking for.) + */ + struct passwd *pwd; + uid_t owner_uid; + char owner_uid_string[33] = ""; + + + /* Get UID of the username we are looking for */ + pwd = getpwnam(owner); + if (NULL == pwd) { + /* Username not defined in /etc/passwd, or error occurred during lookup */ + return NULL; + } + owner_uid = pwd->pw_uid; + sprintf(owner_uid_string, "%lu", (unsigned long int)owner_uid); + + commonio_rewind(db); + while ((range = commonio_next(db)) != NULL) { + unsigned long first = range->start; + unsigned long last = first + range->count - 1; + + /* For performance reasons check range before using getpwnam() */ + if ((val < first) || (val > last)) { + continue; + } + + /* + * Range matches. Check if range owner is specified + * as numeric UID and if it matches. + */ + if (0 == strcmp(range->owner, owner_uid_string)) { + return range; + } + + /* + * Ok, this range owner is not specified as numeric UID + * we are looking for. It may be specified as another + * UID or as a literal username. + * + * If specified as another UID, the call to getpwnam() + * will return NULL. + * + * If specified as literal username, we will get its + * UID and compare that to UID we are looking for. + */ + const struct passwd *range_owner_pwd; + + range_owner_pwd = getpwnam(range->owner); + if (NULL == range_owner_pwd) { + continue; + } + + if (owner_uid == range_owner_pwd->pw_uid) { + return range; + } + } + + return NULL; +} + +/* + * have_range: check whether @owner is authorized to use the range + * (@start .. @start+@count-1). + * @db: database to check + * @owner: owning uid being queried + * @start: start of range + * @count: number of uids in range + * + * Returns true if @owner is authorized to use the range, false otherwise. + */ +static bool have_range(struct commonio_db *db, + const char *owner, unsigned long start, unsigned long count) +{ + const struct subordinate_range *range; + unsigned long end; + + if (count == 0) + return false; + + end = start + count - 1; + range = find_range (db, owner, start); + while (range) { + unsigned long last; + + last = range->start + range->count - 1; + if (last >= (start + count - 1)) + return true; + + count = end - last; + start = last + 1; + range = find_range(db, owner, start); + } + return false; +} + +static bool append_range(struct subid_range **ranges, const struct subordinate_range *new, int n) +{ + if (!*ranges) { + *ranges = malloc(sizeof(struct subid_range)); + if (!*ranges) + return false; + } else { + struct subid_range *alloced; + alloced = realloc(*ranges, (n + 1) * (sizeof(struct subid_range))); + if (!alloced) + return false; + *ranges = alloced; + } + (*ranges)[n].start = new->start; + (*ranges)[n].count = new->count; + return true; +} + +void free_subordinate_ranges(struct subordinate_range **ranges, int count) +{ + int i; + + if (!ranges) + return; + for (i = 0; i < count; i++) + subordinate_free(ranges[i]); + free(ranges); +} + +/* + * subordinate_range_cmp: compare uid ranges + * + * @p1: pointer to a commonio_entry struct to compare + * @p2: pointer to second commonio_entry struct to compare + * + * Returns 0 if the entries are the same. Otherwise return -1 + * if the range in p1 is lower than that in p2, or (if the ranges are + * equal) if the owning uid in p1 is lower than p2's. Return 1 if p1's + * range or owning uid is great than p2's. + */ +static int subordinate_range_cmp (const void *p1, const void *p2) +{ + struct subordinate_range *range1, *range2; + + if ((*(struct commonio_entry **) p1)->eptr == NULL) + return 1; + if ((*(struct commonio_entry **) p2)->eptr == NULL) + return -1; + + range1 = ((struct subordinate_range *) (*(struct commonio_entry **) p1)->eptr); + range2 = ((struct subordinate_range *) (*(struct commonio_entry **) p2)->eptr); + + if (range1->start < range2->start) + return -1; + else if (range1->start > range2->start) + return 1; + else if (range1->count < range2->count) + return -1; + else if (range1->count > range2->count) + return 1; + else + return strcmp(range1->owner, range2->owner); +} + +/* + * find_free_range: find an unused consecutive sequence of ids to allocate + * to a user. + * @db: database to search + * @min: the first uid in the range to find + * @max: the highest uid to find + * @count: the number of uids needed + * + * Return the lowest new uid, or ULONG_MAX on failure. + */ +static unsigned long find_free_range(struct commonio_db *db, + unsigned long min, unsigned long max, + unsigned long count) +{ + const struct subordinate_range *range; + unsigned long low, high; + + /* When given invalid parameters fail */ + if ((count == 0) || (max < min)) + goto fail; + + /* Sort by range then by owner */ + commonio_sort (db, subordinate_range_cmp); + commonio_rewind(db); + + low = min; + while ((range = commonio_next(db)) != NULL) { + unsigned long first = range->start; + unsigned long last = first + range->count - 1; + + /* Find the top end of the hole before this range */ + high = first; + + /* Don't allocate IDs after max (included) */ + if (high > max + 1) { + high = max + 1; + } + + /* Is the hole before this range large enough? */ + if ((high > low) && ((high - low) >= count)) + return low; + + /* Compute the low end of the next hole */ + if (low < (last + 1)) + low = last + 1; + if (low > max) + goto fail; + } + + /* Is the remaining unclaimed area large enough? */ + if (((max - low) + 1) >= count) + return low; +fail: + return ULONG_MAX; +} + +/* + * add_range: add a subuid range to an owning uid's list of authorized + * subuids. + * @db: database to which to add + * @owner: uid which owns the subuid + * @start: the first uid in the owned range + * @count: the number of uids in the range + * + * Return 1 if the range is already present or on success. On error + * return 0 and set errno appropriately. + */ +static int add_range(struct commonio_db *db, + const char *owner, unsigned long start, unsigned long count) +{ + struct subordinate_range range; + range.owner = owner; + range.start = start; + range.count = count; + + /* See if the range is already present */ + if (have_range(db, owner, start, count)) + return 1; + + /* Otherwise append the range */ + return commonio_append(db, &range); +} + +/* + * remove_range: remove a range of subuids from an owning uid's list + * of authorized subuids. + * @db: database to work on + * @owner: owning uid whose range is being removed + * @start: start of the range to be removed + * @count: number of uids in the range. + * + * Returns 0 on failure, 1 on success. Failure means that we needed to + * create a new range to represent the new limits, and failed doing so. + */ +static int remove_range (struct commonio_db *db, + const char *owner, + unsigned long start, unsigned long count) +{ + struct commonio_entry *ent; + unsigned long end; + + if (count == 0) { + return 1; + } + + end = start + count - 1; + for (ent = db->head; NULL != ent; ent = ent->next) { + struct subordinate_range *range = ent->eptr; + unsigned long first; + unsigned long last; + + /* Skip unparsed entries */ + if (NULL == range) { + continue; + } + + first = range->start; + last = first + range->count - 1; + + /* Skip entries with a different owner */ + if (0 != strcmp (range->owner, owner)) { + continue; + } + + /* Skip entries outside of the range to remove */ + if ((end < first) || (start > last)) { + continue; + } + + if (start <= first) { + if (end >= last) { + /* to be removed: [start, end] + * range: [first, last] */ + /* entry completely contained in the + * range to remove */ + commonio_del_entry (db, ent); + } else { + /* to be removed: [start, end] + * range: [first, last] */ + /* Remove only the start of the entry */ + range->start = end + 1; + range->count = (last - range->start) + 1; + + ent->changed = true; + db->changed = true; + } + } else { + if (end >= last) { + /* to be removed: [start, end] + * range: [first, last] */ + /* Remove only the end of the entry */ + range->count = start - range->start; + + ent->changed = true; + db->changed = true; + } else { + /* to be removed: [start, end] + * range: [first, last] */ + /* Remove the middle of the range + * This requires to create a new range */ + struct subordinate_range tail; + tail.owner = range->owner; + tail.start = end + 1; + tail.count = (last - tail.start) + 1; + + if (commonio_append (db, &tail) == 0) { + return 0; + } + + range->count = start - range->start; + + ent->changed = true; + db->changed = true; + } + } + } + + return 1; +} + +static struct commonio_db subordinate_uid_db = { + "/etc/subuid", /* filename */ + &subordinate_ops, /* ops */ + NULL, /* fp */ +#ifdef WITH_SELINUX + NULL, /* scontext */ +#endif + 0644, /* st_mode */ + 0, /* st_uid */ + 0, /* st_gid */ + NULL, /* head */ + NULL, /* tail */ + NULL, /* cursor */ + false, /* changed */ + false, /* isopen */ + false, /* locked */ + false, /* readonly */ + false /* setname */ +}; + +int sub_uid_setdbname (const char *filename) +{ + return commonio_setname (&subordinate_uid_db, filename); +} + +/*@observer@*/const char *sub_uid_dbname (void) +{ + return subordinate_uid_db.filename; +} + +bool sub_uid_file_present (void) +{ + return commonio_present (&subordinate_uid_db); +} + +int sub_uid_lock (void) +{ + return commonio_lock (&subordinate_uid_db); +} + +int sub_uid_open (int mode) +{ + return commonio_open (&subordinate_uid_db, mode); +} + +bool local_sub_uid_assigned(const char *owner) +{ + return range_exists (&subordinate_uid_db, owner); +} + +bool have_sub_uids(const char *owner, uid_t start, unsigned long count) +{ + struct subid_nss_ops *h; + bool found; + enum subid_status status; + h = get_subid_nss_handle(); + if (h) { + status = h->has_range(owner, start, count, ID_TYPE_UID, &found); + if (status == SUBID_STATUS_SUCCESS && found) + return true; + return false; + } + return have_range (&subordinate_uid_db, owner, start, count); +} + +int sub_uid_add (const char *owner, uid_t start, unsigned long count) +{ + if (get_subid_nss_handle()) + return -EOPNOTSUPP; + return add_range (&subordinate_uid_db, owner, start, count); +} + +int sub_uid_remove (const char *owner, uid_t start, unsigned long count) +{ + if (get_subid_nss_handle()) + return -EOPNOTSUPP; + return remove_range (&subordinate_uid_db, owner, start, count); +} + +int sub_uid_close (void) +{ + return commonio_close (&subordinate_uid_db); +} + +int sub_uid_unlock (void) +{ + return commonio_unlock (&subordinate_uid_db); +} + +uid_t sub_uid_find_free_range(uid_t min, uid_t max, unsigned long count) +{ + unsigned long start; + start = find_free_range (&subordinate_uid_db, min, max, count); + return start == ULONG_MAX ? (uid_t) -1 : start; +} + +static struct commonio_db subordinate_gid_db = { + "/etc/subgid", /* filename */ + &subordinate_ops, /* ops */ + NULL, /* fp */ +#ifdef WITH_SELINUX + NULL, /* scontext */ +#endif + 0644, /* st_mode */ + 0, /* st_uid */ + 0, /* st_gid */ + NULL, /* head */ + NULL, /* tail */ + NULL, /* cursor */ + false, /* changed */ + false, /* isopen */ + false, /* locked */ + false, /* readonly */ + false /* setname */ +}; + +int sub_gid_setdbname (const char *filename) +{ + return commonio_setname (&subordinate_gid_db, filename); +} + +/*@observer@*/const char *sub_gid_dbname (void) +{ + return subordinate_gid_db.filename; +} + +bool sub_gid_file_present (void) +{ + return commonio_present (&subordinate_gid_db); +} + +int sub_gid_lock (void) +{ + return commonio_lock (&subordinate_gid_db); +} + +int sub_gid_open (int mode) +{ + return commonio_open (&subordinate_gid_db, mode); +} + +bool have_sub_gids(const char *owner, gid_t start, unsigned long count) +{ + struct subid_nss_ops *h; + bool found; + enum subid_status status; + h = get_subid_nss_handle(); + if (h) { + status = h->has_range(owner, start, count, ID_TYPE_GID, &found); + if (status == SUBID_STATUS_SUCCESS && found) + return true; + return false; + } + return have_range(&subordinate_gid_db, owner, start, count); +} + +bool local_sub_gid_assigned(const char *owner) +{ + return range_exists (&subordinate_gid_db, owner); +} + +int sub_gid_add (const char *owner, gid_t start, unsigned long count) +{ + if (get_subid_nss_handle()) + return -EOPNOTSUPP; + return add_range (&subordinate_gid_db, owner, start, count); +} + +int sub_gid_remove (const char *owner, gid_t start, unsigned long count) +{ + if (get_subid_nss_handle()) + return -EOPNOTSUPP; + return remove_range (&subordinate_gid_db, owner, start, count); +} + +int sub_gid_close (void) +{ + return commonio_close (&subordinate_gid_db); +} + +int sub_gid_unlock (void) +{ + return commonio_unlock (&subordinate_gid_db); +} + +gid_t sub_gid_find_free_range(gid_t min, gid_t max, unsigned long count) +{ + unsigned long start; + start = find_free_range (&subordinate_gid_db, min, max, count); + return start == ULONG_MAX ? (gid_t) -1 : start; +} + +static bool get_owner_id(const char *owner, enum subid_type id_type, char *id) +{ + struct passwd *pw; + struct group *gr; + int ret = 0; + + switch (id_type) { + case ID_TYPE_UID: + pw = getpwnam(owner); + if (pw == NULL) { + return false; + } + ret = snprintf(id, ID_SIZE, "%u", pw->pw_uid); + if (ret < 0 || ret >= ID_SIZE) { + return false; + } + break; + case ID_TYPE_GID: + gr = getgrnam(owner); + if (gr == NULL) { + return false; + } + ret = snprintf(id, ID_SIZE, "%u", gr->gr_gid); + if (ret < 0 || ret >= ID_SIZE) { + return false; + } + break; + default: + return false; + } + + return true; +} + +/* + * int list_owner_ranges(const char *owner, enum subid_type id_type, struct subordinate_range ***ranges) + * + * @owner: username + * @id_type: UID or GUID + * @ranges: pointer to array of ranges into which results will be placed. + * + * Fills in the subuid or subgid ranges which are owned by the specified + * user. Username may be a username or a string representation of a + * UID number. If id_type is UID, then subuids are returned, else + * subgids are given. + + * Returns the number of ranges found, or < 0 on error. + * + * The caller must free the subordinate range list. + */ +int list_owner_ranges(const char *owner, enum subid_type id_type, struct subid_range **in_ranges) +{ + // TODO - need to handle owner being either uid or username + struct subid_range *ranges = NULL; + const struct subordinate_range *range; + struct commonio_db *db; + enum subid_status status; + int count = 0; + struct subid_nss_ops *h; + char id[ID_SIZE]; + bool have_owner_id; + + *in_ranges = NULL; + + h = get_subid_nss_handle(); + if (h) { + status = h->list_owner_ranges(owner, id_type, in_ranges, &count); + if (status == SUBID_STATUS_SUCCESS) + return count; + return -1; + } + + switch (id_type) { + case ID_TYPE_UID: + if (!sub_uid_open(O_RDONLY)) { + return -1; + } + db = &subordinate_uid_db; + break; + case ID_TYPE_GID: + if (!sub_gid_open(O_RDONLY)) { + return -1; + } + db = &subordinate_gid_db; + break; + default: + return -1; + } + + have_owner_id = get_owner_id(owner, id_type, id); + + commonio_rewind(db); + while ((range = commonio_next(db)) != NULL) { + if (0 == strcmp(range->owner, owner)) { + if (!append_range(&ranges, range, count++)) { + free(ranges); + ranges = NULL; + count = -1; + goto out; + } + } + + // Let's also compare with the ID + if (have_owner_id == true && 0 == strcmp(range->owner, id)) { + if (!append_range(&ranges, range, count++)) { + free(ranges); + ranges = NULL; + count = -1; + goto out; + } + } + } + +out: + if (id_type == ID_TYPE_UID) + sub_uid_close(); + else + sub_gid_close(); + + *in_ranges = ranges; + return count; +} + +static bool all_digits(const char *str) +{ + int i; + + for (i = 0; str[i] != '\0'; i++) + if (!isdigit(str[i])) + return false; + return true; +} + +static int append_uids(uid_t **uids, const char *owner, int n) +{ + uid_t owner_uid; + uid_t *ret; + int i; + + if (all_digits(owner)) { + i = sscanf(owner, "%d", &owner_uid); + if (i != 1) { + // should not happen + free(*uids); + *uids = NULL; + return -1; + } + } else { + struct passwd *pwd = getpwnam(owner); + if (NULL == pwd) { + /* Username not defined in /etc/passwd, or error occurred during lookup */ + free(*uids); + *uids = NULL; + return -1; + } + owner_uid = pwd->pw_uid; + } + + for (i = 0; i < n; i++) { + if (owner_uid == (*uids)[i]) + return n; + } + + ret = realloc(*uids, (n + 1) * sizeof(uid_t)); + if (!ret) { + free(*uids); + return -1; + } + ret[n] = owner_uid; + *uids = ret; + return n+1; +} + +int find_subid_owners(unsigned long id, enum subid_type id_type, uid_t **uids) +{ + const struct subordinate_range *range; + struct subid_nss_ops *h; + enum subid_status status; + struct commonio_db *db; + int n = 0; + + h = get_subid_nss_handle(); + if (h) { + status = h->find_subid_owners(id, id_type, uids, &n); + // Several ways we could handle the error cases here. + if (status != SUBID_STATUS_SUCCESS) + return -1; + return n; + } + + switch (id_type) { + case ID_TYPE_UID: + if (!sub_uid_open(O_RDONLY)) { + return -1; + } + db = &subordinate_uid_db; + break; + case ID_TYPE_GID: + if (!sub_gid_open(O_RDONLY)) { + return -1; + } + db = &subordinate_gid_db; + break; + default: + return -1; + } + + *uids = NULL; + + commonio_rewind(db); + while ((range = commonio_next(db)) != NULL) { + if (id >= range->start && id < range->start + range-> count) { + n = append_uids(uids, range->owner, n); + if (n < 0) + break; + } + } + + if (id_type == ID_TYPE_UID) + sub_uid_close(); + else + sub_gid_close(); + + return n; +} + +bool new_subid_range(struct subordinate_range *range, enum subid_type id_type, bool reuse) +{ + struct commonio_db *db; + const struct subordinate_range *r; + bool ret; + + if (get_subid_nss_handle()) + return false; + + switch (id_type) { + case ID_TYPE_UID: + if (!sub_uid_lock()) { + printf("Failed loging subuids (errno %d)\n", errno); + return false; + } + if (!sub_uid_open(O_CREAT | O_RDWR)) { + printf("Failed opening subuids (errno %d)\n", errno); + sub_uid_unlock(); + return false; + } + db = &subordinate_uid_db; + break; + case ID_TYPE_GID: + if (!sub_gid_lock()) { + printf("Failed loging subgids (errno %d)\n", errno); + return false; + } + if (!sub_gid_open(O_CREAT | O_RDWR)) { + printf("Failed opening subgids (errno %d)\n", errno); + sub_gid_unlock(); + return false; + } + db = &subordinate_gid_db; + break; + default: + return false; + } + + commonio_rewind(db); + if (reuse) { + while ((r = commonio_next(db)) != NULL) { + // TODO account for username vs uid_t + if (0 != strcmp(r->owner, range->owner)) + continue; + if (r->count >= range->count) { + range->count = r->count; + range->start = r->start; + return true; + } + } + } + + range->start = find_free_range(db, range->start, ULONG_MAX, range->count); + + if (range->start == ULONG_MAX) { + ret = false; + goto out; + } + + ret = add_range(db, range->owner, range->start, range->count) == 1; + +out: + if (id_type == ID_TYPE_UID) { + sub_uid_close(); + sub_uid_unlock(); + } else { + sub_gid_close(); + sub_gid_unlock(); + } + + return ret; +} + +bool release_subid_range(struct subordinate_range *range, enum subid_type id_type) +{ + struct commonio_db *db; + bool ret; + + if (get_subid_nss_handle()) + return false; + + switch (id_type) { + case ID_TYPE_UID: + if (!sub_uid_lock()) { + printf("Failed loging subuids (errno %d)\n", errno); + return false; + } + if (!sub_uid_open(O_CREAT | O_RDWR)) { + printf("Failed opening subuids (errno %d)\n", errno); + sub_uid_unlock(); + return false; + } + db = &subordinate_uid_db; + break; + case ID_TYPE_GID: + if (!sub_gid_lock()) { + printf("Failed loging subgids (errno %d)\n", errno); + return false; + } + if (!sub_gid_open(O_CREAT | O_RDWR)) { + printf("Failed opening subgids (errno %d)\n", errno); + sub_gid_unlock(); + return false; + } + db = &subordinate_gid_db; + break; + default: + return false; + } + + ret = remove_range(db, range->owner, range->start, range->count) == 1; + + if (id_type == ID_TYPE_UID) { + sub_uid_close(); + sub_uid_unlock(); + } else { + sub_gid_close(); + sub_gid_unlock(); + } + + return ret; +} + +#else /* !ENABLE_SUBIDS */ +extern int errno; /* warning: ANSI C forbids an empty source file */ +#endif /* !ENABLE_SUBIDS */ + diff --git a/lib/subordinateio.h b/lib/subordinateio.h new file mode 100644 index 0000000..d32733d --- /dev/null +++ b/lib/subordinateio.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2012- Eric W. Biederman + */ + +#ifndef _SUBORDINATEIO_H +#define _SUBORDINATEIO_H + +#include <config.h> + +#ifdef ENABLE_SUBIDS + +#include <sys/types.h> + +#include "../libsubid/subid.h" + +extern int sub_uid_close(void); +extern bool have_sub_uids(const char *owner, uid_t start, unsigned long count); +extern bool sub_uid_file_present (void); +extern bool local_sub_uid_assigned(const char *owner); +extern int sub_uid_lock (void); +extern int sub_uid_setdbname (const char *filename); +extern /*@observer@*/const char *sub_uid_dbname (void); +extern int sub_uid_open (int mode); +extern int sub_uid_unlock (void); +extern int sub_uid_add (const char *owner, uid_t start, unsigned long count); +extern int sub_uid_remove (const char *owner, uid_t start, unsigned long count); +extern uid_t sub_uid_find_free_range(uid_t min, uid_t max, unsigned long count); +extern int list_owner_ranges(const char *owner, enum subid_type id_type, struct subid_range **ranges); +extern bool new_subid_range(struct subordinate_range *range, enum subid_type id_type, bool reuse); +extern bool release_subid_range(struct subordinate_range *range, enum subid_type id_type); +extern int find_subid_owners(unsigned long id, enum subid_type id_type, uid_t **uids); +extern void free_subordinate_ranges(struct subordinate_range **ranges, int count); + +extern int sub_gid_close(void); +extern bool have_sub_gids(const char *owner, gid_t start, unsigned long count); +extern bool sub_gid_file_present (void); +extern bool local_sub_gid_assigned(const char *owner); +extern int sub_gid_lock (void); +extern int sub_gid_setdbname (const char *filename); +extern /*@observer@*/const char *sub_gid_dbname (void); +extern int sub_gid_open (int mode); +extern int sub_gid_unlock (void); +extern int sub_gid_add (const char *owner, gid_t start, unsigned long count); +extern int sub_gid_remove (const char *owner, gid_t start, unsigned long count); +extern uid_t sub_gid_find_free_range(gid_t min, gid_t max, unsigned long count); +#endif /* ENABLE_SUBIDS */ + +#endif diff --git a/lib/tcbfuncs.c b/lib/tcbfuncs.c new file mode 100644 index 0000000..1ed5d03 --- /dev/null +++ b/lib/tcbfuncs.c @@ -0,0 +1,604 @@ +/* + * SPDX-FileCopyrightText: 2001 Rafal Wojtczuk, Solar Designer + * SPDX-License-Identifier: 0BSD + */ + +#define _GNU_SOURCE + +#include <errno.h> +#include <fcntl.h> +#include <grp.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <tcb.h> +#include <unistd.h> + +#include "config.h" + +#include "defines.h" +#include "prototypes.h" +#include "getdef.h" +#include "shadowio.h" +#include "tcbfuncs.h" + +#include "shadowlog_internal.h" + +#define SHADOWTCB_HASH_BY 1000 +#define SHADOWTCB_LOCK_SUFFIX ".lock" + +static /*@null@*//*@only@*/char *stored_tcb_user = NULL; + +shadowtcb_status shadowtcb_drop_priv (void) +{ + if (!getdef_bool ("USE_TCB")) { + return SHADOWTCB_SUCCESS; + } + + if (NULL != stored_tcb_user) { + if (tcb_drop_priv (stored_tcb_user) == 0) { + return SHADOWTCB_SUCCESS; + } + } + + return SHADOWTCB_FAILURE; +} + +shadowtcb_status shadowtcb_gain_priv (void) +{ + if (!getdef_bool ("USE_TCB")) { + return SHADOWTCB_SUCCESS; + } + + return (tcb_gain_priv () == 0) ? SHADOWTCB_SUCCESS : SHADOWTCB_FAILURE; +} + +/* In case something goes wrong, we return immediately, not polluting the + * code with free(). All errors are fatal, so the application is expected + * to exit soon. + */ +#define OUT_OF_MEMORY do { \ + fprintf (shadow_logfd, _("%s: out of memory\n"), shadow_progname); \ + (void) fflush (shadow_logfd); \ +} while (false) + +/* Returns user's tcb directory path relative to TCB_DIR. */ +static /*@null@*/ char *shadowtcb_path_rel (const char *name, uid_t uid) +{ + char *ret; + + if (!getdef_bool ("TCB_SYMLINKS") || uid < SHADOWTCB_HASH_BY) { + if (asprintf (&ret, "%s", name) == -1) { + OUT_OF_MEMORY; + return NULL; + } + } else if (uid < SHADOWTCB_HASH_BY * SHADOWTCB_HASH_BY) { + if (asprintf (&ret, ":%dK/%s", + uid / SHADOWTCB_HASH_BY, name) == -1) { + OUT_OF_MEMORY; + return NULL; + } + } else { + if (asprintf (&ret, ":%dM/:%dK/%s", + uid / (SHADOWTCB_HASH_BY * SHADOWTCB_HASH_BY), + (uid % (SHADOWTCB_HASH_BY * SHADOWTCB_HASH_BY)) / SHADOWTCB_HASH_BY, + name) == -1) { + OUT_OF_MEMORY; + return NULL; + } + } + return ret; +} + +static /*@null@*/ char *shadowtcb_path_rel_existing (const char *name) +{ + char *path, *rval; + struct stat st; + char link[8192]; + ssize_t ret; + + if (asprintf (&path, TCB_DIR "/%s", name) == -1) { + OUT_OF_MEMORY; + return NULL; + } + if (lstat (path, &st) != 0) { + fprintf (shadow_logfd, + _("%s: Cannot stat %s: %s\n"), + shadow_progname, path, strerror (errno)); + free (path); + return NULL; + } + if (S_ISDIR (st.st_mode)) { + free (path); + rval = strdup (name); + if (NULL == rval) { + OUT_OF_MEMORY; + return NULL; + } + return rval; + } + if (!S_ISLNK (st.st_mode)) { + fprintf (shadow_logfd, + _("%s: %s is neither a directory, nor a symlink.\n"), + shadow_progname, path); + free (path); + return NULL; + } + ret = readlink (path, link, sizeof (link) - 1); + if (-1 == ret) { + fprintf (shadow_logfd, + _("%s: Cannot read symbolic link %s: %s\n"), + shadow_progname, path, strerror (errno)); + free (path); + return NULL; + } + free (path); + if ((size_t)ret >= sizeof(link) - 1) { + link[sizeof(link) - 1] = '\0'; + fprintf (shadow_logfd, + _("%s: Suspiciously long symlink: %s\n"), + shadow_progname, link); + return NULL; + } + link[(size_t)ret] = '\0'; + rval = strdup (link); + if (NULL == rval) { + OUT_OF_MEMORY; + return NULL; + } + return rval; +} + +static /*@null@*/ char *shadowtcb_path (const char *name, uid_t uid) +{ + char *ret, *rel; + + rel = shadowtcb_path_rel (name, uid); + if (NULL == rel) { + return NULL; + } + if (asprintf (&ret, TCB_DIR "/%s", rel) == -1) { + OUT_OF_MEMORY; + free (rel); + return NULL; + } + free (rel); + return ret; +} + +static /*@null@*/ char *shadowtcb_path_existing (const char *name) +{ + char *ret, *rel; + + rel = shadowtcb_path_rel_existing (name); + if (NULL == rel) { + return NULL; + } + if (asprintf (&ret, TCB_DIR "/%s", rel) == -1) { + OUT_OF_MEMORY; + free (rel); + return NULL; + } + free (rel); + return ret; +} + +static shadowtcb_status mkdir_leading (const char *name, uid_t uid) +{ + char *ind, *dir, *ptr, *path = shadowtcb_path_rel (name, uid); + struct stat st; + + if (NULL == path) { + return SHADOWTCB_FAILURE; + } + ptr = path; + if (stat (TCB_DIR, &st) != 0) { + fprintf (shadow_logfd, + _("%s: Cannot stat %s: %s\n"), + shadow_progname, TCB_DIR, strerror (errno)); + goto out_free_path; + } + while ((ind = strchr (ptr, '/'))) { + *ind = '\0'; + if (asprintf (&dir, TCB_DIR "/%s", path) == -1) { + OUT_OF_MEMORY; + return SHADOWTCB_FAILURE; + } + if ((mkdir (dir, 0700) != 0) && (errno != EEXIST)) { + fprintf (shadow_logfd, + _("%s: Cannot create directory %s: %s\n"), + shadow_progname, dir, strerror (errno)); + goto out_free_dir; + } + if (chown (dir, 0, st.st_gid) != 0) { + fprintf (shadow_logfd, + _("%s: Cannot change owner of %s: %s\n"), + shadow_progname, dir, strerror (errno)); + goto out_free_dir; + } + if (chmod (dir, 0711) != 0) { + fprintf (shadow_logfd, + _("%s: Cannot change mode of %s: %s\n"), + shadow_progname, dir, strerror (errno)); + goto out_free_dir; + } + free (dir); + *ind = '/'; + ptr = ind + 1; + } + free (path); + return SHADOWTCB_SUCCESS; +out_free_dir: + free (dir); +out_free_path: + free (path); + return SHADOWTCB_FAILURE; +} + +static shadowtcb_status unlink_suffs (const char *user) +{ + static char *suffs[] = { "+", "-", SHADOWTCB_LOCK_SUFFIX }; + char *tmp; + int i; + + for (i = 0; i < 3; i++) { + if (asprintf (&tmp, TCB_FMT "%s", user, suffs[i]) == -1) { + OUT_OF_MEMORY; + return SHADOWTCB_FAILURE; + } + if ((unlink (tmp) != 0) && (errno != ENOENT)) { + fprintf (shadow_logfd, + _("%s: unlink: %s: %s\n"), + shadow_progname, tmp, strerror (errno)); + free (tmp); + return SHADOWTCB_FAILURE; + } + free (tmp); + } + + return SHADOWTCB_SUCCESS; +} + +/* path should be a relative existing tcb directory */ +static shadowtcb_status rmdir_leading (char *path) +{ + char *ind, *dir; + shadowtcb_status ret = SHADOWTCB_SUCCESS; + while ((ind = strrchr (path, '/'))) { + *ind = '\0'; + if (asprintf (&dir, TCB_DIR "/%s", path) == -1) { + OUT_OF_MEMORY; + return SHADOWTCB_FAILURE; + } + if (rmdir (dir) != 0) { + if (errno != ENOTEMPTY) { + fprintf (shadow_logfd, + _("%s: Cannot remove directory %s: %s\n"), + shadow_progname, dir, strerror (errno)); + ret = SHADOWTCB_FAILURE; + } + free (dir); + break; + } + free (dir); + } + return ret; +} + +static shadowtcb_status move_dir (const char *user_newname, uid_t user_newid) +{ + char *olddir = NULL, *newdir = NULL; + char *real_old_dir = NULL, *real_new_dir = NULL; + char *real_old_dir_rel = NULL, *real_new_dir_rel = NULL; + uid_t old_uid, the_newid; + struct stat oldmode; + shadowtcb_status ret = SHADOWTCB_FAILURE; + + if (NULL == stored_tcb_user) { + return SHADOWTCB_FAILURE; + } + if (asprintf (&olddir, TCB_DIR "/%s", stored_tcb_user) == -1) { + goto out_free_nomem; + } + if (stat (olddir, &oldmode) != 0) { + fprintf (shadow_logfd, + _("%s: Cannot stat %s: %s\n"), + shadow_progname, olddir, strerror (errno)); + goto out_free; + } + old_uid = oldmode.st_uid; + the_newid = (user_newid == -1) ? old_uid : user_newid; + real_old_dir = shadowtcb_path_existing (stored_tcb_user); + if (NULL == real_old_dir) { + goto out_free; + } + real_new_dir = shadowtcb_path (user_newname, the_newid); + if (NULL == real_new_dir) { + goto out_free; + } + if (strcmp (real_old_dir, real_new_dir) == 0) { + ret = SHADOWTCB_SUCCESS; + goto out_free; + } + real_old_dir_rel = shadowtcb_path_rel_existing (stored_tcb_user); + if (NULL == real_old_dir_rel) { + goto out_free; + } + if (mkdir_leading (user_newname, the_newid) == SHADOWTCB_FAILURE) { + goto out_free; + } + if (rename (real_old_dir, real_new_dir) != 0) { + fprintf (shadow_logfd, + _("%s: Cannot rename %s to %s: %s\n"), + shadow_progname, real_old_dir, real_new_dir, strerror (errno)); + goto out_free; + } + if (rmdir_leading (real_old_dir_rel) == SHADOWTCB_FAILURE) { + goto out_free; + } + if ((unlink (olddir) != 0) && (errno != ENOENT)) { + fprintf (shadow_logfd, + _("%s: Cannot remove %s: %s\n"), + shadow_progname, olddir, strerror (errno)); + goto out_free; + } + if (asprintf (&newdir, TCB_DIR "/%s", user_newname) == -1) { + goto out_free_nomem; + } + real_new_dir_rel = shadowtcb_path_rel (user_newname, the_newid); + if (NULL == real_new_dir_rel) { + goto out_free; + } + if ( (strcmp (real_new_dir, newdir) != 0) + && (symlink (real_new_dir_rel, newdir) != 0)) { + fprintf (shadow_logfd, + _("%s: Cannot create symbolic link %s: %s\n"), + shadow_progname, real_new_dir_rel, strerror (errno)); + goto out_free; + } + ret = SHADOWTCB_SUCCESS; + goto out_free; +out_free_nomem: + OUT_OF_MEMORY; +out_free: + free (olddir); + free (newdir); + free (real_old_dir); + free (real_new_dir); + free (real_old_dir_rel); + free (real_new_dir_rel); + return ret; +} + +shadowtcb_status shadowtcb_set_user (const char* name) +{ + char *buf; + shadowtcb_status retval; + + if (!getdef_bool ("USE_TCB")) { + return SHADOWTCB_SUCCESS; + } + + free (stored_tcb_user); + + stored_tcb_user = strdup (name); + if (NULL == stored_tcb_user) { + OUT_OF_MEMORY; + return SHADOWTCB_FAILURE; + } + if (asprintf (&buf, TCB_FMT, name) == -1) { + OUT_OF_MEMORY; + return SHADOWTCB_FAILURE; + } + + retval = (spw_setdbname (buf) != 0) ? SHADOWTCB_SUCCESS : SHADOWTCB_FAILURE; + free (buf); + return retval; +} + +/* tcb directory must be empty before shadowtcb_remove is called. */ +shadowtcb_status shadowtcb_remove (const char *name) +{ + shadowtcb_status ret = SHADOWTCB_SUCCESS; + char *path = shadowtcb_path_existing (name); + char *rel = shadowtcb_path_rel_existing (name); + if ((NULL == path) || (NULL == rel) || (rmdir (path) != 0)) { + return SHADOWTCB_FAILURE; + } + if (rmdir_leading (rel) == SHADOWTCB_FAILURE) { + return SHADOWTCB_FAILURE; + } + free (path); + free (rel); + if (asprintf (&path, TCB_DIR "/%s", name) == -1) { + OUT_OF_MEMORY; + return SHADOWTCB_FAILURE; + } + if ((unlink (path) != 0) && (errno != ENOENT)) { + ret = SHADOWTCB_FAILURE; + } + free (path); + return ret; +} + +shadowtcb_status shadowtcb_move (/*@NULL@*/const char *user_newname, uid_t user_newid) +{ + struct stat dirmode, filemode; + char *tcbdir, *shadow; + shadowtcb_status ret = SHADOWTCB_FAILURE; + + if (!getdef_bool ("USE_TCB")) { + return SHADOWTCB_SUCCESS; + } + if (NULL == stored_tcb_user) { + return SHADOWTCB_FAILURE; + } + if (NULL == user_newname) { + user_newname = stored_tcb_user; + } + if (move_dir (user_newname, user_newid) == SHADOWTCB_FAILURE) { + return SHADOWTCB_FAILURE; + } + if (-1 == user_newid) { + return SHADOWTCB_SUCCESS; + } + if ( (asprintf (&tcbdir, TCB_DIR "/%s", user_newname) == -1) + || (asprintf (&shadow, TCB_FMT, user_newname) == -1)) { + OUT_OF_MEMORY; + return SHADOWTCB_FAILURE; + } + if (stat (tcbdir, &dirmode) != 0) { + fprintf (shadow_logfd, + _("%s: Cannot stat %s: %s\n"), + shadow_progname, tcbdir, strerror (errno)); + goto out_free; + } + if (chown (tcbdir, 0, 0) != 0) { + fprintf (shadow_logfd, + _("%s: Cannot change owners of %s: %s\n"), + shadow_progname, tcbdir, strerror (errno)); + goto out_free; + } + if (chmod (tcbdir, 0700) != 0) { + fprintf (shadow_logfd, + _("%s: Cannot change mode of %s: %s\n"), + shadow_progname, tcbdir, strerror (errno)); + goto out_free; + } + if (lstat (shadow, &filemode) != 0) { + if (errno != ENOENT) { + fprintf (shadow_logfd, + _("%s: Cannot lstat %s: %s\n"), + shadow_progname, shadow, strerror (errno)); + goto out_free; + } + fprintf (shadow_logfd, + _("%s: Warning, user %s has no tcb shadow file.\n"), + shadow_progname, user_newname); + } else { + if (!S_ISREG (filemode.st_mode) || + filemode.st_nlink != 1) { + fprintf (shadow_logfd, + _("%s: Emergency: %s's tcb shadow is not a " + "regular file with st_nlink=1.\n" + "The account is left locked.\n"), + shadow_progname, user_newname); + goto out_free; + } + if (chown (shadow, user_newid, filemode.st_gid) != 0) { + fprintf (shadow_logfd, + _("%s: Cannot change owner of %s: %s\n"), + shadow_progname, shadow, strerror (errno)); + goto out_free; + } + if (chmod (shadow, filemode.st_mode & 07777) != 0) { + fprintf (shadow_logfd, + _("%s: Cannot change mode of %s: %s\n"), + shadow_progname, shadow, strerror (errno)); + goto out_free; + } + } + if (unlink_suffs (user_newname) == SHADOWTCB_FAILURE) { + goto out_free; + } + if (chown (tcbdir, user_newid, dirmode.st_gid) != 0) { + fprintf (shadow_logfd, + _("%s: Cannot change owner of %s: %s\n"), + shadow_progname, tcbdir, strerror (errno)); + goto out_free; + } + if (chmod (tcbdir, dirmode.st_mode & 07777) != 0) { + fprintf (shadow_logfd, + _("%s: Cannot change mode of %s: %s\n"), + shadow_progname, tcbdir, strerror (errno)); + goto out_free; + } + ret = SHADOWTCB_SUCCESS; +out_free: + free (tcbdir); + free (shadow); + return ret; +} + +shadowtcb_status shadowtcb_create (const char *name, uid_t uid) +{ + char *dir, *shadow; + struct stat tcbdir_stat; + gid_t shadowgid, authgid; + struct group *gr; + int fd; + shadowtcb_status ret = SHADOWTCB_FAILURE; + + if (!getdef_bool ("USE_TCB")) { + return SHADOWTCB_SUCCESS; + } + if (stat (TCB_DIR, &tcbdir_stat) != 0) { + fprintf (shadow_logfd, + _("%s: Cannot stat %s: %s\n"), + shadow_progname, TCB_DIR, strerror (errno)); + return SHADOWTCB_FAILURE; + } + shadowgid = tcbdir_stat.st_gid; + authgid = shadowgid; + if (getdef_bool ("TCB_AUTH_GROUP")) { + gr = getgrnam ("auth"); + if (NULL != gr) { + authgid = gr->gr_gid; + } + } + + if ( (asprintf (&dir, TCB_DIR "/%s", name) == -1) + || (asprintf (&shadow, TCB_FMT, name) == -1)) { + OUT_OF_MEMORY; + return SHADOWTCB_FAILURE; + } + if (mkdir (dir, 0700) != 0) { + fprintf (shadow_logfd, + _("%s: mkdir: %s: %s\n"), + shadow_progname, dir, strerror (errno)); + goto out_free; + } + fd = open (shadow, O_RDWR | O_CREAT | O_TRUNC, 0600); + if (fd < 0) { + fprintf (shadow_logfd, + _("%s: Cannot open %s: %s\n"), + shadow_progname, shadow, strerror (errno)); + goto out_free; + } + close (fd); + if (chown (shadow, 0, authgid) != 0) { + fprintf (shadow_logfd, + _("%s: Cannot change owner of %s: %s\n"), + shadow_progname, shadow, strerror (errno)); + goto out_free; + } + if (chmod (shadow, (mode_t) ((authgid == shadowgid) ? 0600 : 0640)) != 0) { + fprintf (shadow_logfd, + _("%s: Cannot change mode of %s: %s\n"), + shadow_progname, shadow, strerror (errno)); + goto out_free; + } + if (chown (dir, 0, authgid) != 0) { + fprintf (shadow_logfd, + _("%s: Cannot change owner of %s: %s\n"), + shadow_progname, dir, strerror (errno)); + goto out_free; + } + if (chmod (dir, (mode_t) ((authgid == shadowgid) ? 02700 : 02710)) != 0) { + fprintf (shadow_logfd, + _("%s: Cannot change mode of %s: %s\n"), + shadow_progname, dir, strerror (errno)); + goto out_free; + } + if ( (shadowtcb_set_user (name) == SHADOWTCB_FAILURE) + || (shadowtcb_move (NULL, uid) == SHADOWTCB_FAILURE)) { + goto out_free; + } + ret = SHADOWTCB_SUCCESS; +out_free: + free (dir); + free (shadow); + return ret; +} + diff --git a/lib/tcbfuncs.h b/lib/tcbfuncs.h new file mode 100644 index 0000000..6324bc1 --- /dev/null +++ b/lib/tcbfuncs.h @@ -0,0 +1,19 @@ +#ifndef _TCBFUNCS_H +#define _TCBFUNCS_H + +#include <sys/types.h> + +typedef enum { + SHADOWTCB_FAILURE = 0, + SHADOWTCB_SUCCESS = 1 +} shadowtcb_status; + +extern shadowtcb_status shadowtcb_drop_priv (void); +extern shadowtcb_status shadowtcb_gain_priv (void); +extern shadowtcb_status shadowtcb_set_user (const char *name); +extern shadowtcb_status shadowtcb_remove (const char *name); +extern shadowtcb_status shadowtcb_move (/*@null@*/const char *user_newname, + uid_t user_newid); +extern shadowtcb_status shadowtcb_create (const char *name, uid_t uid); + +#endif diff --git a/lib/utent.c b/lib/utent.c new file mode 100644 index 0000000..d5e6dae --- /dev/null +++ b/lib/utent.c @@ -0,0 +1,70 @@ +/* + * SPDX-FileCopyrightText: 1993 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1998, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2005 , Tomasz KÅ‚oczko + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ifndef HAVE_GETUTENT + +#include "defines.h" +#include <stdio.h> +#include <fcntl.h> +#include <utmp.h> + +#ifndef lint +static char rcsid[] = "$Id$"; +#endif + +static int utmp_fd = -1; +static struct utmp utmp_buf; + +/* + * setutent - open or rewind the utmp file + */ + +void setutent (void) +{ + if (utmp_fd == -1) + if ((utmp_fd = open (_UTMP_FILE, O_RDWR)) == -1) + utmp_fd = open (_UTMP_FILE, O_RDONLY); + + if (utmp_fd != -1) + lseek (utmp_fd, (off_t) 0L, SEEK_SET); +} + +/* + * endutent - close the utmp file + */ + +void endutent (void) +{ + if (utmp_fd != -1) + close (utmp_fd); + + utmp_fd = -1; +} + +/* + * getutent - get the next record from the utmp file + */ + +struct utmp *getutent (void) +{ + if (utmp_fd == -1) + setutent (); + + if (utmp_fd == -1) + return 0; + + if (read (utmp_fd, &utmp_buf, sizeof utmp_buf) != sizeof utmp_buf) + return 0; + + return &utmp_buf; +} +#else +extern int errno; /* warning: ANSI C forbids an empty source file */ +#endif diff --git a/libmisc/.indent.pro b/libmisc/.indent.pro new file mode 100644 index 0000000..fe572bb --- /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 0000000..3d319cd --- /dev/null +++ b/libmisc/Makefile.am @@ -0,0 +1,80 @@ + +EXTRA_DIST = .indent.pro xgetXXbyYY.c + +AM_CPPFLAGS = -I$(top_srcdir)/lib -I$(top_srcdir) $(ECONF_CPPFLAGS) + +noinst_LTLIBRARIES = libmisc.la + +libmisc_la_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 \ + date_to_str.c \ + entry.c \ + env.c \ + failure.c \ + failure.h \ + find_new_gid.c \ + find_new_uid.c \ + find_new_sub_gids.c \ + find_new_sub_uids.c \ + getdate.h \ + getdate.y \ + getgr_nam_gid.c \ + getrange.c \ + gettime.c \ + hushed.c \ + idmapping.h \ + idmapping.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_interactive.c \ + prefix_flag.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 + +if WITH_BTRFS +libmisc_la_SOURCES += btrfs.c +endif + diff --git a/libmisc/Makefile.in b/libmisc/Makefile.in new file mode 100644 index 0000000..2defd8e --- /dev/null +++ b/libmisc/Makefile.in @@ -0,0 +1,927 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 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__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +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@ +@WITH_BTRFS_TRUE@am__append_1 = btrfs.c +subdir = libmisc +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/intlmacosx.m4 \ + $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \ + $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \ + $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +libmisc_la_LIBADD = +am__libmisc_la_SOURCES_DIST = 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 \ + date_to_str.c entry.c env.c failure.c failure.h find_new_gid.c \ + find_new_uid.c find_new_sub_gids.c find_new_sub_uids.c \ + getdate.h getdate.y getgr_nam_gid.c getrange.c gettime.c \ + hushed.c idmapping.h idmapping.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_interactive.c prefix_flag.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 btrfs.c +@WITH_BTRFS_TRUE@am__objects_1 = btrfs.lo +am_libmisc_la_OBJECTS = addgrps.lo age.lo audit_help.lo basename.lo \ + chkname.lo chowndir.lo chowntty.lo cleanup.lo cleanup_group.lo \ + cleanup_user.lo console.lo copydir.lo date_to_str.lo entry.lo \ + env.lo failure.lo find_new_gid.lo find_new_uid.lo \ + find_new_sub_gids.lo find_new_sub_uids.lo getdate.lo \ + getgr_nam_gid.lo getrange.lo gettime.lo hushed.lo idmapping.lo \ + isexpired.lo limits.lo list.lo log.lo loginprompt.lo mail.lo \ + motd.lo myname.lo obscure.lo pam_pass.lo \ + pam_pass_non_interactive.lo prefix_flag.lo pwd2spwd.lo \ + pwdcheck.lo pwd_init.lo remove_tree.lo rlogin.lo root_flag.lo \ + salt.lo setugid.lo setupenv.lo shell.lo strtoday.lo sub.lo \ + sulog.lo ttytype.lo tz.lo ulimit.lo user_busy.lo utmp.lo \ + valid.lo xgetpwnam.lo xgetpwuid.lo xgetgrnam.lo xgetgrgid.lo \ + xgetspnam.lo xmalloc.lo yesno.lo $(am__objects_1) +libmisc_la_OBJECTS = $(am_libmisc_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/addgrps.Plo ./$(DEPDIR)/age.Plo \ + ./$(DEPDIR)/audit_help.Plo ./$(DEPDIR)/basename.Plo \ + ./$(DEPDIR)/btrfs.Plo ./$(DEPDIR)/chkname.Plo \ + ./$(DEPDIR)/chowndir.Plo ./$(DEPDIR)/chowntty.Plo \ + ./$(DEPDIR)/cleanup.Plo ./$(DEPDIR)/cleanup_group.Plo \ + ./$(DEPDIR)/cleanup_user.Plo ./$(DEPDIR)/console.Plo \ + ./$(DEPDIR)/copydir.Plo ./$(DEPDIR)/date_to_str.Plo \ + ./$(DEPDIR)/entry.Plo ./$(DEPDIR)/env.Plo \ + ./$(DEPDIR)/failure.Plo ./$(DEPDIR)/find_new_gid.Plo \ + ./$(DEPDIR)/find_new_sub_gids.Plo \ + ./$(DEPDIR)/find_new_sub_uids.Plo ./$(DEPDIR)/find_new_uid.Plo \ + ./$(DEPDIR)/getdate.Plo ./$(DEPDIR)/getgr_nam_gid.Plo \ + ./$(DEPDIR)/getrange.Plo ./$(DEPDIR)/gettime.Plo \ + ./$(DEPDIR)/hushed.Plo ./$(DEPDIR)/idmapping.Plo \ + ./$(DEPDIR)/isexpired.Plo ./$(DEPDIR)/limits.Plo \ + ./$(DEPDIR)/list.Plo ./$(DEPDIR)/log.Plo \ + ./$(DEPDIR)/loginprompt.Plo ./$(DEPDIR)/mail.Plo \ + ./$(DEPDIR)/motd.Plo ./$(DEPDIR)/myname.Plo \ + ./$(DEPDIR)/obscure.Plo ./$(DEPDIR)/pam_pass.Plo \ + ./$(DEPDIR)/pam_pass_non_interactive.Plo \ + ./$(DEPDIR)/prefix_flag.Plo ./$(DEPDIR)/pwd2spwd.Plo \ + ./$(DEPDIR)/pwd_init.Plo ./$(DEPDIR)/pwdcheck.Plo \ + ./$(DEPDIR)/remove_tree.Plo ./$(DEPDIR)/rlogin.Plo \ + ./$(DEPDIR)/root_flag.Plo ./$(DEPDIR)/salt.Plo \ + ./$(DEPDIR)/setugid.Plo ./$(DEPDIR)/setupenv.Plo \ + ./$(DEPDIR)/shell.Plo ./$(DEPDIR)/strtoday.Plo \ + ./$(DEPDIR)/sub.Plo ./$(DEPDIR)/sulog.Plo \ + ./$(DEPDIR)/ttytype.Plo ./$(DEPDIR)/tz.Plo \ + ./$(DEPDIR)/ulimit.Plo ./$(DEPDIR)/user_busy.Plo \ + ./$(DEPDIR)/utmp.Plo ./$(DEPDIR)/valid.Plo \ + ./$(DEPDIR)/xgetgrgid.Plo ./$(DEPDIR)/xgetgrnam.Plo \ + ./$(DEPDIR)/xgetpwnam.Plo ./$(DEPDIR)/xgetpwuid.Plo \ + ./$(DEPDIR)/xgetspnam.Plo ./$(DEPDIR)/xmalloc.Plo \ + ./$(DEPDIR)/yesno.Plo +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +@MAINTAINER_MODE_FALSE@am__skipyacc = test -f $@ || +am__yacc_c2h = sed -e s/cc$$/hh/ -e s/cpp$$/hpp/ -e s/cxx$$/hxx/ \ + -e s/c++$$/h++/ -e s/c$$/h/ +YACCCOMPILE = $(YACC) $(AM_YFLAGS) $(YFLAGS) +LTYACCCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(YACC) $(AM_YFLAGS) $(YFLAGS) +AM_V_YACC = $(am__v_YACC_@AM_V@) +am__v_YACC_ = $(am__v_YACC_@AM_DEFAULT_V@) +am__v_YACC_0 = @echo " YACC " $@; +am__v_YACC_1 = +YLWRAP = $(top_srcdir)/ylwrap +SOURCES = $(libmisc_la_SOURCES) +DIST_SOURCES = $(am__libmisc_la_SOURCES_DIST) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp \ + $(top_srcdir)/ylwrap getdate.c +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +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@ +ECONF_CPPFLAGS = @ECONF_CPPFLAGS@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +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@ +LIBECONF = @LIBECONF@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBMD = @LIBMD@ +LIBOBJS = @LIBOBJS@ +LIBPAM = @LIBPAM@ +LIBS = @LIBS@ +LIBSELINUX = @LIBSELINUX@ +LIBSEMANAGE = @LIBSEMANAGE@ +LIBSKEY = @LIBSKEY@ +LIBSUBID_ABI = @LIBSUBID_ABI@ +LIBSUBID_ABI_MAJOR = @LIBSUBID_ABI_MAJOR@ +LIBSUBID_ABI_MICRO = @LIBSUBID_ABI_MICRO@ +LIBSUBID_ABI_MINOR = @LIBSUBID_ABI_MINOR@ +LIBTCB = @LIBTCB@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LIYESCRYPT = @LIYESCRYPT@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +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@ +VENDORDIR = @VENDORDIR@ +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@ +capcmd = @capcmd@ +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@ +runstatedir = @runstatedir@ +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 +AM_CPPFLAGS = -I$(top_srcdir)/lib -I$(top_srcdir) $(ECONF_CPPFLAGS) +noinst_LTLIBRARIES = libmisc.la +libmisc_la_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 date_to_str.c entry.c env.c \ + failure.c failure.h find_new_gid.c find_new_uid.c \ + find_new_sub_gids.c find_new_sub_uids.c getdate.h getdate.y \ + getgr_nam_gid.c getrange.c gettime.c hushed.c idmapping.h \ + idmapping.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_interactive.c prefix_flag.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 \ + $(am__append_1) +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) --foreign libmisc/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign libmisc/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__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + 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-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libmisc.la: $(libmisc_la_OBJECTS) $(libmisc_la_DEPENDENCIES) $(EXTRA_libmisc_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libmisc_la_OBJECTS) $(libmisc_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/addgrps.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/age.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/audit_help.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/basename.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/btrfs.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chkname.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chowndir.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chowntty.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cleanup.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cleanup_group.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cleanup_user.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/console.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/copydir.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/date_to_str.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/entry.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/env.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/failure.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/find_new_gid.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/find_new_sub_gids.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/find_new_sub_uids.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/find_new_uid.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getdate.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getgr_nam_gid.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getrange.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gettime.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hushed.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/idmapping.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isexpired.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/limits.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/list.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/loginprompt.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/motd.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/myname.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/obscure.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_pass.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_pass_non_interactive.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prefix_flag.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pwd2spwd.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pwd_init.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pwdcheck.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/remove_tree.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rlogin.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/root_flag.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/salt.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/setugid.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/setupenv.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shell.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strtoday.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sub.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sulog.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ttytype.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tz.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ulimit.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/user_busy.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utmp.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/valid.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xgetgrgid.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xgetgrnam.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xgetpwnam.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xgetpwuid.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xgetspnam.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xmalloc.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/yesno.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +.y.c: + $(AM_V_YACC)$(am__skipyacc) $(SHELL) $(YLWRAP) $< y.tab.c $@ y.tab.h `echo $@ | $(am__yacc_c2h)` y.output $*.output -- $(YACCCOMPILE) + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + 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-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + 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" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(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 $(LTLIBRARIES) +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-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/addgrps.Plo + -rm -f ./$(DEPDIR)/age.Plo + -rm -f ./$(DEPDIR)/audit_help.Plo + -rm -f ./$(DEPDIR)/basename.Plo + -rm -f ./$(DEPDIR)/btrfs.Plo + -rm -f ./$(DEPDIR)/chkname.Plo + -rm -f ./$(DEPDIR)/chowndir.Plo + -rm -f ./$(DEPDIR)/chowntty.Plo + -rm -f ./$(DEPDIR)/cleanup.Plo + -rm -f ./$(DEPDIR)/cleanup_group.Plo + -rm -f ./$(DEPDIR)/cleanup_user.Plo + -rm -f ./$(DEPDIR)/console.Plo + -rm -f ./$(DEPDIR)/copydir.Plo + -rm -f ./$(DEPDIR)/date_to_str.Plo + -rm -f ./$(DEPDIR)/entry.Plo + -rm -f ./$(DEPDIR)/env.Plo + -rm -f ./$(DEPDIR)/failure.Plo + -rm -f ./$(DEPDIR)/find_new_gid.Plo + -rm -f ./$(DEPDIR)/find_new_sub_gids.Plo + -rm -f ./$(DEPDIR)/find_new_sub_uids.Plo + -rm -f ./$(DEPDIR)/find_new_uid.Plo + -rm -f ./$(DEPDIR)/getdate.Plo + -rm -f ./$(DEPDIR)/getgr_nam_gid.Plo + -rm -f ./$(DEPDIR)/getrange.Plo + -rm -f ./$(DEPDIR)/gettime.Plo + -rm -f ./$(DEPDIR)/hushed.Plo + -rm -f ./$(DEPDIR)/idmapping.Plo + -rm -f ./$(DEPDIR)/isexpired.Plo + -rm -f ./$(DEPDIR)/limits.Plo + -rm -f ./$(DEPDIR)/list.Plo + -rm -f ./$(DEPDIR)/log.Plo + -rm -f ./$(DEPDIR)/loginprompt.Plo + -rm -f ./$(DEPDIR)/mail.Plo + -rm -f ./$(DEPDIR)/motd.Plo + -rm -f ./$(DEPDIR)/myname.Plo + -rm -f ./$(DEPDIR)/obscure.Plo + -rm -f ./$(DEPDIR)/pam_pass.Plo + -rm -f ./$(DEPDIR)/pam_pass_non_interactive.Plo + -rm -f ./$(DEPDIR)/prefix_flag.Plo + -rm -f ./$(DEPDIR)/pwd2spwd.Plo + -rm -f ./$(DEPDIR)/pwd_init.Plo + -rm -f ./$(DEPDIR)/pwdcheck.Plo + -rm -f ./$(DEPDIR)/remove_tree.Plo + -rm -f ./$(DEPDIR)/rlogin.Plo + -rm -f ./$(DEPDIR)/root_flag.Plo + -rm -f ./$(DEPDIR)/salt.Plo + -rm -f ./$(DEPDIR)/setugid.Plo + -rm -f ./$(DEPDIR)/setupenv.Plo + -rm -f ./$(DEPDIR)/shell.Plo + -rm -f ./$(DEPDIR)/strtoday.Plo + -rm -f ./$(DEPDIR)/sub.Plo + -rm -f ./$(DEPDIR)/sulog.Plo + -rm -f ./$(DEPDIR)/ttytype.Plo + -rm -f ./$(DEPDIR)/tz.Plo + -rm -f ./$(DEPDIR)/ulimit.Plo + -rm -f ./$(DEPDIR)/user_busy.Plo + -rm -f ./$(DEPDIR)/utmp.Plo + -rm -f ./$(DEPDIR)/valid.Plo + -rm -f ./$(DEPDIR)/xgetgrgid.Plo + -rm -f ./$(DEPDIR)/xgetgrnam.Plo + -rm -f ./$(DEPDIR)/xgetpwnam.Plo + -rm -f ./$(DEPDIR)/xgetpwuid.Plo + -rm -f ./$(DEPDIR)/xgetspnam.Plo + -rm -f ./$(DEPDIR)/xmalloc.Plo + -rm -f ./$(DEPDIR)/yesno.Plo + -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 -f ./$(DEPDIR)/addgrps.Plo + -rm -f ./$(DEPDIR)/age.Plo + -rm -f ./$(DEPDIR)/audit_help.Plo + -rm -f ./$(DEPDIR)/basename.Plo + -rm -f ./$(DEPDIR)/btrfs.Plo + -rm -f ./$(DEPDIR)/chkname.Plo + -rm -f ./$(DEPDIR)/chowndir.Plo + -rm -f ./$(DEPDIR)/chowntty.Plo + -rm -f ./$(DEPDIR)/cleanup.Plo + -rm -f ./$(DEPDIR)/cleanup_group.Plo + -rm -f ./$(DEPDIR)/cleanup_user.Plo + -rm -f ./$(DEPDIR)/console.Plo + -rm -f ./$(DEPDIR)/copydir.Plo + -rm -f ./$(DEPDIR)/date_to_str.Plo + -rm -f ./$(DEPDIR)/entry.Plo + -rm -f ./$(DEPDIR)/env.Plo + -rm -f ./$(DEPDIR)/failure.Plo + -rm -f ./$(DEPDIR)/find_new_gid.Plo + -rm -f ./$(DEPDIR)/find_new_sub_gids.Plo + -rm -f ./$(DEPDIR)/find_new_sub_uids.Plo + -rm -f ./$(DEPDIR)/find_new_uid.Plo + -rm -f ./$(DEPDIR)/getdate.Plo + -rm -f ./$(DEPDIR)/getgr_nam_gid.Plo + -rm -f ./$(DEPDIR)/getrange.Plo + -rm -f ./$(DEPDIR)/gettime.Plo + -rm -f ./$(DEPDIR)/hushed.Plo + -rm -f ./$(DEPDIR)/idmapping.Plo + -rm -f ./$(DEPDIR)/isexpired.Plo + -rm -f ./$(DEPDIR)/limits.Plo + -rm -f ./$(DEPDIR)/list.Plo + -rm -f ./$(DEPDIR)/log.Plo + -rm -f ./$(DEPDIR)/loginprompt.Plo + -rm -f ./$(DEPDIR)/mail.Plo + -rm -f ./$(DEPDIR)/motd.Plo + -rm -f ./$(DEPDIR)/myname.Plo + -rm -f ./$(DEPDIR)/obscure.Plo + -rm -f ./$(DEPDIR)/pam_pass.Plo + -rm -f ./$(DEPDIR)/pam_pass_non_interactive.Plo + -rm -f ./$(DEPDIR)/prefix_flag.Plo + -rm -f ./$(DEPDIR)/pwd2spwd.Plo + -rm -f ./$(DEPDIR)/pwd_init.Plo + -rm -f ./$(DEPDIR)/pwdcheck.Plo + -rm -f ./$(DEPDIR)/remove_tree.Plo + -rm -f ./$(DEPDIR)/rlogin.Plo + -rm -f ./$(DEPDIR)/root_flag.Plo + -rm -f ./$(DEPDIR)/salt.Plo + -rm -f ./$(DEPDIR)/setugid.Plo + -rm -f ./$(DEPDIR)/setupenv.Plo + -rm -f ./$(DEPDIR)/shell.Plo + -rm -f ./$(DEPDIR)/strtoday.Plo + -rm -f ./$(DEPDIR)/sub.Plo + -rm -f ./$(DEPDIR)/sulog.Plo + -rm -f ./$(DEPDIR)/ttytype.Plo + -rm -f ./$(DEPDIR)/tz.Plo + -rm -f ./$(DEPDIR)/ulimit.Plo + -rm -f ./$(DEPDIR)/user_busy.Plo + -rm -f ./$(DEPDIR)/utmp.Plo + -rm -f ./$(DEPDIR)/valid.Plo + -rm -f ./$(DEPDIR)/xgetgrgid.Plo + -rm -f ./$(DEPDIR)/xgetgrnam.Plo + -rm -f ./$(DEPDIR)/xgetpwnam.Plo + -rm -f ./$(DEPDIR)/xgetpwuid.Plo + -rm -f ./$(DEPDIR)/xgetspnam.Plo + -rm -f ./$(DEPDIR)/xmalloc.Plo + -rm -f ./$(DEPDIR)/yesno.Plo + -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 TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-noinstLTLIBRARIES \ + cscopelist-am ctags ctags-am 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 tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +# 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 0000000..845d383 --- /dev/null +++ b/libmisc/addgrps.c @@ -0,0 +1,114 @@ +/* + * SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1998, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2001 - 2006, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2007 - 2009, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#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> +#include "shadowlog.h" + +#ident "$Id$" + +#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]; + int ret; + FILE *shadow_logfd = log_get_logfd(); + + 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 (shadow_logfd, _("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"), shadow_logfd); + 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) { + ret = setgroups ((size_t)ngroups, grouplist); + free (grouplist); + return ret; + } + + free (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 0000000..d10f71b --- /dev/null +++ b/libmisc/age.c @@ -0,0 +1,178 @@ +/* + * SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1998, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2001 - 2006, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2008 - 2009, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#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$" + +#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 0000000..e6c2006 --- /dev/null +++ b/libmisc/audit_help.c @@ -0,0 +1,87 @@ +/* + * SPDX-FileCopyrightText: 2005 , Red Hat, Inc. + * SPDX-FileCopyrightText: 2005 , Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2008 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * 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" +#include "shadowlog.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"), + log_get_logfd()); + 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 0000000..fe91653 --- /dev/null +++ b/libmisc/basename.c @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1997, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2005, Tomasz KÅ‚oczko + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * 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$" + +#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/btrfs.c b/libmisc/btrfs.c new file mode 100644 index 0000000..a2563f7 --- /dev/null +++ b/libmisc/btrfs.c @@ -0,0 +1,110 @@ +#include <linux/btrfs_tree.h> +#include <linux/magic.h> +#include <sys/statfs.h> +#include <stdbool.h> + +#include "prototypes.h" + +static bool path_exists(const char *p) +{ + struct stat sb; + + return stat(p, &sb) == 0; +} + +static const char *btrfs_cmd(void) +{ + const char *const btrfs_paths[] = {"/sbin/btrfs", + "/bin/btrfs", "/usr/sbin/btrfs", "/usr/bin/btrfs", NULL}; + const char *p; + int i; + + for (i = 0, p = btrfs_paths[i]; p; i++, p = btrfs_paths[i]) + if (path_exists(p)) + return p; + + return NULL; +} + +static int run_btrfs_subvolume_cmd(const char *subcmd, const char *arg1, const char *arg2) +{ + int status = 0; + const char *cmd = btrfs_cmd(); + const char *argv[] = { + "btrfs", + "subvolume", + subcmd, + arg1, + arg2, + NULL + }; + + if (access(cmd, X_OK)) { + return 1; + } + + if (run_command(cmd, argv, NULL, &status)) + return -1; + return status; +} + + +int btrfs_create_subvolume(const char *path) +{ + return run_btrfs_subvolume_cmd("create", path, NULL); +} + + +int btrfs_remove_subvolume(const char *path) +{ + return run_btrfs_subvolume_cmd("delete", "-C", path); +} + + +/* Adapted from btrfsprogs */ +/* + * This intentionally duplicates btrfs_util_is_subvolume_fd() instead of opening + * a file descriptor and calling it, because fstat() and fstatfs() don't accept + * file descriptors opened with O_PATH on old kernels (before v3.6 and before + * v3.12, respectively), but stat() and statfs() can be called on a path that + * the user doesn't have read or write permissions to. + * + * returns: + * 1 - btrfs subvolume + * 0 - not btrfs subvolume + * -1 - error + */ +int btrfs_is_subvolume(const char *path) +{ + struct stat st; + int ret; + + ret = is_btrfs(path); + if (ret <= 0) + return ret; + + ret = stat(path, &st); + if (ret == -1) + return -1; + + if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode)) { + return 0; + } + + return 1; +} + + +/* Adapted from btrfsprogs */ +int is_btrfs(const char *path) +{ + struct statfs sfs; + int ret; + + ret = statfs(path, &sfs); + if (ret == -1) + return -1; + + return sfs.f_type == BTRFS_SUPER_MAGIC; +} + diff --git a/libmisc/chkname.c b/libmisc/chkname.c new file mode 100644 index 0000000..e31ee8c --- /dev/null +++ b/libmisc/chkname.c @@ -0,0 +1,101 @@ +/* + * SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 2000, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2001 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2005 - 2008, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * 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$" + +#include <ctype.h> +#include "defines.h" +#include "chkname.h" + +int allow_bad_names = false; + +static bool is_valid_name (const char *name) +{ + if (allow_bad_names) { + return true; + } + + /* + * User/group names must match gnu e-regex: + * [a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,30}[a-zA-Z0-9_.$-]? + * + * as a non-POSIX, extension, allow "$" as the last char for + * sake of Samba 3.x "add machine script" + * + * Also do not allow fully numeric names or just "." or "..". + */ + int numeric; + + if ('\0' == *name || + ('.' == *name && (('.' == name[1] && '\0' == name[2]) || + '\0' == name[1])) || + !((*name >= 'a' && *name <= 'z') || + (*name >= 'A' && *name <= 'Z') || + (*name >= '0' && *name <= '9') || + *name == '_' || + *name == '.')) { + return false; + } + + numeric = isdigit(*name); + + while ('\0' != *++name) { + if (!((*name >= 'a' && *name <= 'z') || + (*name >= 'A' && *name <= 'Z') || + (*name >= '0' && *name <= '9') || + *name == '_' || + *name == '.' || + *name == '-' || + (*name == '$' && name[1] == '\0') + )) { + return false; + } + numeric &= isdigit(*name); + } + + return !numeric; +} + +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 0000000..0771347 --- /dev/null +++ b/libmisc/chkname.h @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1997 - 2000, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2005 , Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2008 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* $Id$ */ +#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 0000000..d31618a --- /dev/null +++ b/libmisc/chowndir.c @@ -0,0 +1,146 @@ +/* + * SPDX-FileCopyrightText: 1992 - 1993, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 2000, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2010 - , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#include <sys/types.h> +#include <sys/stat.h> +#include "prototypes.h" +#include "defines.h" +#include <fcntl.h> +#include <stdio.h> +#include <unistd.h> + +static int chown_tree_at (int at_fd, + const char *path, + uid_t old_uid, + uid_t new_uid, + gid_t old_gid, + gid_t new_gid) +{ + DIR *dir; + const struct dirent *ent; + struct stat dir_sb; + int dir_fd, rc = 0; + + dir_fd = openat (at_fd, path, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC); + if (dir_fd < 0) { + return -1; + } + + dir = fdopendir (dir_fd); + if (!dir) { + (void) close (dir_fd); + 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. + */ + while ((ent = readdir (dir))) { + uid_t tmpuid = (uid_t) -1; + gid_t tmpgid = (gid_t) -1; + struct stat ent_sb; + + /* + * Skip the "." and ".." entries + */ + if ( (strcmp (ent->d_name, ".") == 0) + || (strcmp (ent->d_name, "..") == 0)) { + continue; + } + + rc = fstatat (dirfd(dir), ent->d_name, &ent_sb, AT_SYMLINK_NOFOLLOW); + if (rc < 0) { + break; + } + + if (S_ISDIR (ent_sb.st_mode)) { + /* + * Do the entire subdirectory. + */ + rc = chown_tree_at (dirfd(dir), ent->d_name, old_uid, new_uid, old_gid, new_gid); + if (0 != rc) { + break; + } + } + + /* + * 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) || (ent_sb.st_uid == old_uid)) { + tmpuid = new_uid; + } + if (((gid_t) -1 == old_gid) || (ent_sb.st_gid == old_gid)) { + tmpgid = new_gid; + } + if (((uid_t) -1 != tmpuid) || ((gid_t) -1 != tmpgid)) { + rc = fchownat (dirfd(dir), ent->d_name, tmpuid, tmpgid, AT_SYMLINK_NOFOLLOW); + if (0 != rc) { + break; + } + } + } + + /* + * Now do the root of the tree + */ + if ((0 == rc) && (fstat (dirfd(dir), &dir_sb) == 0)) { + uid_t tmpuid = (uid_t) -1; + gid_t tmpgid = (gid_t) -1; + if (((uid_t) -1 == old_uid) || (dir_sb.st_uid == old_uid)) { + tmpuid = new_uid; + } + if (((gid_t) -1 == old_gid) || (dir_sb.st_gid == old_gid)) { + tmpgid = new_gid; + } + if (((uid_t) -1 != tmpuid) || ((gid_t) -1 != tmpgid)) { + rc = fchown (dirfd(dir), tmpuid, tmpgid); + } + } else { + rc = -1; + } + + (void) closedir (dir); + + return rc; +} + +/* + * 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) +{ + return chown_tree_at (AT_FDCWD, root, old_uid, new_uid, old_gid, new_gid); +} diff --git a/libmisc/chowntty.c b/libmisc/chowntty.c new file mode 100644 index 0000000..8043d8c --- /dev/null +++ b/libmisc/chowntty.c @@ -0,0 +1,79 @@ +/* + * SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 2001, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2007 - 2009, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#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" +#include "shadowlog.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; + gr_free (grent); + } 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; + FILE *shadow_logfd = log_get_logfd(); + + fprintf (shadow_logfd, + _("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 0000000..c16f1bc --- /dev/null +++ b/libmisc/cleanup.c @@ -0,0 +1,121 @@ +/* + * SPDX-FileCopyrightText: 2008 - 2011, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#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 0000000..df3ebfd --- /dev/null +++ b/libmisc/cleanup_group.c @@ -0,0 +1,215 @@ +/* + * SPDX-FileCopyrightText: 2008 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#include <assert.h> +#include <stdio.h> + +#include "defines.h" +#include "groupio.h" +#include "sgroupio.h" +#include "prototypes.h" +#include "shadowlog.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, log_get_progname(), + "", + 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, log_get_progname(), + "", + 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, log_get_progname(), + 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, log_get_progname(), + 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, log_get_progname(), + "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, log_get_progname(), + "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, log_get_progname(), + "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, log_get_progname(), + "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 (log_get_logfd(), + _("%s: failed to unlock %s\n"), + log_get_progname(), 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 (log_get_logfd(), + _("%s: failed to unlock %s\n"), + log_get_progname(), 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 0000000..26675c6 --- /dev/null +++ b/libmisc/cleanup_user.c @@ -0,0 +1,130 @@ +/* + * SPDX-FileCopyrightText: 2008 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#include <assert.h> +#include <stdio.h> + +#include "defines.h" +#include "pwio.h" +#include "shadowio.h" +#include "prototypes.h" +#include "shadowlog.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, log_get_progname(), + "", + 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, log_get_progname(), + 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, log_get_progname(), + "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, log_get_progname(), + "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 (log_get_logfd(), + _("%s: failed to unlock %s\n"), + log_get_progname(), 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 (log_get_logfd(), + _("%s: failed to unlock %s\n"), + log_get_progname(), 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 0000000..c475aa2 --- /dev/null +++ b/libmisc/console.c @@ -0,0 +1,108 @@ +/* + * SPDX-FileCopyrightText: 1991 , Julianne Frances Haugh + * SPDX-FileCopyrightText: 1991 , Chip Rosenthal + * SPDX-FileCopyrightText: 1996 - 1998, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2007 - 2010, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> +#include "defines.h" +#include <stdio.h> +#include "getdef.h" +#include "prototypes.h" + +#ident "$Id$" + +/* + * 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[1024], *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; + strncpy (buf, cons, sizeof (buf)); + buf[sizeof (buf) - 1] = '\0'; + 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 0000000..b692aa9 --- /dev/null +++ b/libmisc/copydir.c @@ -0,0 +1,968 @@ +/* + * SPDX-FileCopyrightText: 1991 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 2001, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2006, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2007 - 2010, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#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 */ +#include "shadowlog.h" + + +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; + +struct path_info { + const char *full_path; + int dirfd; + const char *name; +}; + +static int copy_entry (const struct path_info *src, const struct path_info *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 struct path_info *src, const struct path_info *dst, + bool reset_selinux, + const struct stat *statp, const struct timespec mt[], + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid); +static /*@null@*/char *readlink_malloc (const char *filename); +static int copy_symlink (const struct path_info *src, const struct path_info *dst, + unused bool reset_selinux, + const struct stat *statp, const struct timespec mt[], + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid); +static int copy_hardlink (const struct path_info *dst, + unused bool reset_selinux, + struct link_name *lp); +static int copy_special (const struct path_info *src, const struct path_info *dst, + bool reset_selinux, + const struct stat *statp, const struct timespec mt[], + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid); +static int copy_file (const struct path_info *src, const struct path_info *dst, + bool reset_selinux, + const struct stat *statp, const struct timespec mt[], + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid); +static int chownat_if_needed (const struct path_info *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. + */ +format_attr(printf, 2, 3) +static void error_acl (unused struct error_context *ctx, const char *fmt, ...) +{ + va_list ap; + FILE *shadow_logfd = log_get_logfd(); + + /* ignore the case when destination does not support ACLs + * or extended attributes */ + if (ENOTSUP == errno) { + errno = 0; + return; + } + + va_start (ap, fmt); + (void) fprintf (shadow_logfd, _("%s: "), log_get_progname()); + if (vfprintf (shadow_logfd, fmt, ap) != 0) { + (void) fputs (_(": "), shadow_logfd); + } + (void) fprintf (shadow_logfd, "%s\n", strerror (errno)); + va_end (ap); +} + +static struct error_context ctx = { + error_acl, NULL, NULL +}; +#endif /* WITH_ACL || WITH_ATTR */ + +#ifdef WITH_ACL +static int perm_copy_path(const struct path_info *src, + const struct path_info *dst, + struct error_context *errctx) +{ + int src_fd, dst_fd, ret; + + src_fd = openat(src->dirfd, src->name, O_RDONLY | O_NOFOLLOW | O_NONBLOCK | O_CLOEXEC); + if (src_fd < 0) { + return -1; + } + + dst_fd = openat(dst->dirfd, dst->name, O_RDONLY | O_NOFOLLOW | O_NONBLOCK | O_CLOEXEC); + if (dst_fd < 0) { + (void) close (src_fd); + return -1; + } + + ret = perm_copy_fd(src->full_path, src_fd, dst->full_path, dst_fd, errctx); + (void) close (src_fd); + (void) close (dst_fd); + return ret; +} +#endif /* WITH_ACL */ + +#ifdef WITH_ATTR +static int attr_copy_path(const struct path_info *src, + const struct path_info *dst, + int (*callback) (const char *, struct error_context *), + struct error_context *errctx) +{ + int src_fd, dst_fd, ret; + + src_fd = openat(src->dirfd, src->name, O_RDONLY | O_NOFOLLOW | O_NONBLOCK | O_CLOEXEC); + if (src_fd < 0) { + return -1; + } + + dst_fd = openat(dst->dirfd, dst->name, O_RDONLY | O_NOFOLLOW | O_NONBLOCK | O_CLOEXEC); + if (dst_fd < 0) { + (void) close (src_fd); + return -1; + } + + ret = attr_copy_fd(src->full_path, src_fd, dst->full_path, dst_fd, callback, errctx); + (void) close (src_fd); + (void) close (dst_fd); + return ret; +} +#endif /* 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; +} + +static int copy_tree_impl (const struct path_info *src, const struct path_info *dst, + bool copy_root, bool reset_selinux, + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid) +{ + int dst_fd, src_fd, err = 0; + bool set_orig = false; + const struct dirent *ent; + DIR *dir; + + if (copy_root) { + struct stat sb; + + if ( fstatat (dst->dirfd, dst->name, &sb, 0) == 0 + || errno != ENOENT) { + return -1; + } + + if (fstatat (src->dirfd, src->name, &sb, AT_SYMLINK_NOFOLLOW) == -1) { + return -1; + } + + if (!S_ISDIR (sb.st_mode)) { + fprintf (log_get_logfd(), + "%s: %s is not a directory", + log_get_progname(), src->full_path); + return -1; + } + + return copy_entry (src, dst, 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. + */ + + src_fd = openat (src->dirfd, src->name, O_DIRECTORY | O_RDONLY | O_NOFOLLOW | O_CLOEXEC); + if (src_fd < 0) { + return -1; + } + + dst_fd = openat (dst->dirfd, dst->name, O_DIRECTORY | O_RDONLY | O_NOFOLLOW | O_CLOEXEC); + if (dst_fd < 0) { + (void) close (src_fd); + 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 = fdopendir (src_fd); + if (NULL == dir) { + (void) close (src_fd); + (void) close (dst_fd); + return -1; + } + + if (src_orig == NULL) { + src_orig = src->full_path; + dst_orig = dst->full_path; + 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->full_path); + dst_len += strlen (dst->full_path); + + 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. + */ + struct path_info src_entry, dst_entry; + + (void) snprintf (src_name, src_len, "%s/%s", + src->full_path, ent->d_name); + (void) snprintf (dst_name, dst_len, "%s/%s", + dst->full_path, ent->d_name); + + src_entry.full_path = src_name; + src_entry.dirfd = dirfd(dir); + src_entry.name = ent->d_name; + + dst_entry.full_path = dst_name; + dst_entry.dirfd = dst_fd; + dst_entry.name = ent->d_name; + + err = copy_entry (&src_entry, &dst_entry, + reset_selinux, + old_uid, new_uid, + old_gid, new_gid); + } + free (src_name); + free (dst_name); + } + } + (void) closedir (dir); + (void) close (dst_fd); + + 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 struct path_info *src, const struct path_info *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 timespec mt[2]; + + if (fstatat(src->dirfd, src->name, &sb, AT_SYMLINK_NOFOLLOW) == -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_nsec = sb.st_atim.tv_nsec; +#else /* !HAVE_STRUCT_STAT_ST_ATIM */ + mt[0].tv_sec = sb.st_atime; +# ifdef HAVE_STRUCT_STAT_ST_ATIMENSEC + mt[0].tv_nsec = sb.st_atimensec; +# else /* !HAVE_STRUCT_STAT_ST_ATIMENSEC */ + mt[0].tv_nsec = 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_nsec = sb.st_mtim.tv_nsec; +#else /* !HAVE_STRUCT_STAT_ST_MTIM */ + mt[1].tv_sec = sb.st_mtime; +# ifdef HAVE_STRUCT_STAT_ST_MTIMENSEC + mt[1].tv_nsec = sb.st_mtimensec; +# else /* !HAVE_STRUCT_STAT_ST_MTIMENSEC */ + mt[1].tv_nsec = 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); + } + + /* + * 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); + } + + /* + * See if this is a previously copied link + */ + + else if ((lp = check_link (src->full_path, &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 struct path_info *src, const struct path_info *dst, + bool reset_selinux, + const struct stat *statp, const struct timespec 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->full_path, S_IFDIR) != 0) { + return -1; + } +#endif /* WITH_SELINUX */ + if ( (mkdirat (dst->dirfd, dst->name, 0700) != 0) + || (chownat_if_needed (dst, statp, + old_uid, new_uid, old_gid, new_gid) != 0) + || (fchmodat (dst->dirfd, dst->name, statp->st_mode & 07777, AT_SYMLINK_NOFOLLOW) != 0) +#ifdef WITH_ACL + || ( (perm_copy_path (src, dst, &ctx) != 0) + && (errno != 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_path (src, dst, NULL, &ctx) != 0) + && (errno != 0)) +#endif /* WITH_ATTR */ + || (copy_tree_impl (src, dst, false, reset_selinux, + old_uid, new_uid, old_gid, new_gid) != 0) + || (utimensat (dst->dirfd, dst->name, mt, AT_SYMLINK_NOFOLLOW) != 0)) { + err = -1; + } + + return err; +} + +/* + * 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 struct path_info *src, const struct path_info *dst, + unused bool reset_selinux, + const struct stat *statp, const struct timespec 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->full_path); + 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->full_path, S_IFLNK) != 0) { + free (oldlink); + return -1; + } +#endif /* WITH_SELINUX */ + if ( (symlinkat (oldlink, dst->dirfd, dst->name) != 0) + || (chownat_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); + + if (utimensat (dst->dirfd, dst->name, mt, AT_SYMLINK_NOFOLLOW) != 0) { + return -1; + } + + return 0; +} + +/* + * copy_hardlink - copy a hardlink + * + * Copy a hardlink from src to dst. + * + * Return 0 on success, -1 on error. + */ +static int copy_hardlink (const struct path_info *dst, + unused bool reset_selinux, + struct link_name *lp) +{ + /* FIXME: selinux, ACL, Extended Attributes needed? */ + + if (linkat (AT_FDCWD, lp->ln_name, dst->dirfd, dst->name, 0) != 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 struct path_info *src, const struct path_info *dst, + bool reset_selinux, + const struct stat *statp, const struct timespec 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->full_path, statp->st_mode & S_IFMT) != 0) { + return -1; + } +#endif /* WITH_SELINUX */ + + if ( (mknodat (dst->dirfd, dst->name, statp->st_mode & ~07777U, statp->st_rdev) != 0) + || (chownat_if_needed (dst, statp, + old_uid, new_uid, old_gid, new_gid) != 0) + || (fchmodat (dst->dirfd, dst->name, statp->st_mode & 07777, AT_SYMLINK_NOFOLLOW) != 0) +#ifdef WITH_ACL + || ( (perm_copy_path (src, dst, &ctx) != 0) + && (errno != 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_path (src, dst, NULL, &ctx) != 0) + && (errno != 0)) +#endif /* WITH_ATTR */ + || (utimensat (dst->dirfd, dst->name, mt, AT_SYMLINK_NOFOLLOW) != 0)) { + err = -1; + } + + return err; +} + +/* + * full_write - write entire buffer + * + * Write up to count bytes from the buffer starting at buf to the + * file referred to by the file descriptor fd. + * Retry in case of a short write. + * + * Returns the number of bytes written on success, -1 on error. + */ +static ssize_t full_write(int fd, const void *buf, size_t count) { + ssize_t written = 0; + + while (count > 0) { + ssize_t res; + + res = write(fd, buf, count); + if (res < 0) { + if (errno == EINTR) { + continue; + } + + return res; + } + + if (res == 0) { + break; + } + + written += res; + buf = (const unsigned char*)buf + res; + count -= (size_t)res; + } + + return written; +} + +/* + * 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 struct path_info *src, const struct path_info *dst, + bool reset_selinux, + const struct stat *statp, const struct timespec mt[], + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid) +{ + int err = 0; + int ifd; + int ofd; + + ifd = openat (src->dirfd, src->name, O_RDONLY|O_NOFOLLOW|O_CLOEXEC); + if (ifd < 0) { + return -1; + } +#ifdef WITH_SELINUX + if (set_selinux_file_context (dst->full_path, S_IFREG) != 0) { + (void) close (ifd); + return -1; + } +#endif /* WITH_SELINUX */ + ofd = openat (dst->dirfd, dst->name, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC | O_NOFOLLOW | O_CLOEXEC, 0600); + if ( (ofd < 0) + || (fchown_if_needed (ofd, statp, + old_uid, new_uid, old_gid, new_gid) != 0) + || (fchmod (ofd, statp->st_mode & 07777) != 0) +#ifdef WITH_ACL + || ( (perm_copy_fd (src->full_path, ifd, dst->full_path, ofd, &ctx) != 0) + && (errno != 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->full_path, ifd, dst->full_path, ofd, NULL, &ctx) != 0) + && (errno != 0)) +#endif /* WITH_ATTR */ + ) { + if (ofd >= 0) { + (void) close (ofd); + } + (void) close (ifd); + return -1; + } + + while (true) { + char buf[8192]; + ssize_t cnt; + + cnt = read (ifd, buf, sizeof buf); + if (cnt < 0) { + if (errno == EINTR) { + continue; + } + (void) close (ofd); + (void) close (ifd); + return -1; + } + if (cnt == 0) { + break; + } + + if (full_write (ofd, buf, (size_t)cnt) < 0) { + (void) close (ofd); + (void) close (ifd); + return -1; + } + } + + (void) close (ifd); + if (close (ofd) != 0) { + return -1; + } + + if (utimensat (dst->dirfd, dst->name, mt, AT_SYMLINK_NOFOLLOW) != 0) { + return -1; + } + + 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 (fchown, int) + +static int chownat_if_needed (const struct path_info *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 fchownat (dst->dirfd, dst->name, tmpuid, tmpgid, AT_SYMLINK_NOFOLLOW); +} + +/* + * 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) +{ + const struct path_info src = { + .full_path = src_root, + .dirfd = AT_FDCWD, + .name = src_root + }; + const struct path_info dst = { + .full_path = dst_root, + .dirfd = AT_FDCWD, + .name = dst_root + }; + + return copy_tree_impl(&src, &dst, copy_root, reset_selinux, + old_uid, new_uid, old_gid, new_gid); +} diff --git a/libmisc/date_to_str.c b/libmisc/date_to_str.c new file mode 100644 index 0000000..07e99f1 --- /dev/null +++ b/libmisc/date_to_str.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021, Alejandro Colomar <alx.manpages@gmail.com> + * + * 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 <string.h> +#include <time.h> + +#ident "$Id$" + +#include "prototypes.h" + +void date_to_str (size_t size, char buf[size], long date) +{ + time_t t; + + t = date; + if (date < 0) + (void) strncpy (buf, "never", size); + else + (void) strftime (buf, size, "%Y-%m-%d", gmtime (&t)); + buf[size - 1] = '\0'; +} diff --git a/libmisc/entry.c b/libmisc/entry.c new file mode 100644 index 0000000..87f5754 --- /dev/null +++ b/libmisc/entry.c @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 2000, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2007 - 2008, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#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 0000000..fc6dbce --- /dev/null +++ b/libmisc/env.c @@ -0,0 +1,253 @@ +/* + * SPDX-FileCopyrightText: 1989 - 1992, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1999, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2008 - 2009, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "prototypes.h" +#include "defines.h" +#include "shadowlog.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 *const 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 *const 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"), log_get_logfd()); + 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 *const *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 *const *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 0000000..1aab299 --- /dev/null +++ b/libmisc/failure.c @@ -0,0 +1,295 @@ +/* + * SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1998, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2002 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2008 - 2010, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#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) - 1); + (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; + char lasttimeb[256]; + char *lasttime = lasttimeb; + time_t NOW; + + if (0 == fail->fail_cnt) { + return; + } + + tp = localtime (&(fail->fail_time)); + (void) time (&NOW); + + /* + * Print all information we have. + */ + (void) strftime (lasttimeb, sizeof lasttimeb, "%c", tp); + + /*@-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 0000000..2ac30d7 --- /dev/null +++ b/libmisc/failure.h @@ -0,0 +1,61 @@ +/* + * SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1997 - 2000, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2005 , Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2008 - 2009, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* $Id$ */ +#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 cumulative 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 0000000..65ab5d0 --- /dev/null +++ b/libmisc/find_new_gid.c @@ -0,0 +1,483 @@ +/* + * SPDX-FileCopyrightText: 1991 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 2008 - 2011, Nicolas François + * SPDX-FileCopyrightText: 2014, Red Hat, Inc. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#include <assert.h> +#include <stdio.h> +#include <errno.h> + +#include "prototypes.h" +#include "groupio.h" +#include "getdef.h" +#include "shadowlog.h" + +/* + * get_ranges - Get the minimum and maximum ID ranges for the search + * + * This function will return the minimum and maximum ranges for IDs + * + * 0: The function completed successfully + * EINVAL: The provided ranges are impossible (such as maximum < minimum) + * + * preferred_min: The special-case minimum value for a specifically- + * requested ID, which may be lower than the standard min_id + */ +static int get_ranges (bool sys_group, gid_t *min_id, gid_t *max_id, + gid_t *preferred_min) +{ + gid_t gid_def_max = 0; + + if (sys_group) { + /* System groups */ + + /* A requested ID is allowed to be below the autoselect range */ + *preferred_min = (gid_t) 1; + + /* Get the minimum ID range from login.defs or default to 101 */ + *min_id = (gid_t) getdef_ulong ("SYS_GID_MIN", 101UL); + + /* + * If SYS_GID_MAX is unspecified, we should assume it to be one + * less than the GID_MIN (which is reserved for non-system accounts) + */ + gid_def_max = (gid_t) getdef_ulong ("GID_MIN", 1000UL) - 1; + *max_id = (gid_t) getdef_ulong ("SYS_GID_MAX", + (unsigned long) gid_def_max); + + /* Check that the ranges make sense */ + if (*max_id < *min_id) { + (void) fprintf (log_get_logfd(), + _("%s: Invalid configuration: SYS_GID_MIN (%lu), " + "GID_MIN (%lu), SYS_GID_MAX (%lu)\n"), + log_get_progname(), (unsigned long) *min_id, + getdef_ulong ("GID_MIN", 1000UL), + (unsigned long) *max_id); + return EINVAL; + } + /* + * Zero is reserved for root and the allocation algorithm does not + * work right with it. + */ + if (*min_id == 0) { + *min_id = (gid_t) 1; + } + } else { + /* Non-system groups */ + + /* Get the values from login.defs or use reasonable defaults */ + *min_id = (gid_t) getdef_ulong ("GID_MIN", 1000UL); + *max_id = (gid_t) getdef_ulong ("GID_MAX", 60000UL); + + /* + * The preferred minimum should match the standard ID minimum + * for non-system groups. + */ + *preferred_min = *min_id; + + /* Check that the ranges make sense */ + if (*max_id < *min_id) { + (void) fprintf (log_get_logfd(), + _("%s: Invalid configuration: GID_MIN (%lu), " + "GID_MAX (%lu)\n"), + log_get_progname(), (unsigned long) *min_id, + (unsigned long) *max_id); + return EINVAL; + } + } + + return 0; +} + +/* + * check_gid - See if the requested GID is available + * + * On success, return 0 + * If the ID is in use, return EEXIST + * If the ID is outside the range, return ERANGE + * In other cases, return errno from getgrgid() + */ +static int check_gid (const gid_t gid, + const gid_t gid_min, + const gid_t gid_max, + const bool *used_gids) +{ + /* First test that the preferred ID is in the range */ + if (gid < gid_min || gid > gid_max) { + return ERANGE; + } + + /* + * Check whether we already detected this GID + * using the gr_next() loop + */ + if (used_gids != NULL && used_gids[gid]) { + return EEXIST; + } + /* Check if the GID exists according to NSS */ + errno = 0; + if (prefix_getgrgid (gid) != NULL) { + return EEXIST; + } else { + /* getgrgid() was NULL + * we have to ignore errors as temporary + * failures of remote user identity services + * would completely block user/group creation + */ + } + + /* If we've made it here, the GID must be available */ + return 0; +} + +/* + * 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) +{ + bool *used_gids; + const struct group *grp; + gid_t gid_min, gid_max, preferred_min; + gid_t id; + gid_t lowest_found, highest_found; + int result; + int nospam = 0; + + assert(gid != NULL); + + /* + * First, figure out what ID range is appropriate for + * automatic assignment + */ + result = get_ranges (sys_group, &gid_min, &gid_max, &preferred_min); + if (result == EINVAL) { + return -1; + } + + /* Check if the preferred GID is available */ + if (preferred_gid) { + result = check_gid (*preferred_gid, preferred_min, gid_max, NULL); + if (result == 0) { + /* + * Make sure the GID isn't queued for use already + */ + if (gr_locate_gid (*preferred_gid) == NULL) { + *gid = *preferred_gid; + return 0; + } + /* + * gr_locate_gid() found the GID in an as-yet uncommitted + * entry. We'll proceed below and auto-set a GID. + */ + } else if (result == EEXIST || result == ERANGE) { + /* + * Continue on below. At this time, we won't + * treat these two cases differently. + */ + } else { + /* + * An unexpected error occurred. We should report + * this and fail the group creation. + * This differs from the automatic creation + * behavior below, since if a specific GID was + * requested and generated an error, the user is + * more likely to want to stop and address the + * issue. + */ + fprintf (log_get_logfd(), + _("%s: Encountered error attempting to use " + "preferred GID: %s\n"), + log_get_progname(), strerror (result)); + return -1; + } + } + + /* + * Search the entire group file, + * looking for the next unused value. + * + * We first check the local database with gr_rewind/gr_next to find + * all local values that are in use. + * + * We then compare the next free value to all databases (local and + * remote) and iterate until we find a free one. If there are free + * values beyond the lowest (system groups) or highest (non-system + * groups), we will prefer those and avoid potentially reclaiming a + * deleted group (which can be a security issue, since it may grant + * access to files belonging to that former group). + * + * If there are no GIDs available at the end of the search, we will + * have no choice but to iterate through the range looking for gaps. + * + */ + + /* Create an array to hold all of the discovered GIDs */ + used_gids = malloc (sizeof (bool) * (gid_max +1)); + if (NULL == used_gids) { + fprintf (log_get_logfd(), + _("%s: failed to allocate memory: %s\n"), + log_get_progname(), strerror (errno)); + return -1; + } + memset (used_gids, false, sizeof (bool) * (gid_max + 1)); + + /* First look for the lowest and highest value in the local database */ + (void) gr_rewind (); + highest_found = gid_min; + lowest_found = gid_max; + while ((grp = gr_next ()) != NULL) { + /* + * Does this entry have a lower GID than the lowest we've found + * so far? + */ + if ((grp->gr_gid <= lowest_found) && (grp->gr_gid >= gid_min)) { + lowest_found = grp->gr_gid - 1; + } + + /* + * Does this entry have a higher GID than the highest we've found + * so far? + */ + if ((grp->gr_gid >= highest_found) && (grp->gr_gid <= gid_max)) { + highest_found = grp->gr_gid + 1; + } + + /* create index of used GIDs */ + if (grp->gr_gid >= gid_min + && grp->gr_gid <= gid_max) { + + used_gids[grp->gr_gid] = true; + } + } + + if (sys_group) { + /* + * For system groups, we want to start from the + * top of the range and work downwards. + */ + + /* + * At the conclusion of the gr_next() search, we will either + * have a presumed-free GID or we will be at GID_MIN - 1. + */ + if (lowest_found < gid_min) { + /* + * In this case, a GID is in use at GID_MIN. + * + * We will reset the search to GID_MAX and proceed down + * through all the GIDs (skipping those we detected with + * used_gids) for a free one. It is a known issue that + * this may result in reusing a previously-deleted GID, + * so administrators should be instructed to use this + * auto-detection with care (and prefer to assign GIDs + * explicitly). + */ + lowest_found = gid_max; + } + + /* Search through all of the IDs in the range */ + for (id = lowest_found; id >= gid_min; id--) { + result = check_gid (id, gid_min, gid_max, used_gids); + if (result == 0) { + /* This GID is available. Return it. */ + *gid = id; + free (used_gids); + return 0; + } else if (result == EEXIST) { + /* This GID is in use, we'll continue to the next */ + } else { + /* + * An unexpected error occurred. + * + * Only report it the first time to avoid spamming + * the logs + * + */ + if (!nospam) { + fprintf (log_get_logfd(), + _("%s: Can't get unique system GID (%s). " + "Suppressing additional messages.\n"), + log_get_progname(), strerror (result)); + SYSLOG ((LOG_ERR, + "Error checking available GIDs: %s", + strerror (result))); + nospam = 1; + } + /* + * We will continue anyway. Hopefully a later GID + * will work properly. + */ + } + } + + /* + * If we get all the way through the loop, try again from GID_MAX, + * unless that was where we previously started. (NOTE: the worst-case + * scenario here is that we will run through (GID_MAX - GID_MIN - 1) + * cycles *again* if we fall into this case with lowest_found as + * GID_MAX - 1, all groups in the range in use and maintained by + * network services such as LDAP.) + */ + if (lowest_found != gid_max) { + for (id = gid_max; id >= gid_min; id--) { + result = check_gid (id, gid_min, gid_max, used_gids); + if (result == 0) { + /* This GID is available. Return it. */ + *gid = id; + free (used_gids); + return 0; + } else if (result == EEXIST) { + /* This GID is in use, we'll continue to the next */ + } else { + /* + * An unexpected error occurred. + * + * Only report it the first time to avoid spamming + * the logs + * + */ + if (!nospam) { + fprintf (log_get_logfd(), + _("%s: Can't get unique system GID (%s). " + "Suppressing additional messages.\n"), + log_get_progname(), strerror (result)); + SYSLOG ((LOG_ERR, + "Error checking available GIDs: %s", + strerror (result))); + nospam = 1; + } + /* + * We will continue anyway. Hopefully a later GID + * will work properly. + */ + } + } + } + } else { /* !sys_group */ + /* + * For non-system groups, we want to start from the + * bottom of the range and work upwards. + */ + + /* + * At the conclusion of the gr_next() search, we will either + * have a presumed-free GID or we will be at GID_MAX + 1. + */ + if (highest_found > gid_max) { + /* + * In this case, a GID is in use at GID_MAX. + * + * We will reset the search to GID_MIN and proceed up + * through all the GIDs (skipping those we detected with + * used_gids) for a free one. It is a known issue that + * this may result in reusing a previously-deleted GID, + * so administrators should be instructed to use this + * auto-detection with care (and prefer to assign GIDs + * explicitly). + */ + highest_found = gid_min; + } + + /* Search through all of the IDs in the range */ + for (id = highest_found; id <= gid_max; id++) { + result = check_gid (id, gid_min, gid_max, used_gids); + if (result == 0) { + /* This GID is available. Return it. */ + *gid = id; + free (used_gids); + return 0; + } else if (result == EEXIST) { + /* This GID is in use, we'll continue to the next */ + } else { + /* + * An unexpected error occurred. + * + * Only report it the first time to avoid spamming + * the logs + * + */ + if (!nospam) { + fprintf (log_get_logfd(), + _("%s: Can't get unique GID (%s). " + "Suppressing additional messages.\n"), + log_get_progname(), strerror (result)); + SYSLOG ((LOG_ERR, + "Error checking available GIDs: %s", + strerror (result))); + nospam = 1; + } + /* + * We will continue anyway. Hopefully a later GID + * will work properly. + */ + } + } + + /* + * If we get all the way through the loop, try again from GID_MIN, + * unless that was where we previously started. (NOTE: the worst-case + * scenario here is that we will run through (GID_MAX - GID_MIN - 1) + * cycles *again* if we fall into this case with highest_found as + * GID_MIN + 1, all groups in the range in use and maintained by + * network services such as LDAP.) + */ + if (highest_found != gid_min) { + for (id = gid_min; id <= gid_max; id++) { + result = check_gid (id, gid_min, gid_max, used_gids); + if (result == 0) { + /* This GID is available. Return it. */ + *gid = id; + free (used_gids); + return 0; + } else if (result == EEXIST) { + /* This GID is in use, we'll continue to the next */ + } else { + /* + * An unexpected error occurred. + * + * Only report it the first time to avoid spamming + * the logs + * + */ + if (!nospam) { + fprintf (log_get_logfd(), + _("%s: Can't get unique GID (%s). " + "Suppressing additional messages.\n"), + log_get_progname(), strerror (result)); + SYSLOG ((LOG_ERR, + "Error checking available GIDs: %s", + strerror (result))); + nospam = 1; + } + /* + * We will continue anyway. Hopefully a later GID + * will work properly. + */ + } + } + } + } + + /* The code reached here and found no available IDs in the range */ + fprintf (log_get_logfd(), + _("%s: Can't get unique GID (no more available GIDs)\n"), + log_get_progname()); + SYSLOG ((LOG_WARN, "no more available GIDs on the system")); + free (used_gids); + return -1; +} + diff --git a/libmisc/find_new_sub_gids.c b/libmisc/find_new_sub_gids.c new file mode 100644 index 0000000..bbd4570 --- /dev/null +++ b/libmisc/find_new_sub_gids.c @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: 2012 Eric Biederman + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ifdef ENABLE_SUBIDS + +#include <assert.h> +#include <stdio.h> +#include <errno.h> + +#include "prototypes.h" +#include "subordinateio.h" +#include "getdef.h" +#include "shadowlog.h" + +/* + * find_new_sub_gids - Find a new unused range of GIDs. + * + * If successful, find_new_sub_gids provides a range of unused + * user IDs in the [SUB_GID_MIN:SUB_GID_MAX] range. + * + * Return 0 on success, -1 if no unused GIDs are available. + */ +int find_new_sub_gids (gid_t *range_start, unsigned long *range_count) +{ + unsigned long min, max; + unsigned long count; + gid_t start; + + assert (range_start != NULL); + assert (range_count != NULL); + + min = getdef_ulong ("SUB_GID_MIN", 100000UL); + max = getdef_ulong ("SUB_GID_MAX", 600100000UL); + count = getdef_ulong ("SUB_GID_COUNT", 65536); + + if (min > max || count >= max || (min + count - 1) > max) { + (void) fprintf (log_get_logfd(), + _("%s: Invalid configuration: SUB_GID_MIN (%lu)," + " SUB_GID_MAX (%lu), SUB_GID_COUNT (%lu)\n"), + log_get_progname(), min, max, count); + return -1; + } + + start = sub_gid_find_free_range(min, max, count); + if (start == (gid_t)-1) { + fprintf (log_get_logfd(), + _("%s: Can't get unique subordinate GID range\n"), + log_get_progname()); + SYSLOG ((LOG_WARN, "no more available subordinate GIDs on the system")); + return -1; + } + *range_start = start; + *range_count = count; + return 0; +} +#else /* !ENABLE_SUBIDS */ +extern int errno; /* warning: ANSI C forbids an empty source file */ +#endif /* !ENABLE_SUBIDS */ + diff --git a/libmisc/find_new_sub_uids.c b/libmisc/find_new_sub_uids.c new file mode 100644 index 0000000..a86b311 --- /dev/null +++ b/libmisc/find_new_sub_uids.c @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: 2012 Eric Biederman + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ifdef ENABLE_SUBIDS + +#include <assert.h> +#include <stdio.h> +#include <errno.h> + +#include "prototypes.h" +#include "subordinateio.h" +#include "getdef.h" +#include "shadowlog.h" + +/* + * find_new_sub_uids - Find a new unused range of UIDs. + * + * If successful, find_new_sub_uids provides a range of unused + * user IDs in the [SUB_UID_MIN:SUB_UID_MAX] range. + * + * Return 0 on success, -1 if no unused UIDs are available. + */ +int find_new_sub_uids (uid_t *range_start, unsigned long *range_count) +{ + unsigned long min, max; + unsigned long count; + uid_t start; + + assert (range_start != NULL); + assert (range_count != NULL); + + min = getdef_ulong ("SUB_UID_MIN", 100000UL); + max = getdef_ulong ("SUB_UID_MAX", 600100000UL); + count = getdef_ulong ("SUB_UID_COUNT", 65536); + + if (min > max || count >= max || (min + count - 1) > max) { + (void) fprintf (log_get_logfd(), + _("%s: Invalid configuration: SUB_UID_MIN (%lu)," + " SUB_UID_MAX (%lu), SUB_UID_COUNT (%lu)\n"), + log_get_progname(), min, max, count); + return -1; + } + + start = sub_uid_find_free_range(min, max, count); + if (start == (uid_t)-1) { + fprintf (log_get_logfd(), + _("%s: Can't get unique subordinate UID range\n"), + log_get_progname()); + SYSLOG ((LOG_WARN, "no more available subordinate UIDs on the system")); + return -1; + } + *range_start = start; + *range_count = count; + return 0; +} +#else /* !ENABLE_SUBIDS */ +extern int errno; /* warning: ANSI C forbids an empty source file */ +#endif /* !ENABLE_SUBIDS */ + diff --git a/libmisc/find_new_uid.c b/libmisc/find_new_uid.c new file mode 100644 index 0000000..5f7e74b --- /dev/null +++ b/libmisc/find_new_uid.c @@ -0,0 +1,483 @@ +/* + * SPDX-FileCopyrightText: 1991 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 2008 - 2011, Nicolas François + * SPDX-FileCopyrightText: 2014, Red Hat, Inc. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#include <assert.h> +#include <stdio.h> +#include <errno.h> + +#include "prototypes.h" +#include "pwio.h" +#include "getdef.h" +#include "shadowlog.h" + +/* + * get_ranges - Get the minimum and maximum ID ranges for the search + * + * This function will return the minimum and maximum ranges for IDs + * + * 0: The function completed successfully + * EINVAL: The provided ranges are impossible (such as maximum < minimum) + * + * preferred_min: The special-case minimum value for a specifically- + * requested ID, which may be lower than the standard min_id + */ +static int get_ranges (bool sys_user, uid_t *min_id, uid_t *max_id, + uid_t *preferred_min) +{ + uid_t uid_def_max = 0; + + if (sys_user) { + /* System users */ + + /* A requested ID is allowed to be below the autoselect range */ + *preferred_min = (uid_t) 1; + + /* Get the minimum ID range from login.defs or default to 101 */ + *min_id = (uid_t) getdef_ulong ("SYS_UID_MIN", 101UL); + + /* + * If SYS_UID_MAX is unspecified, we should assume it to be one + * less than the UID_MIN (which is reserved for non-system accounts) + */ + uid_def_max = (uid_t) getdef_ulong ("UID_MIN", 1000UL) - 1; + *max_id = (uid_t) getdef_ulong ("SYS_UID_MAX", + (unsigned long) uid_def_max); + + /* Check that the ranges make sense */ + if (*max_id < *min_id) { + (void) fprintf (log_get_logfd(), + _("%s: Invalid configuration: SYS_UID_MIN (%lu), " + "UID_MIN (%lu), SYS_UID_MAX (%lu)\n"), + log_get_progname(), (unsigned long) *min_id, + getdef_ulong ("UID_MIN", 1000UL), + (unsigned long) *max_id); + return EINVAL; + } + /* + * Zero is reserved for root and the allocation algorithm does not + * work right with it. + */ + if (*min_id == 0) { + *min_id = (uid_t) 1; + } + } else { + /* Non-system users */ + + /* Get the values from login.defs or use reasonable defaults */ + *min_id = (uid_t) getdef_ulong ("UID_MIN", 1000UL); + *max_id = (uid_t) getdef_ulong ("UID_MAX", 60000UL); + + /* + * The preferred minimum should match the standard ID minimum + * for non-system users. + */ + *preferred_min = *min_id; + + /* Check that the ranges make sense */ + if (*max_id < *min_id) { + (void) fprintf (log_get_logfd(), + _("%s: Invalid configuration: UID_MIN (%lu), " + "UID_MAX (%lu)\n"), + log_get_progname(), (unsigned long) *min_id, + (unsigned long) *max_id); + return EINVAL; + } + } + + return 0; +} + +/* + * check_uid - See if the requested UID is available + * + * On success, return 0 + * If the ID is in use, return EEXIST + * If the ID is outside the range, return ERANGE + * In other cases, return errno from getpwuid() + */ +static int check_uid(const uid_t uid, + const uid_t uid_min, + const uid_t uid_max, + const bool *used_uids) +{ + /* First test that the preferred ID is in the range */ + if (uid < uid_min || uid > uid_max) { + return ERANGE; + } + + /* + * Check whether we already detected this UID + * using the pw_next() loop + */ + if (used_uids != NULL && used_uids[uid]) { + return EEXIST; + } + /* Check if the UID exists according to NSS */ + errno = 0; + if (prefix_getpwuid(uid) != NULL) { + return EEXIST; + } else { + /* getpwuid() was NULL + * we have to ignore errors as temporary + * failures of remote user identity services + * would completely block user/group creation + */ + } + + /* If we've made it here, the UID must be available */ + return 0; +} + +/* + * 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) +{ + bool *used_uids; + const struct passwd *pwd; + uid_t uid_min, uid_max, preferred_min; + uid_t id; + uid_t lowest_found, highest_found; + int result; + int nospam = 0; + + assert (uid != NULL); + + /* + * First, figure out what ID range is appropriate for + * automatic assignment + */ + result = get_ranges (sys_user, &uid_min, &uid_max, &preferred_min); + if (result == EINVAL) { + return -1; + } + + /* Check if the preferred UID is available */ + if (preferred_uid) { + result = check_uid (*preferred_uid, preferred_min, uid_max, NULL); + if (result == 0) { + /* + * Make sure the UID isn't queued for use already + */ + if (pw_locate_uid (*preferred_uid) == NULL) { + *uid = *preferred_uid; + return 0; + } + /* + * pw_locate_uid() found the UID in an as-yet uncommitted + * entry. We'll proceed below and auto-set an UID. + */ + } else if (result == EEXIST || result == ERANGE) { + /* + * Continue on below. At this time, we won't + * treat these two cases differently. + */ + } else { + /* + * An unexpected error occurred. We should report + * this and fail the user creation. + * This differs from the automatic creation + * behavior below, since if a specific UID was + * requested and generated an error, the user is + * more likely to want to stop and address the + * issue. + */ + fprintf (log_get_logfd(), + _("%s: Encountered error attempting to use " + "preferred UID: %s\n"), + log_get_progname(), strerror (result)); + return -1; + } + } + + /* + * Search the entire passwd file, + * looking for the next unused value. + * + * We first check the local database with pw_rewind/pw_next to find + * all local values that are in use. + * + * We then compare the next free value to all databases (local and + * remote) and iterate until we find a free one. If there are free + * values beyond the lowest (system users) or highest (non-system + * users), we will prefer those and avoid potentially reclaiming a + * deleted user (which can be a security issue, since it may grant + * access to files belonging to that former user). + * + * If there are no UIDs available at the end of the search, we will + * have no choice but to iterate through the range looking for gaps. + * + */ + + /* Create an array to hold all of the discovered UIDs */ + used_uids = malloc (sizeof (bool) * (uid_max +1)); + if (NULL == used_uids) { + fprintf (log_get_logfd(), + _("%s: failed to allocate memory: %s\n"), + log_get_progname(), strerror (errno)); + return -1; + } + memset (used_uids, false, sizeof (bool) * (uid_max + 1)); + + /* First look for the lowest and highest value in the local database */ + (void) pw_rewind (); + highest_found = uid_min; + lowest_found = uid_max; + while ((pwd = pw_next ()) != NULL) { + /* + * Does this entry have a lower UID than the lowest we've found + * so far? + */ + if ((pwd->pw_uid <= lowest_found) && (pwd->pw_uid >= uid_min)) { + lowest_found = pwd->pw_uid - 1; + } + + /* + * Does this entry have a higher UID than the highest we've found + * so far? + */ + if ((pwd->pw_uid >= highest_found) && (pwd->pw_uid <= uid_max)) { + highest_found = pwd->pw_uid + 1; + } + + /* create index of used UIDs */ + if (pwd->pw_uid >= uid_min + && pwd->pw_uid <= uid_max) { + + used_uids[pwd->pw_uid] = true; + } + } + + if (sys_user) { + /* + * For system users, we want to start from the + * top of the range and work downwards. + */ + + /* + * At the conclusion of the pw_next() search, we will either + * have a presumed-free UID or we will be at UID_MIN - 1. + */ + if (lowest_found < uid_min) { + /* + * In this case, an UID is in use at UID_MIN. + * + * We will reset the search to UID_MAX and proceed down + * through all the UIDs (skipping those we detected with + * used_uids) for a free one. It is a known issue that + * this may result in reusing a previously-deleted UID, + * so administrators should be instructed to use this + * auto-detection with care (and prefer to assign UIDs + * explicitly). + */ + lowest_found = uid_max; + } + + /* Search through all of the IDs in the range */ + for (id = lowest_found; id >= uid_min; id--) { + result = check_uid (id, uid_min, uid_max, used_uids); + if (result == 0) { + /* This UID is available. Return it. */ + *uid = id; + free (used_uids); + return 0; + } else if (result == EEXIST) { + /* This UID is in use, we'll continue to the next */ + } else { + /* + * An unexpected error occurred. + * + * Only report it the first time to avoid spamming + * the logs + * + */ + if (!nospam) { + fprintf (log_get_logfd(), + _("%s: Can't get unique system UID (%s). " + "Suppressing additional messages.\n"), + log_get_progname(), strerror (result)); + SYSLOG ((LOG_ERR, + "Error checking available UIDs: %s", + strerror (result))); + nospam = 1; + } + /* + * We will continue anyway. Hopefully a later UID + * will work properly. + */ + } + } + + /* + * If we get all the way through the loop, try again from UID_MAX, + * unless that was where we previously started. (NOTE: the worst-case + * scenario here is that we will run through (UID_MAX - UID_MIN - 1) + * cycles *again* if we fall into this case with lowest_found as + * UID_MAX - 1, all users in the range in use and maintained by + * network services such as LDAP.) + */ + if (lowest_found != uid_max) { + for (id = uid_max; id >= uid_min; id--) { + result = check_uid (id, uid_min, uid_max, used_uids); + if (result == 0) { + /* This UID is available. Return it. */ + *uid = id; + free (used_uids); + return 0; + } else if (result == EEXIST) { + /* This UID is in use, we'll continue to the next */ + } else { + /* + * An unexpected error occurred. + * + * Only report it the first time to avoid spamming + * the logs + * + */ + if (!nospam) { + fprintf (log_get_logfd(), + _("%s: Can't get unique system UID (%s). " + "Suppressing additional messages.\n"), + log_get_progname(), strerror (result)); + SYSLOG((LOG_ERR, + "Error checking available UIDs: %s", + strerror (result))); + nospam = 1; + } + /* + * We will continue anyway. Hopefully a later UID + * will work properly. + */ + } + } + } + } else { /* !sys_user */ + /* + * For non-system users, we want to start from the + * bottom of the range and work upwards. + */ + + /* + * At the conclusion of the pw_next() search, we will either + * have a presumed-free UID or we will be at UID_MAX + 1. + */ + if (highest_found > uid_max) { + /* + * In this case, a UID is in use at UID_MAX. + * + * We will reset the search to UID_MIN and proceed up + * through all the UIDs (skipping those we detected with + * used_uids) for a free one. It is a known issue that + * this may result in reusing a previously-deleted UID, + * so administrators should be instructed to use this + * auto-detection with care (and prefer to assign UIDs + * explicitly). + */ + highest_found = uid_min; + } + + /* Search through all of the IDs in the range */ + for (id = highest_found; id <= uid_max; id++) { + result = check_uid (id, uid_min, uid_max, used_uids); + if (result == 0) { + /* This UID is available. Return it. */ + *uid = id; + free (used_uids); + return 0; + } else if (result == EEXIST) { + /* This UID is in use, we'll continue to the next */ + } else { + /* + * An unexpected error occurred. + * + * Only report it the first time to avoid spamming + * the logs + * + */ + if (!nospam) { + fprintf (log_get_logfd(), + _("%s: Can't get unique UID (%s). " + "Suppressing additional messages.\n"), + log_get_progname(), strerror (result)); + SYSLOG ((LOG_ERR, + "Error checking available UIDs: %s", + strerror (result))); + nospam = 1; + } + /* + * We will continue anyway. Hopefully a later UID + * will work properly. + */ + } + } + + /* + * If we get all the way through the loop, try again from UID_MIN, + * unless that was where we previously started. (NOTE: the worst-case + * scenario here is that we will run through (UID_MAX - UID_MIN - 1) + * cycles *again* if we fall into this case with highest_found as + * UID_MIN + 1, all users in the range in use and maintained by + * network services such as LDAP.) + */ + if (highest_found != uid_min) { + for (id = uid_min; id <= uid_max; id++) { + result = check_uid (id, uid_min, uid_max, used_uids); + if (result == 0) { + /* This UID is available. Return it. */ + *uid = id; + free (used_uids); + return 0; + } else if (result == EEXIST) { + /* This UID is in use, we'll continue to the next */ + } else { + /* + * An unexpected error occurred. + * + * Only report it the first time to avoid spamming + * the logs + * + */ + if (!nospam) { + fprintf (log_get_logfd(), + _("%s: Can't get unique UID (%s). " + "Suppressing additional messages.\n"), + log_get_progname(), strerror (result)); + SYSLOG ((LOG_ERR, + "Error checking available UIDs: %s", + strerror (result))); + nospam = 1; + } + /* + * We will continue anyway. Hopefully a later UID + * will work properly. + */ + } + } + } + } + + /* The code reached here and found no available IDs in the range */ + fprintf (log_get_logfd(), + _("%s: Can't get unique UID (no more available UIDs)\n"), + log_get_progname()); + SYSLOG ((LOG_WARN, "no more available UIDs on the system")); + free (used_uids); + return -1; +} + diff --git a/libmisc/getdate.c b/libmisc/getdate.c new file mode 100644 index 0000000..fc17c18 --- /dev/null +++ b/libmisc/getdate.c @@ -0,0 +1,2540 @@ +/* A Bison parser, made by GNU Bison 3.8.2. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. */ + +/* 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. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +/* 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, and Bison version. */ +#define YYBISON 30802 + +/* Bison version string. */ +#define YYBISON_VERSION "3.8.2" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + + + +/* First part of user prologue. */ +#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> + +#include "getdate.h" + +#include <string.h> + +/* 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 (bcopy) +# define bcopy(from, to, len) memcpy ((to), (from), (len)) +#endif + +/* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc), + as well as gratuitously 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; + + +#line 219 "getdate.c" + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast<Type> (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast<Type> (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + + +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int yydebug; +#endif + +/* Token kinds. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + YYEMPTY = -2, + YYEOF = 0, /* "end of file" */ + YYerror = 256, /* error */ + YYUNDEF = 257, /* "invalid token" */ + tAGO = 258, /* tAGO */ + tDAY = 259, /* tDAY */ + tDAY_UNIT = 260, /* tDAY_UNIT */ + tDAYZONE = 261, /* tDAYZONE */ + tDST = 262, /* tDST */ + tHOUR_UNIT = 263, /* tHOUR_UNIT */ + tID = 264, /* tID */ + tMERIDIAN = 265, /* tMERIDIAN */ + tMINUTE_UNIT = 266, /* tMINUTE_UNIT */ + tMONTH = 267, /* tMONTH */ + tMONTH_UNIT = 268, /* tMONTH_UNIT */ + tSEC_UNIT = 269, /* tSEC_UNIT */ + tSNUMBER = 270, /* tSNUMBER */ + tUNUMBER = 271, /* tUNUMBER */ + tYEAR_UNIT = 272, /* tYEAR_UNIT */ + tZONE = 273 /* tZONE */ + }; + typedef enum yytokentype yytoken_kind_t; +#endif +/* Token kinds. */ +#define YYEMPTY -2 +#define YYEOF 0 +#define YYerror 256 +#define YYUNDEF 257 +#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 + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 149 "getdate.y" + + int Number; + enum _MERIDIAN Meridian; + +#line 310 "getdate.c" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + +extern YYSTYPE yylval; + + +int yyparse (void); + + + +/* Symbol kind. */ +enum yysymbol_kind_t +{ + YYSYMBOL_YYEMPTY = -2, + YYSYMBOL_YYEOF = 0, /* "end of file" */ + YYSYMBOL_YYerror = 1, /* error */ + YYSYMBOL_YYUNDEF = 2, /* "invalid token" */ + YYSYMBOL_tAGO = 3, /* tAGO */ + YYSYMBOL_tDAY = 4, /* tDAY */ + YYSYMBOL_tDAY_UNIT = 5, /* tDAY_UNIT */ + YYSYMBOL_tDAYZONE = 6, /* tDAYZONE */ + YYSYMBOL_tDST = 7, /* tDST */ + YYSYMBOL_tHOUR_UNIT = 8, /* tHOUR_UNIT */ + YYSYMBOL_tID = 9, /* tID */ + YYSYMBOL_tMERIDIAN = 10, /* tMERIDIAN */ + YYSYMBOL_tMINUTE_UNIT = 11, /* tMINUTE_UNIT */ + YYSYMBOL_tMONTH = 12, /* tMONTH */ + YYSYMBOL_tMONTH_UNIT = 13, /* tMONTH_UNIT */ + YYSYMBOL_tSEC_UNIT = 14, /* tSEC_UNIT */ + YYSYMBOL_tSNUMBER = 15, /* tSNUMBER */ + YYSYMBOL_tUNUMBER = 16, /* tUNUMBER */ + YYSYMBOL_tYEAR_UNIT = 17, /* tYEAR_UNIT */ + YYSYMBOL_tZONE = 18, /* tZONE */ + YYSYMBOL_19_ = 19, /* ':' */ + YYSYMBOL_20_ = 20, /* ',' */ + YYSYMBOL_21_ = 21, /* '/' */ + YYSYMBOL_YYACCEPT = 22, /* $accept */ + YYSYMBOL_spec = 23, /* spec */ + YYSYMBOL_item = 24, /* item */ + YYSYMBOL_time = 25, /* time */ + YYSYMBOL_zone = 26, /* zone */ + YYSYMBOL_day = 27, /* day */ + YYSYMBOL_date = 28, /* date */ + YYSYMBOL_rel = 29, /* rel */ + YYSYMBOL_relunit = 30, /* relunit */ + YYSYMBOL_number = 31, /* number */ + YYSYMBOL_o_merid = 32 /* o_merid */ +}; +typedef enum yysymbol_kind_t yysymbol_kind_t; + + + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + <limits.h> and (if available) <stdint.h> are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include <limits.h> /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include <stdint.h> /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +/* Work around bug in HP-UX 11.23, which defines these macros + incorrectly for preprocessor constants. This workaround can likely + be removed in 2023, as HPE has promised support for HP-UX 11.23 + (aka HP-UX 11i v2) only through the end of 2022; see Table 2 of + <https://h20195.www2.hpe.com/V2/getpdf.aspx/4AA4-7673ENW.pdf>. */ +#ifdef __hpux +# undef UINT_LEAST8_MAX +# undef UINT_LEAST16_MAX +# define UINT_LEAST8_MAX 255 +# define UINT_LEAST16_MAX 65535 +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + + +/* Stored state numbers (used for stacks). */ +typedef yytype_int8 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && 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 + + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YY_USE(E) ((void) (E)) +#else +# define YY_USE(E) /* empty */ +#endif + +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +#if defined __GNUC__ && ! defined __ICC && 406 <= __GNUC__ * 100 + __GNUC_MINOR__ +# if __GNUC__ * 100 + __GNUC_MINOR__ < 407 +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") +# else +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# endif +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#if !defined yyoverflow + +/* 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 EXIT_SUCCESS +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (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 EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* !defined yyoverflow */ + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* 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_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* 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 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 61 + +/* YYMAXUTOK -- Last valid token kind. */ +#define YYMAXUTOK 273 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK \ + ? YY_CAST (yysymbol_kind_t, yytranslate[YYX]) \ + : YYSYMBOL_YYUNDEF) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_int8 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 +/* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_int16 yyrline[] = +{ + 0, 165, 165, 166, 169, 172, 175, 178, 181, 184, + 187, 193, 199, 208, 214, 226, 229, 233, 238, 242, + 246, 252, 256, 274, 280, 286, 290, 295, 299, 306, + 314, 317, 320, 323, 326, 329, 332, 335, 338, 341, + 344, 347, 350, 353, 356, 359, 362, 365, 368, 373, + 407, 410 +}; +#endif + +/** Accessing symbol of state STATE. */ +#define YY_ACCESSING_SYMBOL(State) YY_CAST (yysymbol_kind_t, yystos[State]) + +#if YYDEBUG || 0 +/* The user-facing name of the symbol whose (internal) number is + YYSYMBOL. No bounds checking. */ +static const char *yysymbol_name (yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED; + +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "\"end of file\"", "error", "\"invalid token\"", "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", YY_NULLPTR +}; + +static const char * +yysymbol_name (yysymbol_kind_t yysymbol) +{ + return yytname[yysymbol]; +} +#endif + +#define YYPACT_NINF (-20) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-1) + +#define yytable_value_is_error(Yyn) \ + 0 + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +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 +}; + +/* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_int8 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 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, + -7 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + 0, 1, 15, 16, 17, 18, 19, 20, 21, 22, + 55 +}; + +/* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int8 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_int8 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 symbol kind of the accessing symbol of + state STATE-NUM. */ +static const yytype_int8 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 +}; + +/* YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM. */ +static const yytype_int8 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[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM. */ +static const yytype_int8 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 +}; + + +enum { YYENOMEM = -2 }; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab +#define YYNOMEM goto yyexhaustedlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Backward compatibility with an undocumented macro. + Use YYerror or YYUNDEF. */ +#define YYERRCODE YYUNDEF + + +/* 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 (0) + + + + +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Kind, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep) +{ + FILE *yyoutput = yyo; + YY_USE (yyoutput); + if (!yyvaluep) + return; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YY_USE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep) +{ + YYFPRINTF (yyo, "%s %s (", + yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind)); + + yy_symbol_value_print (yyo, yykind, yyvaluep); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, + int yyrule) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + YY_ACCESSING_SYMBOL (+yyssp[yyi + 1 - yynrhs]), + &yyvsp[(yyi + 1) - (yynrhs)]); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, Rule); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) ((void) 0) +# define YY_SYMBOL_PRINT(Title, Kind, 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 + + + + + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, + yysymbol_kind_t yykind, YYSTYPE *yyvaluep) +{ + YY_USE (yyvaluep); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YY_USE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/* Lookahead token kind. */ +int yychar; + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval; +/* Number of syntax errors so far. */ +int yynerrs; + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (void) +{ + yy_state_fast_t yystate = 0; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus = 0; + + /* Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* Their size. */ + YYPTRDIFF_T yystacksize = YYINITDEPTH; + + /* The state stack: array, bottom, top. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss = yyssa; + yy_state_t *yyssp = yyss; + + /* The semantic value stack: array, bottom, top. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp = yyvs; + + int yyn; + /* The return value of yyparse. */ + int yyresult; + /* Lookahead symbol kind. */ + yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* 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")); + + yychar = YYEMPTY; /* Cause a token to be read. */ + + 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 -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + YY_STACK_PRINT (yyss, yyssp); + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + YYNOMEM; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined 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. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + + /* 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 * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + YYNOMEM; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + YYNOMEM; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either empty, or end-of-input, or a valid lookahead. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token\n")); + yychar = yylex (); + } + + if (yychar <= YYEOF) + { + yychar = YYEOF; + yytoken = YYSYMBOL_YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else if (yychar == YYerror) + { + /* The scanner already issued an error message, process directly + to error recovery. But do not keep the error token as + lookahead, it is too special and may lead us to an endless + loop in error recovery. */ + yychar = YYUNDEF; + yytoken = YYSYMBOL_YYerror; + goto yyerrlab1; + } + 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 (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + /* Discard the shifted token. */ + yychar = YYEMPTY; + 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: /* item: time */ +#line 169 "getdate.y" + { + yyHaveTime++; + } +#line 1360 "getdate.c" + break; + + case 5: /* item: zone */ +#line 172 "getdate.y" + { + yyHaveZone++; + } +#line 1368 "getdate.c" + break; + + case 6: /* item: date */ +#line 175 "getdate.y" + { + yyHaveDate++; + } +#line 1376 "getdate.c" + break; + + case 7: /* item: day */ +#line 178 "getdate.y" + { + yyHaveDay++; + } +#line 1384 "getdate.c" + break; + + case 8: /* item: rel */ +#line 181 "getdate.y" + { + yyHaveRel++; + } +#line 1392 "getdate.c" + break; + + case 10: /* time: tUNUMBER tMERIDIAN */ +#line 187 "getdate.y" + { + yyHour = (yyvsp[-1].Number); + yyMinutes = 0; + yySeconds = 0; + yyMeridian = (yyvsp[0].Meridian); + } +#line 1403 "getdate.c" + break; + + case 11: /* time: tUNUMBER ':' tUNUMBER o_merid */ +#line 193 "getdate.y" + { + yyHour = (yyvsp[-3].Number); + yyMinutes = (yyvsp[-1].Number); + yySeconds = 0; + yyMeridian = (yyvsp[0].Meridian); + } +#line 1414 "getdate.c" + break; + + case 12: /* time: tUNUMBER ':' tUNUMBER tSNUMBER */ +#line 199 "getdate.y" + { + yyHour = (yyvsp[-3].Number); + yyMinutes = (yyvsp[-1].Number); + yyMeridian = MER24; + yyHaveZone++; + yyTimezone = ((yyvsp[0].Number) < 0 + ? -(yyvsp[0].Number) % 100 + (-(yyvsp[0].Number) / 100) * 60 + : - ((yyvsp[0].Number) % 100 + ((yyvsp[0].Number) / 100) * 60)); + } +#line 1428 "getdate.c" + break; + + case 13: /* time: tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid */ +#line 208 "getdate.y" + { + yyHour = (yyvsp[-5].Number); + yyMinutes = (yyvsp[-3].Number); + yySeconds = (yyvsp[-1].Number); + yyMeridian = (yyvsp[0].Meridian); + } +#line 1439 "getdate.c" + break; + + case 14: /* time: tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER */ +#line 214 "getdate.y" + { + yyHour = (yyvsp[-5].Number); + yyMinutes = (yyvsp[-3].Number); + yySeconds = (yyvsp[-1].Number); + yyMeridian = MER24; + yyHaveZone++; + yyTimezone = ((yyvsp[0].Number) < 0 + ? -(yyvsp[0].Number) % 100 + (-(yyvsp[0].Number) / 100) * 60 + : - ((yyvsp[0].Number) % 100 + ((yyvsp[0].Number) / 100) * 60)); + } +#line 1454 "getdate.c" + break; + + case 15: /* zone: tZONE */ +#line 226 "getdate.y" + { + yyTimezone = (yyvsp[0].Number); + } +#line 1462 "getdate.c" + break; + + case 16: /* zone: tDAYZONE */ +#line 229 "getdate.y" + { + yyTimezone = (yyvsp[0].Number) - 60; + } +#line 1470 "getdate.c" + break; + + case 17: /* zone: tZONE tDST */ +#line 233 "getdate.y" + { + yyTimezone = (yyvsp[-1].Number) - 60; + } +#line 1478 "getdate.c" + break; + + case 18: /* day: tDAY */ +#line 238 "getdate.y" + { + yyDayOrdinal = 1; + yyDayNumber = (yyvsp[0].Number); + } +#line 1487 "getdate.c" + break; + + case 19: /* day: tDAY ',' */ +#line 242 "getdate.y" + { + yyDayOrdinal = 1; + yyDayNumber = (yyvsp[-1].Number); + } +#line 1496 "getdate.c" + break; + + case 20: /* day: tUNUMBER tDAY */ +#line 246 "getdate.y" + { + yyDayOrdinal = (yyvsp[-1].Number); + yyDayNumber = (yyvsp[0].Number); + } +#line 1505 "getdate.c" + break; + + case 21: /* date: tUNUMBER '/' tUNUMBER */ +#line 252 "getdate.y" + { + yyMonth = (yyvsp[-2].Number); + yyDay = (yyvsp[0].Number); + } +#line 1514 "getdate.c" + break; + + case 22: /* date: tUNUMBER '/' tUNUMBER '/' tUNUMBER */ +#line 256 "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[-4].Number) >= 1000) + { + yyYear = (yyvsp[-4].Number); + yyMonth = (yyvsp[-2].Number); + yyDay = (yyvsp[0].Number); + } + else + { + yyMonth = (yyvsp[-4].Number); + yyDay = (yyvsp[-2].Number); + yyYear = (yyvsp[0].Number); + } + } +#line 1537 "getdate.c" + break; + + case 23: /* date: tUNUMBER tSNUMBER tSNUMBER */ +#line 274 "getdate.y" + { + /* ISO 8601 format. yyyy-mm-dd. */ + yyYear = (yyvsp[-2].Number); + yyMonth = -(yyvsp[-1].Number); + yyDay = -(yyvsp[0].Number); + } +#line 1548 "getdate.c" + break; + + case 24: /* date: tUNUMBER tMONTH tSNUMBER */ +#line 280 "getdate.y" + { + /* e.g. 17-JUN-1992. */ + yyDay = (yyvsp[-2].Number); + yyMonth = (yyvsp[-1].Number); + yyYear = -(yyvsp[0].Number); + } +#line 1559 "getdate.c" + break; + + case 25: /* date: tMONTH tUNUMBER */ +#line 286 "getdate.y" + { + yyMonth = (yyvsp[-1].Number); + yyDay = (yyvsp[0].Number); + } +#line 1568 "getdate.c" + break; + + case 26: /* date: tMONTH tUNUMBER ',' tUNUMBER */ +#line 290 "getdate.y" + { + yyMonth = (yyvsp[-3].Number); + yyDay = (yyvsp[-2].Number); + yyYear = (yyvsp[0].Number); + } +#line 1578 "getdate.c" + break; + + case 27: /* date: tUNUMBER tMONTH */ +#line 295 "getdate.y" + { + yyMonth = (yyvsp[0].Number); + yyDay = (yyvsp[-1].Number); + } +#line 1587 "getdate.c" + break; + + case 28: /* date: tUNUMBER tMONTH tUNUMBER */ +#line 299 "getdate.y" + { + yyMonth = (yyvsp[-1].Number); + yyDay = (yyvsp[-2].Number); + yyYear = (yyvsp[0].Number); + } +#line 1597 "getdate.c" + break; + + case 29: /* rel: relunit tAGO */ +#line 306 "getdate.y" + { + yyRelSeconds = -yyRelSeconds; + yyRelMinutes = -yyRelMinutes; + yyRelHour = -yyRelHour; + yyRelDay = -yyRelDay; + yyRelMonth = -yyRelMonth; + yyRelYear = -yyRelYear; + } +#line 1610 "getdate.c" + break; + + case 31: /* relunit: tUNUMBER tYEAR_UNIT */ +#line 317 "getdate.y" + { + yyRelYear += (yyvsp[-1].Number) * (yyvsp[0].Number); + } +#line 1618 "getdate.c" + break; + + case 32: /* relunit: tSNUMBER tYEAR_UNIT */ +#line 320 "getdate.y" + { + yyRelYear += (yyvsp[-1].Number) * (yyvsp[0].Number); + } +#line 1626 "getdate.c" + break; + + case 33: /* relunit: tYEAR_UNIT */ +#line 323 "getdate.y" + { + yyRelYear++; + } +#line 1634 "getdate.c" + break; + + case 34: /* relunit: tUNUMBER tMONTH_UNIT */ +#line 326 "getdate.y" + { + yyRelMonth += (yyvsp[-1].Number) * (yyvsp[0].Number); + } +#line 1642 "getdate.c" + break; + + case 35: /* relunit: tSNUMBER tMONTH_UNIT */ +#line 329 "getdate.y" + { + yyRelMonth += (yyvsp[-1].Number) * (yyvsp[0].Number); + } +#line 1650 "getdate.c" + break; + + case 36: /* relunit: tMONTH_UNIT */ +#line 332 "getdate.y" + { + yyRelMonth++; + } +#line 1658 "getdate.c" + break; + + case 37: /* relunit: tUNUMBER tDAY_UNIT */ +#line 335 "getdate.y" + { + yyRelDay += (yyvsp[-1].Number) * (yyvsp[0].Number); + } +#line 1666 "getdate.c" + break; + + case 38: /* relunit: tSNUMBER tDAY_UNIT */ +#line 338 "getdate.y" + { + yyRelDay += (yyvsp[-1].Number) * (yyvsp[0].Number); + } +#line 1674 "getdate.c" + break; + + case 39: /* relunit: tDAY_UNIT */ +#line 341 "getdate.y" + { + yyRelDay++; + } +#line 1682 "getdate.c" + break; + + case 40: /* relunit: tUNUMBER tHOUR_UNIT */ +#line 344 "getdate.y" + { + yyRelHour += (yyvsp[-1].Number) * (yyvsp[0].Number); + } +#line 1690 "getdate.c" + break; + + case 41: /* relunit: tSNUMBER tHOUR_UNIT */ +#line 347 "getdate.y" + { + yyRelHour += (yyvsp[-1].Number) * (yyvsp[0].Number); + } +#line 1698 "getdate.c" + break; + + case 42: /* relunit: tHOUR_UNIT */ +#line 350 "getdate.y" + { + yyRelHour++; + } +#line 1706 "getdate.c" + break; + + case 43: /* relunit: tUNUMBER tMINUTE_UNIT */ +#line 353 "getdate.y" + { + yyRelMinutes += (yyvsp[-1].Number) * (yyvsp[0].Number); + } +#line 1714 "getdate.c" + break; + + case 44: /* relunit: tSNUMBER tMINUTE_UNIT */ +#line 356 "getdate.y" + { + yyRelMinutes += (yyvsp[-1].Number) * (yyvsp[0].Number); + } +#line 1722 "getdate.c" + break; + + case 45: /* relunit: tMINUTE_UNIT */ +#line 359 "getdate.y" + { + yyRelMinutes++; + } +#line 1730 "getdate.c" + break; + + case 46: /* relunit: tUNUMBER tSEC_UNIT */ +#line 362 "getdate.y" + { + yyRelSeconds += (yyvsp[-1].Number) * (yyvsp[0].Number); + } +#line 1738 "getdate.c" + break; + + case 47: /* relunit: tSNUMBER tSEC_UNIT */ +#line 365 "getdate.y" + { + yyRelSeconds += (yyvsp[-1].Number) * (yyvsp[0].Number); + } +#line 1746 "getdate.c" + break; + + case 48: /* relunit: tSEC_UNIT */ +#line 368 "getdate.y" + { + yyRelSeconds++; + } +#line 1754 "getdate.c" + break; + + case 49: /* number: tUNUMBER */ +#line 374 "getdate.y" + { + if ((yyHaveTime != 0) && (yyHaveDate != 0) && (yyHaveRel == 0)) + yyYear = (yyvsp[0].Number); + else + { + if ((yyvsp[0].Number)>10000) + { + yyHaveDate++; + yyDay= ((yyvsp[0].Number))%100; + yyMonth= ((yyvsp[0].Number)/100)%100; + yyYear = (yyvsp[0].Number)/10000; + } + else + { + yyHaveTime++; + if ((yyvsp[0].Number) < 100) + { + yyHour = (yyvsp[0].Number); + yyMinutes = 0; + } + else + { + yyHour = (yyvsp[0].Number) / 100; + yyMinutes = (yyvsp[0].Number) % 100; + } + yySeconds = 0; + yyMeridian = MER24; + } + } + } +#line 1789 "getdate.c" + break; + + case 50: /* o_merid: %empty */ +#line 407 "getdate.y" + { + (yyval.Meridian) = MER24; + } +#line 1797 "getdate.c" + break; + + case 51: /* o_merid: tMERIDIAN */ +#line 411 "getdate.y" + { + (yyval.Meridian) = (yyvsp[0].Meridian); + } +#line 1805 "getdate.c" + break; + + +#line 1809 "getdate.c" + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", YY_CAST (yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + + *++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. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYSYMBOL_YYEMPTY : YYTRANSLATE (yychar); + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; + yyerror (YY_("syntax error")); + } + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead 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 lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + ++yynerrs; + + /* Do not reclaim the symbols of the rule whose 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. */ + + /* Pop stack until we find a state that shifts the error token. */ + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYSYMBOL_YYerror; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror) + { + 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", + YY_ACCESSING_SYMBOL (yystate), yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", YY_ACCESSING_SYMBOL (yyn), yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturnlab; + + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturnlab; + + +/*-----------------------------------------------------------. +| yyexhaustedlab -- YYNOMEM (memory exhaustion) comes here. | +`-----------------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + goto yyreturnlab; + + +/*----------------------------------------------------------. +| yyreturnlab -- parsing is finished, clean up and return. | +`----------------------------------------------------------*/ +yyreturnlab: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + YY_ACCESSING_SYMBOL (+*yyssp), yyvsp); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + + return yyresult; +} + +#line 416 "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 0000000..eae56f6 --- /dev/null +++ b/libmisc/getdate.h @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1997 - 2000, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2005 , Tomasz KÅ‚oczko + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#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 0000000..0c07f74 --- /dev/null +++ b/libmisc/getdate.y @@ -0,0 +1,954 @@ +%{ +/* +** 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> + +#include "getdate.h" + +#include <string.h> + +/* 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 (bcopy) +# define bcopy(from, to, len) memcpy ((to), (from), (len)) +#endif + +/* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc), + as well as gratuitously 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 0000000..5294f50 --- /dev/null +++ b/libmisc/getgr_nam_gid.c @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: 1991 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 2000, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2000 - 2006, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2007 - 2009, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#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 0000000..82f2edf --- /dev/null +++ b/libmisc/getrange.c @@ -0,0 +1,101 @@ +/* + * SPDX-FileCopyrightText: 2008 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#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 (const 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/gettime.c b/libmisc/gettime.c new file mode 100644 index 0000000..b288402 --- /dev/null +++ b/libmisc/gettime.c @@ -0,0 +1,68 @@ +/* + * SPDX-FileCopyrightText: 2017, Chris Lamb + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include "defines.h" +#include "prototypes.h" +#include "shadowlog.h" + +/* + * gettime() returns the time as the number of seconds since the Epoch + * + * Like time(), gettime() returns the time as the number of seconds since the + * Epoch, 1970-01-01 00:00:00 +0000 (UTC), except that if the SOURCE_DATE_EPOCH + * environment variable is exported it will use that instead. + */ +/*@observer@*/time_t gettime () +{ + char *endptr; + char *source_date_epoch; + time_t fallback; + unsigned long long epoch; + FILE *shadow_logfd = log_get_logfd(); + + fallback = time (NULL); + source_date_epoch = shadow_getenv ("SOURCE_DATE_EPOCH"); + + if (!source_date_epoch) + return fallback; + + errno = 0; + epoch = strtoull (source_date_epoch, &endptr, 10); + if ((errno == ERANGE && (epoch == ULLONG_MAX || epoch == 0)) + || (errno != 0 && epoch == 0)) { + fprintf (shadow_logfd, + _("Environment variable $SOURCE_DATE_EPOCH: strtoull: %s\n"), + strerror(errno)); + } else if (endptr == source_date_epoch) { + fprintf (shadow_logfd, + _("Environment variable $SOURCE_DATE_EPOCH: No digits were found: %s\n"), + endptr); + } else if (*endptr != '\0') { + fprintf (shadow_logfd, + _("Environment variable $SOURCE_DATE_EPOCH: Trailing garbage: %s\n"), + endptr); + } else if (epoch > ULONG_MAX) { + fprintf (shadow_logfd, + _("Environment variable $SOURCE_DATE_EPOCH: value must be smaller than or equal to %lu but was found to be: %llu\n"), + ULONG_MAX, epoch); + } else if (epoch > fallback) { + fprintf (shadow_logfd, + _("Environment variable $SOURCE_DATE_EPOCH: value must be smaller than or equal to the current time (%lu) but was found to be: %llu\n"), + fallback, epoch); + } else { + /* Valid */ + return (time_t)epoch; + } + + return fallback; +} diff --git a/libmisc/hushed.c b/libmisc/hushed.c new file mode 100644 index 0000000..84b2f55 --- /dev/null +++ b/libmisc/hushed.c @@ -0,0 +1,77 @@ +/* + * SPDX-FileCopyrightText: 1991 - 1993, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1991 - 1993, Chip Rosenthal + * SPDX-FileCopyrightText: 1996 - 2000, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2008 - 2010, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#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[strcspn (buf, "\n")] = '\0'; + found = (strcmp (buf, pw->pw_shell) == 0) || + (strcmp (buf, pw->pw_name) == 0); + } + (void) fclose (fp); + return found; +} + diff --git a/libmisc/idmapping.c b/libmisc/idmapping.c new file mode 100644 index 0000000..30eb89f --- /dev/null +++ b/libmisc/idmapping.c @@ -0,0 +1,223 @@ +/* + * SPDX-FileCopyrightText: 2013 Eric Biederman + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <limits.h> +#include <stdlib.h> +#include <stdio.h> +#include "prototypes.h" +#include "idmapping.h" +#if HAVE_SYS_CAPABILITY_H +#include <sys/prctl.h> +#include <sys/capability.h> +#endif +#include "shadowlog.h" + +struct map_range *get_map_ranges(int ranges, int argc, char **argv) +{ + struct map_range *mappings, *mapping; + int idx, argidx; + + if (ranges < 0 || argc < 0) { + fprintf(log_get_logfd(), "%s: error calculating number of arguments\n", log_get_progname()); + return NULL; + } + + if (ranges != ((argc + 2) / 3)) { + fprintf(log_get_logfd(), "%s: ranges: %u is wrong for argc: %d\n", log_get_progname(), ranges, argc); + return NULL; + } + + if ((ranges * 3) > argc) { + fprintf(log_get_logfd(), "ranges: %u argc: %d\n", + ranges, argc); + fprintf(log_get_logfd(), + _( "%s: Not enough arguments to form %u mappings\n"), + log_get_progname(), ranges); + return NULL; + } + + mappings = calloc(ranges, sizeof(*mappings)); + if (!mappings) { + fprintf(log_get_logfd(), _( "%s: Memory allocation failure\n"), + log_get_progname()); + exit(EXIT_FAILURE); + } + + /* Gather up the ranges from the command line */ + mapping = mappings; + for (idx = 0, argidx = 0; idx < ranges; idx++, argidx += 3, mapping++) { + if (!getulong(argv[argidx + 0], &mapping->upper)) { + free(mappings); + return NULL; + } + if (!getulong(argv[argidx + 1], &mapping->lower)) { + free(mappings); + return NULL; + } + if (!getulong(argv[argidx + 2], &mapping->count)) { + free(mappings); + return NULL; + } + if (ULONG_MAX - mapping->upper <= mapping->count || ULONG_MAX - mapping->lower <= mapping->count) { + fprintf(log_get_logfd(), _( "%s: subuid overflow detected.\n"), log_get_progname()); + exit(EXIT_FAILURE); + } + if (mapping->upper > UINT_MAX || + mapping->lower > UINT_MAX || + mapping->count > UINT_MAX) { + fprintf(log_get_logfd(), _( "%s: subuid overflow detected.\n"), log_get_progname()); + exit(EXIT_FAILURE); + } + if (mapping->lower + mapping->count > UINT_MAX || + mapping->upper + mapping->count > UINT_MAX) { + fprintf(log_get_logfd(), _( "%s: subuid overflow detected.\n"), log_get_progname()); + exit(EXIT_FAILURE); + } + if (mapping->lower + mapping->count < mapping->lower || + mapping->upper + mapping->count < mapping->upper) { + /* this one really shouldn't be possible given previous checks */ + fprintf(log_get_logfd(), _( "%s: subuid overflow detected.\n"), log_get_progname()); + exit(EXIT_FAILURE); + } + } + return mappings; +} + +/* Number of ascii digits needed to print any unsigned long in decimal. + * There are approximately 10 bits for every 3 decimal digits. + * So from bits to digits the formula is roundup((Number of bits)/10) * 3. + * For common sizes of integers this works out to: + * 2bytes --> 6 ascii estimate -> 65536 (5 real) + * 4bytes --> 12 ascii estimated -> 4294967296 (10 real) + * 8bytes --> 21 ascii estimated -> 18446744073709551616 (20 real) + * 16bytes --> 39 ascii estimated -> 340282366920938463463374607431768211456 (39 real) + */ +#define ULONG_DIGITS ((((sizeof(unsigned long) * CHAR_BIT) + 9)/10)*3) + +#if HAVE_SYS_CAPABILITY_H +static inline bool maps_lower_root(int cap, int ranges, const struct map_range *mappings) +{ + int idx; + const struct map_range *mapping; + + if (cap != CAP_SETUID) + return false; + + mapping = mappings; + for (idx = 0; idx < ranges; idx++, mapping++) { + if (mapping->lower == 0) + return true; + } + + return false; +} +#endif + +/* + * The ruid refers to the caller's uid and is used to reset the effective uid + * back to the callers real uid. + * This clutch mainly exists for setuid-based new{g,u}idmap binaries that are + * called in contexts where all capabilities other than the necessary + * CAP_SET{G,U}ID capabilities are dropped. Since the kernel will require + * assurance that the caller holds CAP_SYS_ADMIN over the target user namespace + * the only way it can confirm is in this case is if the effective uid is + * equivalent to the uid owning the target user namespace. + * Note, we only support this when a) new{g,u}idmap is not called by root and + * b) if the caller's uid and the uid retrieved via system appropriate means + * (shadow file or other) are identical. Specifically, this does not support + * when the root user calls the new{g,u}idmap binary for an unprivileged user. + * If this is wanted: use file capabilities! + */ +void write_mapping(int proc_dir_fd, int ranges, const struct map_range *mappings, + const char *map_file, uid_t ruid) +{ + int idx; + const struct map_range *mapping; + size_t bufsize; + char *buf, *pos; + int fd; + +#if HAVE_SYS_CAPABILITY_H + int cap; + struct __user_cap_header_struct hdr = {_LINUX_CAPABILITY_VERSION_3, 0}; + struct __user_cap_data_struct data[2] = {{0}}; + + if (strcmp(map_file, "uid_map") == 0) { + cap = CAP_SETUID; + } else if (strcmp(map_file, "gid_map") == 0) { + cap = CAP_SETGID; + } else { + fprintf(log_get_logfd(), _("%s: Invalid map file %s specified\n"), log_get_progname(), map_file); + exit(EXIT_FAILURE); + } + + /* Align setuid- and fscaps-based new{g,u}idmap behavior. */ + if (geteuid() == 0 && geteuid() != ruid) { + if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) { + fprintf(log_get_logfd(), _("%s: Could not prctl(PR_SET_KEEPCAPS)\n"), log_get_progname()); + exit(EXIT_FAILURE); + } + + if (seteuid(ruid) < 0) { + fprintf(log_get_logfd(), _("%s: Could not seteuid to %d\n"), log_get_progname(), ruid); + exit(EXIT_FAILURE); + } + } + + /* Lockdown new{g,u}idmap by dropping all unneeded capabilities. */ + memset(data, 0, sizeof(data)); + data[0].effective = CAP_TO_MASK(cap); + /* + * When uid 0 from the ancestor userns is supposed to be mapped into + * the child userns we need to retain CAP_SETFCAP. + */ + if (maps_lower_root(cap, ranges, mappings)) + data[0].effective |= CAP_TO_MASK(CAP_SETFCAP); + data[0].permitted = data[0].effective; + if (capset(&hdr, data) < 0) { + fprintf(log_get_logfd(), _("%s: Could not set caps\n"), log_get_progname()); + exit(EXIT_FAILURE); + } +#endif + + bufsize = ranges * ((ULONG_DIGITS + 1) * 3); + pos = buf = xmalloc(bufsize); + + /* Build the mapping command */ + mapping = mappings; + for (idx = 0; idx < ranges; idx++, mapping++) { + /* Append this range to the string that will be written */ + int written = snprintf(pos, bufsize - (pos - buf), + "%lu %lu %lu\n", + mapping->upper, + mapping->lower, + mapping->count); + if ((written <= 0) || (written >= (bufsize - (pos - buf)))) { + fprintf(log_get_logfd(), _("%s: snprintf failed!\n"), log_get_progname()); + exit(EXIT_FAILURE); + } + pos += written; + } + + /* Write the mapping to the mapping file */ + fd = openat(proc_dir_fd, map_file, O_WRONLY); + if (fd < 0) { + fprintf(log_get_logfd(), _("%s: open of %s failed: %s\n"), + log_get_progname(), map_file, strerror(errno)); + exit(EXIT_FAILURE); + } + if (write(fd, buf, pos - buf) != (pos - buf)) { + fprintf(log_get_logfd(), _("%s: write to %s failed: %s\n"), + log_get_progname(), map_file, strerror(errno)); + exit(EXIT_FAILURE); + } + close(fd); + free(buf); +} diff --git a/libmisc/idmapping.h b/libmisc/idmapping.h new file mode 100644 index 0000000..46d4631 --- /dev/null +++ b/libmisc/idmapping.h @@ -0,0 +1,23 @@ +/* + * SPDX-FileCopyrightText: 2013 Eric Biederman + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _IDMAPPING_H_ +#define _IDMAPPING_H_ + +#include <sys/types.h> + +struct map_range { + unsigned long upper; /* first ID inside the namespace */ + unsigned long lower; /* first ID outside the namespace */ + unsigned long count; /* Length of the inside and outside ranges */ +}; + +extern struct map_range *get_map_ranges(int ranges, int argc, char **argv); +extern void write_mapping(int proc_dir_fd, int ranges, + const struct map_range *mappings, const char *map_file, uid_t ruid); + +#endif /* _ID_MAPPING_H_ */ + diff --git a/libmisc/isexpired.c b/libmisc/isexpired.c new file mode 100644 index 0000000..ff20396 --- /dev/null +++ b/libmisc/isexpired.c @@ -0,0 +1,102 @@ +/* + * SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1997, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2001 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2008 - 2009, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * 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 - 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 0000000..fea85fe --- /dev/null +++ b/libmisc/limits.c @@ -0,0 +1,589 @@ +/* + * SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1999, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2006, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2007 - 2010, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * 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$" + +#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" +#include "shadowlog.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 current 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"), log_get_logfd()); + (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 0000000..9d7ec77 --- /dev/null +++ b/libmisc/list.c @@ -0,0 +1,249 @@ +/* + * SPDX-FileCopyrightText: 1991 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1997, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2005, Tomasz KÅ‚oczko + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#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; + free (members); + 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 0000000..a220be0 --- /dev/null +++ b/libmisc/log.c @@ -0,0 +1,93 @@ +/* + * SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1998, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2008 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#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) - 1); +#if HAVE_LL_HOST + strncpy (newlog.ll_host, host, sizeof (newlog.ll_host) - 1); +#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 0000000..0a67f46 --- /dev/null +++ b/libmisc/loginprompt.c @@ -0,0 +1,155 @@ +/* + * SPDX-FileCopyrightText: 1989 - 1993, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 2000, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2008 - 2011, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#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; + + sighandler_t sigquit; +#ifdef SIGTSTP + sighandler_t sigtstp; +#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 0000000..647f879 --- /dev/null +++ b/libmisc/mail.c @@ -0,0 +1,69 @@ +/* + * SPDX-FileCopyrightText: 1989 - 1991, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1998, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2008 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#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$" + + +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 0000000..7f7e523 --- /dev/null +++ b/libmisc/motd.c @@ -0,0 +1,58 @@ +/* + * SPDX-FileCopyrightText: 1989 - 1991, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1997, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2010 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#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; + 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 0000000..1b02617 --- /dev/null +++ b/libmisc/myname.c @@ -0,0 +1,49 @@ +/* + * SPDX-FileCopyrightText: 1996 - 1997, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2007 - 2009, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * myname.c - determine the current username and get the passwd entry + * + */ + +#include <config.h> + +#ident "$Id$" + +#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; + } + if (NULL != pw) { + pw_free (pw); + } + } + + return xgetpwuid (ruid); +} + diff --git a/libmisc/obscure.c b/libmisc/obscure.c new file mode 100644 index 0000000..3daaa95 --- /dev/null +++ b/libmisc/obscure.c @@ -0,0 +1,306 @@ +/* + * SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1999, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2007 - 2010, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ifndef USE_PAM + +#ident "$Id$" + + +/* + * 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 +#ifdef USE_BCRYPT + || (strcmp (result, "BCRYPT") == 0) +#endif +#ifdef USE_YESCRYPT + || (strcmp (result, "YESCRYPT") == 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 0000000..166a42e --- /dev/null +++ b/libmisc/pam_pass.c @@ -0,0 +1,59 @@ +/* + * SPDX-FileCopyrightText: 1997 - 1999, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2001 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2008 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ifdef USE_PAM + +#ident "$Id$" + + +/* + * 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" +#include "shadowlog.h" + +void do_pam_passwd (const char *user, bool silent, bool change_expired) +{ + pam_handle_t *pamh = NULL; + int flags = 0, ret; + FILE *shadow_logfd = log_get_logfd(); + + 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 (shadow_logfd, + _("passwd: pam_start() failed, error %d\n"), ret); + exit (10); /* XXX */ + } + + ret = pam_chauthtok (pamh, flags); + if (ret != PAM_SUCCESS) { + fprintf (shadow_logfd, _("passwd: %s\n"), pam_strerror (pamh, ret)); + fputs (_("passwd: password unchanged\n"), shadow_logfd); + pam_end (pamh, ret); + exit (10); /* XXX */ + } + + fputs (_("passwd: password updated successfully\n"), shadow_logfd); + (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_interactive.c b/libmisc/pam_pass_non_interactive.c new file mode 100644 index 0000000..34cdc1f --- /dev/null +++ b/libmisc/pam_pass_non_interactive.c @@ -0,0 +1,145 @@ +/* + * SPDX-FileCopyrightText: 2009 - 2010, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#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" +#include "shadowlog.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 const 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 (log_get_logfd(), + _("%s: PAM modules requesting echoing are not supported.\n"), + log_get_progname()); + 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 (log_get_logfd(), "%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 (log_get_logfd(), + _("%s: conversation type %d not supported.\n"), + log_get_progname(), 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_interactive (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 (log_get_logfd(), + _("%s: (user %s) pam_start failure %d\n"), + log_get_progname(), username, ret); + return 1; + } + + non_interactive_password = password; + ret = pam_chauthtok (pamh, 0); + if (ret != PAM_SUCCESS) { + fprintf (log_get_logfd(), + _("%s: (user %s) pam_chauthtok() failed, error:\n" + "%s\n"), + log_get_progname(), 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/prefix_flag.c b/libmisc/prefix_flag.c new file mode 100644 index 0000000..4eb5154 --- /dev/null +++ b/libmisc/prefix_flag.c @@ -0,0 +1,340 @@ +/* + * SPDX-FileCopyrightText: 2011 , Julian Pidancet + * SPDX-FileCopyrightText: 2011 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#include <stdio.h> +#include <assert.h> +#include "defines.h" +#include "prototypes.h" +/*@-exitarg@*/ +#include "exitcodes.h" +#include "groupio.h" +#include "pwio.h" +#ifdef SHADOWGRP +#include "sgroupio.h" +#endif +#include "shadowio.h" +#ifdef ENABLE_SUBIDS +#include "subordinateio.h" +#endif /* ENABLE_SUBIDS */ +#include "getdef.h" +#include "shadowlog.h" + +static char *passwd_db_file = NULL; +static char *spw_db_file = NULL; +static char *group_db_file = NULL; +static char *sgroup_db_file = NULL; +static char *suid_db_file = NULL; +static char *sgid_db_file = NULL; +static char *def_conf_file = NULL; +static FILE* fp_pwent = NULL; +static FILE* fp_grent = NULL; + +/* + * process_prefix_flag - prefix all paths if given the --prefix 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 const char* process_prefix_flag (const char* short_opt, int argc, char **argv) +{ + /* + * Parse the command line options. + */ + int i; + const char *prefix = NULL, *val; + + for (i = 0; i < argc; i++) { + val = NULL; + if ( (strcmp (argv[i], "--prefix") == 0) + || ((strncmp (argv[i], "--prefix=", 9) == 0) + && (val = argv[i] + 9)) + || (strcmp (argv[i], short_opt) == 0)) { + if (NULL != prefix) { + fprintf (log_get_logfd(), + _("%s: multiple --prefix options\n"), + log_get_progname()); + exit (E_BAD_ARG); + } + + if (val) { + prefix = val; + } else if (i + 1 == argc) { + fprintf (log_get_logfd(), + _("%s: option '%s' requires an argument\n"), + log_get_progname(), argv[i]); + exit (E_BAD_ARG); + } else { + prefix = argv[++ i]; + } + } + } + + + + if (prefix != NULL) { + if ( prefix[0] == '\0' || !strcmp(prefix, "/")) + return ""; /* if prefix is "/" then we ignore the flag option */ + /* should we prevent symbolic link from being used as a prefix? */ + + if ( prefix[0] != '/') { + fprintf (log_get_logfd(), + _("%s: prefix must be an absolute path\n"), + log_get_progname()); + exit (E_BAD_ARG); + } + size_t len; + len = strlen(prefix) + strlen(PASSWD_FILE) + 2; + passwd_db_file = xmalloc(len); + snprintf(passwd_db_file, len, "%s/%s", prefix, PASSWD_FILE); + pw_setdbname(passwd_db_file); + + len = strlen(prefix) + strlen(GROUP_FILE) + 2; + group_db_file = xmalloc(len); + snprintf(group_db_file, len, "%s/%s", prefix, GROUP_FILE); + gr_setdbname(group_db_file); + +#ifdef SHADOWGRP + len = strlen(prefix) + strlen(SGROUP_FILE) + 2; + sgroup_db_file = xmalloc(len); + snprintf(sgroup_db_file, len, "%s/%s", prefix, SGROUP_FILE); + sgr_setdbname(sgroup_db_file); +#endif +#ifdef USE_NIS + __setspNIS(0); /* disable NIS for now, at least until it is properly supporting a "prefix" */ +#endif + + len = strlen(prefix) + strlen(SHADOW_FILE) + 2; + spw_db_file = xmalloc(len); + snprintf(spw_db_file, len, "%s/%s", prefix, SHADOW_FILE); + spw_setdbname(spw_db_file); + +#ifdef ENABLE_SUBIDS + len = strlen(prefix) + strlen("/etc/subuid") + 2; + suid_db_file = xmalloc(len); + snprintf(suid_db_file, len, "%s/%s", prefix, "/etc/subuid"); + sub_uid_setdbname(suid_db_file); + + len = strlen(prefix) + strlen("/etc/subgid") + 2; + sgid_db_file = xmalloc(len); + snprintf(sgid_db_file, len, "%s/%s", prefix, "/etc/subgid"); + sub_gid_setdbname(sgid_db_file); +#endif + +#ifdef USE_ECONF + setdef_config_file(prefix); +#else + len = strlen(prefix) + strlen("/etc/login.defs") + 2; + def_conf_file = xmalloc(len); + snprintf(def_conf_file, len, "%s/%s", prefix, "/etc/login.defs"); + setdef_config_file(def_conf_file); +#endif + } + + if (prefix == NULL) + return ""; + return prefix; +} + + +extern struct group *prefix_getgrnam(const char *name) +{ + if (group_db_file) { + FILE* fg; + struct group * grp = NULL; + + fg = fopen(group_db_file, "rt"); + if (!fg) + return NULL; + while ((grp = fgetgrent(fg)) != NULL) { + if (!strcmp(name, grp->gr_name)) + break; + } + fclose(fg); + return grp; + } + + return getgrnam(name); +} + +extern struct group *prefix_getgrgid(gid_t gid) +{ + if (group_db_file) { + FILE* fg; + struct group * grp = NULL; + + fg = fopen(group_db_file, "rt"); + if (!fg) + return NULL; + while ((grp = fgetgrent(fg)) != NULL) { + if (gid == grp->gr_gid) + break; + } + fclose(fg); + return grp; + } + + return getgrgid(gid); +} + +extern struct passwd *prefix_getpwuid(uid_t uid) +{ + if (passwd_db_file) { + FILE* fg; + struct passwd *pwd = NULL; + + fg = fopen(passwd_db_file, "rt"); + if (!fg) + return NULL; + while ((pwd = fgetpwent(fg)) != NULL) { + if (uid == pwd->pw_uid) + break; + } + fclose(fg); + return pwd; + } + else { + return getpwuid(uid); + } +} +extern struct passwd *prefix_getpwnam(const char* name) +{ + if (passwd_db_file) { + FILE* fg; + struct passwd *pwd = NULL; + + fg = fopen(passwd_db_file, "rt"); + if (!fg) + return NULL; + while ((pwd = fgetpwent(fg)) != NULL) { + if (!strcmp(name, pwd->pw_name)) + break; + } + fclose(fg); + return pwd; + } + else { + return getpwnam(name); + } +} +extern struct spwd *prefix_getspnam(const char* name) +{ + if (spw_db_file) { + FILE* fg; + struct spwd *sp = NULL; + + fg = fopen(spw_db_file, "rt"); + if (!fg) + return NULL; + while ((sp = fgetspent(fg)) != NULL) { + if (!strcmp(name, sp->sp_namp)) + break; + } + fclose(fg); + return sp; + } + else { + return getspnam(name); + } +} + +extern void prefix_setpwent(void) +{ + if (!passwd_db_file) { + setpwent(); + return; + } + if (fp_pwent) + fclose (fp_pwent); + + fp_pwent = fopen(passwd_db_file, "rt"); + if (!fp_pwent) + return; +} +extern struct passwd* prefix_getpwent(void) +{ + if (!passwd_db_file) { + return getpwent(); + } + if (!fp_pwent) { + return NULL; + } + return fgetpwent(fp_pwent); +} +extern void prefix_endpwent(void) +{ + if (!passwd_db_file) { + endpwent(); + return; + } + if (fp_pwent) + fclose(fp_pwent); + fp_pwent = NULL; +} + +extern void prefix_setgrent(void) +{ + if (!group_db_file) { + setgrent(); + return; + } + if (fp_grent) + fclose (fp_grent); + + fp_grent = fopen(group_db_file, "rt"); + if (!fp_grent) + return; +} +extern struct group* prefix_getgrent(void) +{ + if (!group_db_file) { + return getgrent(); + } + return fgetgrent(fp_grent); +} +extern void prefix_endgrent(void) +{ + if (!group_db_file) { + endgrent(); + return; + } + if (fp_grent) + fclose(fp_grent); + fp_grent = NULL; +} + +extern struct group *prefix_getgr_nam_gid(const char *grname) +{ + long long int gid; + char *endptr; + struct group *g; + + if (NULL == grname) { + return NULL; + } + + if (group_db_file) { + errno = 0; + gid = strtoll (grname, &endptr, 10); + if ( ('\0' != *grname) + && ('\0' == *endptr) + && (ERANGE != errno) + && (gid == (gid_t)gid)) { + return prefix_getgrgid ((gid_t) gid); + } + g = prefix_getgrnam (grname); + return g ? __gr_dup(g) : NULL; + } + else + return getgr_nam_gid(grname); +} diff --git a/libmisc/pwd2spwd.c b/libmisc/pwd2spwd.c new file mode 100644 index 0000000..139a024 --- /dev/null +++ b/libmisc/pwd2spwd.c @@ -0,0 +1,67 @@ +/* + * SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1997, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2007 - 2009, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#ifndef USE_PAM + +#include <sys/types.h> +#include "prototypes.h" +#include "defines.h" +#include <pwd.h> + +/* + * 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) gettime () / 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 0000000..63c71e2 --- /dev/null +++ b/libmisc/pwd_init.c @@ -0,0 +1,79 @@ +/* + * SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1997 , Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2008 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#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 0000000..15fc3a3 --- /dev/null +++ b/libmisc/pwdcheck.c @@ -0,0 +1,38 @@ +/* + * SPDX-FileCopyrightText: 2000 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2007 - 2008, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#ifndef USE_PAM + +#include <stdio.h> +#include <shadow.h> +#include "prototypes.h" +#include "defines.h" +#include "pwauth.h" +#include "shadowlog.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 (log_get_logfd(), _("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 0000000..3d76b95 --- /dev/null +++ b/libmisc/remove_tree.c @@ -0,0 +1,101 @@ +/* + * SPDX-FileCopyrightText: 1991 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 2001, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2006, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2007 - 2010, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#include <fcntl.h> +#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" + +static int remove_tree_at (int at_fd, const char *path, bool remove_root) +{ + DIR *dir; + const struct dirent *ent; + int dir_fd, rc = 0; + + dir_fd = openat (at_fd, path, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC); + if (dir_fd < 0) { + return -1; + } + + dir = fdopendir (dir_fd); + if (!dir) { + (void) close (dir_fd); + return -1; + } + + /* + * Open the source directory and delete each entry. + */ + while ((ent = readdir (dir))) { + struct stat ent_sb; + + /* + * Skip the "." and ".." entries + */ + if (strcmp (ent->d_name, ".") == 0 || + strcmp (ent->d_name, "..") == 0) { + continue; + } + + rc = fstatat (dirfd(dir), ent->d_name, &ent_sb, AT_SYMLINK_NOFOLLOW); + if (rc < 0) { + break; + } + + if (S_ISDIR (ent_sb.st_mode)) { + /* + * Recursively delete this directory. + */ + if (remove_tree_at (dirfd(dir), ent->d_name, true) != 0) { + rc = -1; + break; + } + } else { + /* + * Delete the file. + */ + if (unlinkat (dirfd(dir), ent->d_name, 0) != 0) { + rc = -1; + break; + } + } + } + + (void) closedir (dir); + + if (remove_root && (0 == rc)) { + if (unlinkat (at_fd, path, AT_REMOVEDIR) != 0) { + rc = -1; + } + } + + return rc; +} + +/* + * 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) +{ + return remove_tree_at (AT_FDCWD, root, remove_root); +} diff --git a/libmisc/rlogin.c b/libmisc/rlogin.c new file mode 100644 index 0000000..fa67dc3 --- /dev/null +++ b/libmisc/rlogin.c @@ -0,0 +1,175 @@ +/* + * SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1999, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2007 - 2008, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ifdef RLOGIN + +#ident "$Id$" + +#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 0000000..62915b0 --- /dev/null +++ b/libmisc/root_flag.c @@ -0,0 +1,108 @@ +/* + * SPDX-FileCopyrightText: 2011 , Julian Pidancet + * SPDX-FileCopyrightText: 2011 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#include <stdio.h> +#include <assert.h> +#include "defines.h" +#include "prototypes.h" +/*@-exitarg@*/ +#include "exitcodes.h" +#include "shadowlog.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, *val; + + for (i = 0; i < argc; i++) { + val = NULL; + if ( (strcmp (argv[i], "--root") == 0) + || ((strncmp (argv[i], "--root=", 7) == 0) + && (val = argv[i] + 7)) + || (strcmp (argv[i], short_opt) == 0)) { + if (NULL != newroot) { + fprintf (log_get_logfd(), + _("%s: multiple --root options\n"), + log_get_progname()); + exit (E_BAD_ARG); + } + + if (val) { + newroot = val; + } else if (i + 1 == argc) { + fprintf (log_get_logfd(), + _("%s: option '%s' requires an argument\n"), + log_get_progname(), argv[i]); + exit (E_BAD_ARG); + } else { + newroot = argv[++ i]; + } + } + } + + 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 (log_get_logfd(), _("%s: failed to drop privileges (%s)\n"), + log_get_progname(), strerror (errno)); + exit (EXIT_FAILURE); + } + + if ('/' != newroot[0]) { + fprintf (log_get_logfd(), + _("%s: invalid chroot path '%s', only absolute paths are supported.\n"), + log_get_progname(), newroot); + exit (E_BAD_ARG); + } + + if (access (newroot, F_OK) != 0) { + fprintf(log_get_logfd(), + _("%s: cannot access chroot directory %s: %s\n"), + log_get_progname(), newroot, strerror (errno)); + exit (E_BAD_ARG); + } + + if (chdir (newroot) != 0) { + fprintf(log_get_logfd(), + _("%s: cannot chdir to chroot directory %s: %s\n"), + log_get_progname(), newroot, strerror (errno)); + exit (E_BAD_ARG); + } + + if (chroot (newroot) != 0) { + fprintf(log_get_logfd(), + _("%s: unable to chroot to directory %s: %s\n"), + log_get_progname(), newroot, strerror (errno)); + exit (E_BAD_ARG); + } +} + diff --git a/libmisc/salt.c b/libmisc/salt.c new file mode 100644 index 0000000..e5f633a --- /dev/null +++ b/libmisc/salt.c @@ -0,0 +1,566 @@ +/* + * SPDX-FileCopyrightText: Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl> + * SPDX-FileCopyrightText: J.T. Conklin <jtc@netbsd.org> + * + * SPDX-License-Identifier: Unlicense + */ + +/* + * 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$" + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#if HAVE_SYS_RANDOM_H +#include <sys/random.h> +#endif +#include "prototypes.h" +#include "defines.h" +#include "getdef.h" +#include "shadowlog.h" + +#if (defined CRYPT_GENSALT_IMPLEMENTS_AUTO_ENTROPY && \ + CRYPT_GENSALT_IMPLEMENTS_AUTO_ENTROPY) +#define USE_XCRYPT_GENSALT 1 +#else +#define USE_XCRYPT_GENSALT 0 +#endif + +/* Add the salt prefix. */ +#define MAGNUM(array,ch) (array)[0]=(array)[2]='$',(array)[1]=(ch),(array)[3]='\0' + +#ifdef USE_BCRYPT +/* Use $2b$ as prefix for compatibility with OpenBSD's bcrypt. */ +#define BCRYPTMAGNUM(array) (array)[0]=(array)[3]='$',(array)[1]='2',(array)[2]='b',(array)[4]='\0' +#define BCRYPT_SALT_SIZE 22 +/* Default number of rounds if not explicitly specified. */ +#define B_ROUNDS_DEFAULT 13 +/* Minimum number of rounds. */ +#define B_ROUNDS_MIN 4 +/* Maximum number of rounds. */ +#define B_ROUNDS_MAX 31 +#endif /* USE_BCRYPT */ + +#ifdef USE_SHA_CRYPT +/* Fixed salt len for sha{256,512}crypt. */ +#define SHA_CRYPT_SALT_SIZE 16 +/* Default number of rounds if not explicitly specified. */ +#define SHA_ROUNDS_DEFAULT 5000 +/* Minimum number of rounds. */ +#define SHA_ROUNDS_MIN 1000 +/* Maximum number of rounds. */ +#define SHA_ROUNDS_MAX 999999999 +#endif + +#ifdef USE_YESCRYPT +/* + * Default number of base64 characters used for the salt. + * 24 characters gives a 144 bits (18 bytes) salt. Unlike the more + * traditional 128 bits (16 bytes) salt, this 144 bits salt is always + * represented by the same number of base64 characters without padding + * issue, even with a non-standard base64 encoding scheme. + */ +#define YESCRYPT_SALT_SIZE 24 +/* Default cost if not explicitly specified. */ +#define Y_COST_DEFAULT 5 +/* Minimum cost. */ +#define Y_COST_MIN 1 +/* Maximum cost. */ +#define Y_COST_MAX 11 +#endif + +/* Fixed salt len for md5crypt. */ +#define MD5_CRYPT_SALT_SIZE 8 + +/* Generate salt of size salt_size. */ +#define MAX_SALT_SIZE 44 +#define MIN_SALT_SIZE 8 + +/* Maximum size of the generated salt string. */ +#define GENSALT_SETTING_SIZE 100 + +/* local function prototypes */ +static long read_random_bytes (void); +#if !USE_XCRYPT_GENSALT +static /*@observer@*/const char *gensalt (size_t salt_size); +#endif /* !USE_XCRYPT_GENSALT */ +#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) +static long shadow_random (long min, long max); +#endif /* USE_SHA_CRYPT || USE_BCRYPT */ +#ifdef USE_SHA_CRYPT +static /*@observer@*/unsigned long SHA_get_salt_rounds (/*@null@*/const int *prefered_rounds); +static /*@observer@*/void SHA_salt_rounds_to_buf (char *buf, unsigned long rounds); +#endif /* USE_SHA_CRYPT */ +#ifdef USE_BCRYPT +static /*@observer@*/unsigned long BCRYPT_get_salt_rounds (/*@null@*/const int *prefered_rounds); +static /*@observer@*/void BCRYPT_salt_rounds_to_buf (char *buf, unsigned long rounds); +#endif /* USE_BCRYPT */ +#ifdef USE_YESCRYPT +static /*@observer@*/unsigned long YESCRYPT_get_salt_cost (/*@null@*/const int *prefered_cost); +static /*@observer@*/void YESCRYPT_salt_cost_to_buf (char *buf, unsigned long cost); +#endif /* USE_YESCRYPT */ + +#if !USE_XCRYPT_GENSALT && !defined(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 /* !USE_XCRYPT_GENSALT && !defined(HAVE_L64A) */ + +/* Read sizeof (long) random bytes from /dev/urandom. */ +static long read_random_bytes (void) +{ + long randval = 0; + +#ifdef HAVE_ARC4RANDOM_BUF + /* arc4random_buf, if it exists, can never fail. */ + arc4random_buf (&randval, sizeof (randval)); + goto end; +#endif + +#ifdef HAVE_GETENTROPY + /* getentropy may exist but lack kernel support. */ + if (getentropy (&randval, sizeof (randval)) == 0) { + goto end; + } +#endif + +#ifdef HAVE_GETRANDOM + /* Likewise getrandom. */ + if ((size_t) getrandom (&randval, sizeof (randval), 0) == sizeof (randval)) { + goto end; + } +#endif + + /* Use /dev/urandom as a last resort. */ + FILE *f = fopen ("/dev/urandom", "r"); + if (NULL == f) { + goto fail; + } + + if (fread (&randval, sizeof (randval), 1, f) != 1) { + fclose(f); + goto fail; + } + + fclose(f); + goto end; + +fail: + fprintf (log_get_logfd(), + _("Unable to obtain random bytes.\n")); + exit (1); + +end: + return randval; +} + +#if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) +/* + * Return a random number between min and max (both included). + * + * It favors slightly the higher numbers. + */ +static long shadow_random (long min, long max) +{ + double drand; + long ret; + + drand = (double) (read_random_bytes () & RAND_MAX) / (double) RAND_MAX; + drand *= (double) (max - min + 1); + /* On systems were this is not random() range is lower, we favor + * higher numbers of salt. */ + ret = (long) (max + 1 - drand); + /* And we catch limits, and use the highest number */ + if ((ret < min) || (ret > max)) { + ret = max; + } + return ret; +} +#endif /* USE_SHA_CRYPT || USE_BCRYPT */ + +#ifdef USE_SHA_CRYPT +/* Return the the rounds number for the SHA crypt methods. */ +static /*@observer@*/unsigned long SHA_get_salt_rounds (/*@null@*/const int *prefered_rounds) +{ + unsigned 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); + + if ((-1 == min_rounds) && (-1 == max_rounds)) { + rounds = SHA_ROUNDS_DEFAULT; + } + else { + 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; + } + + rounds = (unsigned long) shadow_random (min_rounds, max_rounds); + } + } else if (0 == *prefered_rounds) { + rounds = SHA_ROUNDS_DEFAULT; + } else { + rounds = (unsigned long) *prefered_rounds; + } + + /* Sanity checks. The libc should also check this, but this + * protects against a rounds_prefix overflow. */ + if (rounds < SHA_ROUNDS_MIN) { + rounds = SHA_ROUNDS_MIN; + } + + if (rounds > SHA_ROUNDS_MAX) { + rounds = SHA_ROUNDS_MAX; + } + + return rounds; +} + +/* + * Fill a salt prefix specifying the rounds number for the SHA crypt methods + * to a buffer. + */ +static /*@observer@*/void SHA_salt_rounds_to_buf (char *buf, unsigned long rounds) +{ + const size_t buf_begin = strlen (buf); + + /* Nothing to do here if SHA_ROUNDS_DEFAULT is used. */ + if (rounds == SHA_ROUNDS_DEFAULT) { + return; + } + + /* + * Check if the result buffer is long enough. + * We are going to write a maximum of 17 bytes, + * plus one byte for the terminator. + * rounds=XXXXXXXXX$ + * 00000000011111111 + * 12345678901234567 + */ + assert (GENSALT_SETTING_SIZE > buf_begin + 17); + + (void) snprintf (buf + buf_begin, 18, "rounds=%lu$", rounds); +} +#endif /* USE_SHA_CRYPT */ + +#ifdef USE_BCRYPT +/* Return the the rounds number for the BCRYPT method. */ +static /*@observer@*/unsigned long BCRYPT_get_salt_rounds (/*@null@*/const int *prefered_rounds) +{ + unsigned long rounds; + + if (NULL == prefered_rounds) { + long min_rounds = getdef_long ("BCRYPT_MIN_ROUNDS", -1); + long max_rounds = getdef_long ("BCRYPT_MAX_ROUNDS", -1); + + if ((-1 == min_rounds) && (-1 == max_rounds)) { + rounds = B_ROUNDS_DEFAULT; + } else { + 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; + } + + rounds = (unsigned long) shadow_random (min_rounds, max_rounds); + } + } else if (0 == *prefered_rounds) { + rounds = B_ROUNDS_DEFAULT; + } else { + rounds = (unsigned long) *prefered_rounds; + } + + /* Sanity checks. */ + if (rounds < B_ROUNDS_MIN) { + rounds = B_ROUNDS_MIN; + } + +#if USE_XCRYPT_GENSALT + if (rounds > B_ROUNDS_MAX) { + rounds = B_ROUNDS_MAX; + } +#else /* USE_XCRYPT_GENSALT */ + /* + * Use 19 as an upper bound for now, + * because musl doesn't allow rounds >= 20. + * If musl ever supports > 20 rounds, + * rounds should be set to B_ROUNDS_MAX. + */ + if (rounds > 19) { + rounds = 19; + } +#endif /* USE_XCRYPT_GENSALT */ + + return rounds; +} + +/* + * Fill a salt prefix specifying the rounds number for the BCRYPT method + * to a buffer. + */ +static /*@observer@*/void BCRYPT_salt_rounds_to_buf (char *buf, unsigned long rounds) +{ + const size_t buf_begin = strlen (buf); + + /* + * Check if the result buffer is long enough. + * We are going to write three bytes, + * plus one byte for the terminator. + * XX$ + * 000 + * 123 + */ + assert (GENSALT_SETTING_SIZE > buf_begin + 3); + + (void) snprintf (buf + buf_begin, 4, "%2.2lu$", rounds); +} +#endif /* USE_BCRYPT */ + +#ifdef USE_YESCRYPT +/* Return the the cost number for the YESCRYPT method. */ +static /*@observer@*/unsigned long YESCRYPT_get_salt_cost (/*@null@*/const int *prefered_cost) +{ + unsigned long cost; + + if (NULL == prefered_cost) { + cost = getdef_num ("YESCRYPT_COST_FACTOR", Y_COST_DEFAULT); + } else if (0 == *prefered_cost) { + cost = Y_COST_DEFAULT; + } else { + cost = (unsigned long) *prefered_cost; + } + + /* Sanity checks. */ + if (cost < Y_COST_MIN) { + cost = Y_COST_MIN; + } + + if (cost > Y_COST_MAX) { + cost = Y_COST_MAX; + } + + return cost; +} + +/* + * Fill a salt prefix specifying the cost for the YESCRYPT method + * to a buffer. + */ +static /*@observer@*/void YESCRYPT_salt_cost_to_buf (char *buf, unsigned long cost) +{ + const size_t buf_begin = strlen (buf); + + /* + * Check if the result buffer is long enough. + * We are going to write four bytes, + * plus one byte for the terminator. + * jXX$ + * 0000 + * 1234 + */ + assert (GENSALT_SETTING_SIZE > buf_begin + 4); + + buf[buf_begin + 0] = 'j'; + if (cost < 3) { + buf[buf_begin + 1] = 0x36 + cost; + } else if (cost < 6) { + buf[buf_begin + 1] = 0x34 + cost; + } else { + buf[buf_begin + 1] = 0x3b + cost; + } + buf[buf_begin + 2] = cost >= 3 ? 'T' : '5'; + buf[buf_begin + 3] = '$'; + buf[buf_begin + 4] = '\0'; +} +#endif /* USE_YESCRYPT */ + +#if !USE_XCRYPT_GENSALT +static /*@observer@*/const char *gensalt (size_t salt_size) +{ + static char salt[MAX_SALT_SIZE + 6]; + + memset (salt, '\0', MAX_SALT_SIZE + 6); + + assert (salt_size >= MIN_SALT_SIZE && + salt_size <= MAX_SALT_SIZE); + strcat (salt, l64a (read_random_bytes ())); + do { + strcat (salt, l64a (read_random_bytes ())); + } while (strlen (salt) < salt_size); + + salt[salt_size] = '\0'; + + return salt; +} +#endif /* !USE_XCRYPT_GENSALT */ + +/* + * 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 ENCRYPT_METHOD + * variable, and if not set according to the MD5_CRYPT_ENAB variable, + * which can both be set inside the login.defs file. + * + * 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). + * * For the YESCRYPT method, this specifies the cost factor (if not NULL). + */ +/*@observer@*/const char *crypt_make_salt (/*@null@*//*@observer@*/const char *meth, /*@null@*/void *arg) +{ + static char result[GENSALT_SETTING_SIZE]; + size_t salt_len = MAX_SALT_SIZE; + const char *method; + unsigned long rounds = 0; + + memset (result, '\0', GENSALT_SETTING_SIZE); + + 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'); + salt_len = MD5_CRYPT_SALT_SIZE; + rounds = 0; +#ifdef USE_BCRYPT + } else if (0 == strcmp (method, "BCRYPT")) { + BCRYPTMAGNUM(result); + salt_len = BCRYPT_SALT_SIZE; + rounds = BCRYPT_get_salt_rounds ((int *) arg); + BCRYPT_salt_rounds_to_buf (result, rounds); +#endif /* USE_BCRYPT */ +#ifdef USE_YESCRYPT + } else if (0 == strcmp (method, "YESCRYPT")) { + MAGNUM(result, 'y'); + salt_len = YESCRYPT_SALT_SIZE; + rounds = YESCRYPT_get_salt_cost ((int *) arg); + YESCRYPT_salt_cost_to_buf (result, rounds); +#endif /* USE_YESCRYPT */ +#ifdef USE_SHA_CRYPT + } else if (0 == strcmp (method, "SHA256")) { + MAGNUM(result, '5'); + salt_len = SHA_CRYPT_SALT_SIZE; + rounds = SHA_get_salt_rounds ((int *) arg); + SHA_salt_rounds_to_buf (result, rounds); + } else if (0 == strcmp (method, "SHA512")) { + MAGNUM(result, '6'); + salt_len = SHA_CRYPT_SALT_SIZE; + rounds = SHA_get_salt_rounds ((int *) arg); + SHA_salt_rounds_to_buf (result, rounds); +#endif /* USE_SHA_CRYPT */ + } else if (0 != strcmp (method, "DES")) { + fprintf (log_get_logfd(), + _("Invalid ENCRYPT_METHOD value: '%s'.\n" + "Defaulting to DES.\n"), + method); + salt_len = MAX_SALT_SIZE; + rounds = 0; + memset (result, '\0', GENSALT_SETTING_SIZE); + } + +#if USE_XCRYPT_GENSALT + /* + * Prepare DES setting for crypt_gensalt(), if result + * has not been filled with anything previously. + */ + if ('\0' == result[0]) { + /* Avoid -Wunused-but-set-variable. */ + salt_len = GENSALT_SETTING_SIZE - 1; + rounds = 0; + memset (result, '.', salt_len); + result[salt_len] = '\0'; + } + + char *retval = crypt_gensalt (result, rounds, NULL, 0); + + /* Should not happen, but... */ + if (NULL == retval) { + fprintf (log_get_logfd(), + _("Unable to generate a salt from setting " + "\"%s\", check your settings in " + "ENCRYPT_METHOD and the corresponding " + "configuration for your selected hash " + "method.\n"), result); + + exit (1); + } + + return retval; +#else /* USE_XCRYPT_GENSALT */ + /* Check if the result buffer is long enough. */ + assert (GENSALT_SETTING_SIZE > strlen (result) + salt_len); + + /* Concatenate a pseudo random salt. */ + strncat (result, gensalt (salt_len), + GENSALT_SETTING_SIZE - strlen (result) - 1); + + return result; +#endif /* USE_XCRYPT_GENSALT */ +} diff --git a/libmisc/setugid.c b/libmisc/setugid.c new file mode 100644 index 0000000..6dbe38e --- /dev/null +++ b/libmisc/setugid.c @@ -0,0 +1,125 @@ +/* + * SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1998, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2008 - 2010, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * Separated from setup.c. --marekm + */ + +#include <config.h> + +#ident "$Id$" + +#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 0000000..5d7aefa --- /dev/null +++ b/libmisc/setupenv.c @@ -0,0 +1,290 @@ +/* + * SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 2000, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2001 - 2006, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2007 - 2010, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * Separated from setup.c. --marekm + */ + +#include <config.h> + +#ident "$Id$" + +#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" +#include "shadowlog.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; + } + else 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 (log_get_logfd(), _("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=/")); + free (info->pw_dir); + info->pw_dir = xstrdup (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; + + free (info->pw_shell); + info->pw_shell = xstrdup (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 0000000..7c67500 --- /dev/null +++ b/libmisc/shell.c @@ -0,0 +1,80 @@ +/* + * SPDX-FileCopyrightText: 1989 - 1991, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1998, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2006, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2009 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#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 (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 0000000..d321873 --- /dev/null +++ b/libmisc/strtoday.c @@ -0,0 +1,220 @@ +/* + * SPDX-FileCopyrightText: 1991 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1999, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2008 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#if !defined(__GLIBC__) +#define _XOPEN_SOURCE 500 +#endif + +#include <config.h> + +#include <ctype.h> + +#ident "$Id$" + +#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 const char *const 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 cumulative number of days in the preceding + * months. they are declared so that january is 1, not 0. + */ +static const short days[13] = { 0, + 31, 28, 31, 30, 31, 30, /* JAN - JUN */ + 31, 31, 30, 31, 30, 31 +}; /* JUL - DEC */ + +static const 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; + const 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 0000000..d30c4c7 --- /dev/null +++ b/libmisc/sub.c @@ -0,0 +1,55 @@ +/* + * SPDX-FileCopyrightText: 1989 - 1991, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1999, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2006, Tomasz KÅ‚oczko + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#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 presence 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 0000000..3e95613 --- /dev/null +++ b/libmisc/sulog.c @@ -0,0 +1,83 @@ +/* + * SPDX-FileCopyrightText: 1989 - 1992, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 2000, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2001 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2008 - 2010, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#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 0000000..f72d957 --- /dev/null +++ b/libmisc/ttytype.c @@ -0,0 +1,67 @@ +/* + * SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1997, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2008 - 2010, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#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 0000000..16d92ad --- /dev/null +++ b/libmisc/tz.c @@ -0,0 +1,57 @@ +/* + * SPDX-FileCopyrightText: 1991 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1991 - 1994, Chip Rosenthal + * SPDX-FileCopyrightText: 1996 - 1998, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2007 - 2010, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ifndef USE_PAM + +#ident "$Id$" + +#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 0000000..e331d46 --- /dev/null +++ b/libmisc/ulimit.c @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1997, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2008 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#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 0000000..c03feb2 --- /dev/null +++ b/libmisc/user_busy.c @@ -0,0 +1,273 @@ +/* + * SPDX-FileCopyrightText: 1991 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 2000, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2000 - 2006, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2007 - 2009, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id: $" + +#include <assert.h> +#include <stdio.h> +#include <sys/types.h> +#include <dirent.h> +#include <fcntl.h> +#include <unistd.h> +#include "defines.h" +#include "prototypes.h" +#ifdef ENABLE_SUBIDS +#include "subordinateio.h" +#endif /* ENABLE_SUBIDS */ +#include "shadowlog.h" + +#ifdef __linux__ +static int check_status (const char *name, 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 (log_get_logfd(), + _("%s: user %s is currently logged in\n"), + log_get_progname(), name); + return 1; + } + + return 0; +} +#endif /* !__linux__ */ + +#ifdef __linux__ +#ifdef ENABLE_SUBIDS +#define in_parentuid_range(uid) ((uid) >= parentuid && (uid) < parentuid + range) +static int different_namespace (const char *sname) +{ + /* 41: /proc/xxxxxxxxxx/task/xxxxxxxxxx/ns/user + \0 */ + char path[41]; + char buf[512], buf2[512]; + ssize_t llen1, llen2; + + snprintf (path, 41, "/proc/%s/ns/user", sname); + + if ((llen1 = readlink (path, buf, sizeof(buf))) == -1) + return 0; + + if ((llen2 = readlink ("/proc/self/ns/user", buf2, sizeof(buf2))) == -1) + return 0; + + if (llen1 == llen2 && memcmp (buf, buf2, llen1) == 0) + return 0; /* same namespace */ + + return 1; +} +#endif /* ENABLE_SUBIDS */ + + +static int check_status (const char *name, 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); + + 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); + (void) fclose (sfile); + 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) ) { + return 1; + } +#ifdef ENABLE_SUBIDS + if ( different_namespace (sname) + && ( have_sub_uids(name, ruid, 1) + || have_sub_uids(name, euid, 1) + || have_sub_uids(name, suid, 1)) + ) { + return 1; + } +#endif /* ENABLE_SUBIDS */ + } else { + /* Ignore errors. This is just a best effort. */ + } + 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; + +#ifdef ENABLE_SUBIDS + sub_uid_open (O_RDONLY); +#endif /* ENABLE_SUBIDS */ + + proc = opendir ("/proc"); + if (proc == NULL) { + perror ("opendir /proc"); +#ifdef ENABLE_SUBIDS + sub_uid_close(); +#endif + return 0; + } + if (stat ("/", &sbroot) != 0) { + perror ("stat (\"/\")"); + (void) closedir (proc); +#ifdef ENABLE_SUBIDS + sub_uid_close(); +#endif + 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 (name, tmp_d_name, uid) != 0) { + (void) closedir (proc); +#ifdef ENABLE_SUBIDS + sub_uid_close(); +#endif + fprintf (log_get_logfd(), + _("%s: user %s is currently used by process %d\n"), + log_get_progname(), 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 (name, task_path+6, uid) != 0) { + (void) closedir (proc); + (void) closedir (task_dir); +#ifdef ENABLE_SUBIDS + sub_uid_close(); +#endif + fprintf (log_get_logfd(), + _("%s: user %s is currently used by process %d\n"), + log_get_progname(), name, pid); + return 1; + } + } + (void) closedir (task_dir); + } else { + /* Ignore errors. This is just a best effort */ + } + } + + (void) closedir (proc); +#ifdef ENABLE_SUBIDS + sub_uid_close(); +#endif /* ENABLE_SUBIDS */ + return 0; +} +#endif /* __linux__ */ + diff --git a/libmisc/utmp.c b/libmisc/utmp.c new file mode 100644 index 0000000..45b479f --- /dev/null +++ b/libmisc/utmp.c @@ -0,0 +1,474 @@ +/* + * SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1999, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2001 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2008 - 2009, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#include "defines.h" +#include "prototypes.h" + +#ifdef USE_UTMPX +#include <utmpx.h> +#else +#include <utmp.h> +#endif + +#include <assert.h> +#include <sys/param.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <stdio.h> + +#ident "$Id$" + + +/* + * 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 entered 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. + */ +#ifndef USE_UTMPX +/*@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; +} +#else +/*@null@*/ /*@only*/struct utmpx *get_current_utmp(void) +{ + struct utmpx *ut; + struct utmpx *ret = NULL; + + setutxent (); + + /* Find the utmpx entry for this PID. */ + while ((ut = getutxent ()) != NULL) { + if ( (ut->ut_pid == getpid ()) + && ('\0' != ut->ut_id[0]) + && ( (LOGIN_PROCESS == ut->ut_type) + || (USER_PROCESS == ut->ut_type)) + && is_my_tty (ut->ut_line)) { + break; + } + } + + if (NULL != ut) { + ret = (struct utmpx *) xmalloc (sizeof (*ret)); + memcpy (ret, ut, sizeof (*ret)); + } + + endutxent (); + + return ret; +} +#endif + + +#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 */ + + +#ifndef USE_UTMPX +/* + * 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) - 1); +#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) - 1); + } +#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) - 1); +#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) - 1); +#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; +} + +#else +/* + * 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 utmpx *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 0000000..326635f --- /dev/null +++ b/libmisc/valid.c @@ -0,0 +1,82 @@ +/* + * SPDX-FileCopyrightText: 1989 - 1993, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1999, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2005, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2007 - 2008, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#ident "$Id$" + +#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) + && (NULL != encrypted) + && (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 0000000..6a3f969 --- /dev/null +++ b/libmisc/xgetXXbyYY.c @@ -0,0 +1,121 @@ +/* + * SPDX-FileCopyrightText: 2007 - 2009, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * 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" +#include "shadowlog.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 (log_get_logfd(), _("%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 (log_get_logfd(), _("%s: out of memory\n"), + "x" STRINGIZE(FUNCTION_NAME)); + exit (13); + } + 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 != status) { + 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 (log_get_logfd(), _("%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 0000000..fe99929 --- /dev/null +++ b/libmisc/xgetgrgid.c @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: 2007 - 2009, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * 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 0000000..66d6f93 --- /dev/null +++ b/libmisc/xgetgrnam.c @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: 2007 - 2009, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * 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 0000000..74eb972 --- /dev/null +++ b/libmisc/xgetpwnam.c @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: 2007 - 2009, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * 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 0000000..23b2d0c --- /dev/null +++ b/libmisc/xgetpwuid.c @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: 2007 - 2009, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * 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 0000000..148185f --- /dev/null +++ b/libmisc/xgetspnam.c @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: 2008 - 2009, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * 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 0000000..056d472 --- /dev/null +++ b/libmisc/xmalloc.c @@ -0,0 +1,46 @@ +/* + * SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1998, Marek MichaÅ‚kiewicz + * SPDX-FileCopyrightText: 2003 - 2006, Tomasz KÅ‚oczko + * SPDX-FileCopyrightText: 2008 , Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* 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$" + +#include <stdio.h> +#include <errno.h> +#include "defines.h" +#include "prototypes.h" +#include "shadowlog.h" + +/*@maynotreturn@*/ /*@only@*//*@out@*//*@notnull@*/void *xmalloc (size_t size) +{ + void *ptr; + + ptr = malloc (size); + if (NULL == ptr) { + (void) fprintf (log_get_logfd(), + _("%s: failed to allocate memory: %s\n"), + log_get_progname(), 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 0000000..cfbe6ec --- /dev/null +++ b/libmisc/yesno.c @@ -0,0 +1,56 @@ +/* + * SPDX-FileCopyrightText: 1992 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 2007 - 2008, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * Common code for yes/no prompting + * + * Used by pwck.c and grpck.c + */ + +#include <config.h> + +#ident "$Id$" + +#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; +} + diff --git a/libsubid/Makefile.am b/libsubid/Makefile.am new file mode 100644 index 0000000..09ec341 --- /dev/null +++ b/libsubid/Makefile.am @@ -0,0 +1,30 @@ +lib_LTLIBRARIES = libsubid.la +libsubid_la_SOURCES = api.c +libsubid_la_LDFLAGS = -version-info @LIBSUBID_ABI_MAJOR@ -export-symbols-regex '^subid_' + +pkginclude_HEADERS = subid.h + +MISCLIBS = \ + $(LIBAUDIT) \ + $(LIBSELINUX) \ + $(LIBSEMANAGE) \ + $(LIBCRACK) \ + $(LIBCRYPT_NOPAM) \ + $(LIBSKEY) \ + $(LIBMD) \ + $(LIBECONF) \ + $(LIBCRYPT) \ + $(LIBACL) \ + $(LIBATTR) \ + $(LIBTCB) \ + $(LIBPAM) + +libsubid_la_LIBADD = \ + $(top_builddir)/lib/libshadow.la \ + $(top_builddir)/libmisc/libmisc.la \ + $(MISCLIBS) -ldl + +AM_CPPFLAGS = \ + -I${top_srcdir}/lib \ + -I${top_srcdir}/libmisc \ + -DLOCALEDIR=\"$(datadir)/locale\" diff --git a/libsubid/Makefile.in b/libsubid/Makefile.in new file mode 100644 index 0000000..ed8dc94 --- /dev/null +++ b/libsubid/Makefile.in @@ -0,0 +1,764 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 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__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +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 = libsubid +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/intlmacosx.m4 \ + $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \ + $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \ + $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(pkginclude_HEADERS) \ + $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = subid.h +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgincludedir)" +LTLIBRARIES = $(lib_LTLIBRARIES) +am__DEPENDENCIES_1 = +am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +libsubid_la_DEPENDENCIES = $(top_builddir)/lib/libshadow.la \ + $(top_builddir)/libmisc/libmisc.la $(am__DEPENDENCIES_2) +am_libsubid_la_OBJECTS = api.lo +libsubid_la_OBJECTS = $(am_libsubid_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libsubid_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libsubid_la_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/api.Plo +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libsubid_la_SOURCES) +DIST_SOURCES = $(libsubid_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(pkginclude_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/subid.h.in \ + $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +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@ +ECONF_CPPFLAGS = @ECONF_CPPFLAGS@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +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@ +LIBECONF = @LIBECONF@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBMD = @LIBMD@ +LIBOBJS = @LIBOBJS@ +LIBPAM = @LIBPAM@ +LIBS = @LIBS@ +LIBSELINUX = @LIBSELINUX@ +LIBSEMANAGE = @LIBSEMANAGE@ +LIBSKEY = @LIBSKEY@ +LIBSUBID_ABI = @LIBSUBID_ABI@ +LIBSUBID_ABI_MAJOR = @LIBSUBID_ABI_MAJOR@ +LIBSUBID_ABI_MICRO = @LIBSUBID_ABI_MICRO@ +LIBSUBID_ABI_MINOR = @LIBSUBID_ABI_MINOR@ +LIBTCB = @LIBTCB@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LIYESCRYPT = @LIYESCRYPT@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +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@ +VENDORDIR = @VENDORDIR@ +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@ +capcmd = @capcmd@ +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@ +runstatedir = @runstatedir@ +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@ +lib_LTLIBRARIES = libsubid.la +libsubid_la_SOURCES = api.c +libsubid_la_LDFLAGS = -version-info @LIBSUBID_ABI_MAJOR@ -export-symbols-regex '^subid_' +pkginclude_HEADERS = subid.h +MISCLIBS = \ + $(LIBAUDIT) \ + $(LIBSELINUX) \ + $(LIBSEMANAGE) \ + $(LIBCRACK) \ + $(LIBCRYPT_NOPAM) \ + $(LIBSKEY) \ + $(LIBMD) \ + $(LIBECONF) \ + $(LIBCRYPT) \ + $(LIBACL) \ + $(LIBATTR) \ + $(LIBTCB) \ + $(LIBPAM) + +libsubid_la_LIBADD = \ + $(top_builddir)/lib/libshadow.la \ + $(top_builddir)/libmisc/libmisc.la \ + $(MISCLIBS) -ldl + +AM_CPPFLAGS = \ + -I${top_srcdir}/lib \ + -I${top_srcdir}/libmisc \ + -DLOCALEDIR=\"$(datadir)/locale\" + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(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) --foreign libsubid/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign libsubid/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__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + 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): +subid.h: $(top_builddir)/config.status $(srcdir)/subid.h.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libsubid.la: $(libsubid_la_OBJECTS) $(libsubid_la_DEPENDENCIES) $(EXTRA_libsubid_la_DEPENDENCIES) + $(AM_V_CCLD)$(libsubid_la_LINK) -rpath $(libdir) $(libsubid_la_OBJECTS) $(libsubid_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/api.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-pkgincludeHEADERS: $(pkginclude_HEADERS) + @$(NORMAL_INSTALL) + @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkgincludedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkgincludedir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkgincludedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkgincludedir)" || exit $$?; \ + done + +uninstall-pkgincludeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkgincludedir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + 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-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + 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" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(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 $(LTLIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgincludedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +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." +clean: clean-am + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/api.Plo + -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-pkgincludeHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES + +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 -f ./$(DEPDIR)/api.Plo + -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: uninstall-libLTLIBRARIES uninstall-pkgincludeHEADERS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libLTLIBRARIES clean-libtool cscopelist-am \ + ctags ctags-am 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-libLTLIBRARIES install-man install-pdf \ + install-pdf-am install-pkgincludeHEADERS 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 tags-am uninstall \ + uninstall-am uninstall-libLTLIBRARIES \ + uninstall-pkgincludeHEADERS + +.PRECIOUS: Makefile + + +# 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/libsubid/api.c b/libsubid/api.c new file mode 100644 index 0000000..00da74f --- /dev/null +++ b/libsubid/api.c @@ -0,0 +1,108 @@ +/* + * SPDX-FileCopyrightText: 2020 Serge Hallyn + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> +#include <fcntl.h> +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <pwd.h> +#include <stdbool.h> +#include "subordinateio.h" +#include "idmapping.h" +#include "subid.h" +#include "shadowlog.h" + +bool subid_init(const char *progname, FILE * logfd) +{ + FILE *shadow_logfd; + if (progname) { + progname = strdup(progname); + if (!progname) + return false; + log_set_progname(progname); + } else { + log_set_progname("(libsubid)"); + } + + if (logfd) { + log_set_logfd(logfd); + return true; + } + shadow_logfd = fopen("/dev/null", "w"); + if (!shadow_logfd) { + log_set_logfd(stderr); + return false; + } + log_set_logfd(shadow_logfd); + return true; +} + +static +int get_subid_ranges(const char *owner, enum subid_type id_type, struct subid_range **ranges) +{ + return list_owner_ranges(owner, id_type, ranges); +} + +int subid_get_uid_ranges(const char *owner, struct subid_range **ranges) +{ + return get_subid_ranges(owner, ID_TYPE_UID, ranges); +} + +int subid_get_gid_ranges(const char *owner, struct subid_range **ranges) +{ + return get_subid_ranges(owner, ID_TYPE_GID, ranges); +} + +static +int get_subid_owner(unsigned long id, enum subid_type id_type, uid_t **owner) +{ + return find_subid_owners(id, id_type, owner); +} + +int subid_get_uid_owners(uid_t uid, uid_t **owner) +{ + return get_subid_owner((unsigned long)uid, ID_TYPE_UID, owner); +} + +int subid_get_gid_owners(gid_t gid, uid_t **owner) +{ + return get_subid_owner((unsigned long)gid, ID_TYPE_GID, owner); +} + +static +bool grant_subid_range(struct subordinate_range *range, bool reuse, + enum subid_type id_type) +{ + return new_subid_range(range, id_type, reuse); +} + +bool subid_grant_uid_range(struct subordinate_range *range, bool reuse) +{ + return grant_subid_range(range, reuse, ID_TYPE_UID); +} + +bool subid_grant_gid_range(struct subordinate_range *range, bool reuse) +{ + return grant_subid_range(range, reuse, ID_TYPE_GID); +} + +static +bool ungrant_subid_range(struct subordinate_range *range, enum subid_type id_type) +{ + return release_subid_range(range, id_type); +} + +bool subid_ungrant_uid_range(struct subordinate_range *range) +{ + return ungrant_subid_range(range, ID_TYPE_UID); +} + +bool subid_ungrant_gid_range(struct subordinate_range *range) +{ + return ungrant_subid_range(range, ID_TYPE_GID); +} diff --git a/libsubid/subid.h b/libsubid/subid.h new file mode 100644 index 0000000..d13ee84 --- /dev/null +++ b/libsubid/subid.h @@ -0,0 +1,163 @@ +#include <sys/types.h> +#include <stdio.h> +#include <stdbool.h> + +#ifndef SUBID_RANGE_DEFINED +#define SUBID_RANGE_DEFINED 1 +#define SUBID_ABI_VERSION 4.0.0 +#define SUBID_ABI_MAJOR 4 +#define SUBID_ABI_MINOR 0 +#define SUBID_ABI_MICRO 0 + +/* subid_range is just a starting point and size of a range */ +struct subid_range { + unsigned long start; + unsigned long count; +}; + +/* subordinage_range is a subid_range plus an owner, representing + * a range in /etc/subuid or /etc/subgid */ +struct subordinate_range { + const char *owner; + unsigned long start; + unsigned long count; +}; + +enum subid_type { + ID_TYPE_UID = 1, + ID_TYPE_GID = 2 +}; + +enum subid_status { + SUBID_STATUS_SUCCESS = 0, + SUBID_STATUS_UNKNOWN_USER = 1, + SUBID_STATUS_ERROR_CONN = 2, + SUBID_STATUS_ERROR = 3, +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * subid_init: initialize libsubid + * + * @progname: Name to display as program. If NULL, then "(libsubid)" will be + * shown in error messages. + * @logfd: Open file pointer to pass error messages to. If NULL, then + * /dev/null will be opened and messages will be sent there. The + * default if libsubid_init() is not called is stderr (2). + * + * This function does not need to be called. If not called, then the defaults + * will be used. + * + * Returns false if an error occurred. + */ +bool subid_init(const char *progname, FILE *logfd); + +/* + * subid_get_uid_ranges: return a list of UID ranges for a user + * + * @owner: username being queried + * @ranges: a pointer to an array of subid_range structs in which the result + * will be returned. + * + * The caller must free(ranges) when done. + * + * returns: number of ranges found, ir < 0 on error. + */ +int subid_get_uid_ranges(const char *owner, struct subid_range **ranges); + +/* + * subid_get_gid_ranges: return a list of GID ranges for a user + * + * @owner: username being queried + * @ranges: a pointer to an array of subid_range structs in which the result + * will be returned. + * + * The caller must free(ranges) when done. + * + * returns: number of ranges found, ir < 0 on error. + */ +int subid_get_gid_ranges(const char *owner, struct subid_range **ranges); + +/* + * subid_get_uid_owners: return a list of uids to which the given uid has been + * delegated. + * + * @uid: The subuid being queried + * @owners: a pointer to an array of uids into which the results are placed. + * The returned array must be freed by the caller. + * + * Returns the number of uids returned, or < 0 on error. + */ +int subid_get_uid_owners(uid_t uid, uid_t **owner); + +/* + * subid_get_gid_owners: return a list of uids to which the given gid has been + * delegated. + * + * @uid: The subgid being queried + * @owners: a pointer to an array of uids into which the results are placed. + * The returned array must be freed by the caller. + * + * Returns the number of uids returned, or < 0 on error. + */ +int subid_get_gid_owners(gid_t gid, uid_t **owner); + +/* + * subid_grant_uid_range: assign a subuid range to a user + * + * @range: pointer to a struct subordinate_range detailing the UID range + * to allocate. ->owner must be the username, and ->count must be + * filled in. ->start is ignored, and will contain the start + * of the newly allocated range, upon success. + * + * Returns true if the delegation succeeded, false otherwise. If true, + * then the range from (range->start, range->start + range->count) will + * be delegated to range->owner. + */ +bool subid_grant_uid_range(struct subordinate_range *range, bool reuse); + +/* + * subid_grant_gid_range: assign a subgid range to a user + * + * @range: pointer to a struct subordinate_range detailing the GID range + * to allocate. ->owner must be the username, and ->count must be + * filled in. ->start is ignored, and will contain the start + * of the newly allocated range, upon success. + * + * Returns true if the delegation succeeded, false otherwise. If true, + * then the range from (range->start, range->start + range->count) will + * be delegated to range->owner. + */ +bool subid_grant_gid_range(struct subordinate_range *range, bool reuse); + +/* + * subid_ungrant_uid_range: remove a subuid allocation. + * + * @range: pointer to a struct subordinate_range detailing the UID allocation + * to remove. + * + * Returns true if successful, false if it failed, for instance if the + * delegation did not exist. + */ +bool subid_ungrant_uid_range(struct subordinate_range *range); + +/* + * subid_ungrant_gid_range: remove a subgid allocation. + * + * @range: pointer to a struct subordinate_range detailing the GID allocation + * to remove. + * + * Returns true if successful, false if it failed, for instance if the + * delegation did not exist. + */ +bool subid_ungrant_gid_range(struct subordinate_range *range); + +#ifdef __cplusplus +} +#endif + +#define SUBID_NFIELDS 3 +#endif diff --git a/libsubid/subid.h.in b/libsubid/subid.h.in new file mode 100644 index 0000000..79744ed --- /dev/null +++ b/libsubid/subid.h.in @@ -0,0 +1,163 @@ +#include <sys/types.h> +#include <stdio.h> +#include <stdbool.h> + +#ifndef SUBID_RANGE_DEFINED +#define SUBID_RANGE_DEFINED 1 +#define SUBID_ABI_VERSION @LIBSUBID_ABI_MAJOR@.@LIBSUBID_ABI_MINOR@.@LIBSUBID_ABI_MICRO@ +#define SUBID_ABI_MAJOR @LIBSUBID_ABI_MAJOR@ +#define SUBID_ABI_MINOR @LIBSUBID_ABI_MINOR@ +#define SUBID_ABI_MICRO @LIBSUBID_ABI_MICRO@ + +/* subid_range is just a starting point and size of a range */ +struct subid_range { + unsigned long start; + unsigned long count; +}; + +/* subordinage_range is a subid_range plus an owner, representing + * a range in /etc/subuid or /etc/subgid */ +struct subordinate_range { + const char *owner; + unsigned long start; + unsigned long count; +}; + +enum subid_type { + ID_TYPE_UID = 1, + ID_TYPE_GID = 2 +}; + +enum subid_status { + SUBID_STATUS_SUCCESS = 0, + SUBID_STATUS_UNKNOWN_USER = 1, + SUBID_STATUS_ERROR_CONN = 2, + SUBID_STATUS_ERROR = 3, +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * subid_init: initialize libsubid + * + * @progname: Name to display as program. If NULL, then "(libsubid)" will be + * shown in error messages. + * @logfd: Open file pointer to pass error messages to. If NULL, then + * /dev/null will be opened and messages will be sent there. The + * default if libsubid_init() is not called is stderr (2). + * + * This function does not need to be called. If not called, then the defaults + * will be used. + * + * Returns false if an error occurred. + */ +bool subid_init(const char *progname, FILE *logfd); + +/* + * subid_get_uid_ranges: return a list of UID ranges for a user + * + * @owner: username being queried + * @ranges: a pointer to an array of subid_range structs in which the result + * will be returned. + * + * The caller must free(ranges) when done. + * + * returns: number of ranges found, ir < 0 on error. + */ +int subid_get_uid_ranges(const char *owner, struct subid_range **ranges); + +/* + * subid_get_gid_ranges: return a list of GID ranges for a user + * + * @owner: username being queried + * @ranges: a pointer to an array of subid_range structs in which the result + * will be returned. + * + * The caller must free(ranges) when done. + * + * returns: number of ranges found, ir < 0 on error. + */ +int subid_get_gid_ranges(const char *owner, struct subid_range **ranges); + +/* + * subid_get_uid_owners: return a list of uids to which the given uid has been + * delegated. + * + * @uid: The subuid being queried + * @owners: a pointer to an array of uids into which the results are placed. + * The returned array must be freed by the caller. + * + * Returns the number of uids returned, or < 0 on error. + */ +int subid_get_uid_owners(uid_t uid, uid_t **owner); + +/* + * subid_get_gid_owners: return a list of uids to which the given gid has been + * delegated. + * + * @uid: The subgid being queried + * @owners: a pointer to an array of uids into which the results are placed. + * The returned array must be freed by the caller. + * + * Returns the number of uids returned, or < 0 on error. + */ +int subid_get_gid_owners(gid_t gid, uid_t **owner); + +/* + * subid_grant_uid_range: assign a subuid range to a user + * + * @range: pointer to a struct subordinate_range detailing the UID range + * to allocate. ->owner must be the username, and ->count must be + * filled in. ->start is ignored, and will contain the start + * of the newly allocated range, upon success. + * + * Returns true if the delegation succeeded, false otherwise. If true, + * then the range from (range->start, range->start + range->count) will + * be delegated to range->owner. + */ +bool subid_grant_uid_range(struct subordinate_range *range, bool reuse); + +/* + * subid_grant_gid_range: assign a subgid range to a user + * + * @range: pointer to a struct subordinate_range detailing the GID range + * to allocate. ->owner must be the username, and ->count must be + * filled in. ->start is ignored, and will contain the start + * of the newly allocated range, upon success. + * + * Returns true if the delegation succeeded, false otherwise. If true, + * then the range from (range->start, range->start + range->count) will + * be delegated to range->owner. + */ +bool subid_grant_gid_range(struct subordinate_range *range, bool reuse); + +/* + * subid_ungrant_uid_range: remove a subuid allocation. + * + * @range: pointer to a struct subordinate_range detailing the UID allocation + * to remove. + * + * Returns true if successful, false if it failed, for instance if the + * delegation did not exist. + */ +bool subid_ungrant_uid_range(struct subordinate_range *range); + +/* + * subid_ungrant_gid_range: remove a subgid allocation. + * + * @range: pointer to a struct subordinate_range detailing the GID allocation + * to remove. + * + * Returns true if successful, false if it failed, for instance if the + * delegation did not exist. + */ +bool subid_ungrant_gid_range(struct subordinate_range *range); + +#ifdef __cplusplus +} +#endif + +#define SUBID_NFIELDS 3 +#endif |