summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 20:46:53 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 20:46:53 +0000
commit19da58be2d9359a9641381feb559be0b918ef710 (patch)
tree109724175f07436696f51b14b5abbd3f4d704d6d /lib
parentInitial commit. (diff)
downloadshadow-19da58be2d9359a9641381feb559be0b918ef710.tar.xz
shadow-19da58be2d9359a9641381feb559be0b918ef710.zip
Adding upstream version 1:4.13+dfsg1.upstream/1%4.13+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--lib/.indent.pro5
-rw-r--r--lib/Makefile.am82
-rw-r--r--lib/Makefile.in1085
-rw-r--r--lib/commonio.c1276
-rw-r--r--lib/commonio.h144
-rw-r--r--lib/defines.h339
-rw-r--r--lib/encrypt.c79
-rw-r--r--lib/exitcodes.h26
-rw-r--r--lib/faillog.h34
-rw-r--r--lib/fields.c107
-rw-r--r--lib/fputsx.c67
-rw-r--r--lib/get_gid.c31
-rw-r--r--lib/get_pid.c31
-rw-r--r--lib/get_uid.c31
-rw-r--r--lib/getdef.c623
-rw-r--r--lib/getdef.h25
-rw-r--r--lib/getlong.c36
-rw-r--r--lib/getulong.c39
-rw-r--r--lib/groupio.c438
-rw-r--r--lib/groupio.h32
-rw-r--r--lib/groupmem.c118
-rw-r--r--lib/gshadow.c506
-rw-r--r--lib/gshadow_.h52
-rw-r--r--lib/lockpw.c85
-rw-r--r--lib/nscd.c58
-rw-r--r--lib/nscd.h13
-rw-r--r--lib/nss.c151
-rw-r--r--lib/pam_defs.h35
-rw-r--r--lib/port.c454
-rw-r--r--lib/port.h60
-rw-r--r--lib/prototypes.h501
-rw-r--r--lib/pwauth.c211
-rw-r--r--lib/pwauth.h48
-rw-r--r--lib/pwio.c207
-rw-r--r--lib/pwio.h32
-rw-r--r--lib/pwmem.c85
-rw-r--r--lib/run_part.c104
-rw-r--r--lib/run_part.h7
-rw-r--r--lib/selinux.c210
-rw-r--r--lib/semanage.c361
-rw-r--r--lib/sgetgrent.c129
-rw-r--r--lib/sgetpwent.c109
-rw-r--r--lib/sgetspent.c187
-rw-r--r--lib/sgroupio.c306
-rw-r--r--lib/sgroupio.h29
-rw-r--r--lib/shadow.c530
-rw-r--r--lib/shadowio.c247
-rw-r--r--lib/shadowio.h30
-rw-r--r--lib/shadowlog.c31
-rw-r--r--lib/shadowlog.h40
-rw-r--r--lib/shadowlog_internal.h7
-rw-r--r--lib/shadowmem.c68
-rw-r--r--lib/spawn.c63
-rw-r--r--lib/sssd.c75
-rw-r--r--lib/sssd.h17
-rw-r--r--lib/subordinateio.c1102
-rw-r--r--lib/subordinateio.h48
-rw-r--r--lib/tcbfuncs.c604
-rw-r--r--lib/tcbfuncs.h19
-rw-r--r--lib/utent.c70
-rw-r--r--libmisc/.indent.pro5
-rw-r--r--libmisc/Makefile.am80
-rw-r--r--libmisc/Makefile.in927
-rw-r--r--libmisc/addgrps.c114
-rw-r--r--libmisc/age.c178
-rw-r--r--libmisc/audit_help.c87
-rw-r--r--libmisc/basename.c27
-rw-r--r--libmisc/btrfs.c110
-rw-r--r--libmisc/chkname.c101
-rw-r--r--libmisc/chkname.h27
-rw-r--r--libmisc/chowndir.c146
-rw-r--r--libmisc/chowntty.c79
-rw-r--r--libmisc/cleanup.c121
-rw-r--r--libmisc/cleanup_group.c215
-rw-r--r--libmisc/cleanup_user.c130
-rw-r--r--libmisc/console.c108
-rw-r--r--libmisc/copydir.c968
-rw-r--r--libmisc/date_to_str.c46
-rw-r--r--libmisc/entry.c45
-rw-r--r--libmisc/env.c253
-rw-r--r--libmisc/failure.c295
-rw-r--r--libmisc/failure.h61
-rw-r--r--libmisc/find_new_gid.c483
-rw-r--r--libmisc/find_new_sub_gids.c64
-rw-r--r--libmisc/find_new_sub_uids.c64
-rw-r--r--libmisc/find_new_uid.c483
-rw-r--r--libmisc/getdate.c2540
-rw-r--r--libmisc/getdate.h16
-rw-r--r--libmisc/getdate.y954
-rw-r--r--libmisc/getgr_nam_gid.c43
-rw-r--r--libmisc/getrange.c101
-rw-r--r--libmisc/gettime.c68
-rw-r--r--libmisc/hushed.c77
-rw-r--r--libmisc/idmapping.c223
-rw-r--r--libmisc/idmapping.h23
-rw-r--r--libmisc/isexpired.c102
-rw-r--r--libmisc/limits.c589
-rw-r--r--libmisc/list.c249
-rw-r--r--libmisc/log.c93
-rw-r--r--libmisc/loginprompt.c155
-rw-r--r--libmisc/mail.c69
-rw-r--r--libmisc/motd.c58
-rw-r--r--libmisc/myname.c49
-rw-r--r--libmisc/obscure.c306
-rw-r--r--libmisc/pam_pass.c59
-rw-r--r--libmisc/pam_pass_non_interactive.c145
-rw-r--r--libmisc/prefix_flag.c340
-rw-r--r--libmisc/pwd2spwd.c67
-rw-r--r--libmisc/pwd_init.c79
-rw-r--r--libmisc/pwdcheck.c38
-rw-r--r--libmisc/remove_tree.c101
-rw-r--r--libmisc/rlogin.c175
-rw-r--r--libmisc/root_flag.c108
-rw-r--r--libmisc/salt.c566
-rw-r--r--libmisc/setugid.c125
-rw-r--r--libmisc/setupenv.c290
-rw-r--r--libmisc/shell.c80
-rw-r--r--libmisc/strtoday.c220
-rw-r--r--libmisc/sub.c55
-rw-r--r--libmisc/sulog.c83
-rw-r--r--libmisc/ttytype.c67
-rw-r--r--libmisc/tz.c57
-rw-r--r--libmisc/ulimit.c47
-rw-r--r--libmisc/user_busy.c273
-rw-r--r--libmisc/utmp.c474
-rw-r--r--libmisc/valid.c82
-rw-r--r--libmisc/xgetXXbyYY.c121
-rw-r--r--libmisc/xgetgrgid.c41
-rw-r--r--libmisc/xgetgrnam.c41
-rw-r--r--libmisc/xgetpwnam.c41
-rw-r--r--libmisc/xgetpwuid.c41
-rw-r--r--libmisc/xgetspnam.c41
-rw-r--r--libmisc/xmalloc.c46
-rw-r--r--libmisc/yesno.c56
-rw-r--r--libsubid/Makefile.am30
-rw-r--r--libsubid/Makefile.in764
-rw-r--r--libsubid/api.c108
-rw-r--r--libsubid/subid.h163
-rw-r--r--libsubid/subid.h.in163
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 &range;
+}
+
+/*
+ * 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