summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorAndrew G. Morgan <morgan@kernel.org>2000-06-20 22:10:38 +0000
committerAndrew G. Morgan <morgan@kernel.org>2000-06-20 22:10:38 +0000
commitea488580c42e8918445a945484de3c8a5addc761 (patch)
treec992f3ba699caafedfadc16af38e6359c3c24698 /modules
Initial revision
Diffstat (limited to 'modules')
-rw-r--r--modules/Makefile70
-rw-r--r--modules/README55
-rw-r--r--modules/dont_makefile19
-rwxr-xr-xmodules/download-all30
-rw-r--r--modules/pam_access/.cvsignore1
-rw-r--r--modules/pam_access/Makefile106
-rw-r--r--modules/pam_access/README40
-rw-r--r--modules/pam_access/access.conf52
-rwxr-xr-xmodules/pam_access/install_conf46
-rw-r--r--modules/pam_access/pam_access.c424
-rw-r--r--modules/pam_cracklib/.cvsignore1
-rw-r--r--modules/pam_cracklib/Makefile118
-rw-r--r--modules/pam_cracklib/README21
-rw-r--r--modules/pam_cracklib/pam_cracklib.c776
-rw-r--r--modules/pam_deny/.cvsignore1
-rw-r--r--modules/pam_deny/Makefile131
-rw-r--r--modules/pam_deny/README4
-rw-r--r--modules/pam_deny/pam_deny.c100
-rw-r--r--modules/pam_env/.cvsignore1
-rw-r--r--modules/pam_env/Makefile111
-rw-r--r--modules/pam_env/README72
-rwxr-xr-xmodules/pam_env/install_conf46
-rw-r--r--modules/pam_env/pam_env.c839
-rw-r--r--modules/pam_env/pam_env.conf-example72
-rw-r--r--modules/pam_filter/.cvsignore2
-rw-r--r--modules/pam_filter/.upperLOWER1
-rw-r--r--modules/pam_filter/Makefile140
-rw-r--r--modules/pam_filter/README94
-rw-r--r--modules/pam_filter/include/pam_filter.h32
-rw-r--r--modules/pam_filter/pam_filter.c738
-rw-r--r--modules/pam_filter/upperLOWER/.cvsignore1
-rw-r--r--modules/pam_filter/upperLOWER/Makefile64
-rw-r--r--modules/pam_filter/upperLOWER/upperLOWER.c174
-rw-r--r--modules/pam_ftp/.cvsignore1
-rw-r--r--modules/pam_ftp/Makefile88
-rw-r--r--modules/pam_ftp/README18
-rw-r--r--modules/pam_ftp/pam_ftp.c299
-rw-r--r--modules/pam_group/.cvsignore1
-rw-r--r--modules/pam_group/Makefile98
-rw-r--r--modules/pam_group/group.conf60
-rwxr-xr-xmodules/pam_group/install_conf46
-rw-r--r--modules/pam_group/pam_group.c843
-rw-r--r--modules/pam_issue/.cvsignore1
-rw-r--r--modules/pam_issue/Makefile86
-rw-r--r--modules/pam_issue/pam_issue.c265
-rw-r--r--modules/pam_lastlog/.cvsignore1
-rw-r--r--modules/pam_lastlog/Makefile112
-rw-r--r--modules/pam_lastlog/pam_lastlog.c463
-rw-r--r--modules/pam_limits/.cvsignore1
-rw-r--r--modules/pam_limits/Makefile98
-rw-r--r--modules/pam_limits/README87
-rwxr-xr-xmodules/pam_limits/install_conf46
-rw-r--r--modules/pam_limits/limits.skel42
-rw-r--r--modules/pam_limits/pam_limits.c635
-rw-r--r--modules/pam_listfile/.cvsignore1
-rw-r--r--modules/pam_listfile/Makefile84
-rw-r--r--modules/pam_listfile/README25
-rw-r--r--modules/pam_listfile/pam_listfile.c437
-rw-r--r--modules/pam_mail/.cvsignore1
-rw-r--r--modules/pam_mail/Makefile113
-rw-r--r--modules/pam_mail/README17
-rw-r--r--modules/pam_mail/pam_mail.c504
-rw-r--r--modules/pam_mkhomedir/.cvsignore1
-rw-r--r--modules/pam_mkhomedir/Makefile90
-rw-r--r--modules/pam_mkhomedir/pam_mkhomedir.c370
-rw-r--r--modules/pam_motd/.cvsignore1
-rw-r--r--modules/pam_motd/Makefile90
-rw-r--r--modules/pam_motd/pam_motd.c119
-rw-r--r--modules/pam_nologin/.cvsignore1
-rw-r--r--modules/pam_nologin/Makefile86
-rw-r--r--modules/pam_nologin/README12
-rw-r--r--modules/pam_nologin/pam_nologin.c130
-rw-r--r--modules/pam_permit/.cvsignore1
-rw-r--r--modules/pam_permit/Makefile132
-rw-r--r--modules/pam_permit/README4
-rw-r--r--modules/pam_permit/pam_permit.c128
-rw-r--r--modules/pam_pwdb/.cvsignore2
-rw-r--r--modules/pam_pwdb/BUGS14
-rw-r--r--modules/pam_pwdb/CHANGELOG10
-rw-r--r--modules/pam_pwdb/Makefile193
-rw-r--r--modules/pam_pwdb/README41
-rw-r--r--modules/pam_pwdb/TODO34
-rw-r--r--modules/pam_pwdb/bigcrypt.-c114
-rw-r--r--modules/pam_pwdb/md5.c270
-rw-r--r--modules/pam_pwdb/md5.h34
-rw-r--r--modules/pam_pwdb/md5_crypt.c153
-rw-r--r--modules/pam_pwdb/pam_pwdb.c256
-rw-r--r--modules/pam_pwdb/pam_unix_acct.-c298
-rw-r--r--modules/pam_pwdb/pam_unix_auth.-c131
-rw-r--r--modules/pam_pwdb/pam_unix_md.-c73
-rw-r--r--modules/pam_pwdb/pam_unix_passwd.-c404
-rw-r--r--modules/pam_pwdb/pam_unix_pwupd.-c260
-rw-r--r--modules/pam_pwdb/pam_unix_sess.-c118
-rw-r--r--modules/pam_pwdb/pwdb_chkpwd.c204
-rw-r--r--modules/pam_pwdb/support.-c938
-rw-r--r--modules/pam_radius/.cvsignore1
-rw-r--r--modules/pam_radius/Makefile99
-rw-r--r--modules/pam_radius/README58
-rw-r--r--modules/pam_radius/pam_radius.c193
-rw-r--r--modules/pam_radius/pam_radius.h38
-rw-r--r--modules/pam_rhosts/.cvsignore1
-rw-r--r--modules/pam_rhosts/Makefile98
-rw-r--r--modules/pam_rhosts/README57
-rw-r--r--modules/pam_rhosts/pam_rhosts_auth.c829
-rw-r--r--modules/pam_rootok/.cvsignore1
-rw-r--r--modules/pam_rootok/Makefile117
-rw-r--r--modules/pam_rootok/README18
-rw-r--r--modules/pam_rootok/pam_rootok.c109
-rw-r--r--modules/pam_securetty/.cvsignore1
-rw-r--r--modules/pam_securetty/Makefile83
-rw-r--r--modules/pam_securetty/README9
-rw-r--r--modules/pam_securetty/pam_securetty.c191
-rw-r--r--modules/pam_shells/.cvsignore1
-rw-r--r--modules/pam_shells/Makefile84
-rw-r--r--modules/pam_shells/README10
-rw-r--r--modules/pam_shells/pam_shells.c133
-rw-r--r--modules/pam_stress/.cvsignore1
-rw-r--r--modules/pam_stress/Makefile115
-rw-r--r--modules/pam_stress/README66
-rw-r--r--modules/pam_stress/pam_stress.c565
-rw-r--r--modules/pam_tally/.cvsignore2
-rw-r--r--modules/pam_tally/Makefile109
-rw-r--r--modules/pam_tally/README95
-rw-r--r--modules/pam_tally/faillog.h55
-rw-r--r--modules/pam_tally/pam_tally.c689
-rw-r--r--modules/pam_tally/pam_tally_app.c7
-rw-r--r--modules/pam_time/.cvsignore1
-rw-r--r--modules/pam_time/Makefile98
-rw-r--r--modules/pam_time/README43
-rwxr-xr-xmodules/pam_time/install_conf46
-rw-r--r--modules/pam_time/pam_time.c612
-rw-r--r--modules/pam_time/time.conf64
-rw-r--r--modules/pam_unix/.cvsignore4
-rw-r--r--modules/pam_unix/CHANGELOG55
-rw-r--r--modules/pam_unix/Makefile155
-rw-r--r--modules/pam_unix/README39
-rw-r--r--modules/pam_unix/bigcrypt.c119
-rw-r--r--modules/pam_unix/lckpwdf.-c117
-rw-r--r--modules/pam_unix/md5.c256
-rw-r--r--modules/pam_unix/md5.h35
-rw-r--r--modules/pam_unix/md5_crypt.c149
-rwxr-xr-xmodules/pam_unix/need_nsl.sh7
-rw-r--r--modules/pam_unix/pam_unix_acct.c210
-rw-r--r--modules/pam_unix/pam_unix_auth.c220
-rw-r--r--modules/pam_unix/pam_unix_passwd.c985
-rw-r--r--modules/pam_unix/pam_unix_sess.c138
-rw-r--r--modules/pam_unix/support.c821
-rw-r--r--modules/pam_unix/support.h139
-rw-r--r--modules/pam_unix/unix_chkpwd.c324
-rw-r--r--modules/pam_unix/yppasswd.h51
-rw-r--r--modules/pam_unix/yppasswd_xdr.c41
-rw-r--r--modules/pam_userdb/.cvsignore1
-rw-r--r--modules/pam_userdb/Makefile81
-rw-r--r--modules/pam_userdb/README30
-rw-r--r--modules/pam_userdb/conv.c125
-rw-r--r--modules/pam_userdb/create.pl23
-rwxr-xr-xmodules/pam_userdb/libdbfound.sh15
-rw-r--r--modules/pam_userdb/pam_userdb.c302
-rw-r--r--modules/pam_userdb/pam_userdb.h61
-rw-r--r--modules/pam_warn/.cvsignore1
-rw-r--r--modules/pam_warn/Makefile102
-rw-r--r--modules/pam_warn/README26
-rw-r--r--modules/pam_warn/pam_warn.c132
-rw-r--r--modules/pam_wheel/.cvsignore1
-rw-r--r--modules/pam_wheel/Makefile86
-rw-r--r--modules/pam_wheel/README33
-rw-r--r--modules/pam_wheel/pam_wheel.c275
-rwxr-xr-xmodules/register_static49
168 files changed, 23474 insertions, 0 deletions
diff --git a/modules/Makefile b/modules/Makefile
new file mode 100644
index 00000000..9b89ccfd
--- /dev/null
+++ b/modules/Makefile
@@ -0,0 +1,70 @@
+# $Id$
+#
+# Makefile
+#
+# This makefile controls the build process of shared and static PAM modules.
+#
+#
+
+MODDIRS=$(shell /bin/ls -d pam_*)
+
+# ////////////////////////////////////////////////////
+# // You should not modify anything below this line //
+# ////////////////////////////////////////////////////
+
+dummy:
+ @echo "*** This is not a top-level Makefile! ***"
+
+# -----------------------------------------------------------
+
+all:
+ @echo modules for $(OS) are:
+ @ls -d $(MODDIRS) 2>/dev/null ; echo :--------
+ @echo
+ifdef STATIC
+ rm -f ./_static_module_*
+endif
+ @for i in $(MODDIRS) ; do \
+ if [ -d $$i ]; then { \
+ $(MAKE) -C $$i all ; \
+ if [ $$? -ne 0 ]; then exit 1 ; fi ; \
+ } elif [ -f ./.$$i ]; then { \
+ cat ./.$$i ; \
+ } fi ; \
+ done
+
+download:
+ @./download-all
+
+install:
+ for i in $(MODDIRS) ; do \
+ if [ -d $$i ]; then { \
+ $(MAKE) -C $$i install ; \
+ if [ $$? -ne 0 ]; then exit 1 ; fi ; \
+ } fi ; \
+ done
+
+remove:
+ for i in $(MODDIRS) ; do \
+ if [ -d $$i ]; then { \
+ $(MAKE) -C $$i remove ; \
+ } fi ; \
+ done
+
+lclean:
+ rm -f _static_module_*
+
+clean: lclean
+ for i in $(MODDIRS) ; do \
+ if [ -d $$i ]; then { \
+ $(MAKE) -C $$i clean ; \
+ } fi ; \
+ done
+
+extraclean: lclean
+ for i in $(MODDIRS) ; do \
+ if [ -d $$i ]; then \
+ $(MAKE) -C $$i extraclean ; \
+ fi ; \
+ done
+
diff --git a/modules/README b/modules/README
new file mode 100644
index 00000000..86415947
--- /dev/null
+++ b/modules/README
@@ -0,0 +1,55 @@
+This directory contains the modules.
+
+If you want to reserve a module name please email <pam-list@redhat.com>
+and announce its name. Andrew Morgan, <morgan@parc.power.net>, will
+add it to the Makefile in the next release of Linux-PAM.
+
+As of Linux-PAM-0.40 modules can optionally conform to the static
+modules conventions.
+
+This file was updated for Linux-PAM-0.53.
+
+The conventions are as follows:
+
+There are only 6 functions that a module may declare as "public" they
+fall into 4 managment groups as follows:
+
+ functions Management group
+ ------------------------------------------ ----------------
+ pam_sm_authenticate, pam_sm_setcred, PAM_SM_AUTH
+ pam_sm_acct_mgmt, PAM_SM_ACCOUNT
+ pam_sm_open_session, pam_sm_close_session, PAM_SM_SESSION
+ pam_sm_chauthtok PAM_SM_PASSWORD
+
+If a module contains definitions for any of the above functions, it
+must supply definitions for all of the functions in the corresponding
+management group.
+
+The header file that defines the ANSI prototypes for these functions
+is <security/pam_modules.h> . In the case that the module wishes to
+offer the functions of a given managment group, it must #define
+PAM_SM_XXX, where XXX is one of the above four tokens. These
+definitions must occur *prior* to the
+#include <security/pam_modules.h> line.
+
+The pam_sm_... functions should be defined to be of type 'PAM_EXTERN int'.
+
+In the case that a module is being compiled with PAM_STATIC #define'd
+it should also define a globally accessible structure
+_"NAME"_modstruct containing references to each of the functions
+defined by the module. (this structure is defined in
+<security/pam_modules.h>. "NAME" is the title of the module
+(eg. "pam_deny")
+
+If a module wants to be included in the static libpam.a its Makefile
+should execute "register_static" with appropriate arguments (in this
+directory).
+
+[
+For SIMPLE working examples, see
+
+ ./modules/pam_deny/* and ./modules/pam_rootok/*
+.]
+
+Andrew Morgan
+96/11/10
diff --git a/modules/dont_makefile b/modules/dont_makefile
new file mode 100644
index 00000000..f256ce1b
--- /dev/null
+++ b/modules/dont_makefile
@@ -0,0 +1,19 @@
+#########################################################################
+# This is a makefile that does nothing. It is designed to be included
+# by module Makefile-s when they are not compatable with the local
+# system
+#########################################################################
+
+all:
+ @echo "This module will not be compiled on this system"
+
+extraclean: clean
+
+install: clean
+
+clean:
+ @echo "Nothing to do"
+
+#########################################################################
+# all over..
+#########################################################################
diff --git a/modules/download-all b/modules/download-all
new file mode 100755
index 00000000..427d0bba
--- /dev/null
+++ b/modules/download-all
@@ -0,0 +1,30 @@
+#!/bin/sh
+#
+# $Id$
+#
+cat <<EOT
+For a number of reasons it is not practical for Linux-PAM to be
+distributed with every module out there. However, this shell script
+is intended as a convenient way for users to download modules from the
+'net in some semiautomated fashion.
+
+Please feel free to send (pam-list@redhat.com) snippets of code that
+will help others to download and unpack your favorite module into the
+Linux-PAM source tree. Especially welcome are snippets of the
+following form:
+
+ncftp ftp://my.ftpsite.org/pub/fluff/pam_fluff.tar.gz
+rm -fr pam_fluff
+tar zvfx pam_fluff.tar.gz
+
+Cheers
+
+Andrew
+morgan@linux.kernel.org
+EOT
+
+# --- insert your snippets below ---
+
+# --- insert your snippets above ---
+
+exit 0
diff --git a/modules/pam_access/.cvsignore b/modules/pam_access/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_access/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_access/Makefile b/modules/pam_access/Makefile
new file mode 100644
index 00000000..3d3611c4
--- /dev/null
+++ b/modules/pam_access/Makefile
@@ -0,0 +1,106 @@
+# $Id$
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+
+TITLE=pam_access
+CONFD=$(CONFIGED)/security
+export CONFD
+CONFILE=$(CONFD)/access.conf
+export CONFILE
+
+# Convenient defaults for compiling independently of the full source
+# tree.
+ifndef FULL_LINUX_PAM_SOURCE_TREE
+export DYNAMIC=-DPAM_DYNAMIC
+export CC=gcc
+export CFLAGS=-O2 -Dlinux -DLINUX_PAM \
+ -ansi -D_POSIX_SOURCE -Wall -Wwrite-strings \
+ -Wpointer-arith -Wcast-qual -Wcast-align -Wtraditional \
+ -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Winline \
+ -Wshadow -pedantic -fPIC
+export MKDIR=mkdir -p
+export LD_D=gcc -shared -Xlinker -x
+endif
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+DEFS=-DCONFILE=\"$(CONFILE)\"
+
+CFLAGS += $(DEFS)
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+ $(MKDIR) $(FAKEROOT)$(SCONFIGED)
+ bash -f ./install_conf
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+ rm -f $(FAKEROOT)$(CONFILE)
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+ rm -f ./.ignore_age
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
diff --git a/modules/pam_access/README b/modules/pam_access/README
new file mode 100644
index 00000000..df10c269
--- /dev/null
+++ b/modules/pam_access/README
@@ -0,0 +1,40 @@
+# Description of its configuration file (/etc/security/access.conf):
+#
+# Login access control table.
+#
+# When someone logs in, the table is scanned for the first entry that
+# matches the (user, host) combination, or, in case of non-networked
+# logins, the first entry that matches the (user, tty) combination. The
+# permissions field of that table entry determines whether the login will
+# be accepted or refused.
+#
+# Format of the login access control table is three fields separated by a
+# ":" character:
+#
+# permission : users : origins
+#
+# The first field should be a "+" (access granted) or "-" (access denied)
+# character.
+#
+# The second field should be a list of one or more login names, group
+# names, or ALL (always matches). A pattern of the form user@host is
+# matched when the login name matches the "user" part, and when the
+# "host" part matches the local machine name.
+#
+# The third field should be a list of one or more tty names (for
+# non-networked logins), host names, domain names (begin with "."), host
+# addresses, internet network numbers (end with "."), ALL (always
+# matches) or LOCAL (matches any string that does not contain a "."
+# character).
+#
+# If you run NIS you can use @netgroupname in host or user patterns; this
+# even works for @usergroup@@hostgroup patterns. Weird.
+#
+# The EXCEPT operator makes it possible to write very compact rules.
+#
+# The group file is searched only when a name does not match that of the
+# logged-in user. Both the user's primary group is matched, as well as
+# groups in which users are explicitly listed.
+#
+# Alexei Nogin <alexei@nogin.dnttm.ru> 1997/06/15
+############################################################################
diff --git a/modules/pam_access/access.conf b/modules/pam_access/access.conf
new file mode 100644
index 00000000..abfefa5e
--- /dev/null
+++ b/modules/pam_access/access.conf
@@ -0,0 +1,52 @@
+# Login access control table.
+#
+# When someone logs in, the table is scanned for the first entry that
+# matches the (user, host) combination, or, in case of non-networked
+# logins, the first entry that matches the (user, tty) combination. The
+# permissions field of that table entry determines whether the login will
+# be accepted or refused.
+#
+# Format of the login access control table is three fields separated by a
+# ":" character:
+#
+# permission : users : origins
+#
+# The first field should be a "+" (access granted) or "-" (access denied)
+# character.
+#
+# The second field should be a list of one or more login names, group
+# names, or ALL (always matches). A pattern of the form user@host is
+# matched when the login name matches the "user" part, and when the
+# "host" part matches the local machine name.
+#
+# The third field should be a list of one or more tty names (for
+# non-networked logins), host names, domain names (begin with "."), host
+# addresses, internet network numbers (end with "."), ALL (always
+# matches) or LOCAL (matches any string that does not contain a "."
+# character).
+#
+# If you run NIS you can use @netgroupname in host or user patterns; this
+# even works for @usergroup@@hostgroup patterns. Weird.
+#
+# The EXCEPT operator makes it possible to write very compact rules.
+#
+# The group file is searched only when a name does not match that of the
+# logged-in user. Both the user's primary group is matched, as well as
+# groups in which users are explicitly listed.
+#
+##############################################################################
+#
+# Disallow console logins to all but a few accounts.
+#
+#-:ALL EXCEPT wheel shutdown sync:console
+#
+# Disallow non-local logins to privileged accounts (group wheel).
+#
+#-:wheel:ALL EXCEPT LOCAL .win.tue.nl
+#
+# Some accounts are not allowed to login from anywhere:
+#
+#-:wsbscaro wsbsecr wsbspac wsbsym wscosor wstaiwde:ALL
+#
+# All other accounts are allowed to login from anywhere.
+#
diff --git a/modules/pam_access/install_conf b/modules/pam_access/install_conf
new file mode 100755
index 00000000..0667b5ec
--- /dev/null
+++ b/modules/pam_access/install_conf
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+CONFILE=$FAKEROOT"$CONFILE"
+IGNORE_AGE=./.ignore_age
+CONF=./access.conf
+QUIET_INSTALL=../../.quiet_install
+MODULE=pam_access
+
+echo
+
+if [ -f "$QUIET_INSTALL" ]; then
+ if [ ! -f "$CONFILE" ]; then
+ yes="y"
+ else
+ yes="skip"
+ fi
+elif [ -f "$IGNORE_AGE" ]; then
+ echo "you don't want to be bothered with the age of your $CONFILE file"
+ yes="n"
+elif [ ! -f "$CONFILE" ] || [ "$CONF" -nt "$CONFILE" ]; then
+ if [ -f "$CONFILE" ]; then
+ echo "An older $MODULE configuration file already exists ($CONFILE)"
+ echo "Do you wish to copy the $CONF file in this distribution"
+ echo "to $CONFILE ? (y/n) [skip] "
+ read yes
+ else
+ yes="y"
+ fi
+else
+ yes="skip"
+fi
+
+if [ "$yes" = "y" ]; then
+ mkdir -p $FAKEROOT$CONFD
+ echo " copying $CONF to $CONFILE"
+ cp $CONF $CONFILE
+else
+ echo " Skipping $CONF installation"
+ if [ "$yes" = "n" ]; then
+ touch "$IGNORE_AGE"
+ fi
+fi
+
+echo
+
+exit 0
diff --git a/modules/pam_access/pam_access.c b/modules/pam_access/pam_access.c
new file mode 100644
index 00000000..12133392
--- /dev/null
+++ b/modules/pam_access/pam_access.c
@@ -0,0 +1,424 @@
+/* pam_access module */
+
+/*
+ * Written by Alexei Nogin <alexei@nogin.dnttm.ru> 1997/06/15
+ * (I took login_access from logdaemon-5.6 and converted it to PAM
+ * using parts of pam_time code.)
+ *
+ */
+
+#ifdef linux
+# define _GNU_SOURCE
+# include <features.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+/* man page says above file includes this... */
+extern int gethostname(char *name, size_t len);
+
+#include <stdarg.h>
+#include <syslog.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <grp.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/utsname.h>
+
+#ifndef BROKEN_NETWORK_MATCH
+# include <netdb.h>
+# include <sys/socket.h>
+#endif
+
+/*
+ * here, we make definitions for the externally accessible functions
+ * in this file (these definitions are required for static modules
+ * but strongly encouraged generally) they are used to instruct the
+ * modules include file to define their prototypes.
+ */
+
+#define PAM_SM_ACCOUNT
+
+#include <security/_pam_macros.h>
+#include <security/pam_modules.h>
+
+/* --- static functions for checking whether the user should be let in --- */
+
+static void _log_err(const char *format, ... )
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("pam_access", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(LOG_ERR, format, args);
+ va_end(args);
+ closelog();
+}
+
+#define PAM_ACCESS_CONFIG CONFILE
+
+int strcasecmp(const char *s1, const char *s2);
+
+/* login_access.c from logdaemon-5.6 with several changes by A.Nogin: */
+
+ /*
+ * This module implements a simple but effective form of login access
+ * control based on login names and on host (or domain) names, internet
+ * addresses (or network numbers), or on terminal line names in case of
+ * non-networked logins. Diagnostics are reported through syslog(3).
+ *
+ * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
+ */
+
+#if !defined(MAXHOSTNAMELEN) || (MAXHOSTNAMELEN < 64)
+#undef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 256
+#endif
+
+ /* Delimiters for fields and for lists of users, ttys or hosts. */
+
+static char fs[] = ":"; /* field separator */
+static char sep[] = ", \t"; /* list-element separator */
+
+ /* Constants to be used in assignments only, not in comparisons... */
+
+#define YES 1
+#define NO 0
+
+ /*
+ * A structure to bundle up all login-related information to keep the
+ * functional interfaces as generic as possible.
+ */
+struct login_info {
+ struct passwd *user;
+ char *from;
+};
+
+typedef int match_func (char *, struct login_info *);
+
+static int list_match (char *, struct login_info *,
+ match_func *);
+static int user_match (char *, struct login_info *);
+static int from_match (char *, struct login_info *);
+static int string_match (char *, char *);
+
+/* login_access - match username/group and host/tty with access control file */
+
+static int login_access(struct passwd *user, char *from)
+{
+ struct login_info item;
+ FILE *fp;
+ char line[BUFSIZ];
+ char *perm; /* becomes permission field */
+ char *users; /* becomes list of login names */
+ char *froms; /* becomes list of terminals or hosts */
+ int match = NO;
+ int end;
+ int lineno = 0; /* for diagnostics */
+
+ /*
+ * Bundle up the arguments to avoid unnecessary clumsiness lateron.
+ */
+ item.user = user;
+ item.from = from;
+
+ /*
+ * Process the table one line at a time and stop at the first match.
+ * Blank lines and lines that begin with a '#' character are ignored.
+ * Non-comment lines are broken at the ':' character. All fields are
+ * mandatory. The first field should be a "+" or "-" character. A
+ * non-existing table means no access control.
+ */
+
+ if ((fp = fopen(PAM_ACCESS_CONFIG, "r"))!=NULL) {
+ while (!match && fgets(line, sizeof(line), fp)) {
+ lineno++;
+ if (line[end = strlen(line) - 1] != '\n') {
+ _log_err("%s: line %d: missing newline or line too long",
+ PAM_ACCESS_CONFIG, lineno);
+ continue;
+ }
+ if (line[0] == '#')
+ continue; /* comment line */
+ while (end > 0 && isspace(line[end - 1]))
+ end--;
+ line[end] = 0; /* strip trailing whitespace */
+ if (line[0] == 0) /* skip blank lines */
+ continue;
+ if (!(perm = strtok(line, fs))
+ || !(users = strtok((char *) 0, fs))
+ || !(froms = strtok((char *) 0, fs))
+ || strtok((char *) 0, fs)) {
+ _log_err("%s: line %d: bad field count", PAM_ACCESS_CONFIG, lineno);
+ continue;
+ }
+ if (perm[0] != '+' && perm[0] != '-') {
+ _log_err("%s: line %d: bad first field", PAM_ACCESS_CONFIG, lineno);
+ continue;
+ }
+ match = (list_match(froms, &item, from_match)
+ && list_match(users, &item, user_match));
+ }
+ (void) fclose(fp);
+ } else if (errno != ENOENT) {
+ _log_err("cannot open %s: %m", PAM_ACCESS_CONFIG);
+ }
+ return (match == 0 || (line[0] == '+'));
+}
+
+/* list_match - match an item against a list of tokens with exceptions */
+
+static int list_match(char *list, struct login_info *item, match_func *match_fn)
+{
+ char *tok;
+ int match = NO;
+
+ /*
+ * Process tokens one at a time. We have exhausted all possible matches
+ * when we reach an "EXCEPT" token or the end of the list. If we do find
+ * a match, look for an "EXCEPT" list and recurse to determine whether
+ * the match is affected by any exceptions.
+ */
+
+ for (tok = strtok(list, sep); tok != 0; tok = strtok((char *) 0, sep)) {
+ if (strcasecmp(tok, "EXCEPT") == 0) /* EXCEPT: give up */
+ break;
+ if ((match = (*match_fn) (tok, item))) /* YES */
+ break;
+ }
+ /* Process exceptions to matches. */
+
+ if (match != NO) {
+ while ((tok = strtok((char *) 0, sep)) && strcasecmp(tok, "EXCEPT"))
+ /* VOID */ ;
+ if (tok == 0 || list_match((char *) 0, item, match_fn) == NO)
+ return (match);
+ }
+ return (NO);
+}
+
+/* myhostname - figure out local machine name */
+
+static char * myhostname(void)
+{
+ static char name[MAXHOSTNAMELEN + 1];
+
+ gethostname(name, MAXHOSTNAMELEN);
+ name[MAXHOSTNAMELEN] = 0;
+ return (name);
+}
+
+/* netgroup_match - match group against machine or user */
+
+static int netgroup_match(char *group, char *machine, char *user)
+{
+#ifdef NIS
+ static char *mydomain = 0;
+
+ if (mydomain == 0)
+ yp_get_default_domain(&mydomain);
+ return (innetgr(group, machine, user, mydomain));
+#else
+ _log_err("NIS netgroup support not configured");
+ return (NO);
+#endif
+}
+
+/* user_match - match a username against one token */
+
+static int user_match(char *tok, struct login_info *item)
+{
+ char *string = item->user->pw_name;
+ struct login_info fake_item;
+ struct group *group;
+ int i;
+ char *at;
+
+ /*
+ * If a token has the magic value "ALL" the match always succeeds.
+ * Otherwise, return YES if the token fully matches the username, if the
+ * token is a group that contains the username, or if the token is the
+ * name of the user's primary group.
+ */
+
+ if ((at = strchr(tok + 1, '@')) != 0) { /* split user@host pattern */
+ *at = 0;
+ fake_item.from = myhostname();
+ return (user_match(tok, item) && from_match(at + 1, &fake_item));
+ } else if (tok[0] == '@') { /* netgroup */
+ return (netgroup_match(tok + 1, (char *) 0, string));
+ } else if (string_match(tok, string)) { /* ALL or exact match */
+ return (YES);
+ } else if ((group = getgrnam(tok))) { /* try group membership */
+ if (item->user->pw_gid == group->gr_gid)
+ return (YES);
+ for (i = 0; group->gr_mem[i]; i++)
+ if (strcasecmp(string, group->gr_mem[i]) == 0)
+ return (YES);
+ }
+ return (NO);
+}
+
+/* from_match - match a host or tty against a list of tokens */
+
+static int from_match(char *tok, struct login_info *item)
+{
+ char *string = item->from;
+ int tok_len;
+ int str_len;
+
+ /*
+ * If a token has the magic value "ALL" the match always succeeds. Return
+ * YES if the token fully matches the string. If the token is a domain
+ * name, return YES if it matches the last fields of the string. If the
+ * token has the magic value "LOCAL", return YES if the string does not
+ * contain a "." character. If the token is a network number, return YES
+ * if it matches the head of the string.
+ */
+
+ if (tok[0] == '@') { /* netgroup */
+ return (netgroup_match(tok + 1, string, (char *) 0));
+ } else if (string_match(tok, string)) { /* ALL or exact match */
+ return (YES);
+ } else if (tok[0] == '.') { /* domain: match last fields */
+ if ((str_len = strlen(string)) > (tok_len = strlen(tok))
+ && strcasecmp(tok, string + str_len - tok_len) == 0)
+ return (YES);
+ } else if (strcasecmp(tok, "LOCAL") == 0) { /* local: no dots */
+ if (strchr(string, '.') == 0)
+ return (YES);
+#ifdef BROKEN_NETWORK_MATCH
+ } else if (tok[(tok_len = strlen(tok)) - 1] == '.' /* network */
+ && strncmp(tok, string, tok_len) == 0) {
+ return (YES);
+#else /* BROKEN_NETWORK_MATCH */
+ } else if (tok[(tok_len = strlen(tok)) - 1] == '.') {
+ /*
+ The code below does a more correct check if the address specified
+ by "string" starts from "tok".
+ 1998/01/27 Andrey V. Savochkin <saw@msu.ru>
+ */
+ struct hostent *h;
+ char hn[3+1+3+1+3+1+3+1];
+ int r;
+
+ h = gethostbyname(string);
+ if (h == NULL)
+ return (NO);
+ if (h->h_addrtype != AF_INET)
+ return (NO);
+ if (h->h_length != 4)
+ return (NO); /* only IPv4 addresses (SAW) */
+ r = snprintf(hn, sizeof(hn), "%u.%u.%u.%u",
+ (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1],
+ (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]);
+ if (r < 0 || r >= sizeof(hn))
+ return (NO);
+ if (!strncmp(tok, hn, tok_len))
+ return (YES);
+#endif /* BROKEN_NETWORK_MATCH */
+ }
+ return (NO);
+}
+
+/* string_match - match a string against one token */
+
+static int string_match(char *tok, char *string)
+{
+
+ /*
+ * If the token has the magic value "ALL" the match always succeeds.
+ * Otherwise, return YES if the token fully matches the string.
+ */
+
+ if (strcasecmp(tok, "ALL") == 0) { /* all: always matches */
+ return (YES);
+ } else if (strcasecmp(tok, string) == 0) { /* try exact match */
+ return (YES);
+ }
+ return (NO);
+}
+
+/* end of login_access.c */
+
+int strcasecmp(const char *s1, const char *s2)
+{
+ while ((toupper(*s1)==toupper(*s2)) && (*s1) && (*s2)) {s1++; s2++;}
+ return(toupper(*s1)-toupper(*s2));
+}
+
+/* --- public account management functions --- */
+
+PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ const char *user=NULL;
+ char *from=NULL;
+ struct passwd *user_pw;
+
+ /* set username */
+
+ if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS || user == NULL
+ || *user == '\0') {
+ _log_err("cannot determine the user's name");
+ return PAM_USER_UNKNOWN;
+ }
+
+ /* remote host name */
+
+ if (pam_get_item(pamh, PAM_RHOST, (const void **)&from)
+ != PAM_SUCCESS) {
+ _log_err("cannot find the remote host name");
+ return PAM_ABORT;
+ }
+
+ if (from==NULL) {
+
+ /* local login, set tty name */
+
+ if (pam_get_item(pamh, PAM_TTY, (const void **)&from) != PAM_SUCCESS
+ || from == NULL) {
+ D(("PAM_TTY not set, probing stdin"));
+ from = ttyname(STDIN_FILENO);
+ if (from == NULL) {
+ _log_err("couldn't get the tty name");
+ return PAM_ABORT;
+ }
+ if (pam_set_item(pamh, PAM_TTY, from) != PAM_SUCCESS) {
+ _log_err("couldn't set tty name");
+ return PAM_ABORT;
+ }
+ }
+ if (strncmp("/dev/",from,5) == 0) { /* strip leading /dev/ */
+ from += 5;
+ }
+
+ }
+ if ((user_pw=getpwnam(user))==NULL) return (PAM_USER_UNKNOWN);
+ if (login_access(user_pw,from)) return (PAM_SUCCESS); else {
+ _log_err("access denied for user `%s' from `%s'",user,from);
+ return (PAM_PERM_DENIED);
+ }
+}
+
+/* end of module definition */
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_access_modstruct = {
+ "pam_access",
+ NULL,
+ NULL,
+ pam_sm_acct_mgmt,
+ NULL,
+ NULL,
+ NULL
+};
+#endif
+
diff --git a/modules/pam_cracklib/.cvsignore b/modules/pam_cracklib/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_cracklib/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_cracklib/Makefile b/modules/pam_cracklib/Makefile
new file mode 100644
index 00000000..554362d4
--- /dev/null
+++ b/modules/pam_cracklib/Makefile
@@ -0,0 +1,118 @@
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# Created by Cristian Gafton <gafton@redhat.com> 1996/09/10
+#
+
+ifndef FULL_LINUX_PAM_SOURCE_TREE
+#
+# here you should make default variable defines...
+#
+MKDIR=mkdir -p
+LD_D=gcc -shared -Xlinker -x
+INSTALL=install
+SECUREDIR=/usr/lib/security
+#
+HAVE_CRACKLIB=yes
+EXTRALS=-lcrypt
+endif
+
+ifeq ($(HAVE_CRACKLIB),yes)
+
+TITLE=pam_cracklib
+CRACKLIB=-lcrack
+ifeq ($(shell if [ -f /usr/lib/cracklib_dict.hwm ]; then echo yes ; fi),yes)
+ CRACKLIB_DICTPATH=/usr/lib/cracklib_dict
+else
+ CRACKLIB_DICTPATH=/usr/share/dict/cracklib_dict
+endif
+#
+ifeq ($(shell if [ -f /usr/include/crypt.h ]; then echo yes ; fi),yes)
+ NEED_CRYPT_HEADER=-DNEED_CRYPT_HEADER
+endif
+#
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+ifdef CRACKLIB_DICTPATH
+CFLAGS+=-DCRACKLIB_DICTPATH=\"$(CRACKLIB_DICTPATH)\"
+endif
+
+dynamic/%.o : %.c
+ $(CC) $(NEED_CRYPT_HEADER) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(NEED_CRYPT_HEADER) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC) Makefile
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD) $(CRACKLIB) $(EXTRALS) $(LINKLIBS)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~ *.so
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak dynamic/* static/*
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+else
+
+include ../dont_makefile
+
+endif
diff --git a/modules/pam_cracklib/README b/modules/pam_cracklib/README
new file mode 100644
index 00000000..e4b02731
--- /dev/null
+++ b/modules/pam_cracklib/README
@@ -0,0 +1,21 @@
+
+pam_cracklib:
+ check the passwd against dictionary words.
+
+RECOGNIZED ARGUMENTS:
+ debug verbose log
+
+ type=XXX alter the message printed as a prompt to the user.
+ the message printed is in the form
+ "New XXX password: ".
+ Default XXX=UNIX
+
+ retry=N Prompt user at most N times before returning with
+ error. Default N=1.
+
+MODULE SERVICES PROVIDED:
+ passwd chauthtok
+
+AUTHOR:
+ Cristian Gafton <gafton@sorosis.ro>
+
diff --git a/modules/pam_cracklib/pam_cracklib.c b/modules/pam_cracklib/pam_cracklib.c
new file mode 100644
index 00000000..d3d74947
--- /dev/null
+++ b/modules/pam_cracklib/pam_cracklib.c
@@ -0,0 +1,776 @@
+/* pam_cracklib module */
+
+/*
+ * 0.85. added six new options to use this with long passwords.
+ * 0.8. tidied output and improved D(()) usage for debugging.
+ * 0.7. added support for more obscure checks for new passwd.
+ * 0.6. root can reset user passwd to any values (it's only warned)
+ * 0.5. supports retries - 'retry=N' argument
+ * 0.4. added argument 'type=XXX' for 'New XXX password' prompt
+ * 0.3. Added argument 'debug'
+ * 0.2. new password is feeded to cracklib for verify after typed once
+ * 0.1. First release
+ */
+
+/*
+ * Written by Cristian Gafton <gafton@redhat.com> 1996/09/10
+ * Long password support by Philip W. Dalrymple <pwd@mdtsoft.com> 1997/07/18
+ * See the end of the file for Copyright Information
+ *
+ * Modification for long password systems (>8 chars). The original
+ * module had problems when used in a md5 password system in that it
+ * allowed too short passwords but required that at least half of the
+ * bytes in the new password did not appear in the old one. this
+ * action is still the default and the changes should not break any
+ * current user. This modification adds 6 new options, one to set the
+ * number of bytes in the new password that are not in the old one,
+ * the other five to control the length checking, these are all
+ * documented (or will be before anyone else sees this code) in the PAM
+ * S.A.G. in the section on the cracklib module.
+ */
+
+#define _GNU_SOURCE
+#define _BSD_SOURCE
+
+#include <stdio.h>
+#ifdef NEED_CRYPT_HEADER
+# include <crypt.h>
+#endif
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+
+extern char *FascistCheck(char *pw, const char *dictpath);
+
+#ifndef CRACKLIB_DICTPATH
+#define CRACKLIB_DICTPATH "/usr/share/dict/cracklib_dict"
+#endif
+
+#define PROMPT1 "New %s password: "
+#define PROMPT2 "Retype new %s password: "
+#define MISTYPED_PASS "Sorry, passwords do not match"
+
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_PASSWORD
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+#ifndef LINUX_PAM
+#include <security/pam_appl.h>
+#endif /* LINUX_PAM */
+
+/* some syslogging */
+
+static void _pam_log(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("PAM-Cracklib", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+/* argument parsing */
+#define PAM_DEBUG_ARG 0x0001
+
+/* module data - AGM: */
+
+struct cracklib_options {
+ int retry_times;
+ int diff_ok;
+ int min_length;
+ int dig_credit;
+ int up_credit;
+ int low_credit;
+ int oth_credit;
+ int use_authtok;
+ char prompt_type[BUFSIZ];
+};
+
+static int _pam_parse(struct cracklib_options *opt, int argc, const char **argv)
+{
+ int ctrl=0;
+
+ /* step through arguments */
+ for (ctrl=0; argc-- > 0; ++argv) {
+ char *ep = NULL;
+
+ /* generic options */
+
+ if (!strcmp(*argv,"debug"))
+ ctrl |= PAM_DEBUG_ARG;
+ else if (!strncmp(*argv,"type=",5))
+ strcpy(opt->prompt_type, *argv+5);
+ else if (!strncmp(*argv,"retry=",6)) {
+ opt->retry_times = strtol(*argv+6,&ep,10);
+ if (!ep || (opt->retry_times < 1))
+ opt->retry_times = 1;
+ } else if (!strncmp(*argv,"difok=",6)) {
+ opt->diff_ok = strtol(*argv+6,&ep,10);
+ if (!ep || (opt->diff_ok < 0))
+ opt->diff_ok = 10;
+ } else if (!strncmp(*argv,"minlen=",7)) {
+ opt->min_length = strtol(*argv+7,&ep,10);
+ if (!ep || (opt->min_length < 5))
+ opt->min_length = 5;
+ } else if (!strncmp(*argv,"dcredit=",8)) {
+ opt->dig_credit = strtol(*argv+8,&ep,10);
+ if (!ep || (opt->dig_credit < 0))
+ opt->dig_credit = 0;
+ } else if (!strncmp(*argv,"ucredit=",8)) {
+ opt->up_credit = strtol(*argv+8,&ep,10);
+ if (!ep || (opt->up_credit < 0))
+ opt->up_credit = 0;
+ } else if (!strncmp(*argv,"lcredit=",8)) {
+ opt->low_credit = strtol(*argv+8,&ep,10);
+ if (!ep || (opt->low_credit < 0))
+ opt->low_credit = 0;
+ } else if (!strncmp(*argv,"ocredit=",8)) {
+ opt->oth_credit = strtol(*argv+8,&ep,10);
+ if (!ep || (opt->oth_credit < 0))
+ opt->oth_credit = 0;
+ } else if (!strncmp(*argv,"use_authtok",11)) {
+ opt->use_authtok = 1;
+ } else {
+ _pam_log(LOG_ERR,"pam_parse: unknown option; %s",*argv);
+ }
+ }
+
+ return ctrl;
+}
+
+/* Helper functions */
+
+/* this is a front-end for module-application conversations */
+static int converse(pam_handle_t *pamh, int ctrl, int nargs,
+ struct pam_message **message,
+ struct pam_response **response)
+{
+ int retval;
+ struct pam_conv *conv;
+
+ retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv);
+
+ if ( retval == PAM_SUCCESS ) {
+ retval = conv->conv(nargs, (const struct pam_message **)message,
+ response, conv->appdata_ptr);
+ if (retval != PAM_SUCCESS && (ctrl && PAM_DEBUG_ARG)) {
+ _pam_log(LOG_DEBUG, "conversation failure [%s]",
+ pam_strerror(pamh, retval));
+ }
+ } else {
+ _pam_log(LOG_ERR, "couldn't obtain coversation function [%s]",
+ pam_strerror(pamh, retval));
+ }
+
+ return retval; /* propagate error status */
+}
+
+static int make_remark(pam_handle_t *pamh, unsigned int ctrl,
+ int type, const char *text)
+{
+ struct pam_message *pmsg[1], msg[1];
+ struct pam_response *resp;
+ int retval;
+
+ pmsg[0] = &msg[0];
+ msg[0].msg = text;
+ msg[0].msg_style = type;
+ resp = NULL;
+
+ retval = converse(pamh, ctrl, 1, pmsg, &resp);
+ if (retval == PAM_SUCCESS)
+ _pam_drop_reply(resp, 1);
+
+ return retval;
+}
+
+/* use this to free strings. ESPECIALLY password strings */
+static char *_pam_delete(register char *xx)
+{
+ _pam_overwrite(xx);
+ free(xx);
+ return NULL;
+}
+
+/*
+ * can't be a palindrome - like `R A D A R' or `M A D A M'
+ */
+static int palindrome(const char *old, const char *new)
+{
+ int i, j;
+
+ i = strlen (new);
+
+ for (j = 0;j < i;j++)
+ if (new[i - j - 1] != new[j])
+ return 0;
+
+ return 1;
+}
+
+/*
+ * more than half of the characters are different ones.
+ * or at least diff_ok are different
+ * NOTE that the defaults are NOT the same as befor this
+ * change. as long as there are at least 10 different bytes
+ * in a new password it will now pass even if the password
+ * is longer than 20 bytes (MD5)
+ */
+
+static int similiar(struct cracklib_options *opt, const char *old, const char *new)
+{
+ int i, j;
+
+ for (i = j = 0;new[i] && old[i];i++)
+ if (strchr (new, old[i]))
+ j++;
+
+ if (j >= opt->diff_ok || i >= j * 2)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * a nice mix of characters.
+ */
+static int simple(struct cracklib_options *opt, const char *old, const char *new)
+{
+ int digits = 0;
+ int uppers = 0;
+ int lowers = 0;
+ int others = 0;
+ int size;
+ int i;
+
+ for (i = 0;new[i];i++) {
+ if (isdigit (new[i]))
+ digits++;
+ else if (isupper (new[i]))
+ uppers++;
+ else if (islower (new[i]))
+ lowers++;
+ else
+ others++;
+ }
+
+ /*
+ * The scam was this - a password of only one character type
+ * must be 8 letters long. Two types, 7, and so on.
+ * This is now changed, the base size and the credits or defaults
+ * see the docs on the module for info on these parameters, the
+ * defaults cause the effect to be the same as before the change
+ */
+
+ if (digits > opt->dig_credit)
+ digits = opt->dig_credit;
+
+ if (uppers > opt->up_credit)
+ uppers = opt->up_credit;
+
+ if (lowers > opt->low_credit)
+ lowers = opt->low_credit;
+
+ if (others > opt->oth_credit)
+ others = opt->oth_credit;
+
+ size = opt->min_length;
+ size -= digits;
+ size -= uppers;
+ size -= lowers;
+ size -= others;
+
+ if (size <= i)
+ return 0;
+
+ return 1;
+}
+
+static char * str_lower(char *string)
+{
+ char *cp;
+
+ for (cp = string; *cp; cp++)
+ *cp = tolower(*cp);
+ return string;
+}
+
+static const char * password_check(struct cracklib_options *opt, const char *old, const char *new)
+{
+ const char *msg = NULL;
+ char *oldmono, *newmono, *wrapped;
+
+ if (strcmp(new, old) == 0) {
+ msg = "is the same as the old one";
+ return msg;
+ }
+
+ newmono = str_lower(x_strdup(new));
+ oldmono = str_lower(x_strdup(old));
+ wrapped = malloc(strlen(oldmono) * 2 + 1);
+ strcpy (wrapped, oldmono);
+ strcat (wrapped, oldmono);
+
+ if (palindrome(oldmono, newmono))
+ msg = "is a palindrome";
+
+ if (!msg && strcmp(oldmono, newmono) == 0)
+ msg = "case changes only";
+
+ if (!msg && similiar(opt, oldmono, newmono))
+ msg = "is too similiar to the old one";
+
+ if (!msg && simple(opt, old, new))
+ msg = "is too simple";
+
+ if (!msg && strstr(wrapped, newmono))
+ msg = "is rotated";
+
+ memset(newmono, 0, strlen(newmono));
+ memset(oldmono, 0, strlen(oldmono));
+ memset(wrapped, 0, strlen(wrapped));
+ free(newmono);
+ free(oldmono);
+ free(wrapped);
+
+ return msg;
+}
+
+
+#define OLD_PASSWORDS_FILE "/etc/security/opasswd"
+
+static const char * check_old_password(const char *forwho, const char *newpass)
+{
+ static char buf[16384];
+ char *s_luser, *s_uid, *s_npas, *s_pas;
+ const char *msg = NULL;
+ FILE *opwfile;
+
+ opwfile = fopen(OLD_PASSWORDS_FILE, "r");
+ if (opwfile == NULL)
+ return NULL;
+
+ while (fgets(buf, 16380, opwfile)) {
+ if (!strncmp(buf, forwho, strlen(forwho))) {
+ buf[strlen(buf)-1] = '\0';
+ s_luser = strtok(buf, ":,");
+ s_uid = strtok(NULL, ":,");
+ s_npas = strtok(NULL, ":,");
+ s_pas = strtok(NULL, ":,");
+ while (s_pas != NULL) {
+ if (!strcmp(crypt(newpass, s_pas), s_pas)) {
+ msg = "has been already used";
+ break;
+ }
+ s_pas = strtok(NULL, ":,");
+ }
+ break;
+ }
+ }
+ fclose(opwfile);
+
+ return msg;
+}
+
+
+static int _pam_unix_approve_pass(pam_handle_t *pamh,
+ unsigned int ctrl,
+ struct cracklib_options *opt,
+ const char *pass_old,
+ const char *pass_new)
+{
+ const char *msg = NULL;
+ const char *user;
+ int retval;
+
+ if (pass_new == NULL || (pass_old && !strcmp(pass_old,pass_new))) {
+ if (ctrl && PAM_DEBUG_ARG)
+ _pam_log(LOG_DEBUG, "bad authentication token");
+ make_remark(pamh, ctrl, PAM_ERROR_MSG,
+ pass_new == NULL ?
+ "No password supplied":"Password unchanged" );
+ return PAM_AUTHTOK_ERR;
+ }
+
+ /*
+ * if one wanted to hardwire authentication token strength
+ * checking this would be the place
+ */
+ msg = password_check(opt, pass_old,pass_new);
+ if (!msg) {
+ retval = pam_get_item(pamh, PAM_USER, (const void **)&user);
+ if (retval != PAM_SUCCESS) {
+ if (ctrl & PAM_DEBUG_ARG) {
+ _pam_log(LOG_ERR,"Can not get username");
+ return PAM_AUTHTOK_ERR;
+ }
+ }
+ msg = check_old_password(user, pass_new);
+ }
+
+ if (msg) {
+ char remark[BUFSIZ];
+
+ memset(remark,0,sizeof(remark));
+ sprintf(remark,"BAD PASSWORD: %s",msg);
+ if (ctrl && PAM_DEBUG_ARG)
+ _pam_log(LOG_NOTICE, "new passwd fails strength check: %s",
+ msg);
+ make_remark(pamh, ctrl, PAM_ERROR_MSG, remark);
+ return PAM_AUTHTOK_ERR;
+ };
+ return PAM_SUCCESS;
+
+}
+
+/* The Main Thing (by Cristian Gafton, CEO at this module :-)
+ * (stolen from http://home.netscape.com)
+ */
+PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ unsigned int ctrl;
+ struct cracklib_options options;
+
+ options.retry_times = 1;
+ options.diff_ok = 10;
+ options.min_length = 9;
+ options.dig_credit = 1;
+ options.up_credit = 1;
+ options.low_credit = 1;
+ options.oth_credit = 1;
+ options.use_authtok = 0;
+ memset(options.prompt_type, 0, BUFSIZ);
+
+ ctrl = _pam_parse(&options, argc, argv);
+
+ D(("called."));
+ if (!options.prompt_type[0])
+ strcpy(options.prompt_type,"UNIX");
+
+ if (flags & PAM_PRELIM_CHECK) {
+ /* Check for passwd dictionary */
+ struct stat st;
+ char buf[sizeof(CRACKLIB_DICTPATH)+10];
+
+ D(("prelim check"));
+
+ memset(buf,0,sizeof(buf)); /* zero the buffer */
+ sprintf(buf,"%s.pwd",CRACKLIB_DICTPATH);
+
+ if (!stat(buf,&st) && st.st_size)
+ return PAM_SUCCESS;
+ else {
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_NOTICE,"dict path '%s'[.pwd] is invalid",
+ CRACKLIB_DICTPATH);
+ return PAM_ABORT;
+ }
+
+ /* Not reached */
+ return PAM_SERVICE_ERR;
+
+ } else if (flags & PAM_UPDATE_AUTHTOK) {
+ int retval;
+ char *token1, *token2, *oldtoken;
+ struct pam_message msg[1],*pmsg[1];
+ struct pam_response *resp;
+ const char *cracklib_dictpath = CRACKLIB_DICTPATH;
+ char prompt[BUFSIZ];
+
+ D(("do update"));
+ retval = pam_get_item(pamh, PAM_OLDAUTHTOK,
+ (const void **)&oldtoken);
+ if (retval != PAM_SUCCESS) {
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_ERR,"Can not get old passwd");
+ oldtoken=NULL;
+ retval = PAM_SUCCESS;
+ }
+
+ do {
+ /*
+ * make sure nothing inappropriate gets returned
+ */
+ token1 = token2 = NULL;
+
+ if (!options.retry_times) {
+ D(("returning %s because maxtries reached",
+ pam_strerror(pamh, retval)));
+ return retval;
+ }
+
+ /* Planned modus operandi:
+ * Get a passwd.
+ * Verify it against cracklib.
+ * If okay get it a second time.
+ * Check to be the same with the first one.
+ * set PAM_AUTHTOK and return
+ */
+
+ if (options.use_authtok == 1) {
+ const char *item = NULL;
+
+ retval = pam_get_item(pamh, PAM_AUTHTOK, (const void **) &item);
+ if (retval != PAM_SUCCESS) {
+ /* very strange. */
+ _pam_log(LOG_ALERT
+ ,"pam_get_item returned error to pam_cracklib"
+ );
+ } else if (item != NULL) { /* we have a password! */
+ token1 = x_strdup(item);
+ item = NULL;
+ } else {
+ retval = PAM_AUTHTOK_RECOVER_ERR; /* didn't work */
+ }
+
+ } else {
+ /* Prepare to ask the user for the first time */
+ memset(prompt,0,sizeof(prompt));
+ sprintf(prompt,PROMPT1,options.prompt_type);
+ pmsg[0] = &msg[0];
+ msg[0].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[0].msg = prompt;
+
+ resp = NULL;
+ retval = converse(pamh, ctrl, 1, pmsg, &resp);
+ if (resp != NULL) {
+ /* interpret the response */
+ if (retval == PAM_SUCCESS) { /* a good conversation */
+ token1 = x_strdup(resp[0].resp);
+ if (token1 == NULL) {
+ _pam_log(LOG_NOTICE,
+ "could not recover authentication token 1");
+ retval = PAM_AUTHTOK_RECOVER_ERR;
+ }
+ }
+ /*
+ * tidy up the conversation (resp_retcode) is ignored
+ */
+ _pam_drop_reply(resp, 1);
+ } else {
+ retval = (retval == PAM_SUCCESS) ?
+ PAM_AUTHTOK_RECOVER_ERR:retval ;
+ }
+ }
+
+ if (retval != PAM_SUCCESS) {
+ if (ctrl && PAM_DEBUG_ARG)
+ _pam_log(LOG_DEBUG,"unable to obtain a password");
+ continue;
+ }
+
+ D(("testing password, retval = %s", pam_strerror(pamh, retval)));
+ /* now test this passwd against cracklib */
+ {
+ char *crack_msg;
+ char remark[BUFSIZ];
+
+ bzero(remark,sizeof(remark));
+ D(("against cracklib"));
+ if ((crack_msg = FascistCheck(token1, cracklib_dictpath))) {
+ if (ctrl && PAM_DEBUG_ARG)
+ _pam_log(LOG_DEBUG,"bad password: %s",crack_msg);
+ sprintf(remark,"BAD PASSWORD: %s", crack_msg);
+ make_remark(pamh, ctrl, PAM_ERROR_MSG, remark);
+ if (getuid() || (flags & PAM_CHANGE_EXPIRED_AUTHTOK))
+ retval = PAM_AUTHTOK_ERR;
+ else
+ retval = PAM_SUCCESS;
+ } else {
+ /* check it for strength too... */
+ D(("for strength"));
+ if (oldtoken) {
+ retval = _pam_unix_approve_pass(pamh,ctrl,&options,
+ oldtoken,token1);
+ if (retval != PAM_SUCCESS) {
+ if (getuid() || (flags & PAM_CHANGE_EXPIRED_AUTHTOK))
+ retval = PAM_AUTHTOK_ERR;
+ else
+ retval = PAM_SUCCESS;
+ }
+ }
+ }
+ }
+
+ D(("after testing: retval = %s", pam_strerror(pamh, retval)));
+ /* if cracklib/strength check said it is a bad passwd... */
+ if ((retval != PAM_SUCCESS) && (retval != PAM_IGNORE)) {
+ int temp_unused;
+
+ temp_unused = pam_set_item(pamh, PAM_AUTHTOK, NULL);
+ token1 = _pam_delete(token1);
+ continue;
+ }
+
+ /* Now we have a good passwd. Ask for it once again */
+
+ if (options.use_authtok == 0) {
+ bzero(prompt,sizeof(prompt));
+ sprintf(prompt,PROMPT2,options.prompt_type);
+ pmsg[0] = &msg[0];
+ msg[0].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[0].msg = prompt;
+
+ resp = NULL;
+ retval = converse(pamh, ctrl, 1, pmsg, &resp);
+ if (resp != NULL) {
+ /* interpret the response */
+ if (retval == PAM_SUCCESS) { /* a good conversation */
+ token2 = x_strdup(resp[0].resp);
+ if (token2 == NULL) {
+ _pam_log(LOG_NOTICE,
+ "could not recover authentication token 2");
+ retval = PAM_AUTHTOK_RECOVER_ERR;
+ }
+ }
+ /*
+ * tidy up the conversation (resp_retcode) is ignored
+ */
+ _pam_drop_reply(resp, 1);
+ } else {
+ retval = (retval == PAM_SUCCESS) ?
+ PAM_AUTHTOK_RECOVER_ERR:retval ;
+ }
+
+ if (retval != PAM_SUCCESS) {
+ if (ctrl && PAM_DEBUG_ARG)
+ _pam_log(LOG_DEBUG
+ ,"unable to obtain the password a second time");
+ continue;
+ }
+
+ /* Hopefully now token1 and token2 the same password ... */
+ if (strcmp(token1,token2) != 0) {
+ /* tell the user */
+ make_remark(pamh, ctrl, PAM_ERROR_MSG, MISTYPED_PASS);
+ token1 = _pam_delete(token1);
+ token2 = _pam_delete(token2);
+ pam_set_item(pamh, PAM_AUTHTOK, NULL);
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_NOTICE,"Password mistyped");
+ retval = PAM_AUTHTOK_RECOVER_ERR;
+ continue;
+ }
+
+ /* Yes, the password was typed correct twice
+ * we store this password as an item
+ */
+
+ {
+ const char *item = NULL;
+
+ retval = pam_set_item(pamh, PAM_AUTHTOK, token1);
+
+ /* clean up */
+ token1 = _pam_delete(token1);
+ token2 = _pam_delete(token2);
+
+ if ( (retval != PAM_SUCCESS) ||
+ ((retval = pam_get_item(pamh, PAM_AUTHTOK,
+ (const void **)&item)
+ ) != PAM_SUCCESS) ) {
+ _pam_log(LOG_CRIT, "error manipulating password");
+ continue;
+ }
+ item = NULL; /* break link to password */
+ return PAM_SUCCESS;
+ }
+ }
+
+ } while (options.retry_times--);
+
+ } else {
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_NOTICE, "UNKNOWN flags setting %02X",flags);
+ return PAM_SERVICE_ERR;
+ }
+
+ /* Not reached */
+ return PAM_SERVICE_ERR;
+}
+
+
+
+#ifdef PAM_STATIC
+/* static module data */
+struct pam_module _pam_cracklib_modstruct = {
+ "pam_cracklib",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ pam_sm_chauthtok
+};
+#endif
+
+/*
+ * Copyright (c) Cristian Gafton <gafton@redhat.com>, 1996.
+ * 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, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 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 author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED `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 AUTHOR 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.
+ *
+ * The following copyright was appended for the long password support
+ * added with the libpam 0.58 release:
+ *
+ * Modificaton Copyright (c) Philip W. Dalrymple III <pwd@mdtsoft.com>
+ * 1997. All rights reserved
+ *
+ * THE MODIFICATION THAT PROVIDES SUPPORT FOR LONG PASSWORD TYPE CHECKING TO
+ * THIS SOFTWARE IS PROVIDED `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 AUTHOR 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.
+ */
diff --git a/modules/pam_deny/.cvsignore b/modules/pam_deny/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_deny/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_deny/Makefile b/modules/pam_deny/Makefile
new file mode 100644
index 00000000..e6fa2e60
--- /dev/null
+++ b/modules/pam_deny/Makefile
@@ -0,0 +1,131 @@
+#
+# $Id$
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# $Log$
+# Revision 1.1 2000/06/20 22:11:33 agmorgan
+# Initial revision
+#
+# Revision 1.1.1.1 1998/07/12 05:17:16 morgan
+# Linux PAM sources pre-0.66
+#
+# Revision 1.7 1997/04/05 06:43:41 morgan
+# full-source-tree and fakeroot
+#
+# Revision 1.6 1997/02/15 19:04:27 morgan
+# fixed email
+#
+# Revision 1.5 1996/11/10 20:11:48 morgan
+# crossplatform support
+#
+# Revision 1.4 1996/09/05 06:50:12 morgan
+# ld --> gcc
+#
+# Revision 1.3 1996/05/26 15:48:38 morgan
+# make dynamic and static dirs
+#
+# Revision 1.2 1996/05/26 04:00:16 morgan
+# changes for automated static/dynamic modules
+#
+# Revision 1.1 1996/03/16 17:47:36 morgan
+# Initial revision
+#
+#
+# Created by Andrew Morgan <morgan@parc.power.net> 1996/3/11
+#
+
+# Convenient defaults for compiling independently of the full source
+# tree.
+ifndef FULL_LINUX_PAM_SOURCE_TREE
+export DYNAMIC=-DPAM_DYNAMIC
+export CC=gcc
+export CFLAGS=-O2 -Dlinux -DLINUX_PAM \
+ -ansi -D_POSIX_SOURCE -Wall -Wwrite-strings \
+ -Wpointer-arith -Wcast-qual -Wcast-align -Wtraditional \
+ -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Winline \
+ -Wshadow -pedantic -fPIC
+export MKDIR=mkdir -p
+export LD_D=gcc -shared -Xlinker -x
+endif
+
+#
+
+TITLE=pam_deny
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_deny/README b/modules/pam_deny/README
new file mode 100644
index 00000000..6683bdcc
--- /dev/null
+++ b/modules/pam_deny/README
@@ -0,0 +1,4 @@
+# $Id$
+#
+
+this module always fails, it ignores all options.
diff --git a/modules/pam_deny/pam_deny.c b/modules/pam_deny/pam_deny.c
new file mode 100644
index 00000000..123cb21b
--- /dev/null
+++ b/modules/pam_deny/pam_deny.c
@@ -0,0 +1,100 @@
+/* pam_permit module */
+
+/*
+ * $Id$
+ *
+ * Written by Andrew Morgan <morgan@parc.power.net> 1996/3/11
+ *
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:33 agmorgan
+ * Initial revision
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:16 morgan
+ * Linux PAM sources pre-0.66
+ *
+ * Revision 1.4 1997/02/15 19:05:15 morgan
+ * fixed email
+ *
+ * Revision 1.3 1996/06/02 08:06:19 morgan
+ * changes for new static protocol
+ *
+ * Revision 1.2 1996/05/26 04:01:12 morgan
+ * added static support
+ *
+ * Revision 1.1 1996/03/16 17:47:36 morgan
+ * Initial revision
+ *
+ */
+
+/*
+ * here, we make definitions for the externally accessible functions
+ * in this file (these definitions are required for static modules
+ * but strongly encouraged generally) they are used to instruct the
+ * modules include file to define their prototypes.
+ */
+
+#define PAM_SM_AUTH
+#define PAM_SM_ACCOUNT
+#define PAM_SM_SESSION
+#define PAM_SM_PASSWORD
+
+#include <security/pam_modules.h>
+
+/* --- authentication management functions --- */
+
+PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_AUTH_ERR;
+}
+
+PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_CRED_UNAVAIL;
+}
+
+/* --- account management functions --- */
+
+PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_ACCT_EXPIRED;
+}
+
+/* --- password management --- */
+
+PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_AUTHTOK_ERR;
+}
+
+/* --- session management --- */
+
+PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_SYSTEM_ERR;
+}
+
+PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_SYSTEM_ERR;
+}
+
+/* end of module definition */
+
+/* static module data */
+#ifdef PAM_STATIC
+struct pam_module _pam_deny_modstruct = {
+ "pam_deny",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ pam_sm_acct_mgmt,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ pam_sm_chauthtok
+};
+#endif
diff --git a/modules/pam_env/.cvsignore b/modules/pam_env/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_env/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_env/Makefile b/modules/pam_env/Makefile
new file mode 100644
index 00000000..e872d1ee
--- /dev/null
+++ b/modules/pam_env/Makefile
@@ -0,0 +1,111 @@
+#
+# $Id$
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# $Log$
+# Revision 1.1 2000/06/20 22:11:33 agmorgan
+# Initial revision
+#
+# Revision 1.2 1999/10/09 05:08:28 morgan
+# removed libpwdb dependencies (libpwdb support is depreciated)
+#
+# Revision 1.1.1.1 1998/07/12 05:17:16 morgan
+# Linux PAM sources pre-0.66
+#
+# Revision 1.1 1997/04/05 06:42:35 morgan
+# Initial revision
+#
+# Revision 1.1 1997/01/04 20:32:52 morgan
+# Initial revision
+#
+# Created by Andrew Morgan <morgan@parc.power.net> 1996/12/8
+# Adaptations by Dave Kinclea and Cristian Gafton
+#
+
+TITLE=pam_env
+
+CONFD=$(CONFIGED)/security
+export CONFD
+CONFILE=$(CONFD)/pam_env.conf
+export CONFILE
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+####################### don't edit below #######################
+
+dummy:
+
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD) $(EXTRALIB)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS) $(EXTRALIB)
+endif
+
+install: all
+ifdef DYNAMIC
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+ $(MKDIR) $(FAKEROOT)$(SCONFIGED)
+ bash -f ./install_conf
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak dynamic/* static/*
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_env/README b/modules/pam_env/README
new file mode 100644
index 00000000..04df323b
--- /dev/null
+++ b/modules/pam_env/README
@@ -0,0 +1,72 @@
+# $Date$
+# $Author$
+# $Id$
+#
+# This is the configuration file for pam_env, a PAM module to load in
+# a configurable list of environment variables for a
+#
+# The original idea for this came from Andrew G. Morgan ...
+#<quote>
+# Mmm. Perhaps you might like to write a pam_env module that reads a
+# default environment from a file? I can see that as REALLY
+# useful... Note it would be an "auth" module that returns PAM_IGNORE
+# for the auth part and sets the environment returning PAM_SUCCESS in
+# the setcred function...
+#</quote>
+#
+# What I wanted was the REMOTEHOST variable set, purely for selfish
+# reasons, and AGM didn't want it added to the SimpleApps login
+# program (which is where I added the patch). So, my first concern is
+# that variable, from there there are numerous others that might/would
+# be useful to be set: NNTPSERVER, LESS, PATH, PAGER, MANPAGER .....
+#
+# Of course, these are a different kind of variable than REMOTEHOST in
+# that they are things that are likely to be configured by
+# administrators rather than set by logging in, how to treat them both
+# in the same config file?
+#
+# Here is my idea:
+#
+# Each line starts with the variable name, there are then two possible
+# options for each variable DEFAULT and OVERRIDE.
+# DEFAULT allows and administrator to set the value of the
+# variable to some default value, if none is supplied then the empty
+# string is assumed. The OVERRIDE option tells pam_env that it should
+# enter in its value (overriding the default value) if there is one
+# to use. OVERRIDE is not used, "" is assumed and no override will be
+# done.
+#
+# VARIABLE [DEFAULT=[value]] [OVERRIDE=[value]]
+#
+# (Possibly non-existent) environment variables may be used in values
+# using the ${string} syntax and (possibly non-existent) PAM_ITEMs may
+# be used in values using the @{string} syntax. Both the $ and @
+# characters can be backslash escaped to be used as literal values
+# values can be delimited with "", escaped " not supported.
+#
+#
+# First, some special variables
+#
+# Set the REMOTEHOST variable for any hosts that are remote, default
+# to "localhost" rather than not being set at all
+REMOTEHOST DEFAULT=localhost OVERRIDE=@{PAM_RHOST}
+#
+# Set the DISPLAY variable if it seems reasonable
+DISPLAY DEFAULT=${REMOTEHOST}:0.0 OVERRIDE=${DISPLAY}
+#
+#
+# Now some simple variables
+#
+PAGER DEFAULT=less
+MANPAGER DEFAULT=less
+LESS DEFAULT="M q e h15 z23 b80"
+NNTPSERVER DEFAULT=localhost
+PATH DEFAULT=${HOME}/bin:/usr/local/bin:/bin\
+:/usr/bin:/usr/local/bin/X11:/usr/bin/X11
+#
+# silly examples of escaped variables, just to show how they work.
+#
+DOLLAR DEFAULT=\$
+DOLLARDOLLAR DEFAULT= OVERRIDE=\$${DOLLAR}
+DOLLARPLUS DEFAULT=\${REMOTEHOST}${REMOTEHOST}
+ATSIGN DEFAULT="" OVERRIDE=\@
diff --git a/modules/pam_env/install_conf b/modules/pam_env/install_conf
new file mode 100755
index 00000000..4c608400
--- /dev/null
+++ b/modules/pam_env/install_conf
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+CONFILE=$FAKEROOT"$CONFILE"
+IGNORE_AGE=./.ignore_age
+QUIET_INSTALL=../../.quiet_install
+CONF=./pam_env.conf-example
+MODULE=pam_env
+
+echo
+
+if [ -f "$QUIET_INSTALL" ]; then
+ if [ ! -f "$CONFILE" ]; then
+ yes="y"
+ else
+ yes="skip"
+ fi
+elif [ -f "$IGNORE_AGE" ]; then
+ echo "you don't want to be bothered with the age of your $CONFILE file"
+ yes="n"
+elif [ ! -f "$CONFILE" ] || [ "$CONF" -nt "$CONFILE" ]; then
+ if [ -f "$CONFILE" ]; then
+ echo "An older $MODULE configuration file already exists ($CONFILE)"
+ echo "Do you wish to copy the $CONF file in this distribution"
+ echo "to $CONFILE ? (y/n) [skip] "
+ read yes
+ else
+ yes="y"
+ fi
+else
+ yes="skip"
+fi
+
+if [ "$yes" = "y" ]; then
+ mkdir -p $FAKEROOT$CONFD
+ echo " copying $CONF to $CONFILE"
+ cp $CONF $CONFILE
+else
+ echo " Skipping $CONF installation"
+ if [ "$yes" = "n" ]; then
+ touch "$IGNORE_AGE"
+ fi
+fi
+
+echo
+
+exit 0
diff --git a/modules/pam_env/pam_env.c b/modules/pam_env/pam_env.c
new file mode 100644
index 00000000..5ba89268
--- /dev/null
+++ b/modules/pam_env/pam_env.c
@@ -0,0 +1,839 @@
+/* pam_mail module */
+
+/*
+ * $Id$
+ *
+ * Written by Dave Kinchlea <kinch@kinch.ark.com> 1997/01/31
+ * Inspired by Andrew Morgan <morgan@parc.power.net, who also supplied the
+ * template for this file (via pam_mail)
+ */
+
+#ifndef DEFAULT_CONF_FILE
+#define DEFAULT_CONF_FILE "/etc/security/pam_env.conf"
+#endif
+
+#define DEFAULT_ETC_ENVFILE "/etc/environment"
+#define DEFAULT_READ_ENVFILE 1
+
+#define _GNU_SOURCE
+#define _BSD_SOURCE
+
+#include <features.h>
+#include <ctype.h>
+#include <errno.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_AUTH /* This is primarily a AUTH_SETCRED module */
+#define PAM_SM_SESSION /* But I like to be friendly */
+#define PAM_SM_PASSWORD /* "" */
+#define PAM_SM_ACCOUNT /* "" */
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+/* This little structure makes it easier to keep variables together */
+
+typedef struct var {
+ char *name;
+ char *value;
+ char *defval;
+ char *override;
+} VAR;
+
+#define BUF_SIZE 1024
+#define MAX_ENV 8192
+
+#define GOOD_LINE 0
+#define BAD_LINE 100 /* This must be > the largest PAM_* error code */
+
+#define DEFINE_VAR 101
+#define UNDEFINE_VAR 102
+#define ILLEGAL_VAR 103
+
+static int _assemble_line(FILE *, char *, int);
+static int _parse_line(char *, VAR *);
+static int _check_var(pam_handle_t *, VAR *); /* This is the real meat */
+static void _clean_var(VAR *);
+static int _expand_arg(pam_handle_t *, char **);
+static const char * _pam_get_item_byname(pam_handle_t *, const char *);
+static int _define_var(pam_handle_t *, VAR *);
+static int _undefine_var(pam_handle_t *, VAR *);
+
+/* This is a flag used to designate an empty string */
+static char quote='Z';
+
+/* some syslogging */
+
+static void _log_err(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("PAM-env", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+/* argument parsing */
+
+#define PAM_DEBUG_ARG 0x01
+#define PAM_NEW_CONF_FILE 0x02
+#define PAM_ENV_SILENT 0x04
+#define PAM_NEW_ENV_FILE 0x10
+
+static int _pam_parse(int flags, int argc, const char **argv, char **conffile,
+ char **envfile, int *readenv)
+{
+ int ctrl=0;
+
+
+ /* step through arguments */
+ for (; argc-- > 0; ++argv) {
+
+ /* generic options */
+
+ if (!strcmp(*argv,"debug"))
+ ctrl |= PAM_DEBUG_ARG;
+ else if (!strncmp(*argv,"conffile=",9)) {
+ *conffile = x_strdup(9+*argv);
+ if (*conffile != NULL) {
+ D(("new Configuration File: %s", *conffile));
+ ctrl |= PAM_NEW_CONF_FILE;
+ } else {
+ _log_err(LOG_CRIT,
+ "Configuration file specification missing argument - ignored");
+ }
+ } else if (!strncmp(*argv,"envfile=",8)) {
+ *envfile = x_strdup(8+*argv);
+ if (*envfile != NULL) {
+ D(("new Env File: %s", *envfile));
+ ctrl |= PAM_NEW_ENV_FILE;
+ } else {
+ _log_err(LOG_CRIT,
+ "Env file specification missing argument - ignored");
+ }
+ } else if (!strncmp(*argv,"readenv=",8))
+ *readenv = atoi(8+*argv);
+ else
+ _log_err(LOG_ERR,"pam_parse: unknown option; %s",*argv);
+ }
+
+ return ctrl;
+}
+
+static int _parse_config_file(pam_handle_t *pamh, int ctrl, char **conffile)
+{
+ int retval;
+ const char *file;
+ char buffer[BUF_SIZE];
+ FILE *conf;
+ VAR Var, *var=&Var;
+
+ var->name=NULL; var->defval=NULL; var->override=NULL;
+ D(("Called."));
+
+ if (ctrl & PAM_NEW_CONF_FILE) {
+ file = *conffile;
+ } else {
+ file = DEFAULT_CONF_FILE;
+ }
+
+ D(("Config file name is: %s", file));
+
+ /*
+ * Lets try to open the config file, parse it and process
+ * any variables found.
+ */
+
+ if ((conf = fopen(file,"r")) == NULL) {
+ _log_err(LOG_ERR, "Unable to open config file: %s",
+ strerror(errno));
+ return PAM_IGNORE;
+ }
+
+ /* _pam_assemble_line will provide a complete line from the config file, with all
+ * comments removed and any escaped newlines fixed up
+ */
+
+ while (( retval = _assemble_line(conf, buffer, BUF_SIZE)) > 0) {
+ D(("Read line: %s", buffer));
+
+ if ((retval = _parse_line(buffer, var)) == GOOD_LINE) {
+ retval = _check_var(pamh, var);
+
+ if (DEFINE_VAR == retval) {
+ retval = _define_var(pamh, var);
+
+ } else if (UNDEFINE_VAR == retval) {
+ retval = _undefine_var(pamh, var);
+ }
+ }
+ if (PAM_SUCCESS != retval && ILLEGAL_VAR != retval
+ && BAD_LINE != retval && PAM_BAD_ITEM != retval) break;
+
+ _clean_var(var);
+
+ } /* while */
+
+ (void) fclose(conf);
+
+ /* tidy up */
+ _clean_var(var); /* We could have got here prematurely, this is safe though */
+ _pam_overwrite(*conffile);
+ _pam_drop(*conffile);
+ file = NULL;
+ D(("Exit."));
+ return (retval<0?PAM_ABORT:PAM_SUCCESS);
+}
+
+static int _parse_env_file(pam_handle_t *pamh, int ctrl, char **env_file)
+{
+ int retval=PAM_SUCCESS, i, t;
+ const char *file;
+ char buffer[BUF_SIZE], *key, *mark;
+ FILE *conf;
+
+ if (ctrl & PAM_NEW_ENV_FILE)
+ file = *env_file;
+ else
+ file = DEFAULT_ETC_ENVFILE;
+
+ D(("Env file name is: %s", file));
+
+ if ((conf = fopen(file,"r")) == NULL) {
+ D(("Unable to open env file: %s", strerror(errno)));
+ return PAM_ABORT;
+ }
+
+ while (_assemble_line(conf, buffer, BUF_SIZE) > 0) {
+ D(("Read line: %s", buffer));
+ key = buffer;
+
+ /* skip leading white space */
+ key += strspn(key, " \n\t");
+
+ /* skip blanks lines and comments */
+ if (!key || key[0] == '#')
+ continue;
+
+ /* skip over "export " if present so we can be compat with
+ bash type declerations */
+ if (strncmp(key, "export ", (size_t) 7) == 0)
+ key += 7;
+
+ /* now find the end of value */
+ mark = key;
+ while(mark[0] != '\n' && mark[0] != '#' && mark[0] != '\0')
+ mark++;
+ if (mark[0] != '\0')
+ mark[0] = '\0';
+
+ /*
+ * sanity check, the key must be alpha-numeric
+ */
+
+ for ( i = 0 ; key[i] != '=' && key[i] != '\0' ; i++ )
+ if (!isalnum(key[i]) && key[i] != '_') {
+ D(("key is not alpha numeric - '%s', ignoring", key));
+ continue;
+ }
+
+ /* now we try to be smart about quotes around the value,
+ but not too smart, we can't get all fancy with escaped
+ values like bash */
+ if (key[i] == '=' && (key[++i] == '\"' || key[i] == '\'')) {
+ for ( t = i+1 ; key[t] != '\0' ; t++)
+ if (key[t] != '\"' && key[t] != '\'')
+ key[i++] = key[t];
+ else if (key[t+1] != '\0')
+ key[i++] = key[t];
+ key[i] = '\0';
+ }
+
+ /* set the env var, if it fails, we break out of the loop */
+ retval = pam_putenv(pamh, key);
+ if (retval != PAM_SUCCESS) {
+ D(("error setting env \"%s\"", key));
+ break;
+ }
+ }
+
+ (void) fclose(conf);
+
+ /* tidy up */
+ _pam_overwrite(*env_file);
+ _pam_drop(*env_file);
+ file = NULL;
+ D(("Exit."));
+ return (retval<0?PAM_IGNORE:PAM_SUCCESS);
+}
+
+/*
+ * This is where we read a line of the PAM config file. The line may be
+ * preceeded by lines of comments and also extended with "\\\n"
+ */
+
+static int _assemble_line(FILE *f, char *buffer, int buf_len)
+{
+ char *p = buffer;
+ char *s, *os;
+ int used = 0;
+
+ /* loop broken with a 'break' when a non-'\\n' ended line is read */
+
+ D(("called."));
+ for (;;) {
+ if (used >= buf_len) {
+ /* Overflow */
+ D(("_assemble_line: overflow"));
+ return -1;
+ }
+ if (fgets(p, buf_len - used, f) == NULL) {
+ if (used) {
+ /* Incomplete read */
+ return -1;
+ } else {
+ /* EOF */
+ return 0;
+ }
+ }
+
+ /* skip leading spaces --- line may be blank */
+
+ s = p + strspn(p, " \n\t");
+ if (*s && (*s != '#')) {
+ os = s;
+
+ /*
+ * we are only interested in characters before the first '#'
+ * character
+ */
+
+ while (*s && *s != '#')
+ ++s;
+ if (*s == '#') {
+ *s = '\0';
+ used += strlen(os);
+ break; /* the line has been read */
+ }
+
+ s = os;
+
+ /*
+ * Check for backslash by scanning back from the end of
+ * the entered line, the '\n' has been included since
+ * normally a line is terminated with this
+ * character. fgets() should only return one though!
+ */
+
+ s += strlen(s);
+ while (s > os && ((*--s == ' ') || (*s == '\t')
+ || (*s == '\n')));
+
+ /* check if it ends with a backslash */
+ if (*s == '\\') {
+ *s = '\0'; /* truncate the line here */
+ used += strlen(os);
+ p = s; /* there is more ... */
+ } else {
+ /* End of the line! */
+ used += strlen(os);
+ break; /* this is the complete line */
+ }
+
+ } else {
+ /* Nothing in this line */
+ /* Don't move p */
+ }
+ }
+
+ return used;
+}
+
+static int _parse_line(char *buffer, VAR *var)
+{
+ /*
+ * parse buffer into var, legal syntax is
+ * VARIABLE [DEFAULT=[[string]] [OVERRIDE=[value]]
+ *
+ * Any other options defined make this a bad line,
+ * error logged and no var set
+ */
+
+ int length, quoteflg=0;
+ char *ptr, **valptr, *tmpptr;
+
+ D(("Called buffer = <%s>", buffer));
+
+ length = strcspn(buffer," \t\n");
+
+ if ((var->name = malloc(length + 1)) == NULL) {
+ _log_err(LOG_ERR, "Couldn't malloc %d bytes", length+1);
+ return PAM_BUF_ERR;
+ }
+
+ /*
+ * The first thing on the line HAS to be the variable name,
+ * it may be the only thing though.
+ */
+ strncpy(var->name, buffer, length);
+ var->name[length] = '\0';
+ D(("var->name = <%s>, length = %d", var->name, length));
+
+ /*
+ * Now we check for arguments, we only support two kinds and ('cause I am lazy)
+ * each one can actually be listed any number of times
+ */
+
+ ptr = buffer+length;
+ while ((length = strspn(ptr, " \t")) > 0) {
+ ptr += length; /* remove leading whitespace */
+ D((ptr));
+ if (strncmp(ptr,"DEFAULT=",8) == 0) {
+ ptr+=8;
+ D(("Default arg found: <%s>", ptr));
+ valptr=&(var->defval);
+ } else if (strncmp(ptr, "OVERRIDE=", 9) == 0) {
+ ptr+=9;
+ D(("Override arg found: <%s>", ptr));
+ valptr=&(var->override);
+ } else {
+ D(("Unrecognized options: <%s> - ignoring line", ptr));
+ _log_err(LOG_ERR, "Unrecognized Option: %s - ignoring line", ptr);
+ return BAD_LINE;
+ }
+
+ if ('"' != *ptr) { /* Escaped quotes not supported */
+ length = strcspn(ptr, " \t\n");
+ tmpptr = ptr+length;
+ } else {
+ tmpptr = strchr(++ptr, '"');
+ if (!tmpptr) {
+ D(("Unterminated quoted string: %s", ptr-1));
+ _log_err(LOG_ERR, "Unterminated quoted string: %s", ptr-1);
+ return BAD_LINE;
+ }
+ length = tmpptr - ptr;
+ if (*++tmpptr && ' ' != *tmpptr && '\t' != *tmpptr && '\n' != *tmpptr) {
+ D(("Quotes must cover the entire string: <%s>", ptr));
+ _log_err(LOG_ERR, "Quotes must cover the entire string: <%s>", ptr);
+ return BAD_LINE;
+ }
+ quoteflg++;
+ }
+ if (length) {
+ if ((*valptr = malloc(length + 1)) == NULL) {
+ D(("Couldn't malloc %d bytes", length+1));
+ _log_err(LOG_ERR, "Couldn't malloc %d bytes", length+1);
+ return PAM_BUF_ERR;
+ }
+ (void)strncpy(*valptr,ptr,length);
+ (*valptr)[length]='\0';
+ } else if (quoteflg--) {
+ *valptr = &quote; /* a quick hack to handle the empty string */
+ }
+ ptr = tmpptr; /* Start the search where we stopped */
+ } /* while */
+
+ /*
+ * The line is parsed, all is well.
+ */
+
+ D(("Exit."));
+ ptr = NULL; tmpptr = NULL; valptr = NULL;
+ return GOOD_LINE;
+}
+
+static int _check_var(pam_handle_t *pamh, VAR *var)
+{
+ /*
+ * Examine the variable and determine what action to take.
+ * Returns DEFINE_VAR, UNDEFINE_VAR depending on action to take
+ * or a PAM_* error code if passed back from other routines
+ *
+ * if no DEFAULT provided, the empty string is assumed
+ * if no OVERRIDE provided, the empty string is assumed
+ * if DEFAULT= and OVERRIDE evaluates to the empty string,
+ * this variable should be undefined
+ * if DEFAULT="" and OVERRIDE evaluates to the empty string,
+ * this variable should be defined with no value
+ * if OVERRIDE=value and value turns into the empty string, DEFAULT is used
+ *
+ * If DEFINE_VAR is to be returned, the correct value to define will
+ * be pointed to by var->value
+ */
+
+ int retval;
+
+ D(("Called."));
+
+ /*
+ * First thing to do is to expand any arguments, but only
+ * if they are not the special quote values (cause expand_arg
+ * changes memory).
+ */
+
+ if (var->defval && (&quote != var->defval) &&
+ ((retval = _expand_arg(pamh, &(var->defval))) != PAM_SUCCESS)) {
+ return retval;
+ }
+ if (var->override && (&quote != var->override) &&
+ ((retval = _expand_arg(pamh, &(var->override))) != PAM_SUCCESS)) {
+ return retval;
+ }
+
+ /* Now its easy */
+
+ if (var->override && *(var->override) && &quote != var->override) {
+ /* if there is a non-empty string in var->override, we use it */
+ D(("OVERRIDE variable <%s> being used: <%s>", var->name, var->override));
+ var->value = var->override;
+ retval = DEFINE_VAR;
+ } else {
+
+ var->value = var->defval;
+ if (&quote == var->defval) {
+ /*
+ * This means that the empty string was given for defval value
+ * which indicates that a variable should be defined with no value
+ */
+ *var->defval = '\0';
+ D(("An empty variable: <%s>", var->name));
+ retval = DEFINE_VAR;
+ } else if (var->defval) {
+ D(("DEFAULT variable <%s> being used: <%s>", var->name, var->defval));
+ retval = DEFINE_VAR;
+ } else {
+ D(("UNDEFINE variable <%s>", var->name));
+ retval = UNDEFINE_VAR;
+ }
+ }
+
+ D(("Exit."));
+ return retval;
+}
+
+static int _expand_arg(pam_handle_t *pamh, char **value)
+{
+ const char *orig=*value, *tmpptr=NULL;
+ char *ptr; /*
+ * Sure would be nice to use tmpptr but it needs to be
+ * a constant so that the compiler will shut up when I
+ * call pam_getenv and _pam_get_item_byname -- sigh
+ */
+
+ char type, tmpval[BUF_SIZE]; /* No unexpanded variable can be bigger than BUF_SIZE */
+ char tmp[MAX_ENV]; /* I know this shouldn't be hard-coded but it's so
+ * much easier this way */
+
+ D(("Remember to initialize tmp!"));
+ tmp[0] = '\0';
+
+ /*
+ * (possibly non-existent) environment variables can be used as values
+ * by prepending a "$" and wrapping in {} (ie: ${HOST}), can escape with "\"
+ * (possibly non-existent) PAM items can be used as values
+ * by prepending a "@" and wrapping in {} (ie: @{PAM_RHOST}, can escape
+ *
+ */
+ D(("Expanding <%s>",orig));
+ while (*orig) { /* while there is some input to deal with */
+ if ('\\' == *orig) {
+ ++orig;
+ if ('$' != *orig && '@' != *orig) {
+ D(("Unrecognized escaped character: <%c> - ignoring", *orig));
+ _log_err(LOG_ERR, "Unrecognized escaped character: <%c> - ignoring",
+ *orig);
+ } else if ((strlen(tmp) + 1) < MAX_ENV) {
+ tmp[strlen(tmp)] = *orig++; /* Note the increment */
+ } else {
+ /* is it really a good idea to try to log this? */
+ D(("Variable buffer overflow: <%s> + <%s>", tmp, tmpptr));
+ _log_err(LOG_ERR, "Variable buffer overflow: <%s> + <%s>", tmp, tmpptr);
+ }
+ continue;
+ }
+ if ('$' == *orig || '@' == *orig) {
+ if ('{' != *(orig+1)) {
+ D(("Expandable variables must be wrapped in {} <%s> - ignoring", orig));
+ _log_err(LOG_ERR, "Expandable variables must be wrapped in {} <%s> - ignoring",
+ orig);
+ if ((strlen(tmp) + 1) < MAX_ENV) {
+ tmp[strlen(tmp)] = *orig++; /* Note the increment */
+ }
+ continue;
+ } else {
+ D(("Expandable argument: <%s>", orig));
+ type = *orig;
+ orig+=2; /* skip the ${ or @{ characters */
+ ptr = strchr(orig, '}');
+ if (ptr) {
+ *ptr++ = '\0';
+ } else {
+ D(("Unterminated expandable variable: <%s>", orig-2));
+ _log_err(LOG_ERR, "Unterminated expandable variable: <%s>", orig-2);
+ return PAM_ABORT;
+ }
+ strncpy(tmpval, orig, (size_t) BUF_SIZE);
+ orig=ptr;
+ /*
+ * so, we know we need to expand tmpval, it is either
+ * an environment variable or a PAM_ITEM. type will tell us which
+ */
+ switch (type) {
+
+ case '$':
+ D(("Expanding env var: <%s>",tmpval));
+ tmpptr = pam_getenv(pamh, tmpval);
+ D(("Expanded to <%s>", tmpptr));
+ break;
+
+ case '@':
+ D(("Expanding pam item: <%s>",tmpval));
+ tmpptr = _pam_get_item_byname(pamh, tmpval);
+ D(("Expanded to <%s>", tmpptr));
+ break;
+
+ default:
+ D(("Impossible error, type == <%c>", type));
+ _log_err(LOG_CRIT, "Impossible error, type == <%c>", type);
+ return PAM_ABORT;
+ } /* switch */
+
+ if (tmpptr) {
+ if ((strlen(tmp) + strlen(tmpptr)) < MAX_ENV) {
+ strcat(tmp, tmpptr);
+ } else {
+ /* is it really a good idea to try to log this? */
+ D(("Variable buffer overflow: <%s> + <%s>", tmp, tmpptr));
+ _log_err(LOG_ERR, "Variable buffer overflow: <%s> + <%s>", tmp, tmpptr);
+ }
+ }
+ } /* if ('{' != *orig++) */
+ } else { /* if ( '$' == *orig || '@' == *orig) */
+ if ((strlen(tmp) + 1) < MAX_ENV) {
+ tmp[strlen(tmp)] = *orig++; /* Note the increment */
+ } else {
+ /* is it really a good idea to try to log this? */
+ D(("Variable buffer overflow: <%s> + <%s>", tmp, tmpptr));
+ _log_err(LOG_ERR, "Variable buffer overflow: <%s> + <%s>", tmp, tmpptr);
+ }
+ }
+ } /* for (;*orig;) */
+
+ if (strlen(tmp) > strlen(*value)) {
+ free(*value);
+ if ((*value = malloc(strlen(tmp) +1)) == NULL) {
+ D(("Couldn't malloc %d bytes for expanded var", strlen(tmp)+1));
+ _log_err(LOG_ERR,"Couldn't malloc %d bytes for expanded var",
+ strlen(tmp)+1);
+ return PAM_BUF_ERR;
+ }
+ }
+ strcpy(*value, tmp);
+ memset(tmp,'\0',sizeof(tmp));
+ D(("Exit."));
+
+ return PAM_SUCCESS;
+}
+
+static const char * _pam_get_item_byname(pam_handle_t *pamh, const char *name)
+{
+ /*
+ * This function just allows me to use names as given in the config
+ * file and translate them into the appropriate PAM_ITEM macro
+ */
+
+ int item;
+ const char *itemval;
+
+ D(("Called."));
+ if (strcmp(name, "PAM_USER") == 0) {
+ item = PAM_USER;
+ } else if (strcmp(name, "PAM_USER_PROMPT") == 0) {
+ item = PAM_USER_PROMPT;
+ } else if (strcmp(name, "PAM_TTY") == 0) {
+ item = PAM_TTY;
+ } else if (strcmp(name, "PAM_RUSER") == 0) {
+ item = PAM_RUSER;
+ } else if (strcmp(name, "PAM_RHOST") == 0) {
+ item = PAM_RHOST;
+ } else {
+ D(("Unknown PAM_ITEM: <%s>", name));
+ _log_err(LOG_ERR, "Unknown PAM_ITEM: <%s>", name);
+ return NULL;
+ }
+
+ if (pam_get_item(pamh, item, (const void **)&itemval) != PAM_SUCCESS) {
+ D(("pam_get_item failed"));
+ return NULL; /* let pam_get_item() log the error */
+ }
+ D(("Exit."));
+ return itemval;
+}
+
+static int _define_var(pam_handle_t *pamh, VAR *var)
+{
+ /* We have a variable to define, this is a simple function */
+
+ char *envvar;
+ int size, retval=PAM_SUCCESS;
+
+ D(("Called."));
+ size = strlen(var->name)+strlen(var->value)+2;
+ if ((envvar = malloc(size)) == NULL) {
+ D(("Malloc fail, size = %d", size));
+ _log_err(LOG_ERR, "Malloc fail, size = %d", size);
+ return PAM_BUF_ERR;
+ }
+ (void) sprintf(envvar,"%s=%s",var->name,var->value);
+ retval = pam_putenv(pamh, envvar);
+ free(envvar); envvar=NULL;
+ D(("Exit."));
+ return retval;
+}
+
+static int _undefine_var(pam_handle_t *pamh, VAR *var)
+{
+ /* We have a variable to undefine, this is a simple function */
+
+ D(("Called and exit."));
+ return pam_putenv(pamh, var->name);
+}
+
+static void _clean_var(VAR *var)
+{
+ if (var->name) {
+ free(var->name);
+ }
+ if (var->defval && (&quote != var->defval)) {
+ free(var->defval);
+ }
+ if (var->override && (&quote != var->override)) {
+ free(var->override);
+ }
+ var->name = NULL;
+ var->value = NULL; /* never has memory specific to it */
+ var->defval = NULL;
+ var->override = NULL;
+ return;
+}
+
+
+
+/* --- authentication management functions (only) --- */
+
+PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc,
+ const char **argv)
+{
+ return PAM_IGNORE;
+}
+
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc,
+ const char **argv)
+{
+ int retval, ctrl, readenv=DEFAULT_READ_ENVFILE;
+ char *conf_file=NULL, *env_file=NULL;
+
+ /*
+ * this module sets environment variables read in from a file
+ */
+
+ D(("Called."));
+ ctrl = _pam_parse(flags, argc, argv, &conf_file, &env_file, &readenv);
+
+ retval = _parse_config_file(pamh, ctrl, &conf_file);
+
+ if(readenv)
+ _parse_env_file(pamh, ctrl, &env_file);
+
+ /* indicate success or failure */
+
+ D(("Exit."));
+ return retval;
+}
+
+PAM_EXTERN
+int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc,
+ const char **argv)
+{
+ _log_err(LOG_NOTICE, "pam_sm_acct_mgmt called inappropriatly");
+ return PAM_SERVICE_ERR;
+}
+
+PAM_EXTERN
+int pam_sm_open_session(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ int retval, ctrl, readenv=DEFAULT_READ_ENVFILE;
+ char *conf_file=NULL, *env_file=NULL;
+
+ /*
+ * this module sets environment variables read in from a file
+ */
+
+ D(("Called."));
+ ctrl = _pam_parse(flags, argc, argv, &conf_file, &env_file, &readenv);
+
+ retval = _parse_config_file(pamh, ctrl, &conf_file);
+
+ if(readenv)
+ _parse_env_file(pamh, ctrl, &env_file);
+
+ /* indicate success or failure */
+
+ D(("Exit."));
+ return retval;
+}
+
+PAM_EXTERN
+int pam_sm_close_session(pam_handle_t *pamh,int flags,int argc,
+ const char **argv)
+{
+ D(("Called and Exit"));
+ return PAM_SUCCESS;
+}
+
+PAM_EXTERN
+int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc,
+ const char **argv)
+{
+ _log_err(LOG_NOTICE, "pam_sm_chauthtok called inappropriatly");
+ return PAM_SERVICE_ERR;
+}
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_env_modstruct = {
+ "pam_env",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ pam_sm_acct_mgmt,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ pam_sm_chauthtok,
+};
+
+#endif
+
+/* end of module definition */
diff --git a/modules/pam_env/pam_env.conf-example b/modules/pam_env/pam_env.conf-example
new file mode 100644
index 00000000..c2c4333f
--- /dev/null
+++ b/modules/pam_env/pam_env.conf-example
@@ -0,0 +1,72 @@
+# $Date$
+# $Author$
+# $Id$
+#
+# This is the configuration file for pam_env, a PAM module to load in
+# a configurable list of environment variables for a
+#
+# The original idea for this came from Andrew G. Morgan ...
+#<quote>
+# Mmm. Perhaps you might like to write a pam_env module that reads a
+# default environment from a file? I can see that as REALLY
+# useful... Note it would be an "auth" module that returns PAM_IGNORE
+# for the auth part and sets the environment returning PAM_SUCCESS in
+# the setcred function...
+#</quote>
+#
+# What I wanted was the REMOTEHOST variable set, purely for selfish
+# reasons, and AGM didn't want it added to the SimpleApps login
+# program (which is where I added the patch). So, my first concern is
+# that variable, from there there are numerous others that might/would
+# be useful to be set: NNTPSERVER, LESS, PATH, PAGER, MANPAGER .....
+#
+# Of course, these are a different kind of variable than REMOTEHOST in
+# that they are things that are likely to be configured by
+# administrators rather than set by logging in, how to treat them both
+# in the same config file?
+#
+# Here is my idea:
+#
+# Each line starts with the variable name, there are then two possible
+# options for each variable DEFAULT and OVERRIDE.
+# DEFAULT allows and administrator to set the value of the
+# variable to some default value, if none is supplied then the empty
+# string is assumed. The OVERRIDE option tells pam_env that it should
+# enter in its value (overriding the default value) if there is one
+# to use. OVERRIDE is not used, "" is assumed and no override will be
+# done.
+#
+# VARIABLE [DEFAULT=[value]] [OVERRIDE=[value]]
+#
+# (Possibly non-existent) environment variables may be used in values
+# using the ${string} syntax and (possibly non-existent) PAM_ITEMs may
+# be used in values using the @{string} syntax. Both the $ and @
+# characters can be backslash escaped to be used as literal values
+# values can be delimited with "", escaped " not supported.
+#
+#
+# First, some special variables
+#
+# Set the REMOTEHOST variable for any hosts that are remote, default
+# to "localhost" rather than not being set at all
+#REMOTEHOST DEFAULT=localhost OVERRIDE=@{PAM_RHOST}
+#
+# Set the DISPLAY variable if it seems reasonable
+#DISPLAY DEFAULT=${REMOTEHOST}:0.0 OVERRIDE=${DISPLAY}
+#
+#
+# Now some simple variables
+#
+#PAGER DEFAULT=less
+#MANPAGER DEFAULT=less
+#LESS DEFAULT="M q e h15 z23 b80"
+#NNTPSERVER DEFAULT=localhost
+#PATH DEFAULT=${HOME}/bin:/usr/local/bin:/bin\
+#:/usr/bin:/usr/local/bin/X11:/usr/bin/X11
+#
+# silly examples of escaped variables, just to show how they work.
+#
+#DOLLAR DEFAULT=\$
+#DOLLARDOLLAR DEFAULT= OVERRIDE=\$${DOLLAR}
+#DOLLARPLUS DEFAULT=\${REMOTEHOST}${REMOTEHOST}
+#ATSIGN DEFAULT="" OVERRIDE=\@
diff --git a/modules/pam_filter/.cvsignore b/modules/pam_filter/.cvsignore
new file mode 100644
index 00000000..877dafe0
--- /dev/null
+++ b/modules/pam_filter/.cvsignore
@@ -0,0 +1,2 @@
+dynamic
+security
diff --git a/modules/pam_filter/.upperLOWER b/modules/pam_filter/.upperLOWER
new file mode 100644
index 00000000..2531b468
--- /dev/null
+++ b/modules/pam_filter/.upperLOWER
@@ -0,0 +1 @@
+a test filter that transposes upper and lower case characters
diff --git a/modules/pam_filter/Makefile b/modules/pam_filter/Makefile
new file mode 100644
index 00000000..f4db56a9
--- /dev/null
+++ b/modules/pam_filter/Makefile
@@ -0,0 +1,140 @@
+#
+# $Id$
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# Created by Andrew Morgan <morgan@linux.kernel.org> 1996/3/11
+#
+
+ifeq ($(OS),solaris)
+
+include ../dont_makefile
+
+else
+
+TITLE=pam_filter
+FILTERS=upperLOWER
+FILTERSDIR=$(SUPLEMENTED)/pam_filter
+export FILTERSDIR
+
+CFLAGS += -I. -I..
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+#
+# this is where we compile this module
+#
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register filters
+
+dirs:
+ if [ ! -f security ]; then ln -sf include security ; fi
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+filters:
+ @for i in $(FILTERS) ; do \
+ if [ -d $$i ]; then \
+ $(MAKE) -C $$i all ; \
+ fi ; \
+ done
+
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+endif
+
+ifdef DYNAMIC
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+endif
+
+ifdef STATIC
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ @for i in $(FILTERS) ; do \
+ if [ -d $$i ]; then \
+ $(MAKE) -C $$i install ; \
+ fi ; \
+ done
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+ $(MKDIR) $(FAKEROOT)$(INCLUDED)
+ $(INSTALL) -m 644 include/pam_filter.h $(FAKEROOT)$(INCLUDED)
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+ rm -f $(FAKEROOT)$(INCLUDED)/pam_filter.h
+ @for i in $(FILTERS) ; do \
+ if [ -d $$i ]; then \
+ $(MAKE) -C $$i remove ; \
+ fi ; \
+ done
+
+lclean:
+ rm -f $(LIBSHARED) $(LIBOBJD) $(LIBOBJS) core *~
+
+clean: lclean
+ @for i in $(FILTERS) ; do \
+ if [ -d $$i ]; then \
+ $(MAKE) -C $$i clean ; \
+ fi ; \
+ done
+
+extraclean: lclean
+ @rm -f security
+ @rm -f *.a *.o *.so *.bak
+ for i in $(FILTERS) ; do \
+ if [ -d $$i ]; then \
+ $(MAKE) -C $$i extraclean ; \
+ fi ; \
+ done
+
+security:
+ ln -s include security
+
+endif
diff --git a/modules/pam_filter/README b/modules/pam_filter/README
new file mode 100644
index 00000000..850f1145
--- /dev/null
+++ b/modules/pam_filter/README
@@ -0,0 +1,94 @@
+#
+# $Id$
+#
+# This describes the behavior of this module with respect to the
+# /etc/pam.conf file.
+#
+# writen by Andrew Morgan <morgan@parc.power.net>
+#
+
+This module is intended to be a platform for providing access to all
+of the input/output that passes between the user and the application.
+It is only suitable for tty-based and (stdin/stdout) applications. And
+is only known to work on Linux based systems.
+
+The action of the module is dictated by the arguments it is given in
+the pam.conf file.
+
+recognized flags are:
+
+ debug print some information to syslog(3)
+
+ new_term set the PAM_TTY item to the new filtered
+ terminal (the default is to set it
+ to be that of the users terminal)
+
+ non_term don't try to set the PAM_TTY item
+
+ run1/run2 these arguments indicate that the
+ module should separate the application
+ from the user and insert a filter
+ program between them. The pathname of
+ the filter program follows the 'runN'
+ argument. Arguments that follow this
+ pathname are passed as arguments to
+ the filter program.
+
+ The distinction between run1 and run2
+ is which of the two functions of
+ the given management-type triggers the
+ execution of the indicated filter.
+
+ type: run1 run2
+ ----- ---- ----
+
+ auth pam_sm_authenticate pam_sm_setcred
+
+ account [ pam_sm_acct_mgmt (either is good) ]
+
+ session pam_sm_open_session pam_sm_close_session
+
+ password pam_sm_chauthtok/PRELIM pam_sm_chauthtok/UPDATE
+
+Note, in the case of 'password' PRELIM/UPDATE indicates which of the
+two calls to pam_sm_chauthtok from libpam (not the application) will
+trigger the filter.
+
+What a filter program should expect:
+------------------------------------
+
+Definitions for filter programs (which may be locally designed) are
+contained in the <security/pam_filter.h> file.
+
+Arguments are not passed to the filter on the command line, since this
+is plainly visible when a user types 'ps -a'. Instead they are passed
+as the filter's environment. Other information is passed in this way
+too.
+
+Here is a list of the environment variables that a filter should
+expect:
+
+ ARGS="filter_path_name argument list"
+ SERVICE="service_name" (as it appears in /etc/pam.conf)
+ USER="username"
+ TYPE="module_fn" (the name of the function in pam_filter.so
+ that invoked the filter)
+
+[This list is likely to grow. If you want something added, email me!]
+
+Among other things this module is intended to provide a useful means
+of logging the activity of users in as discrete a manner as possible.
+
+Existing filters:
+-----------------
+
+Currently, there is a single supplied filter (upperLOWER). The effect
+of using this filter is to transpose upper and lower case letters
+between the user and the application. This is really annoying when you
+try the 'xsh' example application! ;)
+
+TODO: provide more filters...
+ Decide if providing stderr interception is really overkill.
+
+Andrew G. Morgan <morgan@parc.power.net> 1996/5/27
+
diff --git a/modules/pam_filter/include/pam_filter.h b/modules/pam_filter/include/pam_filter.h
new file mode 100644
index 00000000..630198ee
--- /dev/null
+++ b/modules/pam_filter/include/pam_filter.h
@@ -0,0 +1,32 @@
+/*
+ * $Id$
+ *
+ * this file is associated with the Linux-PAM filter module.
+ * it was written by Andrew G. Morgan <morgan@linux.kernel.org>
+ *
+ */
+
+#ifndef PAM_FILTER_H
+#define PAM_FILTER_H
+
+#include <sys/file.h>
+
+/*
+ * this will fail if there is some problem with these file descriptors
+ * being allocated by the pam_filter Linux-PAM module. The numbers
+ * here are thought safe, but the filter developer should use the
+ * macros, as these numbers are subject to change.
+ *
+ * The APPXXX_FILENO file descriptors are the STDIN/OUT/ERR_FILENO of the
+ * application. The filter uses the STDIN/OUT/ERR_FILENO's to converse
+ * with the user, passes (modified) user input to the application via
+ * APPIN_FILENO, and receives application output from APPOUT_FILENO/ERR.
+ */
+
+#define APPIN_FILENO 3 /* write here to give application input */
+#define APPOUT_FILENO 4 /* read here to get application output */
+#define APPERR_FILENO 5 /* read here to get application errors */
+
+#define APPTOP_FILE 6 /* used by select */
+
+#endif
diff --git a/modules/pam_filter/pam_filter.c b/modules/pam_filter/pam_filter.c
new file mode 100644
index 00000000..a1ba10b0
--- /dev/null
+++ b/modules/pam_filter/pam_filter.c
@@ -0,0 +1,738 @@
+/*
+ * $Id$
+ *
+ * written by Andrew Morgan <morgan@transmeta.com> with much help from
+ * Richard Stevens' UNIX Network Programming book.
+ */
+
+#ifdef linux
+# define _GNU_SOURCE
+# include <features.h>
+#endif
+
+#include <stdlib.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <termio.h>
+
+#include <signal.h>
+
+#define PAM_SM_AUTH
+#define PAM_SM_ACCOUNT
+#define PAM_SM_SESSION
+#define PAM_SM_PASSWORD
+
+#include <security/pam_modules.h>
+#include <security/pam_filter.h>
+
+/* ------ some tokens used for convenience throughout this file ------- */
+
+#define FILTER_DEBUG 01
+#define FILTER_RUN1 02
+#define FILTER_RUN2 04
+#define NEW_TERM 010
+#define NON_TERM 020
+
+/* -------------------------------------------------------------------- */
+
+/* log errors */
+
+#include <stdarg.h>
+
+static void _pam_log(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("pam_filter", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+#define TERMINAL_LEN 12
+
+static int master(char *terminal)
+/*
+ * try to open all of the terminals in sequence return first free one,
+ * or -1
+ */
+{
+ const char ptys[] = "pqrs", *pty = ptys;
+ const char hexs[] = "0123456789abcdef", *hex;
+ struct stat tstat;
+ int fd;
+
+ strcpy(terminal, "/dev/pty??");
+
+ while (*pty) { /* step through four types */
+ terminal[8] = *pty++;
+ terminal[9] = '0';
+ if (stat(terminal,&tstat) < 0) {
+ _pam_log(LOG_WARNING, "unknown pseudo terminal; %s", terminal);
+ break;
+ }
+ for (hex = hexs; *hex; ) { /* step through 16 of these */
+ terminal[9] = *hex++;
+ if ((fd = open(terminal, O_RDWR)) >= 0) {
+ return fd;
+ }
+ }
+ }
+
+ /* no terminal found */
+
+ return -1;
+}
+
+static int process_args(pam_handle_t *pamh
+ , int argc, const char **argv, const char *type
+ , char ***evp, const char **filtername)
+{
+ int ctrl=0;
+
+ while (argc-- > 0) {
+ if (strcmp("debug",*argv) == 0) {
+ ctrl |= FILTER_DEBUG;
+ } else if (strcmp("new_term",*argv) == 0) {
+ ctrl |= NEW_TERM;
+ } else if (strcmp("non_term",*argv) == 0) {
+ ctrl |= NON_TERM;
+ } else if (strcmp("run1",*argv) == 0) {
+ ctrl |= FILTER_RUN1;
+ if (argc <= 0) {
+ _pam_log(LOG_ALERT,"no run filter supplied");
+ } else
+ break;
+ } else if (strcmp("run2",*argv) == 0) {
+ ctrl |= FILTER_RUN2;
+ if (argc <= 0) {
+ _pam_log(LOG_ALERT,"no run filter supplied");
+ } else
+ break;
+ } else {
+ _pam_log(LOG_ERR, "unrecognized option: %s (ignored)", *argv);
+ }
+ ++argv; /* step along list */
+ }
+
+ if (argc < 0) {
+ /* there was no reference to a filter */
+ *filtername = NULL;
+ *evp = NULL;
+ } else {
+ char **levp;
+ const char *tmp;
+ int i,size;
+
+ *filtername = *++argv;
+ if (ctrl & FILTER_DEBUG) {
+ _pam_log(LOG_DEBUG,"will run filter %s\n", *filtername);
+ }
+
+ levp = (char **) malloc(5*sizeof(char *));
+ if (levp == NULL) {
+ _pam_log(LOG_CRIT,"no memory for environment of filter");
+ return -1;
+ }
+
+ for (size=i=0; i<argc; ++i) {
+ size += strlen(argv[i])+1;
+ }
+
+ /* the "ARGS" variable */
+
+#define ARGS_OFFSET 5 /* sizeof("ARGS="); */
+#define ARGS_NAME "ARGS="
+
+ size += ARGS_OFFSET;
+
+ levp[0] = (char *) malloc(size);
+ if (levp[0] == NULL) {
+ _pam_log(LOG_CRIT,"no memory for filter arguments");
+ if (levp) {
+ free(levp);
+ }
+ return -1;
+ }
+
+ strncpy(levp[0],ARGS_NAME,ARGS_OFFSET);
+ for (i=0,size=ARGS_OFFSET; i<argc; ++i) {
+ strcpy(levp[0]+size, argv[i]);
+ size += strlen(argv[i]);
+ levp[0][size++] = ' ';
+ }
+ levp[0][--size] = '\0'; /* <NUL> terminate */
+
+ /* the "SERVICE" variable */
+
+#define SERVICE_OFFSET 8 /* sizeof("SERVICE="); */
+#define SERVICE_NAME "SERVICE="
+
+ pam_get_item(pamh, PAM_SERVICE, (const void **)&tmp);
+ size = SERVICE_OFFSET+strlen(tmp);
+
+ levp[1] = (char *) malloc(size+1);
+ if (levp[1] == NULL) {
+ _pam_log(LOG_CRIT,"no memory for service name");
+ if (levp) {
+ free(levp[0]);
+ free(levp);
+ }
+ return -1;
+ }
+
+ strncpy(levp[1],SERVICE_NAME,SERVICE_OFFSET);
+ strcpy(levp[1]+SERVICE_OFFSET, tmp);
+ levp[1][size] = '\0'; /* <NUL> terminate */
+
+ /* the "USER" variable */
+
+#define USER_OFFSET 5 /* sizeof("USER="); */
+#define USER_NAME "USER="
+
+ pam_get_user(pamh, &tmp, NULL);
+ if (tmp == NULL) {
+ tmp = "<unknown>";
+ }
+ size = USER_OFFSET+strlen(tmp);
+
+ levp[2] = (char *) malloc(size+1);
+ if (levp[2] == NULL) {
+ _pam_log(LOG_CRIT,"no memory for user's name");
+ if (levp) {
+ free(levp[1]);
+ free(levp[0]);
+ free(levp);
+ }
+ return -1;
+ }
+
+ strncpy(levp[2],USER_NAME,USER_OFFSET);
+ strcpy(levp[2]+USER_OFFSET, tmp);
+ levp[2][size] = '\0'; /* <NUL> terminate */
+
+ /* the "USER" variable */
+
+#define TYPE_OFFSET 5 /* sizeof("TYPE="); */
+#define TYPE_NAME "TYPE="
+
+ size = TYPE_OFFSET+strlen(type);
+
+ levp[3] = (char *) malloc(size+1);
+ if (levp[3] == NULL) {
+ _pam_log(LOG_CRIT,"no memory for type");
+ if (levp) {
+ free(levp[2]);
+ free(levp[1]);
+ free(levp[0]);
+ free(levp);
+ }
+ return -1;
+ }
+
+ strncpy(levp[3],TYPE_NAME,TYPE_OFFSET);
+ strcpy(levp[3]+TYPE_OFFSET, type);
+ levp[3][size] = '\0'; /* <NUL> terminate */
+
+ levp[4] = NULL; /* end list */
+
+ *evp = levp;
+ }
+
+ if ((ctrl & FILTER_DEBUG) && *filtername) {
+ char **e;
+
+ _pam_log(LOG_DEBUG,"filter[%s]: %s",type,*filtername);
+ _pam_log(LOG_DEBUG,"environment:");
+ for (e=*evp; e && *e; ++e) {
+ _pam_log(LOG_DEBUG," %s",*e);
+ }
+ }
+
+ return ctrl;
+}
+
+static void free_evp(char *evp[])
+{
+ int i;
+
+ if (evp)
+ for (i=0; i<4; ++i) {
+ if (evp[i])
+ free(evp[i]);
+ }
+ free(evp);
+}
+
+static int set_filter(pam_handle_t *pamh, int flags, int ctrl
+ , const char **evp, const char *filtername)
+{
+ int status=-1;
+ char terminal[TERMINAL_LEN];
+ struct termio stored_mode; /* initial terminal mode settings */
+ int fd[2], child=0, child2=0, aterminal;
+
+ if (filtername == NULL || *filtername != '/') {
+ _pam_log(LOG_ALERT, "filtername not permitted; require full path");
+ return PAM_ABORT;
+ }
+
+ if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) {
+ aterminal = 0;
+ } else {
+ aterminal = 1;
+ }
+
+ if (aterminal) {
+
+ /* open the master pseudo terminal */
+
+ fd[0] = master(terminal);
+ if (fd[0] < 0) {
+ _pam_log(LOG_CRIT,"no master terminal");
+ return PAM_AUTH_ERR;
+ }
+
+ /* set terminal into raw mode.. remember old mode so that we can
+ revert to it after the child has quit. */
+
+ /* this is termio terminal handling... */
+
+ if (ioctl(STDIN_FILENO, TCGETA, (char *) &stored_mode ) < 0) {
+ /* in trouble, so close down */
+ close(fd[0]);
+ _pam_log(LOG_CRIT, "couldn't copy terminal mode");
+ return PAM_ABORT;
+ } else {
+ struct termio t_mode = stored_mode;
+
+ t_mode.c_iflag = 0; /* no input control */
+ t_mode.c_oflag &= ~OPOST; /* no ouput post processing */
+
+ /* no signals, canonical input, echoing, upper/lower output */
+ t_mode.c_lflag &= ~(ISIG|ICANON|ECHO|XCASE);
+ t_mode.c_cflag &= ~(CSIZE|PARENB); /* no parity */
+ t_mode.c_cflag |= CS8; /* 8 bit chars */
+
+ t_mode.c_cc[VMIN] = 1; /* number of chars to satisfy a read */
+ t_mode.c_cc[VTIME] = 0; /* 0/10th second for chars */
+
+ if (ioctl(STDIN_FILENO, TCSETA, (char *) &t_mode) < 0) {
+ close(fd[0]);
+ _pam_log(LOG_WARNING, "couldn't put terminal in RAW mode");
+ return PAM_ABORT;
+ }
+
+ /*
+ * NOTE: Unlike the stream socket case here the child
+ * opens the slave terminal as fd[1] *after* the fork...
+ */
+ }
+ } else {
+
+ /*
+ * not a terminal line so just open a stream socket fd[0-1]
+ * both set...
+ */
+
+ if ( socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0 ) {
+ _pam_log(LOG_CRIT,"couldn't open a stream pipe");
+ return PAM_ABORT;
+ }
+ }
+
+ /* start child process */
+
+ if ( (child = fork()) < 0 ) {
+
+ _pam_log(LOG_WARNING,"first fork failed");
+ if (aterminal) {
+ (void) ioctl(STDIN_FILENO, TCSETA, (char *) &stored_mode);
+ }
+
+ return PAM_AUTH_ERR;
+ }
+
+ if ( child == 0 ) { /* child process *is* application */
+
+ if (aterminal) {
+
+ /* close the controlling tty */
+
+#if defined(__hpux) && defined(O_NOCTTY)
+ int t = open("/dev/tty", O_RDWR|O_NOCTTY);
+#else
+ int t = open("/dev/tty",O_RDWR);
+ if (t > 0) {
+ (void) ioctl(t, TIOCNOTTY, NULL);
+ close(t);
+ }
+#endif /* defined(__hpux) && defined(O_NOCTTY) */
+
+ /* make this process it's own process leader */
+ if (setsid() == -1) {
+ _pam_log(LOG_WARNING,"child cannot become new session");
+ return PAM_ABORT;
+ }
+
+ /* find slave's name */
+ terminal[5] = 't'; /* want to open slave terminal */
+ fd[1] = open(terminal, O_RDWR);
+ close(fd[0]); /* process is the child -- uses line fd[1] */
+
+ if (fd[1] < 0) {
+ _pam_log(LOG_WARNING,"cannot open slave terminal; %s"
+ ,terminal);
+ return PAM_ABORT;
+ }
+
+ /* initialize the child's terminal to be the way the
+ parent's was before we set it into RAW mode */
+
+ if (ioctl(fd[1], TCSETA, (char *) &stored_mode) < 0) {
+ _pam_log(LOG_WARNING,"cannot set slave terminal mode; %s"
+ ,terminal);
+ close(fd[1]);
+ return PAM_ABORT;
+ }
+
+ } else {
+
+ /* nothing to do for a simple stream socket */
+
+ }
+
+ /* re-assign the stdin/out to fd[1] <- (talks to filter). */
+
+ if ( dup2(fd[1],STDIN_FILENO) != STDIN_FILENO ||
+ dup2(fd[1],STDOUT_FILENO) != STDOUT_FILENO ||
+ dup2(fd[1],STDERR_FILENO) != STDERR_FILENO ) {
+ _pam_log(LOG_WARNING
+ ,"unable to re-assign STDIN/OUT/ERR...'s");
+ close(fd[1]);
+ return PAM_ABORT;
+ }
+
+ /* make sure that file descriptors survive 'exec's */
+
+ if ( fcntl(STDIN_FILENO, F_SETFD, 0) ||
+ fcntl(STDOUT_FILENO,F_SETFD, 0) ||
+ fcntl(STDERR_FILENO,F_SETFD, 0) ) {
+ _pam_log(LOG_WARNING
+ ,"unable to re-assign STDIN/OUT/ERR...'s");
+ return PAM_ABORT;
+ }
+
+ /* now the user input is read from the parent/filter: forget fd */
+
+ close(fd[1]);
+
+ /* the current process is now aparently working with filtered
+ stdio/stdout/stderr --- success! */
+
+ return PAM_SUCCESS;
+ }
+
+ /*
+ * process is the parent here. So we can close the application's
+ * input/output
+ */
+
+ close(fd[1]);
+
+ /* Clear out passwords... there is a security problem here in
+ * that this process never executes pam_end. Consequently, any
+ * other sensitive data in this process is *not* explicitly
+ * overwritten, before the process terminates */
+
+ (void) pam_set_item(pamh, PAM_AUTHTOK, NULL);
+ (void) pam_set_item(pamh, PAM_OLDAUTHTOK, NULL);
+
+ /* fork a copy of process to run the actual filter executable */
+
+ if ( (child2 = fork()) < 0 ) {
+
+ _pam_log(LOG_WARNING,"filter fork failed");
+ child2 = 0;
+
+ } else if ( child2 == 0 ) { /* exec the child filter */
+
+ if ( dup2(fd[0],APPIN_FILENO) != APPIN_FILENO ||
+ dup2(fd[0],APPOUT_FILENO) != APPOUT_FILENO ||
+ dup2(fd[0],APPERR_FILENO) != APPERR_FILENO ) {
+ _pam_log(LOG_WARNING
+ ,"unable to re-assign APPIN/OUT/ERR...'s");
+ close(fd[0]);
+ exit(1);
+ }
+
+ /* make sure that file descriptors survive 'exec's */
+
+ if ( fcntl(APPIN_FILENO, F_SETFD, 0) == -1 ||
+ fcntl(APPOUT_FILENO,F_SETFD, 0) == -1 ||
+ fcntl(APPERR_FILENO,F_SETFD, 0) == -1 ) {
+ _pam_log(LOG_WARNING
+ ,"unable to retain APPIN/OUT/ERR...'s");
+ close(APPIN_FILENO);
+ close(APPOUT_FILENO);
+ close(APPERR_FILENO);
+ exit(1);
+ }
+
+ /* now the user input is read from the parent through filter */
+
+ execle(filtername, "<pam_filter>", NULL, evp);
+
+ /* getting to here is an error */
+
+ _pam_log(LOG_ALERT, "filter: %s, not executable", filtername);
+
+ } else { /* wait for either of the two children to exit */
+
+ while (child && child2) { /* loop if there are two children */
+ int lstatus=0;
+ int chid;
+
+ chid = wait(&lstatus);
+ if (chid == child) {
+
+ if (WIFEXITED(lstatus)) { /* exited ? */
+ status = WEXITSTATUS(lstatus);
+ } else if (WIFSIGNALED(lstatus)) { /* killed ? */
+ status = -1;
+ } else
+ continue; /* just stopped etc.. */
+ child = 0; /* the child has exited */
+
+ } else if (chid == child2) {
+ /*
+ * if the filter has exited. Let the child die
+ * naturally below
+ */
+ if (WIFEXITED(lstatus) || WIFSIGNALED(lstatus))
+ child2 = 0;
+ } else {
+
+ _pam_log(LOG_ALERT
+ ,"programming error <chid=%d,lstatus=%x>: "
+ __FILE__ " line %d"
+ , lstatus, __LINE__ );
+ child = child2 = 0;
+ status = -1;
+
+ }
+ }
+ }
+
+ close(fd[0]);
+
+ /* if there is something running, wait for it to exit */
+
+ while (child || child2) {
+ int lstatus=0;
+ int chid;
+
+ chid = wait(&lstatus);
+
+ if (child && chid == child) {
+
+ if (WIFEXITED(lstatus)) { /* exited ? */
+ status = WEXITSTATUS(lstatus);
+ } else if (WIFSIGNALED(lstatus)) { /* killed ? */
+ status = -1;
+ } else
+ continue; /* just stopped etc.. */
+ child = 0; /* the child has exited */
+
+ } else if (child2 && chid == child2) {
+
+ if (WIFEXITED(lstatus) || WIFSIGNALED(lstatus))
+ child2 = 0;
+
+ } else {
+
+ _pam_log(LOG_ALERT
+ ,"programming error <chid=%d,lstatus=%x>: "
+ __FILE__ " line %d"
+ , lstatus, __LINE__ );
+ child = child2 = 0;
+ status = -1;
+
+ }
+ }
+
+ if (aterminal) {
+ /* reset to initial terminal mode */
+ (void) ioctl(STDIN_FILENO, TCSETA, (char *) &stored_mode);
+ }
+
+ if (ctrl & FILTER_DEBUG) {
+ _pam_log(LOG_DEBUG,"parent process exited"); /* clock off */
+ }
+
+ /* quit the parent process, returning the child's exit status */
+
+ exit(status);
+}
+
+static int set_the_terminal(pam_handle_t *pamh)
+{
+ const char *tty;
+
+ if (pam_get_item(pamh, PAM_TTY, (const void **)&tty) != PAM_SUCCESS
+ || tty == NULL) {
+ tty = ttyname(STDIN_FILENO);
+ if (tty == NULL) {
+ _pam_log(LOG_ERR, "couldn't get the tty name");
+ return PAM_ABORT;
+ }
+ if (pam_set_item(pamh, PAM_TTY, tty) != PAM_SUCCESS) {
+ _pam_log(LOG_ERR, "couldn't set tty name");
+ return PAM_ABORT;
+ }
+ }
+ return PAM_SUCCESS;
+}
+
+static int need_a_filter(pam_handle_t *pamh
+ , int flags, int argc, const char **argv
+ , const char *name, int which_run)
+{
+ int ctrl;
+ char **evp;
+ const char *filterfile;
+ int retval;
+
+ ctrl = process_args(pamh, argc, argv, name, &evp, &filterfile);
+ if (ctrl == -1) {
+ return PAM_AUTHINFO_UNAVAIL;
+ }
+
+ /* set the tty to the old or the new one? */
+
+ if (!(ctrl & NON_TERM) && !(ctrl & NEW_TERM)) {
+ retval = set_the_terminal(pamh);
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_ERR, "tried and failed to set PAM_TTY");
+ }
+ } else {
+ retval = PAM_SUCCESS; /* nothing to do which is always a success */
+ }
+
+ if (retval == PAM_SUCCESS && (ctrl & which_run)) {
+ retval = set_filter(pamh, flags, ctrl
+ , (const char **)evp, filterfile);
+ }
+
+ if (retval == PAM_SUCCESS
+ && !(ctrl & NON_TERM) && (ctrl & NEW_TERM)) {
+ retval = set_the_terminal(pamh);
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_ERR
+ , "tried and failed to set new terminal as PAM_TTY");
+ }
+ }
+
+ free_evp(evp);
+
+ if (ctrl & FILTER_DEBUG) {
+ _pam_log(LOG_DEBUG, "filter/%s, returning %d", name, retval);
+ _pam_log(LOG_DEBUG, "[%s]", pam_strerror(pamh, retval));
+ }
+
+ return retval;
+}
+
+/* ----------------- public functions ---------------- */
+
+/*
+ * here are the advertised access points ...
+ */
+
+/* ------------------ authentication ----------------- */
+
+PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh
+ , int flags, int argc, const char **argv)
+{
+ return need_a_filter(pamh, flags, argc, argv
+ , "authenticate", FILTER_RUN1);
+}
+
+PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags
+ , int argc, const char **argv)
+{
+ return need_a_filter(pamh, flags, argc, argv, "setcred", FILTER_RUN2);
+}
+
+/* --------------- account management ---------------- */
+
+PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc,
+ const char **argv)
+{
+ return need_a_filter(pamh, flags, argc, argv
+ , "setcred", FILTER_RUN1|FILTER_RUN2 );
+}
+
+/* --------------- session management ---------------- */
+
+PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags
+ , int argc, const char **argv)
+{
+ return need_a_filter(pamh, flags, argc, argv
+ , "open_session", FILTER_RUN1);
+}
+
+PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags
+ , int argc, const char **argv)
+{
+ return need_a_filter(pamh, flags, argc, argv
+ , "close_session", FILTER_RUN2);
+}
+
+/* --------- updating authentication tokens --------- */
+
+
+PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags
+ , int argc, const char **argv)
+{
+ int runN;
+
+ if (flags & PAM_PRELIM_CHECK)
+ runN = FILTER_RUN1;
+ else if (flags & PAM_UPDATE_AUTHTOK)
+ runN = FILTER_RUN2;
+ else {
+ _pam_log(LOG_ERR, "unknown flags for chauthtok (0x%X)", flags);
+ return PAM_TRY_AGAIN;
+ }
+
+ return need_a_filter(pamh, flags, argc, argv, "chauthtok", runN);
+}
+
+#ifdef PAM_STATIC
+
+/* ------------ stuff for static modules ------------ */
+
+struct pam_module _pam_filter_modstruct = {
+ "pam_filter",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ pam_sm_acct_mgmt,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ pam_sm_chauthtok,
+};
+
+#endif
diff --git a/modules/pam_filter/upperLOWER/.cvsignore b/modules/pam_filter/upperLOWER/.cvsignore
new file mode 100644
index 00000000..bcd63650
--- /dev/null
+++ b/modules/pam_filter/upperLOWER/.cvsignore
@@ -0,0 +1 @@
+upperLOWER
diff --git a/modules/pam_filter/upperLOWER/Makefile b/modules/pam_filter/upperLOWER/Makefile
new file mode 100644
index 00000000..a75b06bb
--- /dev/null
+++ b/modules/pam_filter/upperLOWER/Makefile
@@ -0,0 +1,64 @@
+#
+# $Id$
+#
+# $Log$
+# Revision 1.1 2000/06/20 22:11:37 agmorgan
+# Initial revision
+#
+# Revision 1.1.1.1 1998/07/12 05:17:16 morgan
+# Linux PAM sources pre-0.66
+#
+# Revision 1.5 1997/04/05 06:41:35 morgan
+# fakeroot
+#
+# Revision 1.4 1997/01/04 20:25:04 morgan
+# removed need for make
+#
+# Revision 1.3 1996/11/10 20:13:08 morgan
+# email address
+#
+# Revision 1.2 1996/11/10 20:12:24 morgan
+# cross platform support
+#
+# Revision 1.1 1996/06/02 08:17:02 morgan
+# Initial revision
+#
+#
+# This directory contains a pam_filter filter executable
+#
+# Created by Andrew Morgan <morgan@parc.power.net> 1996/3/11
+#
+
+TITLE=upperLOWER
+
+#
+
+OBJS = $(TITLE).o
+
+####################### don't edit below #######################
+
+dummy:
+ @echo "**** This is not a top-level Makefile "
+
+all: $(TITLE)
+
+$(TITLE): $(OBJS)
+ $(CC) -o $(TITLE) $(OBJS)
+ strip $(TITLE)
+
+install:
+ $(MKDIR) $(FAKEROOT)$(FILTERSDIR)
+ $(INSTALL) -m 511 $(TITLE) $(FAKEROOT)$(FILTERSDIR)
+
+remove:
+ cd $(FAKEROOT)$(FILTERSDIR) && rm -f $(TITLE)
+
+clean:
+ rm -f $(TITLE) $(OBJS) core *~
+
+extraclean: clean
+ rm -f *.bak
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_filter/upperLOWER/upperLOWER.c b/modules/pam_filter/upperLOWER/upperLOWER.c
new file mode 100644
index 00000000..3f7d26cd
--- /dev/null
+++ b/modules/pam_filter/upperLOWER/upperLOWER.c
@@ -0,0 +1,174 @@
+/*
+ * $Id$
+ *
+ * This is a sample filter program, for use with pam_filter (a module
+ * provided with Linux-PAM). This filter simply transposes upper and
+ * lower case letters, it is intended for demonstration purposes and
+ * it serves no purpose other than to annoy the user...
+ *
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:37 agmorgan
+ * Initial revision
+ *
+ * Revision 1.2 1999/07/08 05:01:48 morgan
+ * glibc fixes (Thorsten Kukuk, Adam J. Richter)
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:16 morgan
+ * Linux PAM sources pre-0.66
+ *
+ * Revision 1.1 1996/06/02 08:17:02 morgan
+ * Initial revision
+ *
+ */
+
+#ifdef linux
+# define _GNU_SOURCE
+# include <features.h>
+#endif
+
+#include <stdio.h>
+#include <syslog.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <security/pam_filter.h>
+
+/* ---------------------------------------------------------------- */
+
+#include <stdarg.h>
+#ifdef hpux
+# define log_this syslog
+#else
+static void log_this(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("upperLOWER", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+#endif
+
+#include <ctype.h>
+
+static void do_transpose(char *buffer,int len)
+{
+ int i;
+ for (i=0; i<len; ++i) {
+ if (islower(buffer[i])) {
+ buffer[i] = toupper(buffer[i]);
+ } else {
+ buffer[i] = tolower(buffer[i]);
+ }
+ }
+}
+
+int main(int argc, char **argv, char **envp)
+{
+ char buffer[BUFSIZ];
+ fd_set readers;
+ void (*before_user)(char *,int);
+ void (*before_app)(char *,int);
+
+#ifdef DEBUG
+ {
+ int i;
+
+ fprintf(stderr,"environment :[\r\n");
+ for (i=0; envp[i]; ++i) {
+ fprintf(stderr,"-> %s\r\n",envp[i]);
+ }
+ fprintf(stderr,"]: end\r\n");
+ }
+#endif
+
+ if (argc != 1) {
+#ifdef DEBUG
+ fprintf(stderr,"filter invoked as conventional executable\n");
+#else
+ log_this(LOG_ERR, "filter invoked as conventional executable");
+#endif
+ exit(1);
+ }
+
+ before_user = before_app = do_transpose; /* assign filter functions */
+
+ /* enter a loop that deals with the input and output of the
+ user.. passing it to and from the application */
+
+ FD_ZERO(&readers); /* initialize reading mask */
+
+ for (;;) {
+
+ FD_SET(APPOUT_FILENO, &readers); /* wake for output */
+ FD_SET(APPERR_FILENO, &readers); /* wake for error */
+ FD_SET(STDIN_FILENO, &readers); /* wake for input */
+
+ if ( select(APPTOP_FILE,&readers,NULL,NULL,NULL) < 0 ) {
+#ifdef DEBUG
+ fprintf(stderr,"select failed\n");
+#else
+ log_this(LOG_WARNING,"select failed");
+#endif
+ break;
+ }
+
+ /* application errors */
+
+ if ( FD_ISSET(APPERR_FILENO,&readers) ) {
+ int got = read(APPERR_FILENO, buffer, BUFSIZ);
+ if (got <= 0) {
+ break;
+ } else {
+ /* translate to give to real terminal */
+ if (before_user != NULL)
+ before_user(buffer, got);
+ if ( write(STDERR_FILENO, buffer, got) != got ) {
+ log_this(LOG_WARNING,"couldn't write %d bytes?!",got);
+ break;
+ }
+ }
+ } else if ( FD_ISSET(APPOUT_FILENO,&readers) ) { /* app output */
+ int got = read(APPOUT_FILENO, buffer, BUFSIZ);
+ if (got <= 0) {
+ break;
+ } else {
+ /* translate to give to real terminal */
+ if (before_user != NULL)
+ before_user(buffer, got);
+ if ( write(STDOUT_FILENO, buffer, got) != got ) {
+ log_this(LOG_WARNING,"couldn't write %d bytes!?",got);
+ break;
+ }
+ }
+ }
+
+ if ( FD_ISSET(STDIN_FILENO, &readers) ) { /* user input */
+ int got = read(STDIN_FILENO, buffer, BUFSIZ);
+ if (got < 0) {
+ log_this(LOG_WARNING,"user input junked");
+ break;
+ } else if (got) {
+ /* translate to give to application */
+ if (before_app != NULL)
+ before_app(buffer, got);
+ if ( write(APPIN_FILENO, buffer, got) != got ) {
+ log_this(LOG_WARNING,"couldn't pass %d bytes!?",got);
+ break;
+ }
+ } else {
+ /* nothing received -- an error? */
+ log_this(LOG_WARNING,"user input null?");
+ break;
+ }
+ }
+ }
+
+ exit(0);
+}
+
+
+
diff --git a/modules/pam_ftp/.cvsignore b/modules/pam_ftp/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_ftp/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_ftp/Makefile b/modules/pam_ftp/Makefile
new file mode 100644
index 00000000..0ce77a7a
--- /dev/null
+++ b/modules/pam_ftp/Makefile
@@ -0,0 +1,88 @@
+#
+# $Id$
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# Created by Andrew Morgan <morgan@linux.kernel.org> 1996/11/14
+#
+
+TITLE=pam_ftp
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak dynamic/* static/*
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_ftp/README b/modules/pam_ftp/README
new file mode 100644
index 00000000..6d03330c
--- /dev/null
+++ b/modules/pam_ftp/README
@@ -0,0 +1,18 @@
+This is the README for pam_ftp
+------------------------------
+
+This module is an authentication module that does simple ftp
+authentication.
+
+Recognized arguments:
+
+ "debug" print debug messages
+ "users=" comma separated list of users which
+ could login only with email adress
+ "ignore" allow invalid email adresses
+
+Options for:
+auth: for authentication it provides pam_authenticate() and
+ pam_setcred() hooks.
+
+Thorsten Kukuk <kukuk@suse.de>, 17. June 1999
diff --git a/modules/pam_ftp/pam_ftp.c b/modules/pam_ftp/pam_ftp.c
new file mode 100644
index 00000000..eb2c3fd9
--- /dev/null
+++ b/modules/pam_ftp/pam_ftp.c
@@ -0,0 +1,299 @@
+/* pam_ftp module */
+
+/*
+ * $Id$
+ *
+ * Written by Andrew Morgan <morgan@linux.kernel.org> 1996/3/11
+ *
+ */
+
+#define PLEASE_ENTER_PASSWORD "Password required for %s."
+#define GUEST_LOGIN_PROMPT "Guest login ok, " \
+"send your complete e-mail address as password."
+
+/* the following is a password that "can't be correct" */
+#define BLOCK_PASSWORD "\177BAD PASSWPRD\177"
+
+#define _GNU_SOURCE
+#define _BSD_SOURCE
+
+#include <features.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <string.h>
+
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_AUTH
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+/* some syslogging */
+
+static void _pam_log(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("PAM-ftp", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+static int converse(pam_handle_t *pamh, int nargs
+ , struct pam_message **message
+ , struct pam_response **response)
+{
+ int retval;
+ struct pam_conv *conv;
+
+ D(("begin to converse\n"));
+
+ retval = pam_get_item( pamh, PAM_CONV, (const void **) &conv ) ;
+ if ( retval == PAM_SUCCESS ) {
+
+ retval = conv->conv(nargs, ( const struct pam_message ** ) message
+ , response, conv->appdata_ptr);
+
+ D(("returned from application's conversation function\n"));
+
+ if ((retval != PAM_SUCCESS) && (retval != PAM_CONV_AGAIN)) {
+ _pam_log(LOG_DEBUG, "conversation failure [%s]"
+ , pam_strerror(pamh, retval));
+ }
+
+ } else {
+ _pam_log(LOG_ERR, "couldn't obtain coversation function [%s]"
+ , pam_strerror(pamh, retval));
+ }
+
+ D(("ready to return from module conversation\n"));
+
+ return retval; /* propagate error status */
+}
+
+/* argument parsing */
+
+#define PAM_DEBUG_ARG 01
+#define PAM_IGNORE_EMAIL 02
+#define PAM_NO_ANON 04
+
+static int _pam_parse(int argc, const char **argv, char **users)
+{
+ int ctrl=0;
+
+ /* step through arguments */
+ for (ctrl=0; argc-- > 0; ++argv) {
+
+ /* generic options */
+
+ if (!strcmp(*argv,"debug"))
+ ctrl |= PAM_DEBUG_ARG;
+ else if (!strncmp(*argv,"users=",6)) {
+ *users = x_strdup(6+*argv);
+ if (*users == NULL) {
+ ctrl |= PAM_NO_ANON;
+ _pam_log(LOG_CRIT, "failed to duplicate user list - anon off");
+ }
+ } else if (!strcmp(*argv,"ignore")) {
+ ctrl |= PAM_IGNORE_EMAIL;
+ } else {
+ _pam_log(LOG_ERR,"pam_parse: unknown option; %s",*argv);
+ }
+ }
+
+ return ctrl;
+}
+
+/*
+ * check if name is in list or default list. place users name in *_user
+ * return 1 if listed 0 if not.
+ */
+
+static int lookup(const char *name, char *list, const char **_user)
+{
+ int anon = 0;
+
+ *_user = name; /* this is the default */
+ if (list) {
+ const char *l;
+ char *x;
+
+ x = list;
+ while ((l = strtok(x, ","))) {
+ x = NULL;
+ if (!strcmp(name, l)) {
+ *_user = list;
+ anon = 1;
+ }
+ }
+ } else {
+#define MAX_L 2
+ static const char *l[MAX_L] = { "ftp", "anonymous" };
+ int i;
+
+ for (i=0; i<MAX_L; ++i) {
+ if (!strcmp(l[i], name)) {
+ *_user = l[0];
+ anon = 1;
+ break;
+ }
+ }
+ }
+
+ return anon;
+}
+
+/* --- authentication management functions (only) --- */
+
+PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ int retval, anon=0, ctrl;
+ const char *user;
+ char *users=NULL;
+
+ /*
+ * this module checks if the user name is ftp or annonymous. If
+ * this is the case, it can set the PAM_RUSER to the entered email
+ * address and SUCCEEDS, otherwise it FAILS.
+ */
+
+ ctrl = _pam_parse(argc, argv, &users);
+
+ retval = pam_get_user(pamh, &user, NULL);
+ if (retval != PAM_SUCCESS || user == NULL) {
+ _pam_log(LOG_ERR, "no user specified");
+ return PAM_USER_UNKNOWN;
+ }
+
+ if (!(ctrl & PAM_NO_ANON)) {
+ anon = lookup(user, users, &user);
+ }
+
+ if (anon) {
+ retval = pam_set_item(pamh, PAM_USER, (const void *)user);
+ if (retval != PAM_SUCCESS || user == NULL) {
+ _pam_log(LOG_ERR, "user resetting failed");
+ return PAM_USER_UNKNOWN;
+ }
+ }
+
+ /*
+ * OK. we require an email address for user or the user's password.
+ * - build conversation and get their input.
+ */
+
+ {
+ struct pam_message msg[1], *mesg[1];
+ struct pam_response *resp=NULL;
+ const char *token;
+ char *prompt=NULL;
+ int i=0;
+
+ if (!anon) {
+ prompt = malloc(strlen(PLEASE_ENTER_PASSWORD) + strlen(user));
+ if (prompt == NULL) {
+ D(("out of memory!?"));
+ return PAM_BUF_ERR;
+ } else {
+ sprintf(prompt, PLEASE_ENTER_PASSWORD, user);
+ msg[i].msg = prompt;
+ }
+ } else {
+ msg[i].msg = GUEST_LOGIN_PROMPT;
+ }
+
+ msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+ mesg[i] = &msg[i];
+
+ retval = converse(pamh, ++i, mesg, &resp);
+ if (prompt) {
+ _pam_overwrite(prompt);
+ _pam_drop(prompt);
+ }
+
+ if (retval != PAM_SUCCESS) {
+ if (resp != NULL)
+ _pam_drop_reply(resp,i);
+ return ((retval == PAM_CONV_AGAIN)
+ ? PAM_INCOMPLETE:PAM_AUTHINFO_UNAVAIL);
+ }
+
+ if (anon) {
+ /* XXX: Some effort should be made to verify this email address! */
+
+ if (!(ctrl & PAM_IGNORE_EMAIL)) {
+ token = strtok(resp->resp, "@");
+ retval = pam_set_item(pamh, PAM_RUSER, token);
+
+ if ((token) && (retval == PAM_SUCCESS)) {
+ token = strtok(NULL, "@");
+ retval = pam_set_item(pamh, PAM_RHOST, token);
+ }
+ }
+
+ /* we are happy to grant annonymous access to the user */
+ retval = PAM_SUCCESS;
+
+ } else {
+ /*
+ * we have a password so set AUTHTOK
+ */
+
+ (void) pam_set_item(pamh, PAM_AUTHTOK, resp->resp);
+
+ /*
+ * this module failed, but the next one might succeed with
+ * this password.
+ */
+
+ retval = PAM_AUTH_ERR;
+ }
+
+ if (resp) { /* clean up */
+ _pam_drop_reply(resp, i);
+ }
+
+ /* success or failure */
+
+ return retval;
+ }
+}
+
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_IGNORE;
+}
+
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_ftp_modstruct = {
+ "pam_ftp",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+#endif
+
+/* end of module definition */
diff --git a/modules/pam_group/.cvsignore b/modules/pam_group/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_group/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_group/Makefile b/modules/pam_group/Makefile
new file mode 100644
index 00000000..b61a57de
--- /dev/null
+++ b/modules/pam_group/Makefile
@@ -0,0 +1,98 @@
+#
+# $Id$
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# Created by Andrew Morgan <morgan@linux.kernel.org> 1996/6/11
+#
+
+TITLE=pam_group
+CONFD=$(CONFIGED)/security
+export CONFD
+CONFILE=$(CONFD)/group.conf
+export CONFILE
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+DEFS=-DCONFILE=\"$(CONFILE)\"
+
+CFLAGS += $(DEFS)
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD) $(ELIBS)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS) $(ELIBS)
+endif
+
+install: all
+ifdef DYNAMIC
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+ $(MKDIR) $(FAKEROOT)$(SCONFIGED)
+ bash -f ./install_conf
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+ rm -f $(FAKEROOT)$(CONFILE)
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+ rm -f ./.ignore_age
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_group/group.conf b/modules/pam_group/group.conf
new file mode 100644
index 00000000..bdd76adb
--- /dev/null
+++ b/modules/pam_group/group.conf
@@ -0,0 +1,60 @@
+##
+## Note, to get this to work as it is currently typed you need
+##
+## 1. to run an application as root
+## 2. add the following groups to the /etc/group file:
+## floppy, games, sound
+##
+#
+# *** Please note that giving group membership on a session basis is
+# *** NOT inherently secure. If a user can create an executable that
+# *** is setgid a group that they are infrequently given membership
+# *** of, they can basically obtain group membership any time they
+# *** like. Example: games are alowed between the hours of 6pm and 6am
+# *** user joe logs in at 7pm writes a small C-program toplay.c that
+# *** invokes their favorite shell, compiles it and does
+# *** "chgrp games toplay; chmod g+s toplay". They are basically able
+# *** to play games any time... You have been warned. AGM
+#
+# this is an example configuration file for the pam_group module. Its
+# syntax is based on that of the pam_time module and (at some point in
+# the distant past was inspired by the 'shadow' package)
+#
+# the syntax of the lines is as follows:
+#
+# services;ttys;users;times;groups
+#
+# white space is ignored and lines maybe extended with '\\n' (escaped
+# newlines). From reading these comments, it is clear that
+# text following a '#' is ignored to the end of the line.
+#
+# the first four fields are described in the pam_time directory.
+# The only difference for these is how the time field is interpretted:
+# it is used to indicate "when" these groups are to be given to the user.
+#
+# groups
+# The (comma or space separated) list of groups that the user
+# inherits membership of. These groups are added if the previous
+# fields are satisfied by the user's request
+#
+
+#
+# Here is a simple example: running 'xsh' on tty* (any ttyXXX device),
+# the user 'us' is given access to the floppy (through membership of
+# the floppy group)
+#
+
+#xsh;tty*&!ttyp*;us;Al0000-2400;floppy
+
+#
+# another example: running 'xsh' on tty* (any ttyXXX device),
+# the user 'sword' is given access to games (through membership of
+# the floppy group) after work hours
+#
+
+#xsh; tty* ;sword;!Wk0900-1800;games, sound
+#xsh; tty* ;*;Al0900-1800;floppy
+
+#
+# End of group.conf file
+#
diff --git a/modules/pam_group/install_conf b/modules/pam_group/install_conf
new file mode 100755
index 00000000..03bb7edb
--- /dev/null
+++ b/modules/pam_group/install_conf
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+CONFILE=$FAKEROOT"$CONFILE"
+IGNORE_AGE=./.ignore_age
+QUIET_INSTALL=../../.quiet_install
+CONF=./group.conf
+MODULE=pam_group
+
+echo
+
+if [ -f "$QUIET_INSTALL" ]; then
+ if [ ! -f "$CONFILE" ]; then
+ yes="y"
+ else
+ yes="skip"
+ fi
+elif [ -f "$IGNORE_AGE" ]; then
+ echo "you don't want to be bothered with the age of your $CONFILE file"
+ yes="n"
+elif [ ! -f "$CONFILE" ] || [ "$CONF" -nt "$CONFILE" ]; then
+ if [ -f "$CONFILE" ]; then
+ echo "An older $MODULE configuration file already exists ($CONFILE)"
+ echo "Do you wish to copy the $CONF file in this distribution"
+ echo "to $CONFILE ? (y/n) [skip] "
+ read yes
+ else
+ yes="y"
+ fi
+else
+ yes="skip"
+fi
+
+if [ "$yes" = "y" ]; then
+ mkdir -p $FAKEROOT$CONFD
+ echo " copying $CONF to $CONFILE"
+ cp $CONF $CONFILE
+else
+ echo " Skipping $CONF installation"
+ if [ "$yes" = "n" ]; then
+ touch "$IGNORE_AGE"
+ fi
+fi
+
+echo
+
+exit 0
diff --git a/modules/pam_group/pam_group.c b/modules/pam_group/pam_group.c
new file mode 100644
index 00000000..81953f76
--- /dev/null
+++ b/modules/pam_group/pam_group.c
@@ -0,0 +1,843 @@
+/* pam_group module */
+
+/*
+ * $Id$
+ *
+ * Written by Andrew Morgan <morgan@linux.kernel.org> 1996/7/6
+ */
+
+const static char rcsid[] =
+"$Id$;\n"
+"Version 0.5 for Linux-PAM\n"
+"Copyright (c) Andrew G. Morgan 1996 <morgan@linux.kernel.org>\n";
+
+#define _BSD_SOURCE
+
+#include <sys/file.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <time.h>
+#include <syslog.h>
+#include <string.h>
+
+#include <grp.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define PAM_GROUP_CONF CONFILE /* from external define */
+#define PAM_GROUP_BUFLEN 1000
+#define FIELD_SEPARATOR ';' /* this is new as of .02 */
+
+typedef enum { FALSE, TRUE } boolean;
+typedef enum { AND, OR } operator;
+
+/*
+ * here, we make definitions for the externally accessible functions
+ * in this file (these definitions are required for static modules
+ * but strongly encouraged generally) they are used to instruct the
+ * modules include file to define their prototypes.
+ */
+
+#define PAM_SM_AUTH
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+/* --- static functions for checking whether the user should be let in --- */
+
+static void _log_err(const char *format, ... )
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("pam_group", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(LOG_CRIT, format, args);
+ va_end(args);
+ closelog();
+}
+
+static void shift_bytes(char *mem, int from, int by)
+{
+ while (by-- > 0) {
+ *mem = mem[from];
+ ++mem;
+ }
+}
+
+static int read_field(int fd, char **buf, int *from, int *to)
+{
+ /* is buf set ? */
+
+ if (! *buf) {
+ *buf = (char *) malloc(PAM_GROUP_BUFLEN);
+ if (! *buf) {
+ _log_err("out of memory");
+ return -1;
+ }
+ *from = *to = 0;
+ fd = open(PAM_GROUP_CONF, O_RDONLY);
+ }
+
+ /* do we have a file open ? return error */
+
+ if (fd < 0 && *to <= 0) {
+ _log_err( PAM_GROUP_CONF " not opened");
+ memset(*buf, 0, PAM_GROUP_BUFLEN);
+ _pam_drop(*buf);
+ return -1;
+ }
+
+ /* check if there was a newline last time */
+
+ if ((*to > *from) && (*to > 0)
+ && ((*buf)[*from] == '\0')) { /* previous line ended */
+ (*from)++;
+ (*buf)[0] = '\0';
+ return fd;
+ }
+
+ /* ready for more data: first shift the buffer's remaining data */
+
+ *to -= *from;
+ shift_bytes(*buf, *from, *to);
+ *from = 0;
+ (*buf)[*to] = '\0';
+
+ while (fd >= 0 && *to < PAM_GROUP_BUFLEN) {
+ int i;
+
+ /* now try to fill the remainder of the buffer */
+
+ i = read(fd, *to + *buf, PAM_GROUP_BUFLEN - *to);
+ if (i < 0) {
+ _log_err("error reading " PAM_GROUP_CONF);
+ return -1;
+ } else if (!i) {
+ close(fd);
+ fd = -1; /* end of file reached */
+ } else
+ *to += i;
+
+ /*
+ * contract the buffer. Delete any comments, and replace all
+ * multiple spaces with single commas
+ */
+
+ i = 0;
+#ifdef DEBUG_DUMP
+ D(("buffer=<%s>",*buf));
+#endif
+ while (i < *to) {
+ if ((*buf)[i] == ',') {
+ int j;
+
+ for (j=++i; j<*to && (*buf)[j] == ','; ++j);
+ if (j!=i) {
+ shift_bytes(i + (*buf), j-i, (*to) - j);
+ *to -= j-i;
+ }
+ }
+ switch ((*buf)[i]) {
+ int j,c;
+ case '#':
+ for (j=i; j < *to && (c = (*buf)[j]) != '\n'; ++j);
+ if (j >= *to) {
+ (*buf)[*to = ++i] = '\0';
+ } else if (c == '\n') {
+ shift_bytes(i + (*buf), j-i, (*to) - j);
+ *to -= j-i;
+ ++i;
+ } else {
+ _log_err("internal error in " __FILE__
+ " at line %d", __LINE__ );
+ return -1;
+ }
+ break;
+ case '\\':
+ if ((*buf)[i+1] == '\n') {
+ shift_bytes(i + *buf, 2, *to - (i+2));
+ *to -= 2;
+ }
+ break;
+ case '!':
+ case ' ':
+ case '\t':
+ if ((*buf)[i] != '!')
+ (*buf)[i] = ',';
+ /* delete any trailing spaces */
+ for (j=++i; j < *to && ( (c = (*buf)[j]) == ' '
+ || c == '\t' ); ++j);
+ shift_bytes(i + *buf, j-i, (*to)-j );
+ *to -= j-i;
+ break;
+ default:
+ ++i;
+ }
+ }
+ }
+
+ (*buf)[*to] = '\0';
+
+ /* now return the next field (set the from/to markers) */
+ {
+ int i;
+
+ for (i=0; i<*to; ++i) {
+ switch ((*buf)[i]) {
+ case '#':
+ case '\n': /* end of the line/file */
+ (*buf)[i] = '\0';
+ *from = i;
+ return fd;
+ case FIELD_SEPARATOR: /* end of the field */
+ (*buf)[i] = '\0';
+ *from = ++i;
+ return fd;
+ }
+ }
+ *from = i;
+ (*buf)[*from] = '\0';
+ }
+
+ if (*to <= 0) {
+ D(("[end of text]"));
+ *buf = NULL;
+ }
+ return fd;
+}
+
+/* read a member from a field */
+
+static int logic_member(const char *string, int *at)
+{
+ int len,c,to;
+ int done=0;
+ int token=0;
+
+ len=0;
+ to=*at;
+ do {
+ c = string[to++];
+
+ switch (c) {
+
+ case '\0':
+ --to;
+ done = 1;
+ break;
+
+ case '&':
+ case '|':
+ case '!':
+ if (token) {
+ --to;
+ }
+ done = 1;
+ break;
+
+ default:
+ if (isalpha(c) || c == '*' || isdigit(c) || c == '_'
+ || c == '-' || c == '.') {
+ token = 1;
+ } else if (token) {
+ --to;
+ done = 1;
+ } else {
+ ++*at;
+ }
+ }
+ } while (!done);
+
+ return to - *at;
+}
+
+typedef enum { VAL, OP } expect;
+
+static boolean logic_field(const void *me, const char *x, int rule,
+ boolean (*agrees)(const void *, const char *
+ , int, int))
+{
+ boolean left=FALSE, right, not=FALSE;
+ operator oper=OR;
+ int at=0, l;
+ expect next=VAL;
+
+ while ((l = logic_member(x,&at))) {
+ int c = x[at];
+
+ if (next == VAL) {
+ if (c == '!')
+ not = !not;
+ else if (isalpha(c) || c == '*') {
+ right = not ^ agrees(me, x+at, l, rule);
+ if (oper == AND)
+ left &= right;
+ else
+ left |= right;
+ next = OP;
+ } else {
+ _log_err("garbled syntax; expected name (rule #%d)", rule);
+ return FALSE;
+ }
+ } else { /* OP */
+ switch (c) {
+ case '&':
+ oper = AND;
+ break;
+ case '|':
+ oper = OR;
+ break;
+ default:
+ _log_err("garbled syntax; expected & or | (rule #%d)"
+ , rule);
+ D(("%c at %d",c,at));
+ return FALSE;
+ }
+ next = VAL;
+ }
+ at += l;
+ }
+
+ return left;
+}
+
+static boolean is_same(const void *A, const char *b, int len, int rule)
+{
+ int i;
+ const char *a;
+
+ a = A;
+ for (i=0; len > 0; ++i, --len) {
+ if (b[i] != a[i]) {
+ if (b[i++] == '*') {
+ return (!--len || !strncmp(b+i,a+strlen(a)-len,len));
+ } else
+ return FALSE;
+ }
+ }
+ return ( !len );
+}
+
+typedef struct {
+ int day; /* array of 7 bits, one set for today */
+ int minute; /* integer, hour*100+minute for now */
+} TIME;
+
+struct day {
+ const char *d;
+ int bit;
+} static const days[11] = {
+ { "su", 01 },
+ { "mo", 02 },
+ { "tu", 04 },
+ { "we", 010 },
+ { "th", 020 },
+ { "fr", 040 },
+ { "sa", 0100 },
+ { "wk", 076 },
+ { "wd", 0101 },
+ { "al", 0177 },
+ { NULL, 0 }
+};
+
+static TIME time_now(void)
+{
+ struct tm *local;
+ time_t the_time;
+ TIME this;
+
+ the_time = time((time_t *)0); /* get the current time */
+ local = localtime(&the_time);
+ this.day = days[local->tm_wday].bit;
+ this.minute = local->tm_hour*100 + local->tm_min;
+
+ D(("day: 0%o, time: %.4d", this.day, this.minute));
+ return this;
+}
+
+/* take the current date and see if the range "date" passes it */
+static boolean check_time(const void *AT, const char *times, int len, int rule)
+{
+ boolean not,pass;
+ int marked_day, time_start, time_end;
+ const TIME *at;
+ int i,j=0;
+
+ at = AT;
+ D(("checking: 0%o/%.4d vs. %s", at->day, at->minute, times));
+
+ if (times == NULL) {
+ /* this should not happen */
+ _log_err("internal error: " __FILE__ " line %d", __LINE__);
+ return FALSE;
+ }
+
+ if (times[j] == '!') {
+ ++j;
+ not = TRUE;
+ } else {
+ not = FALSE;
+ }
+
+ for (marked_day = 0; len > 0 && isalpha(times[j]); --len) {
+ int this_day=-1;
+
+ D(("%c%c ?", times[j], times[j+1]));
+ for (i=0; days[i].d != NULL; ++i) {
+ if (tolower(times[j]) == days[i].d[0]
+ && tolower(times[j+1]) == days[i].d[1] ) {
+ this_day = days[i].bit;
+ break;
+ }
+ }
+ j += 2;
+ if (this_day == -1) {
+ _log_err("bad day specified (rule #%d)", rule);
+ return FALSE;
+ }
+ marked_day ^= this_day;
+ }
+ if (marked_day == 0) {
+ _log_err("no day specified");
+ return FALSE;
+ }
+ D(("day range = 0%o", marked_day));
+
+ time_start = 0;
+ for (i=0; len > 0 && i < 4 && isdigit(times[i+j]); ++i, --len) {
+ time_start *= 10;
+ time_start += times[i+j]-'0'; /* is this portable? */
+ }
+ j += i;
+
+ if (times[j] == '-') {
+ time_end = 0;
+ for (i=1; len > 0 && i < 5 && isdigit(times[i+j]); ++i, --len) {
+ time_end *= 10;
+ time_end += times[i+j]-'0'; /* is this portable? */
+ }
+ j += i;
+ } else
+ time_end = -1;
+
+ D(("i=%d, time_end=%d, times[j]='%c'", i, time_end, times[j]));
+ if (i != 5 || time_end == -1) {
+ _log_err("no/bad times specified (rule #%d)", rule);
+ return TRUE;
+ }
+ D(("times(%d to %d)", time_start,time_end));
+ D(("marked_day = 0%o", marked_day));
+
+ /* compare with the actual time now */
+
+ pass = FALSE;
+ if (time_start < time_end) { /* start < end ? --> same day */
+ if ((at->day & marked_day) && (at->minute >= time_start)
+ && (at->minute < time_end)) {
+ D(("time is listed"));
+ pass = TRUE;
+ }
+ } else { /* spans two days */
+ if ((at->day & marked_day) && (at->minute >= time_start)) {
+ D(("caught on first day"));
+ pass = TRUE;
+ } else {
+ marked_day <<= 1;
+ marked_day |= (marked_day & 0200) ? 1:0;
+ D(("next day = 0%o", marked_day));
+ if ((at->day & marked_day) && (at->minute <= time_end)) {
+ D(("caught on second day"));
+ pass = TRUE;
+ }
+ }
+ }
+
+ return (not ^ pass);
+}
+
+static int find_member(const char *string, int *at)
+{
+ int len,c,to;
+ int done=0;
+ int token=0;
+
+ len=0;
+ to=*at;
+ do {
+ c = string[to++];
+
+ switch (c) {
+
+ case '\0':
+ --to;
+ done = 1;
+ break;
+
+ case '&':
+ case '|':
+ case '!':
+ if (token) {
+ --to;
+ }
+ done = 1;
+ break;
+
+ default:
+ if (isalpha(c) || isdigit(c) || c == '_' || c == '*'
+ || c == '-') {
+ token = 1;
+ } else if (token) {
+ --to;
+ done = 1;
+ } else {
+ ++*at;
+ }
+ }
+ } while (!done);
+
+ return to - *at;
+}
+
+#define GROUP_BLK 10
+#define blk_size(len) (((len-1 + GROUP_BLK)/GROUP_BLK)*GROUP_BLK)
+
+static int mkgrplist(char *buf, gid_t **list, int len)
+{
+ int l,at=0;
+ int blks;
+
+ blks = blk_size(len);
+ D(("cf. blks=%d and len=%d", blks,len));
+
+ while ((l = find_member(buf,&at))) {
+ int edge;
+
+ if (len >= blks) {
+ gid_t *tmp;
+
+ D(("allocating new block"));
+ tmp = (gid_t *) realloc((*list)
+ , sizeof(gid_t) * (blks += GROUP_BLK));
+ if (tmp != NULL) {
+ (*list) = tmp;
+ } else {
+ _log_err("out of memory for group list");
+ free(*list);
+ (*list) = NULL;
+ return -1;
+ }
+ }
+
+ /* '\0' terminate the entry */
+
+ edge = (buf[at+l]) ? 1:0;
+ buf[at+l] = '\0';
+ D(("found group: %s",buf+at));
+
+ /* this is where we convert a group name to a gid_t */
+#ifdef WANT_PWDB
+ {
+ int retval;
+ const struct pwdb *pw=NULL;
+
+ retval = pwdb_locate("group", PWDB_DEFAULT, buf+at
+ , PWDB_ID_UNKNOWN, &pw);
+ if (retval != PWDB_SUCCESS) {
+ _log_err("bad group: %s; %s", buf+at, pwdb_strerror(retval));
+ } else {
+ const struct pwdb_entry *pwe=NULL;
+
+ D(("group %s exists", buf+at));
+ retval = pwdb_get_entry(pw, "gid", &pwe);
+ if (retval == PWDB_SUCCESS) {
+ D(("gid = %d [%p]",* (const gid_t *) pwe->value,list));
+ (*list)[len++] = * (const gid_t *) pwe->value;
+ pwdb_entry_delete(&pwe); /* tidy up */
+ } else {
+ _log_err("%s group entry is bad; %s"
+ , pwdb_strerror(retval));
+ }
+ pw = NULL; /* break link - cached for later use */
+ }
+ }
+#else
+ {
+ const struct group *grp;
+
+ grp = getgrnam(buf+at);
+ if (grp == NULL) {
+ _log_err("bad group: %s", buf+at);
+ } else {
+ D(("group %s exists", buf+at));
+ (*list)[len++] = grp->gr_gid;
+ }
+ }
+#endif
+
+ /* next entry along */
+
+ at += l + edge;
+ }
+ D(("returning with [%p/len=%d]->%p",list,len,*list));
+ return len;
+}
+
+
+static int check_account(const char *service, const char *tty
+ , const char *user)
+{
+ int from=0,to=0,fd=-1;
+ char *buffer=NULL;
+ int count=0;
+ TIME here_and_now;
+ int retval=PAM_SUCCESS;
+ gid_t *grps;
+ int no_grps;
+
+ /*
+ * first we get the current list of groups - the application
+ * will have previously done an initgroups(), or equivalent.
+ */
+
+ D(("counting supplementary groups"));
+ no_grps = getgroups(0, NULL); /* find the current number of groups */
+ if (no_grps > 0) {
+ grps = calloc( blk_size(no_grps) , sizeof(gid_t) );
+ D(("copying current list into grps [%d big]",blk_size(no_grps)));
+ (void) getgroups(no_grps, grps);
+#ifdef DEBUG
+ {
+ int z;
+ for (z=0; z<no_grps; ++z) {
+ D(("gid[%d]=%d", z, grps[z]));
+ }
+ }
+#endif
+ } else {
+ D(("no supplementary groups known"));
+ no_grps = 0;
+ grps = NULL;
+ }
+
+ here_and_now = time_now(); /* find current time */
+
+ /* parse the rules in the configuration file */
+ do {
+ int good=TRUE;
+
+ /* here we get the service name field */
+
+ fd = read_field(fd,&buffer,&from,&to);
+ if (!buffer || !buffer[0]) {
+ /* empty line .. ? */
+ continue;
+ }
+ ++count;
+ D(("working on rule #%d",count));
+
+ good = logic_field(service, buffer, count, is_same);
+ D(("with service: %s", good ? "passes":"fails" ));
+
+ /* here we get the terminal name field */
+
+ fd = read_field(fd,&buffer,&from,&to);
+ if (!buffer || !buffer[0]) {
+ _log_err(PAM_GROUP_CONF "; no tty entry #%d", count);
+ continue;
+ }
+ good &= logic_field(tty, buffer, count, is_same);
+ D(("with tty: %s", good ? "passes":"fails" ));
+
+ /* here we get the username field */
+
+ fd = read_field(fd,&buffer,&from,&to);
+ if (!buffer || !buffer[0]) {
+ _log_err(PAM_GROUP_CONF "; no user entry #%d", count);
+ continue;
+ }
+ good &= logic_field(user, buffer, count, is_same);
+ D(("with user: %s", good ? "passes":"fails" ));
+
+ /* here we get the time field */
+
+ fd = read_field(fd,&buffer,&from,&to);
+ if (!buffer || !buffer[0]) {
+ _log_err(PAM_GROUP_CONF "; no time entry #%d", count);
+ continue;
+ }
+
+ good &= logic_field(&here_and_now, buffer, count, check_time);
+ D(("with time: %s", good ? "passes":"fails" ));
+
+ fd = read_field(fd,&buffer,&from,&to);
+ if (!buffer || !buffer[0]) {
+ _log_err(PAM_GROUP_CONF "; no listed groups for rule #%d"
+ , count);
+ continue;
+ }
+
+ /*
+ * so we have a list of groups, we need to turn it into
+ * something to send to setgroups(2)
+ */
+
+ if (good) {
+ D(("adding %s to gid list", buffer));
+ good = mkgrplist(buffer, &grps, no_grps);
+ if (good < 0) {
+ no_grps = 0;
+ } else {
+ no_grps = good;
+ }
+ }
+
+ /* check the line is terminated correctly */
+
+ fd = read_field(fd,&buffer,&from,&to);
+ if (buffer && buffer[0]) {
+ _log_err(PAM_GROUP_CONF "; poorly terminated rule #%d", count);
+ }
+
+ if (good > 0) {
+ D(("rule #%d passed, added %d groups", count, good));
+ } else if (good < 0) {
+ retval = PAM_BUF_ERR;
+ } else {
+ D(("rule #%d failed", count));
+ }
+
+ } while (buffer);
+
+ /* now set the groups for the user */
+
+ if (no_grps > 0) {
+ int err;
+ D(("trying to set %d groups", no_grps));
+#ifdef DEBUG
+ for (err=0; err<no_grps; ++err) {
+ D(("gid[%d]=%d", err, grps[err]));
+ }
+#endif
+ if ((err = setgroups(no_grps, grps))) {
+ D(("but couldn't set groups %d", err));
+ _log_err("unable to set the group membership for user (err=%d)"
+ , err);
+ retval = PAM_CRED_ERR;
+ }
+ }
+
+ if (grps) { /* tidy up */
+ memset(grps, 0, sizeof(gid_t) * blk_size(no_grps));
+ _pam_drop(grps);
+ no_grps = 0;
+ }
+
+ return retval;
+}
+
+/* --- public authentication management functions --- */
+
+PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags
+ , int argc, const char **argv)
+{
+ return PAM_IGNORE;
+}
+
+PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags
+ , int argc, const char **argv)
+{
+ const char *service=NULL, *tty=NULL;
+ const char *user=NULL;
+ int retval;
+ unsigned setting;
+
+ /* only interested in establishing credentials */
+
+ setting = flags;
+ if (!(setting & PAM_ESTABLISH_CRED)) {
+ D(("ignoring call - not for establishing credentials"));
+ return PAM_SUCCESS; /* don't fail because of this */
+ }
+
+ /* set service name */
+
+ if (pam_get_item(pamh, PAM_SERVICE, (const void **)&service)
+ != PAM_SUCCESS || service == NULL) {
+ _log_err("cannot find the current service name");
+ return PAM_ABORT;
+ }
+
+ /* set username */
+
+ if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS || user == NULL
+ || *user == '\0') {
+ _log_err("cannot determine the user's name");
+ return PAM_USER_UNKNOWN;
+ }
+
+ /* set tty name */
+
+ if (pam_get_item(pamh, PAM_TTY, (const void **)&tty) != PAM_SUCCESS
+ || tty == NULL) {
+ D(("PAM_TTY not set, probing stdin"));
+ tty = ttyname(STDIN_FILENO);
+ if (tty == NULL) {
+ _log_err("couldn't get the tty name");
+ return PAM_ABORT;
+ }
+ if (pam_set_item(pamh, PAM_TTY, tty) != PAM_SUCCESS) {
+ _log_err("couldn't set tty name");
+ return PAM_ABORT;
+ }
+ }
+
+ if (strncmp("/dev/",tty,5) == 0) { /* strip leading /dev/ */
+ tty += 5;
+ }
+
+ /* good, now we have the service name, the user and the terminal name */
+
+ D(("service=%s", service));
+ D(("user=%s", user));
+ D(("tty=%s", tty));
+
+#ifdef WANT_PWDB
+
+ /* We initialize the pwdb library and check the account */
+ retval = pwdb_start(); /* initialize */
+ if (retval == PWDB_SUCCESS) {
+ retval = check_account(service,tty,user); /* get groups */
+ (void) pwdb_end(); /* tidy up */
+ } else {
+ D(("failed to initialize pwdb; %s", pwdb_strerror(retval)));
+ _log_err("unable to initialize libpwdb");
+ retval = PAM_ABORT;
+ }
+
+#else /* WANT_PWDB */
+ retval = check_account(service,tty,user); /* get groups */
+#endif /* WANT_PWDB */
+
+ return retval;
+}
+
+/* end of module definition */
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_group_modstruct = {
+ "pam_group",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+#endif
diff --git a/modules/pam_issue/.cvsignore b/modules/pam_issue/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_issue/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_issue/Makefile b/modules/pam_issue/Makefile
new file mode 100644
index 00000000..6770ef36
--- /dev/null
+++ b/modules/pam_issue/Makefile
@@ -0,0 +1,86 @@
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# Michael K. Johnson <johnsonm@redhat.com> 1996/10/24
+#
+
+TITLE=pam_issue
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ mkdir -p ./dynamic
+endif
+ifdef STATIC
+ mkdir -p ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD) $(LINKLIBS)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ mkdir -p $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ install -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak dynamic/* static/*
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_issue/pam_issue.c b/modules/pam_issue/pam_issue.c
new file mode 100644
index 00000000..9fbf2551
--- /dev/null
+++ b/modules/pam_issue/pam_issue.c
@@ -0,0 +1,265 @@
+/* pam_issue module - a simple /etc/issue parser to set PAM_USER_PROMPT
+ *
+ * Copyright 1999 by Ben Collins <bcollins@debian.org>
+ *
+ * Needs to be called before any other auth modules so we can setup the
+ * user prompt before it's first used. Allows one argument option, which
+ * is the full path to a file to be used for issue (uses /etc/issue as a
+ * default) such as "issue=/etc/issue.telnet".
+ *
+ * We can also parse escapes within the the issue file (enabled by
+ * default, but can be disabled with the "noesc" option). It's the exact
+ * same parsing as util-linux's agetty program performs.
+ *
+ * Released under the GNU LGPL version 2 or later
+ */
+
+#define _GNU_SOURCE
+#define _BSD_SOURCE
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+#include <utmp.h>
+#include <malloc.h>
+
+#include <security/_pam_macros.h>
+
+#define PAM_SM_AUTH
+
+#include <security/pam_modules.h>
+
+static int _user_prompt_set = 0;
+
+char *do_prompt (FILE *);
+
+/* --- authentication management functions (only) --- */
+
+PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc,
+ const char **argv)
+{
+ int retval = PAM_SUCCESS;
+ FILE *fd;
+ int parse_esc = 1;
+ char *prompt_tmp = NULL, *cur_prompt = NULL;
+ struct stat st;
+ char *issue_file = NULL;
+
+ /* If we've already set the prompt, don't set it again */
+ if(_user_prompt_set)
+ return PAM_IGNORE;
+ else
+ /* we set this here so if we fail below, we wont get further
+ than this next time around (only one real failure) */
+ _user_prompt_set = 1;
+
+ for ( ; argc-- > 0 ; ++argv ) {
+ if (!strncmp(*argv,"issue=",6)) {
+ issue_file = (char *) strdup(6+*argv);
+ if (issue_file != NULL) {
+ D(("set issue_file to: %s", issue_file));
+ } else {
+ D(("failed to strdup issue_file - ignored"));
+ return PAM_IGNORE;
+ }
+ } else if (!strcmp(*argv,"noesc")) {
+ parse_esc = 0;
+ D(("turning off escape parsing by request"));
+ } else
+ D(("unknown option passed: %s", *argv));
+ }
+
+ if (issue_file == NULL)
+ issue_file = strdup("/etc/issue");
+
+ if ((fd = fopen(issue_file, "r")) != NULL) {
+ int tot_size = 0;
+
+ if (stat(issue_file, &st) < 0)
+ return PAM_IGNORE;
+
+ retval = pam_get_item(pamh, PAM_USER_PROMPT, (const void **) &cur_prompt);
+ if (retval != PAM_SUCCESS)
+ return PAM_IGNORE;
+
+ /* first read in the issue file */
+
+ if (parse_esc)
+ prompt_tmp = do_prompt(fd);
+ else {
+ int count = 0;
+ prompt_tmp = malloc(st.st_size + 1);
+ if (prompt_tmp == NULL) return PAM_IGNORE;
+ memset (prompt_tmp, '\0', st.st_size + 1);
+ count = fread(prompt_tmp, sizeof(char *), st.st_size, fd);
+ prompt_tmp[st.st_size] = '\0';
+ }
+
+ fclose(fd);
+
+ tot_size = strlen(prompt_tmp) + strlen(cur_prompt) + 1;
+
+ /*
+ * alloc some extra space for the original prompt
+ * and postpend it to the buffer
+ */
+ prompt_tmp = realloc(prompt_tmp, tot_size);
+ strcpy(prompt_tmp+strlen(prompt_tmp), cur_prompt);
+
+ prompt_tmp[tot_size] = '\0';
+
+ retval = pam_set_item(pamh, PAM_USER_PROMPT, (const char *) prompt_tmp);
+
+ free(issue_file);
+ free(prompt_tmp);
+ } else {
+ D(("could not open issue_file: %s", issue_file));
+ return PAM_IGNORE;
+ }
+
+ return retval;
+}
+
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc,
+ const char **argv)
+{
+ return PAM_IGNORE;
+}
+
+char *do_prompt(FILE *fd)
+{
+ int c, size = 1024;
+ char *issue = (char *)malloc(size);
+ char buf[1024];
+ struct utsname uts;
+
+ if (issue == NULL || fd == NULL)
+ return NULL;
+
+ issue[0] = '\0'; /* zero this, for strcat to work on first buf */
+ (void) uname(&uts);
+
+ while ((c = getc(fd)) != EOF) {
+ if (c == '\\') {
+ c = getc(fd);
+ switch (c) {
+ case 's':
+ snprintf (buf, 1024, "%s", uts.sysname);
+ break;
+ case 'n':
+ snprintf (buf, 1024, "%s", uts.nodename);
+ break;
+ case 'r':
+ snprintf (buf, 1024, "%s", uts.release);
+ break;
+ case 'v':
+ snprintf (buf, 1024, "%s", uts.version);
+ break;
+ case 'm':
+ snprintf (buf, 1024, "%s", uts.machine);
+ break;
+ case 'o':
+ {
+ char domainname[256];
+
+ getdomainname(domainname, sizeof(domainname));
+ domainname[sizeof(domainname)-1] = '\0';
+ snprintf (buf, 1024, "%s", domainname);
+ }
+ break;
+
+ case 'd':
+ case 't':
+ {
+ const char *weekday[] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu",
+ "Fri", "Sat" };
+ const char *month[] = {
+ "Jan", "Feb", "Mar", "Apr", "May",
+ "Jun", "Jul", "Aug", "Sep", "Oct",
+ "Nov", "Dec" };
+ time_t now;
+ struct tm *tm;
+
+ (void) time (&now);
+ tm = localtime(&now);
+
+ if (c == 'd')
+ snprintf (buf, 1024, "%s %s %d %d",
+ weekday[tm->tm_wday], month[tm->tm_mon],
+ tm->tm_mday,
+ tm->tm_year + 1900);
+ else
+ snprintf (buf, 1024, "%02d:%02d:%02d",
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+ }
+ break;
+ case 'l':
+ {
+ char *ttyn = ttyname(1);
+ if (!strncmp(ttyn, "/dev/", 5))
+ ttyn += 5;
+ snprintf (buf, 1024, "%s", ttyn);
+ }
+ break;
+ case 'u':
+ case 'U':
+ {
+ int users = 0;
+ struct utmp *ut;
+ setutent();
+ while ((ut = getutent()))
+ if (ut->ut_type == USER_PROCESS)
+ users++;
+ endutent();
+ printf ("%d ", users);
+ if (c == 'U')
+ snprintf (buf, 1024, "%s", (users == 1) ?
+ " user" : " users");
+ break;
+ }
+ default:
+ buf[0] = c; buf[1] = '\0';
+ }
+ if ((strlen(issue) + strlen(buf)) < size + 1) {
+ size += strlen(buf) + 1;
+ issue = (char *) realloc (issue, size);
+ }
+ strcat(issue, buf);
+ } else {
+ buf[0] = c; buf[1] = '\0';
+ if ((strlen(issue) + strlen(buf)) < size + 1) {
+ size += strlen(buf) + 1;
+ issue = (char *) realloc (issue, size);
+ }
+ strcat(issue, buf);
+ }
+ }
+ return issue;
+}
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_issue_modstruct = {
+ "pam_issue",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+#endif
+
+/* end of module definition */
diff --git a/modules/pam_lastlog/.cvsignore b/modules/pam_lastlog/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_lastlog/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_lastlog/Makefile b/modules/pam_lastlog/Makefile
new file mode 100644
index 00000000..b5a6f212
--- /dev/null
+++ b/modules/pam_lastlog/Makefile
@@ -0,0 +1,112 @@
+#
+# $Id$
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# $Log$
+# Revision 1.1 2000/06/20 22:11:40 agmorgan
+# Initial revision
+#
+# Revision 1.1.1.1 1998/07/12 05:17:17 morgan
+# Linux PAM sources pre-0.66
+#
+# Revision 1.2 1997/04/05 06:17:14 morgan
+# fakeroot fixed
+#
+# Revision 1.1 1997/01/04 20:29:28 morgan
+# Initial revision
+#
+#
+#
+# Created by Andrew Morgan <morgan@parc.power.net> 1996/12/8
+#
+
+# Convenient defaults for compiling independently of the full source
+# tree.
+ifndef FULL_LINUX_PAM_SOURCE_TREE
+export DYNAMIC=-DPAM_DYNAMIC
+export CC=gcc
+export CFLAGS=-O2 -Dlinux -DLINUX_PAM \
+ -ansi -D_POSIX_SOURCE -Wall -Wwrite-strings \
+ -Wpointer-arith -Wcast-qual -Wcast-align -Wtraditional \
+ -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Winline \
+ -Wshadow -pedantic -fPIC
+export MKDIR=mkdir -p
+export LD_D=gcc -shared -Xlinker -x
+endif
+
+TITLE=pam_lastlog
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+
+####################### don't edit below #######################
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak dynamic/* static/*
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_lastlog/pam_lastlog.c b/modules/pam_lastlog/pam_lastlog.c
new file mode 100644
index 00000000..7207976e
--- /dev/null
+++ b/modules/pam_lastlog/pam_lastlog.c
@@ -0,0 +1,463 @@
+/* pam_lastlog module */
+
+/*
+ * $Id$
+ *
+ * Written by Andrew Morgan <morgan@linux.kernel.org> 1996/3/11
+ *
+ * This module does the necessary work to display the last login
+ * time+date for this user, it then updates this entry for the
+ * present (login) service.
+ */
+
+#ifdef linux
+# define _GNU_SOURCE
+# include <features.h>
+#endif
+
+#include <fcntl.h>
+#include <time.h>
+#ifdef HAVE_UTMP_H
+# include <utmp.h>
+#else
+# include <lastlog.h>
+#endif
+#include <pwd.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#ifdef WANT_PWDB
+#include <pwdb/pwdb_public.h> /* use POSIX front end */
+#endif
+
+#if defined(hpux) || defined(sunos) || defined(solaris)
+# ifndef _PATH_LASTLOG
+# define _PATH_LASTLOG "/usr/adm/lastlog"
+# endif /* _PATH_LASTLOG */
+# ifndef UT_HOSTSIZE
+# define UT_HOSTSIZE 16
+# endif /* UT_HOSTSIZE */
+# ifndef UT_LINESIZE
+# define UT_LINESIZE 12
+# endif /* UT_LINESIZE */
+#endif
+#if defined(hpux)
+struct lastlog {
+ time_t ll_time;
+ char ll_line[UT_LINESIZE];
+ char ll_host[UT_HOSTSIZE]; /* same as in utmp */
+};
+#endif /* hpux */
+
+/* XXX - time before ignoring lock. Is 1 sec enough? */
+#define LASTLOG_IGNORE_LOCK_TIME 1
+
+#define DEFAULT_HOST "" /* "[no.where]" */
+#define DEFAULT_TERM "" /* "tt???" */
+#define LASTLOG_NEVER_WELCOME "Welcome to your new account!"
+#define LASTLOG_INTRO "Last login:"
+#define LASTLOG_TIME " %s"
+#define _LASTLOG_HOST_FORMAT " from %%.%ds"
+#define _LASTLOG_LINE_FORMAT " on %%.%ds"
+#define LASTLOG_TAIL ""
+#define LASTLOG_MAXSIZE (sizeof(LASTLOG_INTRO)+0 \
+ +sizeof(LASTLOG_TIME)+strlen(the_time) \
+ +sizeof(_LASTLOG_HOST_FORMAT)+UT_HOSTSIZE \
+ +sizeof(_LASTLOG_LINE_FORMAT)+UT_LINESIZE \
+ +sizeof(LASTLOG_TAIL))
+
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_SESSION
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+/* some syslogging */
+
+static void _log_err(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("PAM-lastlog", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+/* argument parsing */
+
+#define LASTLOG_DATE 01 /* display the date of the last login */
+#define LASTLOG_HOST 02 /* display the last host used (if set) */
+#define LASTLOG_LINE 04 /* display the last terminal used */
+#define LASTLOG_NEVER 010 /* display a welcome message for first login */
+#define LASTLOG_DEBUG 020 /* send info to syslog(3) */
+#define LASTLOG_QUIET 040 /* keep quiet about things */
+
+static int _pam_parse(int flags, int argc, const char **argv)
+{
+ int ctrl=(LASTLOG_DATE|LASTLOG_HOST|LASTLOG_LINE);
+
+ /* does the appliction require quiet? */
+ if (flags & PAM_SILENT) {
+ ctrl |= LASTLOG_QUIET;
+ }
+
+ /* step through arguments */
+ for (; argc-- > 0; ++argv) {
+
+ /* generic options */
+
+ if (!strcmp(*argv,"debug")) {
+ ctrl |= LASTLOG_DEBUG;
+ } else if (!strcmp(*argv,"nodate")) {
+ ctrl |= ~LASTLOG_DATE;
+ } else if (!strcmp(*argv,"noterm")) {
+ ctrl |= ~LASTLOG_LINE;
+ } else if (!strcmp(*argv,"nohost")) {
+ ctrl |= ~LASTLOG_HOST;
+ } else if (!strcmp(*argv,"silent")) {
+ ctrl |= LASTLOG_QUIET;
+ } else if (!strcmp(*argv,"never")) {
+ ctrl |= LASTLOG_NEVER;
+ } else {
+ _log_err(LOG_ERR,"unknown option; %s",*argv);
+ }
+ }
+
+ D(("ctrl = %o", ctrl));
+ return ctrl;
+}
+
+/* a front end for conversations */
+
+static int converse(pam_handle_t *pamh, int ctrl, int nargs
+ , struct pam_message **message
+ , struct pam_response **response)
+{
+ int retval;
+ struct pam_conv *conv;
+
+ D(("begin to converse"));
+
+ retval = pam_get_item( pamh, PAM_CONV, (const void **) &conv ) ;
+ if ( retval == PAM_SUCCESS ) {
+
+ retval = conv->conv(nargs, ( const struct pam_message ** ) message
+ , response, conv->appdata_ptr);
+
+ D(("returned from application's conversation function"));
+
+ if (retval != PAM_SUCCESS && (ctrl & LASTLOG_DEBUG) ) {
+ _log_err(LOG_DEBUG, "conversation failure [%s]"
+ , pam_strerror(pamh, retval));
+ }
+
+ } else {
+ _log_err(LOG_ERR, "couldn't obtain coversation function [%s]"
+ , pam_strerror(pamh, retval));
+ }
+
+ D(("ready to return from module conversation"));
+
+ return retval; /* propagate error status */
+}
+
+static int make_remark(pam_handle_t *pamh, int ctrl, const char *remark)
+{
+ int retval;
+
+ if (!(ctrl & LASTLOG_QUIET)) {
+ struct pam_message msg[1], *mesg[1];
+ struct pam_response *resp=NULL;
+
+ mesg[0] = &msg[0];
+ msg[0].msg_style = PAM_TEXT_INFO;
+ msg[0].msg = remark;
+
+ retval = converse(pamh, ctrl, 1, mesg, &resp);
+
+ msg[0].msg = NULL;
+ if (resp) {
+ _pam_drop_reply(resp, 1);
+ }
+ } else {
+ D(("keeping quiet"));
+ retval = PAM_SUCCESS;
+ }
+
+ D(("returning %s", pam_strerror(pamh, retval)));
+ return retval;
+}
+
+/*
+ * Values for the announce flags..
+ */
+
+static int last_login_date(pam_handle_t *pamh, int announce, uid_t uid)
+{
+ struct flock last_lock;
+ struct lastlog last_login;
+ int retval = PAM_SESSION_ERR;
+ int last_fd;
+
+ /* obtain the last login date and all the relevant info */
+ last_fd = open(_PATH_LASTLOG, O_RDWR);
+ if (last_fd < 0) {
+ D(("unable to open the %s file", _PATH_LASTLOG));
+ if (announce & LASTLOG_DEBUG) {
+ _log_err(LOG_DEBUG, "unable to open %s file", _PATH_LASTLOG);
+ }
+ retval = PAM_PERM_DENIED;
+ } else {
+ int win;
+
+ /* read the lastlogin file - for this uid */
+ (void) lseek(last_fd, sizeof(last_login) * (off_t) uid, SEEK_SET);
+
+ memset(&last_lock, 0, sizeof(last_lock));
+ last_lock.l_type = F_RDLCK;
+ last_lock.l_whence = SEEK_SET;
+ last_lock.l_start = sizeof(last_login) * (off_t) uid;
+ last_lock.l_len = sizeof(last_login);
+
+ if ( fcntl(last_fd, F_SETLK, &last_lock) < 0 ) {
+ D(("locking %s failed..(waiting a little)", _PATH_LASTLOG));
+ _log_err(LOG_ALERT, "%s file is locked/read", _PATH_LASTLOG);
+ sleep(LASTLOG_IGNORE_LOCK_TIME);
+ }
+
+ win = ( read(last_fd, &last_login, sizeof(last_login))
+ == sizeof(last_login) );
+
+ last_lock.l_type = F_UNLCK;
+ (void) fcntl(last_fd, F_SETLK, &last_lock); /* unlock */
+
+ if (!win) {
+ D(("First login for user uid=%d", _PATH_LASTLOG, uid));
+ if (announce & LASTLOG_DEBUG) {
+ _log_err(LOG_DEBUG, "creating lastlog for uid %d", uid);
+ }
+ memset(&last_login, 0, sizeof(last_login));
+ }
+
+ /* rewind */
+ (void) lseek(last_fd, sizeof(last_login) * (off_t) uid, SEEK_SET);
+
+ if (!(announce & LASTLOG_QUIET)) {
+ if (last_login.ll_time) {
+ char *the_time;
+ char *remark;
+
+ the_time = ctime(&last_login.ll_time);
+ the_time[-1+strlen(the_time)] = '\0'; /* delete '\n' */
+
+ remark = malloc(LASTLOG_MAXSIZE);
+ if (remark == NULL) {
+ D(("no memory for last login remark"));
+ retval = PAM_BUF_ERR;
+ } else {
+ int at;
+
+ /* printing prefix */
+ at = sprintf(remark, "%s", LASTLOG_INTRO);
+
+ /* we want the date? */
+ if (announce & LASTLOG_DATE) {
+ at += sprintf(remark+at, LASTLOG_TIME, the_time);
+ }
+
+ /* we want & have the host? */
+ if ((announce & LASTLOG_HOST)
+ && (last_login.ll_host[0] != '\0')) {
+ char format[2*sizeof(_LASTLOG_HOST_FORMAT)];
+
+ (void) sprintf(format, _LASTLOG_HOST_FORMAT
+ , UT_HOSTSIZE);
+ D(("format: %s", format));
+ at += sprintf(remark+at, format, last_login.ll_host);
+ _pam_overwrite(format);
+ }
+
+ /* we want and have the terminal? */
+ if ((announce & LASTLOG_LINE)
+ && (last_login.ll_line[0] != '\0')) {
+ char format[2*sizeof(_LASTLOG_LINE_FORMAT)];
+
+ (void) sprintf(format, _LASTLOG_LINE_FORMAT
+ , UT_LINESIZE);
+ D(("format: %s", format));
+ at += sprintf(remark+at, format, last_login.ll_line);
+ _pam_overwrite(format);
+ }
+
+ /* display requested combo */
+ sprintf(remark+at, "%s", LASTLOG_TAIL);
+
+ retval = make_remark(pamh, announce, remark);
+
+ /* free all the stuff malloced */
+ _pam_overwrite(remark);
+ _pam_drop(remark);
+ }
+ } else if ((!last_login.ll_time) && (announce & LASTLOG_NEVER)) {
+ D(("this is the first time this user has logged in"));
+ retval = make_remark(pamh, announce, LASTLOG_NEVER_WELCOME);
+ }
+ } else {
+ D(("no text was requested"));
+ retval = PAM_SUCCESS;
+ }
+
+ /* write latest value */
+ {
+ const char *remote_host=NULL
+ , *terminal_line=DEFAULT_TERM;
+
+ /* set this login date */
+ D(("set the most recent login time"));
+
+ (void) time(&last_login.ll_time); /* set the time */
+
+ /* set the remote host */
+ (void) pam_get_item(pamh, PAM_RHOST, (const void **)&remote_host);
+ if (remote_host == NULL) {
+ remote_host = DEFAULT_HOST;
+ }
+
+ /* copy to last_login */
+ strncpy(last_login.ll_host, remote_host
+ , sizeof(last_login.ll_host));
+ remote_host = NULL;
+
+ /* set the terminal line */
+ (void) pam_get_item(pamh, PAM_TTY, (const void **)&terminal_line);
+ D(("terminal = %s", terminal_line));
+ if (terminal_line == NULL) {
+ terminal_line = DEFAULT_TERM;
+ } else if ( !strncmp("/dev/", terminal_line, 5) ) {
+ /* strip leading "/dev/" from tty.. */
+ terminal_line += 5;
+ }
+ D(("terminal = %s", terminal_line));
+
+ /* copy to last_login */
+ strncpy(last_login.ll_line, terminal_line
+ , sizeof(last_login.ll_line));
+ terminal_line = NULL;
+
+ D(("locking last_log file"));
+
+ /* now we try to lock this file-record exclusively; non-blocking */
+ memset(&last_lock, 0, sizeof(last_lock));
+ last_lock.l_type = F_WRLCK;
+ last_lock.l_whence = SEEK_SET;
+ last_lock.l_start = sizeof(last_login) * (off_t) uid;
+ last_lock.l_len = sizeof(last_login);
+
+ if ( fcntl(last_fd, F_SETLK, &last_lock) < 0 ) {
+ D(("locking %s failed..(waiting a little)", _PATH_LASTLOG));
+ _log_err(LOG_ALERT, "%s file is locked/write", _PATH_LASTLOG);
+ sleep(LASTLOG_IGNORE_LOCK_TIME);
+ }
+
+ D(("writing to the last_log file"));
+ (void) write(last_fd, &last_login, sizeof(last_login));
+
+ last_lock.l_type = F_UNLCK;
+ (void) fcntl(last_fd, F_SETLK, &last_lock); /* unlock */
+ D(("unlocked"));
+
+ close(last_fd); /* all done */
+ }
+ D(("all done with last login"));
+ }
+
+ /* reset the last login structure */
+ memset(&last_login, 0, sizeof(last_login));
+
+ return retval;
+}
+
+/* --- authentication management functions (only) --- */
+
+PAM_EXTERN
+int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc
+ , const char **argv)
+{
+ int retval, ctrl;
+ const char *user;
+ const struct passwd *pwd;
+ uid_t uid;
+
+ /*
+ * this module gets the uid of the PAM_USER. Uses it to display
+ * last login info and then updates the lastlog for that user.
+ */
+
+ ctrl = _pam_parse(flags, argc, argv);
+
+ /* which user? */
+
+ retval = pam_get_item(pamh, PAM_USER, (const void **)&user);
+ if (retval != PAM_SUCCESS || user == NULL || *user == '\0') {
+ _log_err(LOG_NOTICE, "user unknown");
+ return PAM_USER_UNKNOWN;
+ }
+
+ /* what uid? */
+
+ pwd = getpwnam(user);
+ if (pwd == NULL) {
+ D(("couldn't identify user %s", user));
+ return PAM_CRED_INSUFFICIENT;
+ }
+ uid = pwd->pw_uid;
+ pwd = NULL; /* tidy up */
+
+ /* process the current login attempt (indicate last) */
+
+ retval = last_login_date(pamh, ctrl, uid);
+
+ /* indicate success or failure */
+
+ uid = -1; /* forget this */
+
+ return retval;
+}
+
+PAM_EXTERN
+int pam_sm_close_session(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_lastlog_modstruct = {
+ "pam_lastlog",
+ NULL,
+ NULL,
+ NULL,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ NULL,
+};
+
+#endif
+
+/* end of module definition */
diff --git a/modules/pam_limits/.cvsignore b/modules/pam_limits/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_limits/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_limits/Makefile b/modules/pam_limits/Makefile
new file mode 100644
index 00000000..fe684863
--- /dev/null
+++ b/modules/pam_limits/Makefile
@@ -0,0 +1,98 @@
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# Created by Cristian Gafton <gafton@redhat.com> 1996/09/10
+#
+
+ifeq ($(OS),linux)
+TITLE=pam_limits
+CONFD=$(CONFIGED)/security
+export CONFD
+CONFILE=$(CONFD)/limits.conf
+export CONFILE
+
+CFLAGS+=-DLIMITS_FILE=\"$(CONFILE)\"
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ifdef DYNAMIC
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+ $(MKDIR) $(FAKEROOT)$(SCONFIGED)
+ bash -f ./install_conf
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~ *.so
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak dynamic/* static/*
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+else
+include ../dont_makefile
+endif
diff --git a/modules/pam_limits/README b/modules/pam_limits/README
new file mode 100644
index 00000000..06a6857a
--- /dev/null
+++ b/modules/pam_limits/README
@@ -0,0 +1,87 @@
+
+pam_limits module:
+ Imposing user limits on login.
+
+THEORY OF OPERATION:
+
+First, make a root-only-readable file (/etc/limits by default or LIMITS_FILE
+defined Makefile) that describes the resource limits you wish to impose. No
+limits are imposed on UID 0 accounts.
+
+Each line describes a limit for a user in the form:
+
+<domain> <type> <item> <value>
+
+Where:
+<domain> can be:
+ - an user name
+ - a group name, with @group syntax
+ - the wildcard *, for default entry
+
+<type> can have the two values:
+ - "soft" for enforcinf the soft limits
+ - "hard" for enforcing hard limits
+
+<item> can be one of the following:
+ - core - limits the core file size (KB)
+ - data - max data size (KB)
+ - fsize - maximum filesize (KB)
+ - memlock - max locked-in-memory address space (KB)
+ - nofile - max number of open files
+ - rss - max resident set size (KB)
+ - stack - max stack size (KB)
+ - cpu - max CPU time (MIN)
+ - nproc - max number of processes
+ - as - address space limit
+ - maxlogins - max number of logins for this user
+ - maxsyslogins - max number of logins on the system
+
+To completely disable limits for a user (or a group), a single dash (-)
+will do (Example: 'bin -', '@admin -'). Please remember that individual
+limits have priority over group limits, so if you impose no limits for admin
+group, but one of the members in this group have a limits line, the user
+will have its limits set according to this line.
+
+Also, please note that all limit settings are set PER LOGIN. They are
+not global, nor are they permanent (the session only)
+
+In the LIMITS_FILE, the # character introduces a comment - the rest of the
+line is ignored.
+
+The pam_limits module does its best to report configuration problems found
+in LIMITS_FILE via syslog.
+
+EXAMPLE configuration file:
+===========================
+* soft core 0
+* hard rss 10000
+@student hard nproc 20
+@faculty soft nproc 20
+@faculty hard nproc 50
+ftp hard nproc 0
+@student - maxlogins 4
+
+
+ARGUMENTS RECOGNIZED:
+ debug verbose logging
+
+ conf=/path/to/file the limits configuration file if different from the
+ one set at compile time.
+
+MODULE SERVICES PROVIDED:
+ session _open_session and _close_session (blank)
+
+USAGE:
+ For the services you need resources limits (login for example) put a
+ the following line in /etc/pam.conf as the last line for that
+ service (usually after the pam_unix session line:
+
+ login session required /lib/security/pam_limits.so
+
+ Replace "login" for each service you are using this module, replace
+ "/lib/security" path with your real modules path.
+
+AUTHOR:
+ Cristian Gafton <gafton@redhat.com>
+ Thanks to Elliot Lee <sopwith@redhat.com> for his comments on
+ improving this module.
diff --git a/modules/pam_limits/install_conf b/modules/pam_limits/install_conf
new file mode 100755
index 00000000..d92c1f95
--- /dev/null
+++ b/modules/pam_limits/install_conf
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+CONFILE=$FAKEROOT"$CONFILE"
+IGNORE_AGE=./.ignore_age
+QUIET_INSTALL=../../.quiet_install
+CONF=./limits.skel
+MODULE=pam_limits
+
+echo
+
+if [ -f "$QUIET_INSTALL" ]; then
+ if [ ! -f "$CONFILE" ]; then
+ yes="y"
+ else
+ yes="skip"
+ fi
+elif [ -f "$IGNORE_AGE" ]; then
+ echo "you don't want to be bothered with the age of your $CONFILE file"
+ yes="n"
+elif [ ! -f "$CONFILE" ] || [ "$CONF" -nt "$CONFILE" ]; then
+ if [ -f "$CONFILE" ]; then
+ echo "An older $MODULE configuration file already exists ($CONFILE)"
+ echo "Do you wish to copy the $CONF file in this distribution"
+ echo "to $CONFILE ? (y/n) [skip] "
+ read yes
+ else
+ yes="y"
+ fi
+else
+ yes="skip"
+fi
+
+if [ "$yes" = "y" ]; then
+ mkdir -p $FAKEROOT$CONFD
+ echo " copying $CONF to $CONFILE"
+ cp $CONF $CONFILE
+else
+ echo " Skipping $CONF installation"
+ if [ "$yes" = "n" ]; then
+ touch "$IGNORE_AGE"
+ fi
+fi
+
+echo
+
+exit 0
diff --git a/modules/pam_limits/limits.skel b/modules/pam_limits/limits.skel
new file mode 100644
index 00000000..5ddd9b4c
--- /dev/null
+++ b/modules/pam_limits/limits.skel
@@ -0,0 +1,42 @@
+# /etc/security/limits.conf
+#
+#Each line describes a limit for a user in the form:
+#
+#<domain> <type> <item> <value>
+#
+#Where:
+#<domain> can be:
+# - an user name
+# - a group name, with @group syntax
+# - the wildcard *, for default entry
+#
+#<type> can have the two values:
+# - "soft" for enforcing the soft limits
+# - "hard" for enforcing hard limits
+#
+#<item> can be one of the following:
+# - core - limits the core file size (KB)
+# - data - max data size (KB)
+# - fsize - maximum filesize (KB)
+# - memlock - max locked-in-memory address space (KB)
+# - nofile - max number of open files
+# - rss - max resident set size (KB)
+# - stack - max stack size (KB)
+# - cpu - max CPU time (MIN)
+# - nproc - max number of processes
+# - as - address space limit
+# - maxlogins - max number of logins for this user
+# - priority - the priority to run user process with
+#
+#<domain> <type> <item> <value>
+#
+
+#* soft core 0
+#* hard rss 10000
+#@student hard nproc 20
+#@faculty soft nproc 20
+#@faculty hard nproc 50
+#ftp hard nproc 0
+#@student - maxlogins 4
+
+# End of file
diff --git a/modules/pam_limits/pam_limits.c b/modules/pam_limits/pam_limits.c
new file mode 100644
index 00000000..e832dc63
--- /dev/null
+++ b/modules/pam_limits/pam_limits.c
@@ -0,0 +1,635 @@
+/*
+ * pam_limits - impose resource limits when opening a user session
+ *
+ * 1.6 - modified for PLD (added process priority settings)
+ * by Marcin Korzonek <mkorz@shadow.eu.org
+ * 1.5 - Elliot Lee's "max system logins patch"
+ * 1.4 - addressed bug in configuration file parser
+ * 1.3 - modified the configuration file format
+ * 1.2 - added 'debug' and 'conf=' arguments
+ * 1.1 - added @group support
+ * 1.0 - initial release - Linux ONLY
+ *
+ * See end for Copyright information
+ */
+
+#if !(defined(linux))
+#error THIS CODE IS KNOWN TO WORK ONLY ON LINUX !!!
+#endif
+
+#define _GNU_SOURCE
+#define _BSD_SOURCE
+
+#include <features.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#ifndef __USE_POSIX2
+#define __USE_POSIX2
+#endif /* __USE_POSIX2 */
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/resource.h>
+#include <utmp.h>
+#ifndef UT_USER /* some systems have ut_name instead of ut_user */
+#define UT_USER ut_user
+#endif
+
+#include <grp.h>
+#include <pwd.h>
+
+/* Module defines */
+#define LINE_LENGTH 1024
+
+#define LIMITS_DEF_USER 0 /* limit was set by an user entry */
+#define LIMITS_DEF_GROUP 1 /* limit was set by a group entry */
+#define LIMITS_DEF_DEFAULT 2 /* limit was set by an default entry */
+#define LIMITS_DEF_NONE 3 /* this limit was not set yet */
+
+/* internal data */
+static char conf_file[BUFSIZ];
+
+struct user_limits_struct {
+ int src_soft;
+ int src_hard;
+ struct rlimit limit;
+};
+
+static struct user_limits_struct limits[RLIM_NLIMITS];
+static int login_limit; /* the max logins limit */
+static int login_limit_def; /* which entry set the login limit */
+static int flag_numsyslogins; /* whether to limit logins only for a
+ specific user or to count all logins */
+static int priority; /* the priority to run user process with */
+
+#define LIMIT_LOGIN RLIM_NLIMITS+1
+#define LIMIT_NUMSYSLOGINS RLIM_NLIMITS+2
+
+#define LIMIT_PRI RLIM_NLIMITS+3
+
+#define LIMIT_SOFT 1
+#define LIMIT_HARD 2
+
+#define PAM_SM_SESSION
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+/* logging */
+static void _pam_log(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("pam_limits", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+/* argument parsing */
+
+#define PAM_DEBUG_ARG 0x0001
+
+static int _pam_parse(int argc, const char **argv)
+{
+ int ctrl=0;
+
+ /* step through arguments */
+ for (ctrl=0; argc-- > 0; ++argv) {
+
+ /* generic options */
+
+ if (!strcmp(*argv,"debug"))
+ ctrl |= PAM_DEBUG_ARG;
+ else if (!strncmp(*argv,"conf=",5))
+ strcpy(conf_file,*argv+5);
+ else {
+ _pam_log(LOG_ERR,"pam_parse: unknown option; %s",*argv);
+ }
+ }
+
+ return ctrl;
+}
+
+
+/* limits stuff */
+#ifndef LIMITS_FILE
+#define LIMITS_FILE "/etc/security/limits.conf"
+#endif
+
+#define LIMIT_ERR 1 /* error setting a limit */
+#define LOGIN_ERR 2 /* too many logins err */
+
+/* Counts the number of user logins and check against the limit*/
+static int check_logins(const char *name, int limit, int ctrl)
+{
+ struct utmp *ut;
+ unsigned int count;
+
+ if (ctrl & PAM_DEBUG_ARG) {
+ _pam_log(LOG_DEBUG, "checking logins for '%s' / %d\n", name,limit);
+ }
+
+ if (limit < 0)
+ return 0; /* no limits imposed */
+ if (limit == 0) /* maximum 0 logins ? */ {
+ _pam_log(LOG_WARNING, "No logins allowed for '%s'\n", name);
+ return LOGIN_ERR;
+ }
+
+ setutent();
+ count = 0;
+ while((ut = getutent())) {
+#ifdef USER_PROCESS
+ if (ut->ut_type != USER_PROCESS)
+ continue;
+#endif
+ if (ut->UT_USER[0] == '\0')
+ continue;
+ if (!flag_numsyslogins
+ && strncmp(name, ut->UT_USER, sizeof(ut->UT_USER)) != 0)
+ continue;
+ if (++count >= limit)
+ break;
+ }
+ endutent();
+ if (count >= limit) {
+ if (name) {
+ _pam_log(LOG_WARNING, "Too many logins (max %d) for %s",
+ limit, name);
+ } else {
+ _pam_log(LOG_WARNING, "Too many system logins (max %d)", limit);
+ }
+ return LOGIN_ERR;
+ }
+ return 0;
+}
+
+/* checks if a user is on a list of members of the GID 0 group */
+static int is_on_list(char * const *list, const char *member)
+{
+ while (*list) {
+ if (strcmp(*list, member) == 0)
+ return 1;
+ list++;
+ }
+ return 0;
+}
+
+/* Checks if a user is a member of a group */
+static int is_on_group(const char *user_name, const char *group_name)
+{
+ struct passwd *pwd;
+ struct group *grp, *pgrp;
+ char uname[LINE_LENGTH], gname[LINE_LENGTH];
+
+ if (!strlen(user_name))
+ return 0;
+ if (!strlen(group_name))
+ return 0;
+ memset(uname, 0, sizeof(uname));
+ strncpy(uname, user_name, LINE_LENGTH);
+ memset(gname, 0, sizeof(gname));
+ strncpy(gname, group_name, LINE_LENGTH);
+
+ setpwent();
+ pwd = getpwnam(uname);
+ endpwent();
+ if (!pwd)
+ return 0;
+
+ /* the info about this group */
+ setgrent();
+ grp = getgrnam(gname);
+ endgrent();
+ if (!grp)
+ return 0;
+
+ /* first check: is a member of the group_name group ? */
+ if (is_on_list(grp->gr_mem, uname))
+ return 1;
+
+ /* next check: user primary group is group_name ? */
+ setgrent();
+ pgrp = getgrgid(pwd->pw_gid);
+ endgrent();
+ if (!pgrp)
+ return 0;
+ if (!strcmp(pgrp->gr_name, gname))
+ return 1;
+
+ return 0;
+}
+
+static int init_limits(void)
+{
+ int retval = PAM_SUCCESS;
+
+ D(("called."));
+
+ retval |= getrlimit(RLIMIT_CPU, &limits[RLIMIT_CPU].limit);
+ limits[RLIMIT_CPU].src_soft = LIMITS_DEF_NONE;
+ limits[RLIMIT_CPU].src_hard = LIMITS_DEF_NONE;
+
+ retval |= getrlimit(RLIMIT_FSIZE, &limits[RLIMIT_FSIZE].limit);
+ limits[RLIMIT_FSIZE].src_soft = LIMITS_DEF_NONE;
+ limits[RLIMIT_FSIZE].src_hard = LIMITS_DEF_NONE;
+
+ retval |= getrlimit(RLIMIT_DATA, &limits[RLIMIT_DATA].limit);
+ limits[RLIMIT_DATA].src_soft = LIMITS_DEF_NONE;
+ limits[RLIMIT_DATA].src_hard = LIMITS_DEF_NONE;
+
+ retval |= getrlimit(RLIMIT_STACK, &limits[RLIMIT_STACK].limit);
+ limits[RLIMIT_STACK].src_soft = LIMITS_DEF_NONE;
+ limits[RLIMIT_STACK].src_hard = LIMITS_DEF_NONE;
+
+ retval |= getrlimit(RLIMIT_CORE, &limits[RLIMIT_CORE].limit);
+ limits[RLIMIT_CORE].src_soft = LIMITS_DEF_NONE;
+ limits[RLIMIT_CORE].src_hard = LIMITS_DEF_NONE;
+
+ retval |= getrlimit(RLIMIT_RSS, &limits[RLIMIT_RSS].limit);
+ limits[RLIMIT_RSS].src_soft = LIMITS_DEF_NONE;
+ limits[RLIMIT_RSS].src_hard = LIMITS_DEF_NONE;
+
+ retval |= getrlimit(RLIMIT_NPROC, &limits[RLIMIT_NPROC].limit);
+ limits[RLIMIT_NPROC].src_soft = LIMITS_DEF_NONE;
+ limits[RLIMIT_NPROC].src_hard = LIMITS_DEF_NONE;
+
+ retval |= getrlimit(RLIMIT_NOFILE, &limits[RLIMIT_NOFILE].limit);
+ limits[RLIMIT_NOFILE].src_soft = LIMITS_DEF_NONE;
+ limits[RLIMIT_NOFILE].src_hard = LIMITS_DEF_NONE;
+
+ retval |= getrlimit(RLIMIT_MEMLOCK, &limits[RLIMIT_MEMLOCK].limit);
+ limits[RLIMIT_MEMLOCK].src_soft = LIMITS_DEF_NONE;
+ limits[RLIMIT_MEMLOCK].src_hard = LIMITS_DEF_NONE;
+
+ retval |= getrlimit(RLIMIT_AS, &limits[RLIMIT_AS].limit);
+ limits[RLIMIT_AS].src_soft = LIMITS_DEF_NONE;
+ limits[RLIMIT_AS].src_hard = LIMITS_DEF_NONE;
+ priority = 0;
+ login_limit = -2;
+ login_limit_def = LIMITS_DEF_NONE;
+ return retval;
+}
+
+static void process_limit(int source, const char *lim_type,
+ const char *lim_item, const char *lim_value,
+ int ctrl)
+{
+ int limit_item;
+ int limit_type = 0;
+ long limit_value;
+ const char **endptr = &lim_value;
+ const char *value_orig = lim_value;
+
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_DEBUG, "%s: processing(%d) %s %s %s\n",
+ __FUNCTION__,source,lim_type,lim_item,lim_value);
+
+ if (strcmp(lim_item, "cpu") == 0)
+ limit_item = RLIMIT_CPU;
+ else if (strcmp(lim_item, "fsize") == 0)
+ limit_item = RLIMIT_FSIZE;
+ else if (strcmp(lim_item, "data") == 0)
+ limit_item = RLIMIT_DATA;
+ else if (strcmp(lim_item, "stack") == 0)
+ limit_item = RLIMIT_STACK;
+ else if (strcmp(lim_item, "core") == 0)
+ limit_item = RLIMIT_CORE;
+ else if (strcmp(lim_item, "rss") == 0)
+ limit_item = RLIMIT_RSS;
+ else if (strcmp(lim_item, "nproc") == 0)
+ limit_item = RLIMIT_NPROC;
+ else if (strcmp(lim_item, "nofile") == 0)
+ limit_item = RLIMIT_NOFILE;
+ else if (strcmp(lim_item, "memlock") == 0)
+ limit_item = RLIMIT_MEMLOCK;
+ else if (strcmp(lim_item, "as") == 0)
+ limit_item = RLIMIT_AS;
+ else if (strcmp(lim_item, "maxlogins") == 0) {
+ limit_item = LIMIT_LOGIN;
+ flag_numsyslogins = 0;
+ } else if (strcmp(lim_item, "maxsyslogins") == 0) {
+ limit_item = LIMIT_NUMSYSLOGINS;
+ flag_numsyslogins = 1;
+ } else if (strcmp(lim_item, "priority") == 0) {
+ limit_item = LIMIT_PRI;
+ } else {
+ _pam_log(LOG_DEBUG,"unknown limit item '%s'", lim_item);
+ return;
+ }
+
+ if (strcmp(lim_type,"soft")==0)
+ limit_type=LIMIT_SOFT;
+ else if (strcmp(lim_type, "hard")==0)
+ limit_type=LIMIT_HARD;
+ else if (strcmp(lim_type,"-")==0)
+ limit_type=LIMIT_SOFT | LIMIT_HARD;
+ else if (limit_item != LIMIT_LOGIN && limit_item != LIMIT_NUMSYSLOGINS) {
+ _pam_log(LOG_DEBUG,"unknown limit type '%s'", lim_type);
+ return;
+ }
+
+ limit_value = strtol(lim_value, endptr, 10);
+ if (limit_value == 0 && value_orig == *endptr) { /* no chars read */
+ if (strcmp(lim_value,"-") != 0) {
+ _pam_log(LOG_DEBUG,"wrong limit value '%s'", lim_value);
+ return;
+ } else
+ if (limit_item != LIMIT_LOGIN) {
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_DEBUG,
+ "'-' limit value valid for maxlogins type only");
+ return;
+ } else
+ limit_value = -1;
+ }
+
+ switch(limit_item) {
+ case RLIMIT_CPU:
+ limit_value *= 60;
+ break;
+ case RLIMIT_FSIZE:
+ case RLIMIT_DATA:
+ case RLIMIT_STACK:
+ case RLIMIT_CORE:
+ case RLIMIT_RSS:
+ case RLIMIT_MEMLOCK:
+ case RLIMIT_AS:
+ limit_value *= 1024;
+ break;
+ }
+
+ if (limit_item != LIMIT_LOGIN && limit_item != LIMIT_NUMSYSLOGINS
+ && limit_item != LIMIT_PRI
+ ) {
+ if (limit_type & LIMIT_SOFT) {
+ if (limits[limit_item].src_soft < source) {
+ return;
+ } else {
+ limits[limit_item].limit.rlim_cur = limit_value;
+ limits[limit_item].src_soft = source;
+ }
+ }
+ if (limit_type & LIMIT_HARD) {
+ if (limits[limit_item].src_hard < source) {
+ return;
+ } else {
+ limits[limit_item].limit.rlim_max = limit_value;
+ limits[limit_item].src_hard = source;
+ }
+ }
+ } else
+ if (limit_item == LIMIT_PRI) {
+ /* additional check */
+ priority = ((limit_value>0)?limit_value:0);
+ } else {
+ if (login_limit_def < source) {
+ return;
+ } else {
+ login_limit = limit_value;
+ login_limit_def = source;
+ }
+ }
+ return;
+}
+
+static int parse_config_file(const char *uname, int ctrl)
+{
+ FILE *fil;
+ char buf[LINE_LENGTH];
+
+#define CONF_FILE (conf_file[0])?conf_file:LIMITS_FILE
+ /* check for the LIMITS_FILE */
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_DEBUG,"reading settings from '%s'", CONF_FILE);
+ fil = fopen(CONF_FILE, "r");
+ if (fil == NULL) {
+ _pam_log (LOG_WARNING, "can not read settings from %s", CONF_FILE);
+ return PAM_SERVICE_ERR;
+ }
+#undef CONF_FILE
+
+ /* init things */
+ memset(buf, 0, sizeof(buf));
+ /* start the show */
+ while (fgets(buf, LINE_LENGTH, fil) != NULL) {
+ char domain[LINE_LENGTH];
+ char ltype[LINE_LENGTH];
+ char item[LINE_LENGTH];
+ char value[LINE_LENGTH];
+ int i,j;
+ char *tptr;
+
+ tptr = buf;
+ /* skip the leading white space */
+ while (*tptr && isspace(*tptr))
+ tptr++;
+ strcpy(buf, (const char *)tptr);
+
+ /* Rip off the comments */
+ tptr = strchr(buf,'#');
+ if (tptr)
+ *tptr = '\0';
+ /* Rip off the newline char */
+ tptr = strchr(buf,'\n');
+ if (tptr)
+ *tptr = '\0';
+ /* Anything left ? */
+ if (!strlen(buf)) {
+ memset(buf, 0, sizeof(buf));
+ continue;
+ }
+
+ memset(domain, 0, sizeof(domain));
+ memset(ltype, 0, sizeof(ltype));
+ memset(item, 0, sizeof(item));
+ memset(value, 0, sizeof(value));
+
+ i = sscanf(buf,"%s%s%s%s", domain, ltype, item, value);
+ for(j=0; j < strlen(domain); j++)
+ domain[j]=tolower(domain[j]);
+ for(j=0; j < strlen(ltype); j++)
+ ltype[j]=tolower(ltype[j]);
+ for(j=0; j < strlen(item); j++)
+ item[j]=tolower(item[j]);
+ for(j=0; j < strlen(value); j++)
+ value[j]=tolower(value[j]);
+
+ if (i == 4) { /* a complete line */
+ if (strcmp(uname, domain) == 0) /* this user have a limit */
+ process_limit(LIMITS_DEF_USER, ltype, item, value, ctrl);
+ else if (domain[0]=='@') {
+ if (is_on_group(uname, domain+1))
+ process_limit(LIMITS_DEF_GROUP, ltype, item, value, ctrl);
+ } else if (strcmp(domain, "*") == 0)
+ process_limit(LIMITS_DEF_DEFAULT, ltype, item, value, ctrl);
+ } else if (i == 2 && ltype[0] == '-') { /* Probably a no-limit line */
+ if (strcmp(uname, domain) == 0) {
+ _pam_log(LOG_DEBUG, "no limits for '%s'", uname);
+ fclose(fil);
+ return PAM_IGNORE;
+ } else if (domain[0] == '@' && is_on_group(uname, domain+1)) {
+ _pam_log(LOG_DEBUG, "no limits for '%s' in group '%s'",
+ uname, domain+1);
+ fclose(fil);
+ return PAM_IGNORE;
+ }
+ } else {
+ _pam_log(LOG_DEBUG,"invalid line '%s'", buf);
+ }
+ }
+ fclose(fil);
+ return PAM_SUCCESS;
+}
+
+static int setup_limits(const char * uname, int ctrl)
+{
+ int i;
+ int retval = PAM_SUCCESS;
+
+ for (i=0; i<RLIM_NLIMITS; i++) {
+ if (limits[i].limit.rlim_cur > limits[i].limit.rlim_max)
+ limits[i].limit.rlim_cur = limits[i].limit.rlim_max;
+ retval |= setrlimit(i, &limits[i].limit);
+ }
+
+ if (retval != PAM_SUCCESS)
+ retval = LIMIT_ERR;
+
+ retval=setpriority(PRIO_PROCESS, 0, priority);
+
+ if (retval != PAM_SUCCESS)
+ retval = LIMIT_ERR;
+
+ if (login_limit > 0) {
+ if (check_logins(uname, login_limit, ctrl) == LOGIN_ERR)
+ retval |= LOGIN_ERR;
+ } else if (login_limit == 0)
+ retval |= LOGIN_ERR;
+ return retval;
+}
+
+/* now the session stuff */
+PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ int retval;
+ char *user_name;
+ struct passwd *pwd;
+ int ctrl;
+
+ D(("called."));
+
+ memset(conf_file, 0, sizeof(conf_file));
+
+ ctrl = _pam_parse(argc, argv);
+ retval = pam_get_item( pamh, PAM_USER, (void*) &user_name );
+ if ( user_name == NULL || retval != PAM_SUCCESS ) {
+ _pam_log(LOG_CRIT, "open_session - error recovering username");
+ return PAM_SESSION_ERR;
+ }
+
+ setpwent();
+ pwd = getpwnam(user_name);
+ endpwent();
+ if (!pwd) {
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_WARNING, "open_session username '%s' does not exist",
+ user_name);
+ return PAM_SESSION_ERR;
+ }
+
+ /* do not impose limits on UID 0 accounts */
+ if (!pwd->pw_uid) {
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_DEBUG, "user '%s' have UID 0 - no limits imposed",
+ user_name);
+ return PAM_SUCCESS;
+ }
+
+ retval = init_limits();
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_WARNING, "can not initialize");
+ return PAM_IGNORE;
+ }
+
+ retval = parse_config_file(pwd->pw_name,ctrl);
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_WARNING, "error parsing the configuration file");
+ return PAM_IGNORE;
+ }
+
+ retval = setup_limits(pwd->pw_name, ctrl);
+ if (retval & LOGIN_ERR) {
+ printf("\nToo many logins for '%s'\n",pwd->pw_name);
+ sleep(2);
+ return PAM_PERM_DENIED;
+ }
+
+ return PAM_SUCCESS;
+}
+
+PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ /* nothing to do */
+ return PAM_SUCCESS;
+}
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_limits_modstruct = {
+ "pam_limits",
+ NULL,
+ NULL,
+ NULL,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ NULL
+};
+#endif
+
+/*
+ * Copyright (c) Cristian Gafton, 1996-1997, <gafton@redhat.com>
+ * 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, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 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 author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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.
+ */
diff --git a/modules/pam_listfile/.cvsignore b/modules/pam_listfile/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_listfile/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_listfile/Makefile b/modules/pam_listfile/Makefile
new file mode 100644
index 00000000..02940390
--- /dev/null
+++ b/modules/pam_listfile/Makefile
@@ -0,0 +1,84 @@
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+
+TITLE=pam_listfile
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak dynamic/* static/*
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_listfile/README b/modules/pam_listfile/README
new file mode 100644
index 00000000..b65e7dbb
--- /dev/null
+++ b/modules/pam_listfile/README
@@ -0,0 +1,25 @@
+SUMMARY:
+ pam_listfile:
+ Checks a specified item against a list in a file.
+ Options:
+ * item=[tty|user|rhost|ruser|group|shell]
+ * sense=[allow|deny] (action to take if found in file,
+ if the item is NOT found in the file, then
+ the opposite action is requested)
+ * file=/the/file/to/get/the/list/from
+ * onerr=[succeed|fail] (if something weird happens
+ such as unable to open the file, what to do?)
+ * apply=[user|@group]
+ restrict the user class for which the restriction
+ apply. Note that with item=[user|ruser|group] this
+ does not make sense, but for item=[tty|rhost|shell]
+ it have a meaning. (Cristian Gafton)
+
+ Also checks to make sure that the list file is a plain
+ file and not world writable.
+
+ - Elliot Lee <sopwith@redhat.com>, Red Hat Software.
+ v0.9 August 16, 1996.
+
+BUGS:
+ Bugs?
diff --git a/modules/pam_listfile/pam_listfile.c b/modules/pam_listfile/pam_listfile.c
new file mode 100644
index 00000000..8b1b45f0
--- /dev/null
+++ b/modules/pam_listfile/pam_listfile.c
@@ -0,0 +1,437 @@
+/*
+ * $Id$
+ *
+ */
+
+/*
+ * by Elliot Lee <sopwith@redhat.com>, Red Hat Software. July 25, 1996.
+ * log refused access error christopher mccrory <chrismcc@netus.com> 1998/7/11
+ *
+ * This code began life as the pam_rootok module.
+ */
+
+#ifdef linux
+# define _SVID_SOURCE
+# define _BSD_SOURCE
+# define __USE_BSD
+# define __USE_SVID
+# define __USE_MISC
+# define _GNU_SOURCE
+# include <features.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <string.h>
+#include <pwd.h>
+#include <grp.h>
+
+#ifdef DEBUG
+#include <assert.h>
+#endif
+
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_AUTH
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+/* some syslogging */
+
+#define LOCAL_LOG_PREFIX "PAM-listfile: "
+
+static void _pam_log(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ vsyslog(LOG_AUTH | err, format, args);
+ va_end(args);
+}
+
+/* checks if a user is on a list of members */
+static int is_on_list(char * const *list, const char *member)
+{
+ while (*list) {
+ if (strcmp(*list, member) == 0)
+ return 1;
+ list++;
+ }
+ return 0;
+}
+
+/* Checks if a user is a member of a group */
+static int is_on_group(const char *user_name, const char *group_name)
+{
+ struct passwd *pwd;
+ struct group *grp, *pgrp;
+ char uname[BUFSIZ], gname[BUFSIZ];
+
+ if (!strlen(user_name))
+ return 0;
+ if (!strlen(group_name))
+ return 0;
+ bzero(uname, sizeof(uname));
+ strncpy(uname, user_name, BUFSIZ-1);
+ bzero(gname, sizeof(gname));
+ strncpy(gname, group_name, BUFSIZ-1);
+
+ setpwent();
+ pwd = getpwnam(uname);
+ endpwent();
+ if (!pwd)
+ return 0;
+
+ /* the info about this group */
+ setgrent();
+ grp = getgrnam(gname);
+ endgrent();
+ if (!grp)
+ return 0;
+
+ /* first check: is a member of the group_name group ? */
+ if (is_on_list(grp->gr_mem, uname))
+ return 1;
+
+ /* next check: user primary group is group_name ? */
+ setgrent();
+ pgrp = getgrgid(pwd->pw_gid);
+ endgrent();
+ if (!pgrp)
+ return 0;
+ if (!strcmp(pgrp->gr_name, gname))
+ return 1;
+
+ return 0;
+}
+
+/* --- authentication management functions (only) --- */
+
+/* Extended Items that are not directly available via pam_get_item() */
+#define EI_GROUP (1 << 0)
+#define EI_SHELL (1 << 1)
+
+/* Constants for apply= parameter */
+#define APPLY_TYPE_NULL 0
+#define APPLY_TYPE_NONE 1
+#define APPLY_TYPE_USER 2
+#define APPLY_TYPE_GROUP 3
+
+PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+ int retval, i, citem=0, extitem=0, onerr=PAM_SERVICE_ERR, sense=2;
+ const char *citemp;
+ char *ifname=NULL;
+ char aline[256];
+ char mybuf[256],myval[256];
+ struct stat fileinfo;
+ FILE *inf;
+ char apply_val[256];
+ int apply_type;
+
+ /* Stuff for "extended" items */
+ struct passwd *userinfo;
+ struct group *grpinfo;
+ char *itemlist[256]; /* Maximum of 256 items */
+
+ D(("called."));
+
+ apply_type=APPLY_TYPE_NULL;
+ memset(apply_val,0,sizeof(apply_val));
+
+ for(i=0; i < argc; i++) {
+ {
+ char *junk;
+ junk = (char *) malloc(strlen(argv[i])+1);
+ if (junk == NULL) {
+ return PAM_BUF_ERR;
+ }
+ strcpy(junk,argv[i]);
+ strncpy(mybuf,strtok(junk,"="),255);
+ strncpy(myval,strtok(NULL,"="),255);
+ free(junk);
+ }
+ if(!strcmp(mybuf,"onerr"))
+ if(!strcmp(myval,"succeed"))
+ onerr = PAM_SUCCESS;
+ else if(!strcmp(myval,"fail"))
+ onerr = PAM_SERVICE_ERR;
+ else
+ return PAM_SERVICE_ERR;
+ else if(!strcmp(mybuf,"sense"))
+ if(!strcmp(myval,"allow"))
+ sense=0;
+ else if(!strcmp(myval,"deny"))
+ sense=1;
+ else
+ return onerr;
+ else if(!strcmp(mybuf,"file")) {
+ ifname = (char *)malloc(strlen(myval)+1);
+ strcpy(ifname,myval);
+ } else if(!strcmp(mybuf,"item"))
+ if(!strcmp(myval,"user"))
+ citem = PAM_USER;
+ else if(!strcmp(myval,"tty"))
+ citem = PAM_TTY;
+ else if(!strcmp(myval,"rhost"))
+ citem = PAM_RHOST;
+ else if(!strcmp(myval,"ruser"))
+ citem = PAM_RUSER;
+ else { /* These items are related to the user, but are not
+ directly gettable with pam_get_item */
+ citem = PAM_USER;
+ if(!strcmp(myval,"group"))
+ extitem = EI_GROUP;
+ else if(!strcmp(myval,"shell"))
+ extitem = EI_SHELL;
+ else
+ citem = 0;
+ } else if(!strcmp(mybuf,"apply")) {
+ apply_type=APPLY_TYPE_NONE;
+ if (myval[0]=='@') {
+ apply_type=APPLY_TYPE_GROUP;
+ strncpy(apply_val,myval+1,sizeof(apply_val)-1);
+ } else {
+ apply_type=APPLY_TYPE_USER;
+ strncpy(apply_val,myval,sizeof(apply_val)-1);
+ }
+ } else {
+ _pam_log(LOG_ERR,LOCAL_LOG_PREFIX "Unknown option: %s",mybuf);
+ return onerr;
+ }
+ }
+
+ if(!citem) {
+ _pam_log(LOG_ERR,
+ LOCAL_LOG_PREFIX "Unknown item or item not specified");
+ return onerr;
+ } else if(!ifname) {
+ _pam_log(LOG_ERR,LOCAL_LOG_PREFIX "List filename not specified");
+ return onerr;
+ } else if(sense == 2) {
+ _pam_log(LOG_ERR,
+ LOCAL_LOG_PREFIX "Unknown sense or sense not specified");
+ return onerr;
+ } else if(
+ (apply_type==APPLY_TYPE_NONE) ||
+ ((apply_type!=APPLY_TYPE_NULL) && (*apply_val=='\0'))
+ ) {
+ _pam_log(LOG_ERR,
+ LOCAL_LOG_PREFIX "Invalid usage for apply= parameter");
+ return onerr;
+ }
+
+ /* Check if it makes sense to use the apply= parameter */
+ if (apply_type != APPLY_TYPE_NULL) {
+ if((citem==PAM_USER) || (citem==PAM_RUSER)) {
+ _pam_log(LOG_WARNING,
+ LOCAL_LOG_PREFIX "Non-sense use for apply= parameter");
+ apply_type=APPLY_TYPE_NULL;
+ }
+ if(extitem && (extitem==EI_GROUP)) {
+ _pam_log(LOG_WARNING,
+ LOCAL_LOG_PREFIX "Non-sense use for apply= parameter");
+ apply_type=APPLY_TYPE_NULL;
+ }
+ }
+
+ /* Short-circuit - test if this session apply for this user */
+ {
+ const char *user_name;
+ int rval;
+
+ rval=pam_get_user(pamh,&user_name,NULL);
+ if((rval==PAM_SUCCESS) && user_name[0]) {
+ /* Got it ? Valid ? */
+ if(apply_type==APPLY_TYPE_USER) {
+ if(strcmp(user_name, apply_val)) {
+ /* Does not apply to this user */
+#ifdef DEBUG
+ _pam_log(LOG_DEBUG,
+ LOCAL_LOG_PREFIX "don't apply: apply=%s, user=%s",
+ apply_val,user_name);
+#endif /* DEBUG */
+ return PAM_IGNORE;
+ }
+ } else if(apply_type==APPLY_TYPE_GROUP) {
+ if(!is_on_group(user_name,apply_val)) {
+ /* Not a member of apply= group */
+#ifdef DEBUG
+ _pam_log(LOG_DEBUG,
+ LOCAL_LOG_PREFIX
+ "don't apply: %s not a member of group %s",
+ user_name,apply_val);
+#endif /* DEBUG */
+ return PAM_IGNORE;
+ }
+ }
+ }
+ }
+
+ retval = pam_get_item(pamh,citem,(const void **)&citemp);
+ if(retval != PAM_SUCCESS) {
+ return onerr;
+ }
+ if((citem == PAM_USER) && !citemp) {
+ pam_get_user(pamh,&citemp,NULL);
+ if (retval != PAM_SUCCESS)
+ return PAM_SERVICE_ERR;
+ }
+
+ if(!citemp || (strlen(citemp) <= 0)) {
+ /* The item was NULL - we are sure not to match */
+ return sense?PAM_SUCCESS:PAM_AUTH_ERR;
+ }
+
+ if(extitem) {
+ switch(extitem) {
+ case EI_GROUP:
+ setpwent();
+ userinfo = getpwnam(citemp);
+ setgrent();
+ grpinfo = getgrgid(userinfo->pw_gid);
+ itemlist[0] = x_strdup(grpinfo->gr_name);
+ setgrent();
+ for (i=1; (i < sizeof(itemlist)/sizeof(itemlist[0])-1) &&
+ (grpinfo = getgrent()); ) {
+ if (is_on_list(grpinfo->gr_mem,citemp)) {
+ itemlist[i++] = x_strdup(grpinfo->gr_name);
+ }
+ }
+ itemlist[i] = NULL;
+ endgrent();
+ endpwent();
+ break;
+ case EI_SHELL:
+ setpwent();
+ userinfo = getpwnam(citemp); /* Assume that we have already gotten
+ PAM_USER in pam_get_item() - a valid
+ assumption since citem gets set to
+ PAM_USER in the extitem switch */
+ citemp = userinfo->pw_shell;
+ endpwent();
+ break;
+ default:
+ _pam_log(LOG_ERR,
+ LOCAL_LOG_PREFIX
+ "Internal weirdness, unknown extended item %d",
+ extitem);
+ return onerr;
+ }
+ }
+#ifdef DEBUG
+ _pam_log(LOG_INFO,
+ LOCAL_LOG_PREFIX
+ "Got file = %s, item = %d, value = %s, sense = %d",
+ ifname, citem, citemp, sense);
+#endif
+ if(lstat(ifname,&fileinfo)) {
+ _pam_log(LOG_ERR,LOCAL_LOG_PREFIX "Couldn't open %s",ifname);
+ return onerr;
+ }
+
+ if((fileinfo.st_mode & S_IWOTH)
+ || !S_ISREG(fileinfo.st_mode)) {
+ /* If the file is world writable or is not a
+ normal file, return error */
+ _pam_log(LOG_ERR,LOCAL_LOG_PREFIX
+ "%s is either world writable or not a normal file",
+ ifname);
+ return PAM_AUTH_ERR;
+ }
+
+ inf = fopen(ifname,"r");
+ if(inf == NULL) { /* Check that we opened it successfully */
+ if (onerr == PAM_SERVICE_ERR) {
+ /* Only report if it's an error... */
+ _pam_log(LOG_ERR,LOCAL_LOG_PREFIX "Error opening %s", ifname);
+ }
+ return onerr;
+ }
+ /* There should be no more errors from here on */
+ retval=PAM_AUTH_ERR;
+ /* This loop assumes that PAM_SUCCESS == 0
+ and PAM_AUTH_ERR != 0 */
+#ifdef DEBUG
+ assert(PAM_SUCCESS == 0);
+ assert(PAM_AUTH_ERR != 0);
+#endif
+ if(extitem == EI_GROUP) {
+ while((fgets(aline,255,inf) != NULL)
+ && retval) {
+ if(aline[strlen(aline) - 1] == '\n')
+ aline[strlen(aline) - 1] = '\0';
+ for(i=0;itemlist[i];)
+ /* If any of the items match, strcmp() == 0, and we get out
+ of this loop */
+ retval = (strcmp(aline,itemlist[i++]) && retval);
+ }
+ for(i=0;itemlist[i];)
+ free(itemlist[i++]);
+ } else {
+ while((fgets(aline,255,inf) != NULL)
+ && retval) {
+ if(aline[strlen(aline) - 1] == '\n')
+ aline[strlen(aline) - 1] = '\0';
+ retval = strcmp(aline,citemp);
+ }
+ }
+ fclose(inf);
+ free(ifname);
+ if ((sense && retval) || (!sense && !retval)) {
+#ifdef DEBUG
+ _pam_log(LOG_INFO, LOCAL_LOG_PREFIX
+ "Returning PAM_SUCCESS, retval = %d", retval);
+#endif
+ return PAM_SUCCESS;
+ }
+ else {
+ const char *service, *user_name;
+#ifdef DEBUG
+ _pam_log(LOG_INFO,LOCAL_LOG_PREFIX
+ "Returning PAM_AUTH_ERR, retval = %d", retval);
+#endif
+ (void) pam_get_item(pamh, PAM_SERVICE, (const void **)&service);
+ (void) pam_get_user(pamh, &user_name, NULL);
+ _pam_log(LOG_ALERT,LOCAL_LOG_PREFIX "Refused user %s for service %s",
+ user_name, service);
+ return PAM_AUTH_ERR;
+ }
+}
+
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_listfile_modstruct = {
+ "pam_listfile",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+#endif
+
+/* end of module definition */
+
diff --git a/modules/pam_mail/.cvsignore b/modules/pam_mail/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_mail/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_mail/Makefile b/modules/pam_mail/Makefile
new file mode 100644
index 00000000..64f73b0b
--- /dev/null
+++ b/modules/pam_mail/Makefile
@@ -0,0 +1,113 @@
+#
+# $Id$
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# $Log$
+# Revision 1.1 2000/06/20 22:11:45 agmorgan
+# Initial revision
+#
+# Revision 1.1.1.1 1998/07/12 05:17:17 morgan
+# Linux PAM sources pre-0.66
+#
+# Revision 1.3 1997/04/05 06:37:45 morgan
+# fakeroot
+#
+# Revision 1.2 1997/02/15 16:07:22 morgan
+# optional libpwdb compilation
+#
+# Revision 1.1 1997/01/04 20:32:52 morgan
+# Initial revision
+#
+#
+#
+# Created by Andrew Morgan <morgan@parc.power.net> 1996/12/8
+#
+
+TITLE=pam_mail
+
+ifndef STATIC
+ifeq ($(HAVE_PWDBLIB),yes)
+CFLAGS += -DWANT_PWDB
+EXTRALIB = -lpwdb
+endif
+endif
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD) $(EXTRALIB)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS) $(EXTRALIB)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak dynamic/* static/*
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_mail/README b/modules/pam_mail/README
new file mode 100644
index 00000000..155bd1db
--- /dev/null
+++ b/modules/pam_mail/README
@@ -0,0 +1,17 @@
+This is the README for pam_mail
+-------------------------------
+
+This PAM module tells the User that he has new/unread email.
+
+Options for:
+auth: for authentication it provides pam_authenticate() and
+ pam_setcred() hooks.
+
+ "debug" write more information to syslog
+ "dir=maildir" users mailbox is maildir/<login>
+ "hash=count" mail directory hash depth
+ "close" print message also on logout
+ "nopen" print message not on login
+ "noenv" don't set the MAIL environment variable
+ "empty" also print message if user has no mail
+
diff --git a/modules/pam_mail/pam_mail.c b/modules/pam_mail/pam_mail.c
new file mode 100644
index 00000000..f67b6f02
--- /dev/null
+++ b/modules/pam_mail/pam_mail.c
@@ -0,0 +1,504 @@
+/* pam_mail module */
+
+/*
+ * $Id$
+ *
+ * Written by Andrew Morgan <morgan@linux.kernel.org> 1996/3/11
+ * $HOME additions by David Kinchlea <kinch@kinch.ark.com> 1997/1/7
+ * mailhash additions by Chris Adams <cadams@ro.com> 1998/7/11
+ */
+
+#define DEFAULT_MAIL_DIRECTORY "/var/spool/mail"
+#define MAIL_FILE_FORMAT "%s%s/%s"
+#define MAIL_ENV_NAME "MAIL"
+#define MAIL_ENV_FORMAT MAIL_ENV_NAME "=%s"
+#define YOUR_MAIL_VERBOSE_FORMAT "You have %s mail in %s."
+#define YOUR_MAIL_STANDARD_FORMAT "You have %smail."
+#define NO_MAIL_STANDARD_FORMAT "No mail."
+
+#define _BSD_SOURCE
+
+#ifdef linux
+# define _GNU_SOURCE
+# include <features.h>
+#endif
+
+#include <ctype.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#ifdef WANT_PWDB
+#include <pwdb/pwdb_public.h>
+#endif
+
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_SESSION
+#define PAM_SM_AUTH
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+/* some syslogging */
+
+static void _log_err(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("PAM-mail", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+/* argument parsing */
+
+#define PAM_DEBUG_ARG 0x0001
+#define PAM_NO_LOGIN 0x0002
+#define PAM_LOGOUT_TOO 0x0004
+#define PAM_NEW_MAIL_DIR 0x0010
+#define PAM_MAIL_SILENT 0x0020
+#define PAM_NO_ENV 0x0040
+#define PAM_HOME_MAIL 0x0100
+#define PAM_EMPTY_TOO 0x0200
+#define PAM_STANDARD_MAIL 0x0400
+#define PAM_QUIET_MAIL 0x1000
+
+static int _pam_parse(int flags, int argc, const char **argv, char **maildir,
+ int *hashcount)
+{
+ int ctrl=0;
+
+ if (flags & PAM_SILENT) {
+ ctrl |= PAM_MAIL_SILENT;
+ }
+
+ *hashcount = 0;
+
+ /* step through arguments */
+ for (; argc-- > 0; ++argv) {
+
+ /* generic options */
+
+ if (!strcmp(*argv,"debug"))
+ ctrl |= PAM_DEBUG_ARG;
+ else if (!strcmp(*argv,"quiet"))
+ ctrl |= PAM_QUIET_MAIL;
+ else if (!strcmp(*argv,"standard"))
+ ctrl |= PAM_STANDARD_MAIL | PAM_EMPTY_TOO;
+ else if (!strncmp(*argv,"dir=",4)) {
+ *maildir = x_strdup(4+*argv);
+ if (*maildir != NULL) {
+ D(("new mail directory: %s", *maildir));
+ ctrl |= PAM_NEW_MAIL_DIR;
+ } else {
+ _log_err(LOG_CRIT,
+ "failed to duplicate mail directory - ignored");
+ }
+ } else if (!strncmp(*argv,"hash=",5)) {
+ char *ep = NULL;
+ *hashcount = strtol(*argv+5,&ep,10);
+ if (!ep || (*hashcount < 0)) {
+ *hashcount = 0;
+ }
+ } else if (!strcmp(*argv,"close")) {
+ ctrl |= PAM_LOGOUT_TOO;
+ } else if (!strcmp(*argv,"nopen")) {
+ ctrl |= PAM_NO_LOGIN;
+ } else if (!strcmp(*argv,"noenv")) {
+ ctrl |= PAM_NO_ENV;
+ } else if (!strcmp(*argv,"empty")) {
+ ctrl |= PAM_EMPTY_TOO;
+ } else {
+ _log_err(LOG_ERR,"pam_parse: unknown option; %s",*argv);
+ }
+ }
+
+ if ((*hashcount != 0) && !(ctrl & PAM_NEW_MAIL_DIR)) {
+ *maildir = x_strdup(DEFAULT_MAIL_DIRECTORY);
+ ctrl |= PAM_NEW_MAIL_DIR;
+ }
+
+ return ctrl;
+}
+
+/* a front end for conversations */
+
+static int converse(pam_handle_t *pamh, int ctrl, int nargs
+ , struct pam_message **message
+ , struct pam_response **response)
+{
+ int retval;
+ struct pam_conv *conv;
+
+ D(("begin to converse"));
+
+ retval = pam_get_item( pamh, PAM_CONV, (const void **) &conv ) ;
+ if ( retval == PAM_SUCCESS ) {
+
+ retval = conv->conv(nargs, ( const struct pam_message ** ) message
+ , response, conv->appdata_ptr);
+
+ D(("returned from application's conversation function"));
+
+ if (retval != PAM_SUCCESS && (PAM_DEBUG_ARG & ctrl) ) {
+ _log_err(LOG_DEBUG, "conversation failure [%s]"
+ , pam_strerror(pamh, retval));
+ }
+
+ } else {
+ _log_err(LOG_ERR, "couldn't obtain coversation function [%s]"
+ , pam_strerror(pamh, retval));
+ }
+
+ D(("ready to return from module conversation"));
+
+ return retval; /* propagate error status */
+}
+
+static int get_folder(pam_handle_t *pamh, int ctrl,
+ char **path_mail, char **folder_p, int hashcount)
+{
+ int retval;
+ const char *user, *path;
+ char *folder;
+ const struct passwd *pwd=NULL;
+
+ retval = pam_get_user(pamh, &user, NULL);
+ if (retval != PAM_SUCCESS || user == NULL) {
+ _log_err(LOG_ERR, "no user specified");
+ return PAM_USER_UNKNOWN;
+ }
+
+ if (ctrl & PAM_NEW_MAIL_DIR) {
+ path = *path_mail;
+ if (*path == '~') { /* support for $HOME delivery */
+ pwd = getpwnam(user);
+ if (pwd == NULL) {
+ _log_err(LOG_ERR, "user [%s] unknown", user);
+ _pam_overwrite(*path_mail);
+ _pam_drop(*path_mail);
+ return PAM_USER_UNKNOWN;
+ }
+ /*
+ * "~/xxx" and "~xxx" are treated as same
+ */
+ if (!*++path || (*path == '/' && !*++path)) {
+ _log_err(LOG_ALERT, "badly formed mail path [%s]", *path_mail);
+ _pam_overwrite(*path_mail);
+ _pam_drop(*path_mail);
+ return PAM_ABORT;
+ }
+ ctrl |= PAM_HOME_MAIL;
+ if (hashcount != 0) {
+ _log_err(LOG_ALERT, "can't do hash= and home directory mail");
+ }
+ }
+ } else {
+ path = DEFAULT_MAIL_DIRECTORY;
+ }
+
+ /* put folder together */
+
+ if (ctrl & PAM_HOME_MAIL) {
+ folder = malloc(sizeof(MAIL_FILE_FORMAT)
+ +strlen(pwd->pw_dir)+strlen(path));
+ } else {
+ folder = malloc(sizeof(MAIL_FILE_FORMAT)+strlen(path)+strlen(user)
+ +2*hashcount);
+ }
+
+ if (folder != NULL) {
+ if (ctrl & PAM_HOME_MAIL) {
+ sprintf(folder, MAIL_FILE_FORMAT, pwd->pw_dir, "", path);
+ } else {
+ int i;
+ char *hash = malloc(2*hashcount+1);
+
+ if (hash) {
+ for (i = 0; i < hashcount; i++) {
+ hash[2*i] = '/';
+ hash[2*i+1] = user[i];
+ }
+ hash[2*i] = '\0';
+ sprintf(folder, MAIL_FILE_FORMAT, path, hash, user);
+ _pam_overwrite(hash);
+ _pam_drop(hash);
+ } else {
+ sprintf(folder, "error");
+ }
+ }
+ D(("folder =[%s]", folder));
+ }
+
+ /* tidy up */
+
+ _pam_overwrite(*path_mail);
+ _pam_drop(*path_mail);
+ user = NULL;
+
+ if (folder == NULL) {
+ _log_err(LOG_CRIT, "out of memory for mail folder");
+ return PAM_BUF_ERR;
+ }
+
+ *folder_p = folder;
+ folder = NULL;
+
+ return PAM_SUCCESS;
+}
+
+static const char *get_mail_status(int ctrl, const char *folder)
+{
+ const char *type = NULL;
+ static char dir[256];
+ struct stat mail_st;
+ struct dirent **namelist;
+ int i;
+
+ if (stat(folder, &mail_st) == 0) {
+ if (S_ISDIR(mail_st.st_mode)) { /* Assume Maildir format */
+ sprintf(dir, "%.250s/new", folder);
+ i = scandir(dir, &namelist, 0, alphasort);
+ if (i > 2) {
+ type = "new";
+ while (--i)
+ free(namelist[i]);
+ } else {
+ while (--i >= 0)
+ free(namelist[i]);
+ sprintf(dir, "%.250s/cur", folder);
+ i = scandir(dir, &namelist, 0, alphasort);
+ if (i > 2) {
+ type = "old";
+ while (--i)
+ free(namelist[i]);
+ } else if (ctrl & PAM_EMPTY_TOO) {
+ while (--i >= 0)
+ free(namelist[i]);
+ type = "no";
+ } else {
+ type = NULL;
+ }
+ }
+ } else {
+ if (mail_st.st_size > 0) {
+ if (mail_st.st_atime < mail_st.st_mtime) /* new */
+ type = (ctrl & PAM_STANDARD_MAIL) ? "new " : "new";
+ else /* old */
+ type = (ctrl & PAM_STANDARD_MAIL) ? "" : "old";
+ } else if (ctrl & PAM_EMPTY_TOO) {
+ type = "no";
+ } else {
+ type = NULL;
+ }
+ }
+ }
+
+ memset(dir, 0, 256);
+ memset(&mail_st, 0, sizeof(mail_st));
+ D(("user has %s mail in %s folder", type, folder));
+ return type;
+}
+
+static int report_mail(pam_handle_t *pamh, int ctrl
+ , const char *type, const char *folder)
+{
+ int retval;
+
+ if (!(ctrl & PAM_MAIL_SILENT) || ((ctrl & PAM_QUIET_MAIL) && strcmp(type, "new"))) {
+ char *remark;
+
+ if (ctrl & PAM_STANDARD_MAIL)
+ if (!strcmp(type, "no"))
+ remark = malloc(strlen(NO_MAIL_STANDARD_FORMAT)+1);
+ else
+ remark = malloc(strlen(YOUR_MAIL_STANDARD_FORMAT)+strlen(type)+1);
+ else
+ remark = malloc(strlen(YOUR_MAIL_VERBOSE_FORMAT)+strlen(type)+strlen(folder)+1);
+ if (remark == NULL) {
+ retval = PAM_BUF_ERR;
+ } else {
+ struct pam_message msg[1], *mesg[1];
+ struct pam_response *resp=NULL;
+
+ if (ctrl & PAM_STANDARD_MAIL)
+ if (!strcmp(type, "no"))
+ sprintf(remark, NO_MAIL_STANDARD_FORMAT);
+ else
+ sprintf(remark, YOUR_MAIL_STANDARD_FORMAT, type);
+ else
+ sprintf(remark, YOUR_MAIL_VERBOSE_FORMAT, type, folder);
+
+ mesg[0] = &msg[0];
+ msg[0].msg_style = PAM_TEXT_INFO;
+ msg[0].msg = remark;
+
+ retval = converse(pamh, ctrl, 1, mesg, &resp);
+
+ _pam_overwrite(remark);
+ _pam_drop(remark);
+ if (resp)
+ _pam_drop_reply(resp, 1);
+ }
+ } else {
+ D(("keeping quiet"));
+ retval = PAM_SUCCESS;
+ }
+
+ D(("returning %s", pam_strerror(pamh, retval)));
+ return retval;
+}
+
+static int _do_mail(pam_handle_t *, int, int, const char **, int);
+
+/* --- authentication functions --- */
+
+PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc,
+ const char **argv)
+{
+ return PAM_IGNORE;
+}
+
+/* Checking mail as part of authentication */
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc,
+ const char **argv)
+{
+ if (!(flags & (PAM_ESTABLISH_CRED|PAM_DELETE_CRED)))
+ return PAM_IGNORE;
+ return _do_mail(pamh,flags,argc,argv,(flags & PAM_ESTABLISH_CRED));
+}
+
+/* --- session management functions --- */
+
+PAM_EXTERN
+int pam_sm_close_session(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return _do_mail(pamh,flags,argc,argv,0);;
+}
+
+/* Checking mail as part of the session management */
+PAM_EXTERN
+int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc,
+ const char **argv)
+{
+ return _do_mail(pamh,flags,argc,argv,1);
+}
+
+
+/* --- The Beaf (Tm) --- */
+
+static int _do_mail(pam_handle_t *pamh, int flags, int argc,
+ const char **argv, int est)
+{
+ int retval, ctrl, hashcount;
+ char *path_mail=NULL, *folder;
+ const char *type;
+
+ /*
+ * this module (un)sets the MAIL environment variable, and checks if
+ * the user has any new mail.
+ */
+
+ ctrl = _pam_parse(flags, argc, argv, &path_mail, &hashcount);
+
+ /* Do we have anything to do? */
+
+ if (flags & PAM_SILENT)
+ return PAM_SUCCESS;
+
+ /* which folder? */
+
+ retval = get_folder(pamh, ctrl, &path_mail, &folder, hashcount);
+ if (retval != PAM_SUCCESS) {
+ D(("failed to find folder"));
+ return retval;
+ }
+
+ /* set the MAIL variable? */
+
+ if (!(ctrl & PAM_NO_ENV) && est) {
+ char *tmp;
+
+ tmp = malloc(strlen(folder)+sizeof(MAIL_ENV_FORMAT));
+ if (tmp != NULL) {
+ sprintf(tmp, MAIL_ENV_FORMAT, folder);
+ D(("setting env: %s", tmp));
+ retval = pam_putenv(pamh, tmp);
+ _pam_overwrite(tmp);
+ _pam_drop(tmp);
+ if (retval != PAM_SUCCESS) {
+ _pam_overwrite(folder);
+ _pam_drop(folder);
+ _log_err(LOG_CRIT, "unable to set " MAIL_ENV_NAME " variable");
+ return retval;
+ }
+ } else {
+ _log_err(LOG_CRIT, "no memory for " MAIL_ENV_NAME " variable");
+ _pam_overwrite(folder);
+ _pam_drop(folder);
+ return retval;
+ }
+ } else {
+ D(("not setting " MAIL_ENV_NAME " variable"));
+ }
+
+ /*
+ * OK. we've got the mail folder... what about its status?
+ */
+
+ if ((est && !(ctrl & PAM_NO_LOGIN))
+ || (!est && (ctrl & PAM_LOGOUT_TOO))) {
+ type = get_mail_status(ctrl, folder);
+ if (type != NULL) {
+ retval = report_mail(pamh, ctrl, type, folder);
+ type = NULL;
+ }
+ }
+
+ /* Delete environment variable? */
+ if (!est)
+ (void) pam_putenv(pamh, MAIL_ENV_NAME);
+
+ _pam_overwrite(folder); /* clean up */
+ _pam_drop(folder);
+
+ /* indicate success or failure */
+
+ return retval;
+}
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_mail_modstruct = {
+ "pam_mail",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ NULL,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ NULL,
+};
+
+#endif
+
+/* end of module definition */
diff --git a/modules/pam_mkhomedir/.cvsignore b/modules/pam_mkhomedir/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_mkhomedir/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_mkhomedir/Makefile b/modules/pam_mkhomedir/Makefile
new file mode 100644
index 00000000..7c9f5d3a
--- /dev/null
+++ b/modules/pam_mkhomedir/Makefile
@@ -0,0 +1,90 @@
+#
+# $Id$
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# Created by Andrew Morgan <morgan@parc.power.net> 1996/3/11
+#
+
+# Convenient defaults for compiling independently of the full source
+# tree.
+
+#
+
+TITLE=pam_mkhomedir
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD) $(LINKLIBS) -lpam
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
diff --git a/modules/pam_mkhomedir/pam_mkhomedir.c b/modules/pam_mkhomedir/pam_mkhomedir.c
new file mode 100644
index 00000000..ec05993d
--- /dev/null
+++ b/modules/pam_mkhomedir/pam_mkhomedir.c
@@ -0,0 +1,370 @@
+/* PAM Make Home Dir module
+
+ This module will create a users home directory if it does not exist
+ when the session begins. This allows users to be present in central
+ database (such as nis, kerb or ldap) without using a distributed
+ file system or pre-creating a large number of directories.
+
+ Here is a sample /etc/pam.d/login file for Debian GNU/Linux
+ 2.1:
+
+ auth requisite pam_securetty.so
+ auth sufficient pam_ldap.so
+ auth required pam_pwdb.so
+ auth optional pam_group.so
+ auth optional pam_mail.so
+ account requisite pam_time.so
+ account sufficient pam_ldap.so
+ account required pam_pwdb.so
+ session required pam_mkhomedir.so skel=/etc/skel/ umask=0022
+ session required pam_pwdb.so
+ session optional pam_lastlog.so
+ password required pam_pwdb.so
+
+ Released under the GNU LGPL version 2 or later
+ Originally written by Jason Gunthorpe <jgg@debian.org> Feb 1999
+ Structure taken from pam_lastlogin by Andrew Morgan
+ <morgan@parc.power.net> 1996
+ */
+
+/* I want snprintf dammit */
+#define _GNU_SOURCE 1
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <dirent.h>
+
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_SESSION
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+/* argument parsing */
+#define MKHOMEDIR_DEBUG 020 /* keep quiet about things */
+#define MKHOMEDIR_QUIET 040 /* keep quiet about things */
+
+static unsigned int UMask = 0022;
+static char SkelDir[BUFSIZ] = "/etc/skel";
+
+/* some syslogging */
+static void _log_err(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("PAM-mkhomedir", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+static int _pam_parse(int flags, int argc, const char **argv)
+{
+ int ctrl = 0;
+
+ /* does the appliction require quiet? */
+ if ((flags & PAM_SILENT) == PAM_SILENT)
+ ctrl |= MKHOMEDIR_QUIET;
+
+ /* step through arguments */
+ for (; argc-- > 0; ++argv)
+ {
+ if (!strcmp(*argv, "silent"))
+ {
+ ctrl |= MKHOMEDIR_QUIET;
+ }
+ else if (!strncmp(*argv,"umask=",6))
+ UMask = strtol(*argv+6,0,0);
+ else if (!strncmp(*argv,"skel=",5))
+ strcpy(SkelDir,*argv+5);
+ else
+ {
+ _log_err(LOG_ERR, "unknown option; %s", *argv);
+ }
+ }
+
+ D(("ctrl = %o", ctrl));
+ return ctrl;
+}
+
+/* This common function is used to send a message to the applications
+ conversion function. Our only use is to ask the application to print
+ an informative message that we are creating a home directory */
+static int converse(pam_handle_t * pamh, int ctrl, int nargs
+ ,struct pam_message **message
+ ,struct pam_response **response)
+{
+ int retval;
+ struct pam_conv *conv;
+
+ D(("begin to converse"));
+
+ retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv);
+ if (retval == PAM_SUCCESS)
+ {
+
+ retval = conv->conv(nargs, (const struct pam_message **) message
+ ,response, conv->appdata_ptr);
+
+ D(("returned from application's conversation function"));
+
+ if (retval != PAM_SUCCESS && (ctrl & MKHOMEDIR_DEBUG))
+ {
+ _log_err(LOG_DEBUG, "conversation failure [%s]"
+ ,pam_strerror(pamh, retval));
+ }
+
+ }
+ else
+ {
+ _log_err(LOG_ERR, "couldn't obtain coversation function [%s]"
+ ,pam_strerror(pamh, retval));
+ }
+
+ D(("ready to return from module conversation"));
+
+ return retval; /* propagate error status */
+}
+
+/* Ask the application to display a short text string for us. */
+static int make_remark(pam_handle_t * pamh, int ctrl, const char *remark)
+{
+ int retval;
+
+ if ((ctrl & MKHOMEDIR_QUIET) != MKHOMEDIR_QUIET)
+ {
+ struct pam_message msg[1], *mesg[1];
+ struct pam_response *resp = NULL;
+
+ mesg[0] = &msg[0];
+ msg[0].msg_style = PAM_TEXT_INFO;
+ msg[0].msg = remark;
+
+ retval = converse(pamh, ctrl, 1, mesg, &resp);
+
+ msg[0].msg = NULL;
+ if (resp)
+ {
+ _pam_drop_reply(resp, 1);
+ }
+ }
+ else
+ {
+ D(("keeping quiet"));
+ retval = PAM_SUCCESS;
+ }
+
+ D(("returning %s", pam_strerror(pamh, retval)));
+ return retval;
+}
+
+/* Do the actual work of creating a home dir */
+static int create_homedir(pam_handle_t * pamh, int ctrl,
+ const struct passwd *pwd)
+{
+ char *remark;
+ DIR *D;
+ struct dirent *Dir;
+
+ /* Some scratch space */
+ remark = malloc(BUFSIZ);
+ if (remark == NULL)
+ {
+ D(("no memory for last login remark"));
+ return PAM_BUF_ERR;
+ }
+
+ /* Mention what is happening, if the notification fails that is OK */
+ if (snprintf(remark,BUFSIZ,"Creating home directory '%s'.",
+ pwd->pw_dir) == -1)
+ return PAM_PERM_DENIED;
+
+ make_remark(pamh, ctrl, remark);
+
+ /* Crete the home directory */
+ if (mkdir(pwd->pw_dir,0700) != 0)
+ {
+ free(remark);
+ _log_err(LOG_DEBUG, "unable to create home directory %s",pwd->pw_dir);
+ return PAM_PERM_DENIED;
+ }
+ if (chmod(pwd->pw_dir,0777 & (~UMask)) != 0 ||
+ chown(pwd->pw_dir,pwd->pw_uid,pwd->pw_gid) != 0)
+ {
+ free(remark);
+ _log_err(LOG_DEBUG, "unable to chance perms on home directory %s",pwd->pw_dir);
+ return PAM_PERM_DENIED;
+ }
+
+ /* See if we need to copy the skel dir over. */
+ if (SkelDir[0] == 0)
+ {
+ free(remark);
+ return PAM_SUCCESS;
+ }
+
+ /* Scan the directory */
+ D = opendir(SkelDir);
+ if (D == 0)
+ {
+ free(remark);
+ _log_err(LOG_DEBUG, "unable to read directory %s",SkelDir);
+ return PAM_PERM_DENIED;
+ }
+
+ for (Dir = readdir(D); Dir != 0; Dir = readdir(D))
+ {
+ int SrcFd;
+ int DestFd;
+ int Res;
+ struct stat St;
+
+ /* Skip some files.. */
+ if (strcmp(Dir->d_name,".") == 0 ||
+ strcmp(Dir->d_name,"..") == 0)
+ continue;
+
+ /* Check if it is a directory */
+ snprintf(remark,BUFSIZ,"%s/%s",SkelDir,Dir->d_name);
+ if (stat(remark,&St) != 0)
+ continue;
+ if (S_ISDIR(St.st_mode))
+ {
+ snprintf(remark,BUFSIZ,"%s/%s",pwd->pw_dir,Dir->d_name);
+ if (mkdir(remark,(St.st_mode | 0222) & (~UMask)) != 0 ||
+ chmod(remark,(St.st_mode | 0222) & (~UMask)) != 0 ||
+ chown(remark,pwd->pw_uid,pwd->pw_gid) != 0)
+ {
+ free(remark);
+ _log_err(LOG_DEBUG, "unable to change perms on copy %s",remark);
+ return PAM_PERM_DENIED;
+ }
+ continue;
+ }
+
+ /* Open the source file */
+ if ((SrcFd = open(remark,O_RDONLY)) < 0 || fstat(SrcFd,&St) != 0)
+ {
+ free(remark);
+ _log_err(LOG_DEBUG, "unable to open src file %s",remark);
+ return PAM_PERM_DENIED;
+ }
+ stat(remark,&St);
+
+ /* Open the dest file */
+ snprintf(remark,BUFSIZ,"%s/%s",pwd->pw_dir,Dir->d_name);
+ if ((DestFd = open(remark,O_WRONLY | O_TRUNC | O_CREAT,0600)) < 0)
+ {
+ close(SrcFd);
+ free(remark);
+ _log_err(LOG_DEBUG, "unable to open dest file %s",remark);
+ return PAM_PERM_DENIED;
+ }
+
+ /* Set the proper ownership and permissions for the module. We make
+ the file a+w and then mask it with the set mask. This preseves
+ execute bits */
+ if (fchmod(DestFd,(St.st_mode | 0222) & (~UMask)) != 0 ||
+ fchown(DestFd,pwd->pw_uid,pwd->pw_gid) != 0)
+ {
+ free(remark);
+ _log_err(LOG_DEBUG, "unable to chang perms on copy %s",remark);
+ return PAM_PERM_DENIED;
+ }
+
+ /* Copy the file */
+ do
+ {
+ Res = read(SrcFd,remark,BUFSIZ);
+ if (Res < 0 || write(DestFd,remark,Res) != Res)
+ {
+ close(SrcFd);
+ close(DestFd);
+ free(remark);
+ _log_err(LOG_DEBUG, "unable to perform IO");
+ return PAM_PERM_DENIED;
+ }
+ }
+ while (Res != 0);
+ close(SrcFd);
+ close(DestFd);
+ }
+
+ free(remark);
+ return PAM_SUCCESS;
+}
+
+/* --- authentication management functions (only) --- */
+
+PAM_EXTERN
+int pam_sm_open_session(pam_handle_t * pamh, int flags, int argc
+ ,const char **argv)
+{
+ int retval, ctrl;
+ const char *user;
+ const struct passwd *pwd;
+ struct stat St;
+
+ /* Parse the flag values */
+ ctrl = _pam_parse(flags, argc, argv);
+
+ /* Determine the user name so we can get the home directory */
+ retval = pam_get_item(pamh, PAM_USER, (const void **) &user);
+ if (retval != PAM_SUCCESS || user == NULL || *user == '\0')
+ {
+ _log_err(LOG_NOTICE, "user unknown");
+ return PAM_USER_UNKNOWN;
+ }
+
+ /* Get the password entry */
+ pwd = getpwnam(user);
+ if (pwd == NULL)
+ {
+ D(("couldn't identify user %s", user));
+ return PAM_CRED_INSUFFICIENT;
+ }
+
+ /* Stat the home directory, if something exists then we assume it is
+ correct and return a success*/
+ if (stat(pwd->pw_dir,&St) == 0)
+ return PAM_SUCCESS;
+
+ return create_homedir(pamh,ctrl,pwd);
+}
+
+/* Ignore */
+PAM_EXTERN
+int pam_sm_close_session(pam_handle_t * pamh, int flags, int argc
+ ,const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+#ifdef PAM_STATIC
+
+/* static module data */
+struct pam_module _pam_mkhomedir_modstruct =
+{
+ "pam_mkhomedir",
+ NULL,
+ NULL,
+ NULL,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ NULL,
+};
+
+#endif
diff --git a/modules/pam_motd/.cvsignore b/modules/pam_motd/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_motd/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_motd/Makefile b/modules/pam_motd/Makefile
new file mode 100644
index 00000000..ef9f3113
--- /dev/null
+++ b/modules/pam_motd/Makefile
@@ -0,0 +1,90 @@
+#
+# $Id$
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# Created by Andrew Morgan <morgan@parc.power.net> 1996/3/11
+#
+
+# Convenient defaults for compiling independently of the full source
+# tree.
+
+#
+
+TITLE=pam_motd
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD) $(LINKLIBS)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
diff --git a/modules/pam_motd/pam_motd.c b/modules/pam_motd/pam_motd.c
new file mode 100644
index 00000000..98976b66
--- /dev/null
+++ b/modules/pam_motd/pam_motd.c
@@ -0,0 +1,119 @@
+/* pam_motd module */
+
+/*
+ * Modified for pam_motd by Ben Collins <bcollins@debian.org>
+ *
+ * Based off of:
+ * $Id$
+ *
+ * Written by Michael K. Johnson <johnsonm@redhat.com> 1996/10/24
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+
+#include <security/_pam_macros.h>
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_SESSION
+#define DEFAULT_MOTD "/etc/motd"
+
+#include <security/pam_modules.h>
+
+/* --- session management functions (only) --- */
+
+PAM_EXTERN
+int pam_sm_close_session(pam_handle_t *pamh, int flags, int argc,
+ const char **argv)
+{
+ return PAM_IGNORE;
+}
+
+PAM_EXTERN
+int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc,
+ const char **argv)
+{
+ int retval = PAM_IGNORE;
+ int fd;
+ char *mtmp=NULL, *motd_path=NULL;
+ struct pam_conv *conversation;
+ struct pam_message message;
+ struct pam_message *pmessage = &message;
+ struct pam_response *resp = NULL;
+ struct stat st;
+
+ if (flags & PAM_SILENT) {
+ return retval;
+ }
+
+ for (; argc-- > 0; ++argv) {
+ if (!strncmp(*argv,"motd=",5)) {
+ motd_path = (char *) strdup(5+*argv);
+ if (motd_path != NULL) {
+ D(("set motd path: %s", motd_path));
+ } else {
+ D(("failed to duplicate motd path - ignored"));
+ }
+ }
+ }
+
+ if (motd_path == NULL)
+ motd_path = DEFAULT_MOTD;
+
+ message.msg_style = PAM_TEXT_INFO;
+
+ if ((fd = open(motd_path, O_RDONLY, 0)) >= 0) {
+ /* fill in message buffer with contents of motd */
+ if ((fstat(fd, &st) < 0) || !st.st_size)
+ return retval;
+ message.msg = mtmp = malloc(st.st_size+1);
+ /* if malloc failed... */
+ if (!message.msg) return retval;
+ read(fd, mtmp, st.st_size);
+ if (mtmp[st.st_size-1] == '\n')
+ mtmp[st.st_size-1] = '\0';
+ else
+ mtmp[st.st_size] = '\0';
+ close(fd);
+ /* Use conversation function to give user contents of motd */
+ pam_get_item(pamh, PAM_CONV, (const void **)&conversation);
+ conversation->conv(1, (const struct pam_message **)&pmessage,
+ &resp, conversation->appdata_ptr);
+ free(mtmp);
+ if (resp)
+ _pam_drop_reply(resp, 1);
+ }
+
+ return retval;
+}
+
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_motd_modstruct = {
+ "pam_motd",
+ NULL,
+ NULL,
+ NULL,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ NULL,
+};
+
+#endif
+
+/* end of module definition */
diff --git a/modules/pam_nologin/.cvsignore b/modules/pam_nologin/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_nologin/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_nologin/Makefile b/modules/pam_nologin/Makefile
new file mode 100644
index 00000000..03d779bf
--- /dev/null
+++ b/modules/pam_nologin/Makefile
@@ -0,0 +1,86 @@
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# Michael K. Johnson <johnsonm@redhat.com> 1996/10/24
+#
+
+TITLE=pam_nologin
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ mkdir -p ./dynamic
+endif
+ifdef STATIC
+ mkdir -p ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD) -lc
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ mkdir -p $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ install -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak dynamic/* static/*
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_nologin/README b/modules/pam_nologin/README
new file mode 100644
index 00000000..0586de66
--- /dev/null
+++ b/modules/pam_nologin/README
@@ -0,0 +1,12 @@
+# $Id$
+#
+
+This module always lets root in; it lets other users in only if the file
+/etc/nologin doesn't exist. In any case, if /etc/nologin exists, it's
+contents are displayed to the user.
+
+module services provided:
+
+ auth _authentication and _setcred (blank)
+
+Michael K. Johnson
diff --git a/modules/pam_nologin/pam_nologin.c b/modules/pam_nologin/pam_nologin.c
new file mode 100644
index 00000000..306619e1
--- /dev/null
+++ b/modules/pam_nologin/pam_nologin.c
@@ -0,0 +1,130 @@
+/* pam_nologin module */
+
+/*
+ * $Id$
+ *
+ * Written by Michael K. Johnson <johnsonm@redhat.com> 1996/10/24
+ *
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:46 agmorgan
+ * Initial revision
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:17 morgan
+ * Linux PAM sources pre-0.66
+ *
+ * Revision 1.4 1997/04/05 06:36:47 morgan
+ * display message when the user is unknown
+ *
+ * Revision 1.3 1996/12/01 03:00:54 morgan
+ * added prototype to conversation, gave static structure name of module
+ *
+ * Revision 1.2 1996/11/10 21:02:31 morgan
+ * compile against .53
+ *
+ * Revision 1.1 1996/10/25 03:19:36 morgan
+ * Initial revision
+ *
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+
+#include <security/_pam_macros.h>
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_AUTH
+
+#include <security/pam_modules.h>
+
+/* --- authentication management functions (only) --- */
+
+PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc,
+ const char **argv)
+{
+ int retval = PAM_SUCCESS;
+ int fd;
+ const char *username;
+ char *mtmp=NULL;
+ struct passwd *user_pwd;
+ struct pam_conv *conversation;
+ struct pam_message message;
+ struct pam_message *pmessage = &message;
+ struct pam_response *resp = NULL;
+ struct stat st;
+
+ if ((fd = open("/etc/nologin", O_RDONLY, 0)) >= 0) {
+ /* root can still log in; lusers cannot */
+ if ((pam_get_user(pamh, &username, NULL) != PAM_SUCCESS)
+ || !username) {
+ return PAM_SERVICE_ERR;
+ }
+ user_pwd = getpwnam(username);
+ if (user_pwd && user_pwd->pw_uid == 0) {
+ message.msg_style = PAM_TEXT_INFO;
+ } else {
+ if (!user_pwd) {
+ retval = PAM_USER_UNKNOWN;
+ } else {
+ retval = PAM_AUTH_ERR;
+ }
+ message.msg_style = PAM_ERROR_MSG;
+ }
+
+ /* fill in message buffer with contents of /etc/nologin */
+ if (fstat(fd, &st) < 0) /* give up trying to display message */
+ return retval;
+ message.msg = mtmp = malloc(st.st_size+1);
+ /* if malloc failed... */
+ if (!message.msg) return retval;
+ read(fd, mtmp, st.st_size);
+ mtmp[st.st_size] = '\000';
+
+ /* Use conversation function to give user contents of /etc/nologin */
+ pam_get_item(pamh, PAM_CONV, (const void **)&conversation);
+ conversation->conv(1, (const struct pam_message **)&pmessage,
+ &resp, conversation->appdata_ptr);
+ free(mtmp);
+ if (resp)
+ _pam_drop_reply(resp, 1);
+ }
+
+ return retval;
+}
+
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc,
+ const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_nologin_modstruct = {
+ "pam_nologin",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+#endif
+
+/* end of module definition */
diff --git a/modules/pam_permit/.cvsignore b/modules/pam_permit/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_permit/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_permit/Makefile b/modules/pam_permit/Makefile
new file mode 100644
index 00000000..c291599e
--- /dev/null
+++ b/modules/pam_permit/Makefile
@@ -0,0 +1,132 @@
+#
+# $Id$
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# $Log$
+# Revision 1.1 2000/06/20 22:11:46 agmorgan
+# Initial revision
+#
+# Revision 1.1.1.1 1998/07/12 05:17:16 morgan
+# Linux PAM sources pre-0.66
+#
+# Revision 1.8 1997/04/05 06:33:25 morgan
+# fakeroot
+#
+# Revision 1.7 1997/02/15 19:02:27 morgan
+# updated email address
+#
+# Revision 1.6 1996/11/10 20:14:34 morgan
+# cross platform support
+#
+# Revision 1.5 1996/09/05 06:32:45 morgan
+# ld --> gcc
+#
+# Revision 1.4 1996/05/26 15:49:25 morgan
+# make dynamic and static dirs
+#
+# Revision 1.3 1996/05/26 04:04:26 morgan
+# automated static support
+#
+# Revision 1.2 1996/03/16 17:56:38 morgan
+# tidied up
+#
+#
+# Created by Andrew Morgan <morgan@parc.power.net> 1996/3/11
+#
+
+# Convenient defaults for compiling independently of the full source
+# tree.
+ifndef FULL_LINUX_PAM_SOURCE_TREE
+export DYNAMIC=-DPAM_DYNAMIC
+export CC=gcc
+export CFLAGS=-O2 -Dlinux -DLINUX_PAM \
+ -ansi -D_POSIX_SOURCE -Wall -Wwrite-strings \
+ -Wpointer-arith -Wcast-qual -Wcast-align -Wtraditional \
+ -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Winline \
+ -Wshadow -pedantic -fPIC
+export MKDIR=mkdir -p
+export LD_D=gcc -shared -Xlinker -x
+endif
+
+#
+#
+
+TITLE=pam_permit
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(TARGET_ARCH) -c $< -o $@
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+endif
+
+ifdef DYNAMIC
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+endif
+
+ifdef STATIC
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_permit/README b/modules/pam_permit/README
new file mode 100644
index 00000000..c399710c
--- /dev/null
+++ b/modules/pam_permit/README
@@ -0,0 +1,4 @@
+# $Id$
+#
+
+this module always returns PAM_SUCCESS, it ignores all options.
diff --git a/modules/pam_permit/pam_permit.c b/modules/pam_permit/pam_permit.c
new file mode 100644
index 00000000..cf11a2dc
--- /dev/null
+++ b/modules/pam_permit/pam_permit.c
@@ -0,0 +1,128 @@
+/* pam_permit module */
+
+/*
+ * $Id$
+ *
+ * Written by Andrew Morgan <morgan@parc.power.net> 1996/3/11
+ *
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:46 agmorgan
+ * Initial revision
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:16 morgan
+ * Linux PAM sources pre-0.66
+ *
+ * Revision 1.5 1997/02/15 19:03:15 morgan
+ * fixed email address
+ *
+ * Revision 1.4 1997/02/15 16:03:10 morgan
+ * force a name for user
+ *
+ * Revision 1.3 1996/06/02 08:10:14 morgan
+ * updated for new static protocol
+ *
+ */
+
+#define DEFAULT_USER "nobody"
+
+#include <stdio.h>
+
+/*
+ * here, we make definitions for the externally accessible functions
+ * in this file (these definitions are required for static modules
+ * but strongly encouraged generally) they are used to instruct the
+ * modules include file to define their prototypes.
+ */
+
+#define PAM_SM_AUTH
+#define PAM_SM_ACCOUNT
+#define PAM_SM_SESSION
+#define PAM_SM_PASSWORD
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+/* --- authentication management functions --- */
+
+PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ int retval;
+ const char *user=NULL;
+
+ /*
+ * authentication requires we know who the user wants to be
+ */
+ retval = pam_get_user(pamh, &user, NULL);
+ if (retval != PAM_SUCCESS) {
+ D(("get user returned error: %s", pam_strerror(pamh,retval)));
+ return retval;
+ }
+ if (user == NULL || *user == '\0') {
+ D(("username not known"));
+ pam_set_item(pamh, PAM_USER, (const void *) DEFAULT_USER);
+ }
+ user = NULL; /* clean up */
+
+ return PAM_SUCCESS;
+}
+
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+/* --- account management functions --- */
+
+PAM_EXTERN
+int pam_sm_acct_mgmt(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+/* --- password management --- */
+
+PAM_EXTERN
+int pam_sm_chauthtok(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+/* --- session management --- */
+
+PAM_EXTERN
+int pam_sm_open_session(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+PAM_EXTERN
+int pam_sm_close_session(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+/* end of module definition */
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_permit_modstruct = {
+ "pam_permit",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ pam_sm_acct_mgmt,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ pam_sm_chauthtok
+};
+
+#endif
diff --git a/modules/pam_pwdb/.cvsignore b/modules/pam_pwdb/.cvsignore
new file mode 100644
index 00000000..f0420bac
--- /dev/null
+++ b/modules/pam_pwdb/.cvsignore
@@ -0,0 +1,2 @@
+dynamic
+pwdb_chkpwd
diff --git a/modules/pam_pwdb/BUGS b/modules/pam_pwdb/BUGS
new file mode 100644
index 00000000..e7170b70
--- /dev/null
+++ b/modules/pam_pwdb/BUGS
@@ -0,0 +1,14 @@
+$Id$
+
+$Log$
+Revision 1.1 2000/06/20 22:11:46 agmorgan
+Initial revision
+
+Revision 1.1.1.1 1998/07/12 05:17:16 morgan
+Linux PAM sources pre-0.66
+
+Revision 1.2 1996/09/05 06:36:16 morgan
+revised for .52 to be released
+
+
+As of Linux-PAM-0.52 this is new. No known bugs yet.
diff --git a/modules/pam_pwdb/CHANGELOG b/modules/pam_pwdb/CHANGELOG
new file mode 100644
index 00000000..7bad5cd8
--- /dev/null
+++ b/modules/pam_pwdb/CHANGELOG
@@ -0,0 +1,10 @@
+$Id$
+
+Tue Apr 23 12:28:09 EDT 1996 (Alexander O. Yuriev alex@bach.cis.temple.edu)
+
+ * PAM_DISALLOW_NULL_AUTHTOK implemented in the authentication module
+ * pam_sm_open_session() and pam_sm_close_session() implemented
+ A new "trace" flag added to flags of /etc/pam.conf. Using this
+ flag system administrator is able to make pam_unix module provide
+ very extensive audit trail sent so syslog with LOG_AUTHPRIV level.
+ * pam_sm_set_cred() is done
diff --git a/modules/pam_pwdb/Makefile b/modules/pam_pwdb/Makefile
new file mode 100644
index 00000000..fcb7aec4
--- /dev/null
+++ b/modules/pam_pwdb/Makefile
@@ -0,0 +1,193 @@
+# $Id$
+#
+# This Makefile controls a build process of the pam_unix module
+# for Linux-PAM. You should not modify this Makefile.
+#
+# rewritten to compile new module Andrew Morgan
+# <morgan@parc.power.net> 1996/11/6
+#
+
+#
+# Note, the STATIC module is commented out because it doesn't work.
+# please fix!
+#
+
+ifndef FULL_LINUX_PAM_SOURCE_TREE
+export DYNAMIC=-DPAM_DYNAMIC
+export CC=gcc
+export CFLAGS=-O2 -Dlinux -DLINUX_PAM \
+ -ansi -D_POSIX_SOURCE -Wall -Wwrite-strings \
+ -Wpointer-arith -Wcast-qual -Wcast-align -Wtraditional \
+ -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Winline \
+ -Wshadow -pedantic -fPIC
+export MKDIR=mkdir -p
+export LD_D=gcc -shared -Xlinker -x
+export HAVE_PWDBLIB=yes
+endif
+
+ifeq ($(shell if [ -f /lib/libcrypt.so.* ]; then echo yes ; else echo no ; fi),yes)
+EXTRALS += -lcrypt
+endif
+
+ifeq ($(HAVE_PWDBLIB),yes)
+
+TITLE=pam_pwdb
+CHKPWD=pwdb_chkpwd
+
+# compilation flags
+EXTRAS=
+# extra object files
+PLUS=
+# extra files that may be needed to be created
+CREATE=
+
+# NOTE: this module links dynamically to the libpwdb library.
+EXTRALS += -lpwdb
+EXTRAS += -DCHKPWD_HELPER=\"$(SUPLEMENTED)/$(CHKPWD)\"
+
+########################### don't edit below ##########################
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+#LIBOBJS = $(addprefix static/,$(LIBOBJ))
+LIBDEPS = pam_unix_acct.-c pam_unix_auth.-c pam_unix_passwd.-c \
+ pam_unix_sess.-c pam_unix_pwupd.-c support.-c bigcrypt.-c
+
+PLUS += md5_good.o md5_broken.o md5_crypt_good.o md5_crypt_broken.o
+CFLAGS += $(EXTRAS)
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+#ifdef STATIC
+#LIBSTATIC = lib$(TITLE).o
+#endif
+
+all: info dirs $(PLUS) $(LIBSHARED) $(LIBSTATIC) register $(CHKPWD)
+
+dynamic/$(LIBOBJ) : $(LIBSRC) $(LIBDEPS)
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+#static/$(LIBOBJ) : $(LIBSRC) $(LIBDEPS)
+# $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+info:
+ @echo
+ @echo "*** Building PAM_pwdb module..."
+ @echo
+
+$(CHKPWD): pwdb_chkpwd.o md5_good.o md5_broken.o \
+ md5_crypt_good.o md5_crypt_broken.o
+ $(CC) -o $(CHKPWD) $^ -lpwdb
+
+pwdb_chkpwd.o: pwdb_chkpwd.c pam_unix_md.-c bigcrypt.-c
+
+md5_good.o: md5.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -DHIGHFIRST -D'MD5Name(x)=Good##x' \
+ $(TARGET_ARCH) -c $< -o $@
+
+md5_broken.o: md5.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -D'MD5Name(x)=Broken##x' \
+ $(TARGET_ARCH) -c $< -o $@
+
+md5_crypt_good.o: md5_crypt.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -D'MD5Name(x)=Good##x' \
+ $(TARGET_ARCH) -c $< -o $@
+
+md5_crypt_broken.o: md5_crypt.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -D'MD5Name(x)=Broken##x' \
+ $(TARGET_ARCH) -c $< -o $@
+
+dirs:
+ifdef DYNAMIC
+ @$(MKDIR) ./dynamic
+endif
+#ifdef STATIC
+# @$(MKDIR) ./static
+#endif
+
+register:
+#ifdef STATIC
+# ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+#endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD) $(PLUS) $(EXTRALS)
+endif
+
+#ifdef STATIC
+#$(LIBOBJS): $(LIBSRC)
+#
+#$(LIBSTATIC): $(LIBOBJS)
+# $(LD) -r -o $@ $(LIBOBJS) $(PLUS) $(EXTRALS)
+#endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+ $(MKDIR) $(FAKEROOT)$(SUPLEMENTED)
+ $(INSTALL) -m 4555 -o root -g root $(CHKPWD) $(FAKEROOT)$(SUPLEMENTED)
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+ rm -f $(FAKEROOT)$(SUPLEMENTED)/$(CHKPWD)
+
+clean:
+ rm -f $(CHKPWD) $(LIBOBJD) $(LIBOBJS) $(MOREDELS) core *~ *.o *.so
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak
+
+else
+
+include ../dont_makefile
+
+endif
+
+#####################################################################
+# $Log$
+# Revision 1.1 2000/06/20 22:11:47 agmorgan
+# Initial revision
+#
+# Revision 1.4 1999/08/01 16:18:27 morgan
+# added a conditional for libcrypt
+#
+# Revision 1.3 1999/07/08 05:02:02 morgan
+# glibc fixes (Thorsten Kukuk, Adam J. Richter)
+#
+# Revision 1.2 1999/07/04 23:22:38 morgan
+# Andrey's MD5 (bigendian) work around + cleanup to address problems with
+# applications that let an (ab)user kill them off without giving PAM the
+# opportunity to end. [Problem report from Tani Hosokawa on bugtraq.]
+#
+# Revision 1.1.1.1 1998/07/12 05:17:16 morgan
+# Linux PAM sources pre-0.66
+#
+# Revision 1.7 1997/04/05 06:28:50 morgan
+# fakeroot
+#
+# Revision 1.6 1997/02/15 17:25:32 morgan
+# update for .56 . extra commands for new helper binary
+#
+# Revision 1.5 1997/01/04 20:39:08 morgan
+# conditional on having libpwdb
+#
+# Revision 1.4 1996/12/01 03:02:03 morgan
+# changed banner, removed linking libraries
+#
+# Revision 1.3 1996/11/10 20:14:42 morgan
+# cross platform support
+#
+# Revision 1.2 1996/09/05 06:36:49 morgan
+# options added and use of LD altered
+#
+# Revision 1.1 1996/08/29 13:23:29 morgan
+# Initial revision
+#
+#
diff --git a/modules/pam_pwdb/README b/modules/pam_pwdb/README
new file mode 100644
index 00000000..4f420855
--- /dev/null
+++ b/modules/pam_pwdb/README
@@ -0,0 +1,41 @@
+This is the pam_unix module. It has been significantly rewritten since
+.51 was released (due mostly to the efforts of Cristian Gafton), and
+now takes more options and correctly updates vanilla UNIX/shadow/md5
+passwords.
+
+[Please read the source and make a note of all the warnings there, as
+the license suggests -- use at your own risk.]
+
+So far as I am concerned this module is now pretty stable. If you find
+any bugs, PLEASE tell me! <morgan@linux.kernel.org>
+
+Options recognized by this module are as follows:
+
+ debug - log more debugging info
+ audit - a little more extreme than debug
+ use_first_pass - don't prompt the user for passwords
+ take them from PAM_ items instead
+ try_first_pass - don't prompt the user for the passwords
+ unless PAM_(OLD)AUTHTOK is unset
+ use_authtok - like try_first_pass, but *fail* if the new
+ PAM_AUTHTOK has not been previously set.
+ (intended for stacking password modules only)
+ not_set_pass - don't set the PAM_ items with the passwords
+ used by this module.
+ shadow - try to maintian a shadow based system.
+ unix - when changing passwords, they are placed
+ in the /etc/passwd file
+ md5 - when a user changes their password next,
+ encrypt it with the md5 algorithm.
+ bigcrypt - when a user changes their password next,
+ excrypt it with the DEC C2-algorithm(0).
+ nodelay - used to prevent failed authentication
+ resulting in a delay of about 1 second.
+
+There is some support for building a shadow file on-the-fly from an
+/etc/passwd file. This is VERY alpha. If you want to play with it you
+should read the source to find the appropriate #define that you will
+need.
+
+---------------------
+Andrew Morgan <morgan@linux.kernel.org>
diff --git a/modules/pam_pwdb/TODO b/modules/pam_pwdb/TODO
new file mode 100644
index 00000000..9dc7fc7e
--- /dev/null
+++ b/modules/pam_pwdb/TODO
@@ -0,0 +1,34 @@
+$Id$
+
+ * get NIS working
+ * .. including "nonis" argument
+ * add helper binary
+
+Wed Sep 4 23:40:09 PDT 1996 Andrew G. Morgan
+
+ * verify that it works for everyone
+ * look more seriously at the issue of generating a shadow
+ system on the fly
+ * add some more password flavors
+
+Thu Aug 29 06:26:42 PDT 1996 Andrew G. Morgan
+
+ * check that complete rewrite works! ;^)
+ * complete shadow support to the password changing code.
+ Also some code needed here for session managment?
+ (both pam.conf argument to turn it on/off, and some
+ conditional compilation.)
+ * md5 passwords...
+ * make the exclusive nature of the arguments work. That is,
+ only recognize the flags when appropriate.
+
+Wed May 8 19:08:49 EDT 1996 Alexander O. Yuriev
+
+ * support.c should go.
+
+Tue Apr 23 21:43:55 EDT 1996 Alexander O. Yuriev
+
+ * pam_sm_chauth_tok() should be written
+ * QUICK FIX: pam_sm_setcred() probably returns incorrect error code
+
+
diff --git a/modules/pam_pwdb/bigcrypt.-c b/modules/pam_pwdb/bigcrypt.-c
new file mode 100644
index 00000000..321f2491
--- /dev/null
+++ b/modules/pam_pwdb/bigcrypt.-c
@@ -0,0 +1,114 @@
+/*
+ * This function implements the "bigcrypt" algorithm specifically for
+ * Linux-PAM.
+ *
+ * This algorithm is algorithm 0 (default) shipped with the C2 secure
+ * implementation of Digital UNIX.
+ *
+ * Disclaimer: This work is not based on the source code to Digital
+ * UNIX, nor am I connected to Digital Equipment Corp, in any way
+ * other than as a customer. This code is based on published
+ * interfaces and reasonable guesswork.
+ *
+ * Description: The cleartext is divided into blocks of SEGMENT_SIZE=8
+ * characters or less. Each block is encrypted using the standard UNIX
+ * libc crypt function. The result of the encryption for one block
+ * provides the salt for the suceeding block.
+ *
+ * Restrictions: The buffer used to hold the encrypted result is
+ * statically allocated. (see MAX_PASS_LEN below). This is necessary,
+ * as the returned pointer points to "static data that are overwritten
+ * by each call", (XPG3: XSI System Interface + Headers pg 109), and
+ * this is a drop in replacement for crypt();
+ *
+ * Andy Phillips <atp@mssl.ucl.ac.uk>
+ */
+
+/*
+ * Max cleartext password length in segments of 8 characters this
+ * function can deal with (16 segments of 8 chars= max 128 character
+ * password).
+ */
+
+#define MAX_PASS_LEN 16
+#define SEGMENT_SIZE 8
+#define SALT_SIZE 2
+#define KEYBUF_SIZE ((MAX_PASS_LEN*SEGMENT_SIZE)+SALT_SIZE)
+#define ESEGMENT_SIZE 11
+#define CBUF_SIZE ((MAX_PASS_LEN*ESEGMENT_SIZE)+SALT_SIZE+1)
+
+static char *bigcrypt(const char *key, const char *salt)
+{
+ static char dec_c2_cryptbuf[CBUF_SIZE]; /* static storage area */
+
+ unsigned long int keylen,n_seg,j;
+ char *cipher_ptr,*plaintext_ptr,*tmp_ptr,*salt_ptr;
+ char keybuf[KEYBUF_SIZE+1];
+
+ D(("called with key='%s', salt='%s'.", key, salt));
+
+ /* reset arrays */
+ memset(keybuf, 0, KEYBUF_SIZE+1);
+ memset(dec_c2_cryptbuf, 0, CBUF_SIZE);
+
+ /* fill KEYBUF_SIZE with key */
+ strncpy(keybuf, key, KEYBUF_SIZE);
+
+ /* deal with case that we are doing a password check for a
+ conventially encrypted password: the salt will be
+ SALT_SIZE+ESEGMENT_SIZE long. */
+ if (strlen(salt) == (SALT_SIZE+ESEGMENT_SIZE))
+ keybuf[SEGMENT_SIZE] = '\0'; /* terminate password early(?) */
+
+ keylen = strlen(keybuf);
+
+ if (!keylen) {
+ n_seg = 1;
+ } else {
+ /* work out how many segments */
+ n_seg = 1 + ((keylen-1)/SEGMENT_SIZE);
+ }
+
+ if (n_seg > MAX_PASS_LEN)
+ n_seg = MAX_PASS_LEN; /* truncate at max length */
+
+ /* set up some pointers */
+ cipher_ptr = dec_c2_cryptbuf;
+ plaintext_ptr = keybuf;
+
+ /* do the first block with supplied salt */
+ tmp_ptr = crypt(plaintext_ptr,salt); /* libc crypt() */
+
+ /* and place in the static area */
+ strncpy(cipher_ptr, tmp_ptr, 13);
+ cipher_ptr += ESEGMENT_SIZE + SALT_SIZE;
+ plaintext_ptr += SEGMENT_SIZE; /* first block of SEGMENT_SIZE */
+
+ /* change the salt (1st 2 chars of previous block) - this was found
+ by dowsing */
+
+ salt_ptr = cipher_ptr - ESEGMENT_SIZE;
+
+ /* so far this is identical to "return crypt(key, salt);", if
+ there is more than one block encrypt them... */
+
+ if (n_seg > 1) {
+ for (j=2; j <= n_seg; j++) {
+
+ tmp_ptr = crypt(plaintext_ptr, salt_ptr);
+
+ /* skip the salt for seg!=0 */
+ strncpy(cipher_ptr, (tmp_ptr+SALT_SIZE), ESEGMENT_SIZE);
+
+ cipher_ptr += ESEGMENT_SIZE;
+ plaintext_ptr += SEGMENT_SIZE;
+ salt_ptr = cipher_ptr - ESEGMENT_SIZE;
+ }
+ }
+
+ D(("key=|%s|, salt=|%s|\nbuf=|%s|\n", key, salt, dec_c2_cryptbuf));
+
+ /* this is the <NUL> terminated encrypted password */
+
+ return dec_c2_cryptbuf;
+}
diff --git a/modules/pam_pwdb/md5.c b/modules/pam_pwdb/md5.c
new file mode 100644
index 00000000..d3f47763
--- /dev/null
+++ b/modules/pam_pwdb/md5.c
@@ -0,0 +1,270 @@
+/* $Id$
+ *
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ *
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:48 agmorgan
+ * Initial revision
+ *
+ * Revision 1.2 1999/07/04 23:22:38 morgan
+ * Andrey's MD5 (bigendian) work around + cleanup to address problems with
+ * applications that let an (ab)user kill them off without giving PAM the
+ * opportunity to end. [Problem report from Tani Hosokawa on bugtraq.]
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:17 morgan
+ * Linux PAM sources pre-0.66
+ *
+ * Revision 1.1 1996/09/05 06:43:31 morgan
+ * Initial revision
+ *
+ */
+
+#include <string.h>
+#include "md5.h"
+
+#ifndef HIGHFIRST
+#define byteReverse(buf, len) /* Nothing */
+#else
+static void byteReverse(unsigned char *buf, unsigned longs);
+
+#ifndef ASM_MD5
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void byteReverse(unsigned char *buf, unsigned longs)
+{
+ uint32 t;
+ do {
+ t = (uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+ ((unsigned) buf[1] << 8 | buf[0]);
+ *(uint32 *) buf = t;
+ buf += 4;
+ } while (--longs);
+}
+#endif
+#endif
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Name(MD5Init)(struct MD5Context *ctx)
+{
+ ctx->buf[0] = 0x67452301U;
+ ctx->buf[1] = 0xefcdab89U;
+ ctx->buf[2] = 0x98badcfeU;
+ ctx->buf[3] = 0x10325476U;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Name(MD5Update)(struct MD5Context *ctx, unsigned const char *buf, unsigned len)
+{
+ uint32 t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((uint32) len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if (t) {
+ unsigned char *p = (unsigned char *) ctx->in + t;
+
+ t = 64 - t;
+ if (len < t) {
+ memcpy(p, buf, len);
+ return;
+ }
+ memcpy(p, buf, t);
+ byteReverse(ctx->in, 16);
+ MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in);
+ buf += t;
+ len -= t;
+ }
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ byteReverse(ctx->in, 16);
+ MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Name(MD5Final)(unsigned char digest[16], struct MD5Context *ctx)
+{
+ unsigned count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset(p, 0, count);
+ byteReverse(ctx->in, 16);
+ MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ memset(ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(p, 0, count - 8);
+ }
+ byteReverse(ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ((uint32 *) ctx->in)[14] = ctx->bits[0];
+ ((uint32 *) ctx->in)[15] = ctx->bits[1];
+
+ MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in);
+ byteReverse((unsigned char *) ctx->buf, 4);
+ memcpy(digest, ctx->buf, 16);
+ memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void MD5Name(MD5Transform)(uint32 buf[4], uint32 const in[16])
+{
+ register uint32 a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478U, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756U, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070dbU, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceeeU, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0fafU, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62aU, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613U, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501U, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8U, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7afU, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1U, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7beU, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122U, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193U, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438eU, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821U, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562U, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340U, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51U, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aaU, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105dU, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453U, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681U, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8U, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6U, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6U, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87U, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14edU, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905U, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8U, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9U, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8aU, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942U, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681U, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122U, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380cU, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44U, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9U, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60U, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70U, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6U, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127faU, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085U, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05U, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039U, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5U, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8U, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665U, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244U, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97U, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7U, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039U, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3U, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92U, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47dU, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1U, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4fU, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0U, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314U, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1U, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82U, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235U, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bbU, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391U, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+#endif
diff --git a/modules/pam_pwdb/md5.h b/modules/pam_pwdb/md5.h
new file mode 100644
index 00000000..279ce46f
--- /dev/null
+++ b/modules/pam_pwdb/md5.h
@@ -0,0 +1,34 @@
+#ifndef MD5_H
+#define MD5_H
+
+#ifdef __alpha
+typedef unsigned int uint32;
+#else
+typedef unsigned long uint32;
+#endif
+
+struct MD5Context {
+ uint32 buf[4];
+ uint32 bits[2];
+ unsigned char in[64];
+};
+
+void GoodMD5Init(struct MD5Context *);
+void GoodMD5Update(struct MD5Context *, unsigned const char *, unsigned);
+void GoodMD5Final(unsigned char digest[16], struct MD5Context *);
+void GoodMD5Transform(uint32 buf[4], uint32 const in[16]);
+void BrokenMD5Init(struct MD5Context *);
+void BrokenMD5Update(struct MD5Context *, unsigned const char *, unsigned);
+void BrokenMD5Final(unsigned char digest[16], struct MD5Context *);
+void BrokenMD5Transform(uint32 buf[4], uint32 const in[16]);
+
+char *Goodcrypt_md5(const char *pw, const char *salt);
+char *Brokencrypt_md5(const char *pw, const char *salt);
+
+/*
+* This is needed to make RSAREF happy on some MS-DOS compilers.
+*/
+
+typedef struct MD5Context MD5_CTX;
+
+#endif /* MD5_H */
diff --git a/modules/pam_pwdb/md5_crypt.c b/modules/pam_pwdb/md5_crypt.c
new file mode 100644
index 00000000..1d755a08
--- /dev/null
+++ b/modules/pam_pwdb/md5_crypt.c
@@ -0,0 +1,153 @@
+/* $Id$
+ *
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * Origin: Id: crypt.c,v 1.3 1995/05/30 05:42:22 rgrimes Exp
+ *
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:48 agmorgan
+ * Initial revision
+ *
+ * Revision 1.2 1999/07/04 23:22:38 morgan
+ * Andrey's MD5 (bigendian) work around + cleanup to address problems with
+ * applications that let an (ab)user kill them off without giving PAM the
+ * opportunity to end. [Problem report from Tani Hosokawa on bugtraq.]
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:17 morgan
+ * Linux PAM sources pre-0.66
+ *
+ * Revision 1.1 1996/09/05 06:43:31 morgan
+ * Initial revision
+ *
+ */
+
+#include <string.h>
+#include "md5.h"
+
+static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */
+ "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+static void
+to64(char *s, unsigned long v, int n)
+{
+ while (--n >= 0) {
+ *s++ = itoa64[v&0x3f];
+ v >>= 6;
+ }
+}
+
+/*
+ * UNIX password
+ *
+ * Use MD5 for what it is best at...
+ */
+
+char * MD5Name(crypt_md5)(const char *pw, const char *salt)
+{
+ const char *magic = "$1$";
+ /* This string is magic for this algorithm. Having
+ * it this way, we can get get better later on */
+ static char passwd[120], *p;
+ static const char *sp,*ep;
+ unsigned char final[16];
+ int sl,pl,i,j;
+ MD5_CTX ctx,ctx1;
+ unsigned long l;
+
+ /* Refine the Salt first */
+ sp = salt;
+
+ /* If it starts with the magic string, then skip that */
+ if(!strncmp(sp,magic,strlen(magic)))
+ sp += strlen(magic);
+
+ /* It stops at the first '$', max 8 chars */
+ for(ep=sp;*ep && *ep != '$' && ep < (sp+8);ep++)
+ continue;
+
+ /* get the length of the true salt */
+ sl = ep - sp;
+
+ MD5Name(MD5Init)(&ctx);
+
+ /* The password first, since that is what is most unknown */
+ MD5Name(MD5Update)(&ctx,(unsigned const char *)pw,strlen(pw));
+
+ /* Then our magic string */
+ MD5Name(MD5Update)(&ctx,(unsigned const char *)magic,strlen(magic));
+
+ /* Then the raw salt */
+ MD5Name(MD5Update)(&ctx,(unsigned const char *)sp,sl);
+
+ /* Then just as many characters of the MD5(pw,salt,pw) */
+ MD5Name(MD5Init)(&ctx1);
+ MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw));
+ MD5Name(MD5Update)(&ctx1,(unsigned const char *)sp,sl);
+ MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw));
+ MD5Name(MD5Final)(final,&ctx1);
+ for(pl = strlen(pw); pl > 0; pl -= 16)
+ MD5Name(MD5Update)(&ctx,(unsigned const char *)final,pl>16 ? 16 : pl);
+
+ /* Don't leave anything around in vm they could use. */
+ memset(final,0,sizeof final);
+
+ /* Then something really weird... */
+ for (j=0,i = strlen(pw); i ; i >>= 1)
+ if(i&1)
+ MD5Name(MD5Update)(&ctx, (unsigned const char *)final+j, 1);
+ else
+ MD5Name(MD5Update)(&ctx, (unsigned const char *)pw+j, 1);
+
+ /* Now make the output string */
+ strcpy(passwd,magic);
+ strncat(passwd,sp,sl);
+ strcat(passwd,"$");
+
+ MD5Name(MD5Final)(final,&ctx);
+
+ /*
+ * and now, just to make sure things don't run too fast
+ * On a 60 Mhz Pentium this takes 34 msec, so you would
+ * need 30 seconds to build a 1000 entry dictionary...
+ */
+ for(i=0;i<1000;i++) {
+ MD5Name(MD5Init)(&ctx1);
+ if(i & 1)
+ MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw));
+ else
+ MD5Name(MD5Update)(&ctx1,(unsigned const char *)final,16);
+
+ if(i % 3)
+ MD5Name(MD5Update)(&ctx1,(unsigned const char *)sp,sl);
+
+ if(i % 7)
+ MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw));
+
+ if(i & 1)
+ MD5Name(MD5Update)(&ctx1,(unsigned const char *)final,16);
+ else
+ MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw));
+ MD5Name(MD5Final)(final,&ctx1);
+ }
+
+ p = passwd + strlen(passwd);
+
+ l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p,l,4); p += 4;
+ l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p,l,4); p += 4;
+ l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p,l,4); p += 4;
+ l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p,l,4); p += 4;
+ l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p,l,4); p += 4;
+ l = final[11] ; to64(p,l,2); p += 2;
+ *p = '\0';
+
+ /* Don't leave anything around in vm they could use. */
+ memset(final,0,sizeof final);
+
+ return passwd;
+}
+
diff --git a/modules/pam_pwdb/pam_pwdb.c b/modules/pam_pwdb/pam_pwdb.c
new file mode 100644
index 00000000..68ca68fd
--- /dev/null
+++ b/modules/pam_pwdb/pam_pwdb.c
@@ -0,0 +1,256 @@
+/*
+ * $Id$
+ *
+ * This is the single file that will be compiled for pam_unix.
+ * it includes each of the modules that have beed defined in the .-c
+ * files in this directory.
+ *
+ * It is a little ugly to do it this way, but it is a simple way of
+ * defining static functions only once, and yet keeping the separate
+ * files modular. If you can think of something better, please email
+ * Andrew Morgan <morgan@linux.kernel.org>
+ *
+ * See the end of this file for Copyright information.
+ */
+
+static const char rcsid[] =
+"$Id$\n"
+" - PWDB Pluggable Authentication module. <morgan@linux.kernel.org>"
+;
+
+/* #define DEBUG */
+
+#define _SVID_SOURCE
+#define _BSD_SOURCE
+#define _BSD_COMPAT
+
+#ifdef linux
+# define _GNU_SOURCE
+# include <features.h>
+#endif
+
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h> /* for time() */
+#include <fcntl.h>
+#include <ctype.h>
+
+#include <sys/time.h>
+#include <unistd.h>
+
+#include <pwdb/pwdb_public.h>
+
+/* indicate the following groups are defined */
+
+#define PAM_SM_AUTH
+#define PAM_SM_ACCOUNT
+#define PAM_SM_SESSION
+#define PAM_SM_PASSWORD
+
+#include <security/_pam_macros.h>
+#include <security/pam_modules.h>
+
+#ifndef LINUX_PAM
+#include <security/pam_appl.h>
+#endif /* LINUX_PAM */
+
+#include "./support.-c"
+
+/*
+ * PAM framework looks for these entry-points to pass control to the
+ * authentication module.
+ */
+
+#include "./pam_unix_auth.-c"
+
+PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags
+ , int argc, const char **argv)
+{
+ unsigned int ctrl;
+ int retval;
+
+ D(("called."));
+
+ pwdb_start();
+ ctrl = set_ctrl(flags, argc, argv);
+ retval = _unix_auth( pamh, ctrl );
+ pwdb_end();
+
+ if ( on(UNIX_LIKE_AUTH, ctrl) ) {
+ D(("recording return code for next time [%d]", retval));
+ pam_set_data(pamh, "pwdb_setcred_return", (void *) &retval, NULL);
+ }
+
+ D(("done. [%s]", pam_strerror(pamh, retval)));
+
+ return retval;
+}
+
+PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags
+ , int argc, const char **argv)
+{
+ unsigned int ctrl;
+ int retval;
+
+ D(("called."));
+
+ pwdb_start();
+ ctrl = set_ctrl(flags, argc, argv);
+ retval = _unix_set_credentials(pamh, ctrl);
+ pwdb_end();
+
+ if ( on(UNIX_LIKE_AUTH, ctrl) ) {
+ int *pretval = &retval;
+
+ D(("recovering return code from auth call"));
+ pam_get_data(pamh, "pwdb_setcred_return", (const void **) &pretval);
+ pam_set_data(pamh, "pwdb_setcred_return", NULL, NULL);
+ D(("recovered data indicates that old retval was %d", retval));
+ }
+
+ return retval;
+}
+
+/*
+ * PAM framework looks for these entry-points to pass control to the
+ * account management module.
+ */
+
+#include "./pam_unix_acct.-c"
+
+PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ unsigned int ctrl;
+ int retval;
+
+ D(("called."));
+
+ pwdb_start();
+ ctrl = set_ctrl(flags, argc, argv);
+ retval = _unix_acct_mgmt(pamh, ctrl);
+ pwdb_end();
+
+ D(("done."));
+
+ return retval;
+}
+
+/*
+ * PAM framework looks for these entry-points to pass control to the
+ * session module.
+ */
+
+#include "./pam_unix_sess.-c"
+
+PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ unsigned int ctrl;
+ int retval;
+
+ D(("called."));
+
+ pwdb_start();
+ ctrl = set_ctrl(flags, argc, argv);
+ retval = _unix_open_session(pamh, ctrl);
+ pwdb_end();
+
+ return retval;
+}
+
+PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ unsigned int ctrl;
+ int retval;
+
+ D(("called."));
+
+ pwdb_start();
+ ctrl = set_ctrl(flags, argc, argv);
+ retval = _unix_close_session(pamh, ctrl);
+ pwdb_end();
+
+ return retval;
+}
+
+/*
+ * PAM framework looks for these entry-points to pass control to the
+ * password changing module.
+ */
+
+#include "./pam_unix_passwd.-c"
+
+PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ unsigned int ctrl;
+ int retval;
+
+ D(("called."));
+
+ pwdb_start();
+ ctrl = set_ctrl(flags, argc, argv);
+ retval = _unix_chauthtok(pamh, ctrl);
+ pwdb_end();
+
+ D(("done."));
+
+ return retval;
+}
+
+/* static module data */
+
+#ifdef PAM_STATIC
+struct pam_module _pam_pwdb_modstruct = {
+ "pam_pwdb",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ pam_sm_acct_mgmt,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ pam_sm_chauthtok
+};
+
+#endif
+
+/*
+ * Copyright (c) Andrew G. Morgan, 1996. 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, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 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 author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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.
+ */
diff --git a/modules/pam_pwdb/pam_unix_acct.-c b/modules/pam_pwdb/pam_unix_acct.-c
new file mode 100644
index 00000000..16869054
--- /dev/null
+++ b/modules/pam_pwdb/pam_unix_acct.-c
@@ -0,0 +1,298 @@
+/*
+ * $Id$
+ *
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:49 agmorgan
+ * Initial revision
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:17 morgan
+ * Linux PAM sources pre-0.66
+ *
+ * Revision 1.6 1997/01/04 20:37:15 morgan
+ * extra debugging
+ *
+ * Revision 1.5 1996/12/01 03:05:54 morgan
+ * debugging with _pam_macros.h
+ *
+ * Revision 1.4 1996/11/10 21:03:57 morgan
+ * pwdb conversion
+ *
+ * Revision 1.3 1996/09/05 06:45:45 morgan
+ * tidied shadow acct management
+ *
+ * Revision 1.2 1996/09/01 01:13:14 morgan
+ * Cristian Gafton's patches
+ *
+ * Revision 1.1 1996/08/29 13:27:51 morgan
+ * Initial revision
+ *
+ *
+ * See end of file for copyright information
+ */
+
+static const char rcsid_acct[] =
+"$Id$\n"
+" - PAM_PWDB account management <gafton@redhat.com>";
+
+/* the shadow suite has accout managment.. */
+
+static int _shadow_acct_mgmt_exp(pam_handle_t *pamh, unsigned int ctrl,
+ const struct pwdb *pw, const char *uname)
+{
+ const struct pwdb_entry *pwe = NULL;
+ time_t curdays;
+ int last_change, max_change;
+ int retval;
+
+ D(("called."));
+
+ /* Now start the checks */
+
+ curdays = time(NULL)/(60*60*24); /* today */
+
+ /* First: has account expired ? (CG)
+ * - expire < curdays
+ * - or (last_change + max_change + defer_change) < curdays
+ * - in both cases, deny access
+ */
+
+ D(("pwdb_get_entry"));
+ retval = pwdb_get_entry(pw, "expire", &pwe);
+ if (retval == PWDB_SUCCESS) {
+ int expire;
+
+ expire = *( (const int *) pwe->value );
+ (void) pwdb_entry_delete(&pwe); /* no longer needed */
+
+ if ((curdays > expire) && (expire > 0)) {
+
+ _log_err(LOG_NOTICE
+ , "acct: account %s has expired (account expired)"
+ , uname);
+ make_remark(pamh, ctrl, PAM_ERROR_MSG
+ , "Your account has expired; "
+ "please contact your system administrator");
+
+ D(("account expired"));
+ return PAM_ACCT_EXPIRED;
+ }
+ }
+
+ D(("pwdb_get_entry"));
+ retval = pwdb_get_entry(pw, "last_change", &pwe);
+ if ( retval == PWDB_SUCCESS ) {
+ last_change = *( (const int *) pwe->value );
+ } else {
+ last_change = curdays;
+ }
+ (void) pwdb_entry_delete(&pwe);
+
+ D(("pwdb_get_entry"));
+ retval = pwdb_get_entry(pw, "max_change", &pwe);
+ if ( retval == PWDB_SUCCESS ) {
+ max_change = *( (const int *) pwe->value );
+ } else {
+ max_change = -1;
+ }
+ (void) pwdb_entry_delete(&pwe);
+
+ D(("pwdb_get_entry"));
+ retval = pwdb_get_entry(pw, "defer_change", &pwe);
+ if (retval == PWDB_SUCCESS) {
+ int defer_change;
+
+ defer_change = *( (const int *) pwe->value );
+ (void) pwdb_entry_delete(&pwe);
+
+ if ((curdays > (last_change + max_change + defer_change))
+ && (max_change != -1) && (defer_change != -1)
+ && (last_change > 0)) {
+
+ if ( on(UNIX_DEBUG, ctrl) ) {
+ _log_err(LOG_NOTICE, "acct: account %s has expired "
+ "(failed to change password)", uname);
+ }
+ make_remark(pamh, ctrl, PAM_ERROR_MSG
+ , "Your password has expired; "
+ "please see your system administrator");
+
+ D(("account expired2"));
+ return PAM_ACCT_EXPIRED;
+ }
+ }
+
+ /* Now test if the password is expired, but the user still can
+ * change their password. (CG)
+ * - last_change = 0
+ * - last_change + max_change < curdays
+ */
+
+ D(("when was the last change"));
+ if (last_change == 0) {
+
+ if ( on(UNIX_DEBUG, ctrl) ) {
+ _log_err(LOG_NOTICE
+ , "acct: expired password for user %s (root enforced)"
+ , uname);
+ }
+ make_remark(pamh, ctrl, PAM_ERROR_MSG
+ , "You are required to change your password immediately"
+ );
+
+ D(("need a new password"));
+ return PAM_NEW_AUTHTOK_REQD;
+ }
+
+ if (((last_change + max_change) < curdays) &&
+ (max_change < 99999) && (max_change > 0)) {
+
+ if ( on(UNIX_DEBUG, ctrl) ) {
+ _log_err(LOG_DEBUG
+ , "acct: expired password for user %s (password aged)"
+ , uname);
+ }
+ make_remark(pamh, ctrl, PAM_ERROR_MSG
+ , "Your password has expired; please change it!");
+
+ D(("need a new password 2"));
+ return PAM_NEW_AUTHTOK_REQD;
+ }
+
+ /*
+ * Now test if the password is about to expire (CG)
+ * - last_change + max_change - curdays <= warn_change
+ */
+
+ retval = pwdb_get_entry(pw, "warn_change", &pwe);
+ if ( retval == PWDB_SUCCESS ) {
+ int warn_days, daysleft;
+
+ daysleft = last_change + max_change - curdays;
+ warn_days = *((const int *) pwe->value);
+ (void) pwdb_entry_delete(&pwe);
+
+ if ((daysleft <= warn_days) && (warn_days > 0)) {
+ char *s;
+
+ if ( on(UNIX_DEBUG, ctrl) ) {
+ _log_err(LOG_DEBUG
+ , "acct: password for user %s will expire in %d days"
+ , uname, daysleft);
+ }
+
+#define LocalComment "Warning: your password will expire in %d day%s"
+ if ((s = (char *) malloc(30+sizeof(LocalComment))) == NULL) {
+ _log_err(LOG_CRIT, "malloc failure in " __FILE__);
+ retval = PAM_BUF_ERR;
+ } else {
+
+ sprintf(s, LocalComment, daysleft, daysleft == 1 ? "":"s");
+
+ make_remark(pamh, ctrl, PAM_TEXT_INFO, s);
+ free(s);
+ }
+#undef LocalComment
+ }
+ } else {
+ retval = PAM_SUCCESS;
+ }
+
+ D(("all done"));
+ return retval;
+}
+
+
+/*
+ * this function checks for the account details. The user may not be
+ * permitted to log in at this time etc.. Within the context of
+ * vanilla Unix, this function simply does nothing. The shadow suite
+ * added password/account expiry, but PWDB takes care of this
+ * transparently.
+ */
+
+static int _unix_acct_mgmt(pam_handle_t *pamh, unsigned int ctrl)
+{
+ const struct pwdb *pw = NULL;
+
+ char *uname=NULL;
+ int retval;
+
+ D(("called."));
+
+ /* identify user */
+
+ retval = pam_get_item(pamh,PAM_USER,(const void **)&uname);
+ D(("user = `%s'", uname));
+ if (retval != PAM_SUCCESS || uname == NULL) {
+ _log_err(LOG_ALERT
+ , "acct; could not identify user (from uid=%d)"
+ , getuid());
+ return PAM_USER_UNKNOWN;
+ }
+
+ /* get database information for user */
+
+ retval = pwdb_locate("user", PWDB_DEFAULT, uname, PWDB_ID_UNKNOWN, &pw);
+ if (retval != PWDB_SUCCESS || pw == NULL) {
+
+ _log_err(LOG_ALERT, "acct; %s (%s from uid=%d)"
+ , pwdb_strerror(retval), uname, getuid());
+ if ( pw ) {
+ (void) pwdb_delete(&pw);
+ }
+ return PAM_USER_UNKNOWN;
+ }
+
+ /* now check the user's times etc.. */
+
+ retval = _shadow_acct_mgmt_exp(pamh, ctrl, pw, uname);
+ if (retval != PAM_SUCCESS) {
+ _log_err(LOG_NOTICE, "expiry check failed for '%s'", uname);
+ }
+
+ /* Done with pw */
+
+ (void) pwdb_delete(&pw);
+
+ /* all done */
+
+ D(("done."));
+ return retval;
+}
+
+/*
+ * Copyright (c) Elliot Lee, 1996.
+ * Copyright (c) Andrew Morgan <morgan@parc.power.net> 1996.
+ * Copyright (c) Cristian Gafton <gafton@redhat.com> 1996.
+ *
+ * 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, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 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 author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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.
+ */
diff --git a/modules/pam_pwdb/pam_unix_auth.-c b/modules/pam_pwdb/pam_unix_auth.-c
new file mode 100644
index 00000000..e4dfe136
--- /dev/null
+++ b/modules/pam_pwdb/pam_unix_auth.-c
@@ -0,0 +1,131 @@
+/*
+ * $Id$
+ *
+ * See end of file for Copyright information.
+ */
+
+static const char rcsid_auth[] =
+"$Id$: pam_unix_auth.-c,v 1.2 1996/09/05 06:46:53 morgan Exp morgan $\n"
+" - PAM_PWDB authentication functions. <morgan@parc.power.net>";
+
+/*
+ * _unix_auth() is a front-end for UNIX/shadow authentication
+ *
+ * First, obtain the password from the user. Then use a
+ * routine in 'support.-c' to authenticate the user.
+ */
+
+#define _UNIX_AUTHTOK "-UN*X-PASS"
+
+static int _unix_auth(pam_handle_t *pamh, unsigned int ctrl)
+{
+ int retval;
+ const char *name, *p;
+
+ D(("called."));
+
+ /* get the user'name' */
+
+ retval = _unix_get_user(pamh, ctrl, NULL, &name);
+ if (retval != PAM_SUCCESS ) {
+ if (retval != PAM_CONV_AGAIN) {
+ if ( on(UNIX_DEBUG,ctrl) ) {
+ _log_err(LOG_DEBUG, "auth could not identify user");
+ }
+ } else {
+ D(("pam_get_user/conv() function is not ready yet"));
+ /* it is safe to resume this function so we translate this
+ retval to the value that indicates we're happy to resume. */
+ retval = PAM_INCOMPLETE;
+ }
+ return retval;
+ }
+
+ /* if this user does not have a password... */
+
+ if ( _unix_blankpasswd(ctrl, name) ) {
+ D(("user '%s' has blank passwd", name));
+ name = NULL;
+ return PAM_SUCCESS;
+ }
+
+ /* get this user's authentication token */
+
+ retval = _unix_read_password(pamh, ctrl, NULL, "Password: ", NULL
+ , _UNIX_AUTHTOK, &p);
+ if (retval != PAM_SUCCESS) {
+ if (retval != PAM_CONV_AGAIN) {
+ _log_err(LOG_CRIT, "auth could not identify password for [%s]"
+ , name);
+ } else {
+ D(("conversation function is not ready yet"));
+ /* it is safe to resume this function so we translate this
+ retval to the value that indicates we're happy to resume. */
+ retval = PAM_INCOMPLETE;
+ }
+ name = NULL;
+ return retval;
+ }
+ D(("user=%s, password=[%s]", name, p));
+
+ /* verify the password of this user */
+ retval = _unix_verify_password(pamh, name, p, ctrl);
+ name = p = NULL;
+
+ D(("done [%d]", retval));
+
+ return retval;
+}
+
+/*
+ * This function is for setting unix credentials. Sun has indicated
+ * that there are *NO* authentication credentials for unix. The
+ * obvious credentials would be the group membership of the user as
+ * listed in the /etc/group file. However, Sun indicates that it is
+ * the responsibility of the application to set these.
+ */
+
+static int _unix_set_credentials(pam_handle_t *pamh, unsigned int ctrl)
+{
+ D(("called <empty function> returning."));
+
+ return PAM_SUCCESS;
+}
+
+/********************************************************************
+ * Copyright (c) Alexander O. Yuriev, 1996.
+ * Copyright (c) Andrew G. Morgan <morgan@parc.power.net> 1996
+ * Copyright (c) Cristian Gafton <gafton@redhat.com> 1996, 1997
+ *
+ * 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, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 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 author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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.
+ */
+
diff --git a/modules/pam_pwdb/pam_unix_md.-c b/modules/pam_pwdb/pam_unix_md.-c
new file mode 100644
index 00000000..d9b2c51b
--- /dev/null
+++ b/modules/pam_pwdb/pam_unix_md.-c
@@ -0,0 +1,73 @@
+/*
+ * This function is a front-end for the message digest algorithms used
+ * to compute the user's encrypted passwords. No reversible encryption
+ * is used here and I intend to keep it that way.
+ *
+ * While there are many sources of encryption outside the United
+ * States, it *may* be illegal to re-export reversible encryption
+ * computer code. Until such time as it is legal to export encryption
+ * software freely from the US, please do not send me any. (AGM)
+ */
+
+/* this should have been defined in a header file.. Why wasn't it? AGM */
+extern char *crypt(const char *key, const char *salt);
+
+#include "md5.h"
+#include "bigcrypt.-c"
+
+struct cfns {
+ const char *salt;
+ int len;
+ char * (* mdfn)(const char *key, const char *salt);
+};
+
+/* array of non-standard digest algorithms available */
+
+#define N_MDS 1
+const static struct cfns cfn_list[N_MDS] = {
+ { "$1$", 3, Goodcrypt_md5 },
+};
+
+static char *_pam_md(const char *key, const char *salt)
+{
+ char *x,*e=NULL;
+ int i;
+
+ D(("called with key='%s', salt='%s'", key, salt));
+
+ /* check for non-standard salts */
+
+ for (i=0; i<N_MDS; ++i) {
+ if ( !strncmp(cfn_list[i].salt, salt, cfn_list[i].len) ) {
+ e = cfn_list[i].mdfn(key, salt);
+ break;
+ }
+ }
+
+ if ( i >= N_MDS ) {
+ e = bigcrypt(key, salt); /* (defaults to standard algorithm) */
+ }
+
+ x = x_strdup(e); /* put e in malloc()ed memory */
+ _pam_overwrite(e); /* clean up */
+ return x; /* this must be deleted elsewhere */
+}
+
+#ifndef PWDB_NO_MD_COMPAT
+static char *_pam_md_compat(const char *key, const char *salt)
+{
+ char *x,*e=NULL;
+
+ D(("called with key='%s', salt='%s'", key, salt));
+
+ if ( !strncmp("$1$", salt, 3) ) {
+ e = Brokencrypt_md5(key, salt);
+ x = x_strdup(e); /* put e in malloc()ed memory */
+ _pam_overwrite(e); /* clean up */
+ } else {
+ x = x_strdup("*");
+ }
+
+ return x; /* this must be deleted elsewhere */
+}
+#endif /* PWDB_NO_MD_COMPAT */
diff --git a/modules/pam_pwdb/pam_unix_passwd.-c b/modules/pam_pwdb/pam_unix_passwd.-c
new file mode 100644
index 00000000..13801ba3
--- /dev/null
+++ b/modules/pam_pwdb/pam_unix_passwd.-c
@@ -0,0 +1,404 @@
+/* $Id$ */
+
+/*
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:50 agmorgan
+ * Initial revision
+ *
+ * Revision 1.2 1999/07/04 23:22:38 morgan
+ * Andrey's MD5 (bigendian) work around + cleanup to address problems with
+ * applications that let an (ab)user kill them off without giving PAM the
+ * opportunity to end. [Problem report from Tani Hosokawa on bugtraq.]
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:16 morgan
+ * Linux PAM sources pre-0.66
+ *
+ * Revision 1.6 1997/04/05 06:31:06 morgan
+ * mostly a reformat.
+ *
+ * Revision 1.5 1996/12/01 03:05:54 morgan
+ * debugging with _pam_macros.h
+ *
+ * Revision 1.4 1996/11/10 21:04:51 morgan
+ * pwdb conversion
+ *
+ * Revision 1.3 1996/09/05 06:48:15 morgan
+ * A lot has changed. I'd recommend you study the diff.
+ *
+ * Revision 1.2 1996/09/01 16:33:27 morgan
+ * Cristian Gafton's changes
+ *
+ * Revision 1.1 1996/08/29 13:21:27 morgan
+ * Initial revision
+ *
+ */
+
+static const char rcsid_pass[] =
+"$Id$\n"
+" - PAM_PWDB password module <morgan@parc.power.net>"
+;
+
+#include "pam_unix_pwupd.-c"
+
+/* passwd/salt conversion macros */
+
+#define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.')
+#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
+
+/* data tokens */
+
+#define _UNIX_OLD_AUTHTOK "-UN*X-OLD-PASS"
+#define _UNIX_NEW_AUTHTOK "-UN*X-NEW-PASS"
+
+/* Implementation */
+
+/*
+ * i64c - convert an integer to a radix 64 character
+ */
+static int i64c(int i)
+{
+ if (i < 0)
+ return ('.');
+ else if (i > 63)
+ return ('z');
+ if (i == 0)
+ return ('.');
+ if (i == 1)
+ return ('/');
+ if (i >= 2 && i <= 11)
+ return ('0' - 2 + i);
+ if (i >= 12 && i <= 37)
+ return ('A' - 12 + i);
+ if (i >= 38 && i <= 63)
+ return ('a' - 38 + i);
+ return ('\0');
+}
+
+/*
+ * FUNCTION: _pam_unix_chauthtok()
+ *
+ * this function works in two passes. The first, when UNIX__PRELIM is
+ * set, obtains the previous password. It sets the PAM_OLDAUTHTOK item
+ * or stores it as a data item. The second function obtains a new
+ * password (verifying if necessary, that the user types it the same a
+ * second time.) depending on the 'ctrl' flags this new password may
+ * be stored in the PAM_AUTHTOK item or a private data item.
+ *
+ * Having obtained a new password. The function updates the
+ * /etc/passwd (and optionally the /etc/shadow) file(s).
+ *
+ * Provision is made for the creation of a blank shadow file if none
+ * is available, but one is required to update the shadow file -- the
+ * intention being for shadow passwords to be seamlessly implemented
+ * from the generic UNIX scheme. -- THIS BIT IS PRE-ALPHA.. and included
+ * in this release (.52) mostly for the purpose of discussion.
+ */
+
+static int _unix_chauthtok(pam_handle_t *pamh, unsigned int ctrl)
+{
+ int retval;
+ unsigned int lctrl;
+
+ /* <DO NOT free() THESE> */
+ const char *user;
+ const char *pass_old, *pass_new;
+ /* </DO NOT free() THESE> */
+
+ D(("called"));
+
+ /*
+ * First get the name of a user
+ */
+
+ retval = _unix_get_user( pamh, ctrl, "Username: ", &user );
+ if ( retval != PAM_SUCCESS ) {
+ if ( on(UNIX_DEBUG,ctrl) ) {
+ _log_err(LOG_DEBUG, "password - could not identify user");
+ }
+ return retval;
+ }
+
+ if ( on(UNIX__PRELIM, ctrl) ) {
+ /*
+ * obtain and verify the current password (OLDAUTHTOK) for
+ * the user.
+ */
+
+ char *Announce;
+
+ D(("prelim check"));
+
+ if ( _unix_blankpasswd(ctrl, user) ) {
+
+ return PAM_SUCCESS;
+
+ } else if ( off(UNIX__IAMROOT, ctrl) ) {
+
+ /* instruct user what is happening */
+#define greeting "Changing password for "
+ Announce = (char *) malloc(sizeof(greeting)+strlen(user));
+ if (Announce == NULL) {
+ _log_err(LOG_CRIT, "password - out of memory");
+ return PAM_BUF_ERR;
+ }
+ (void) strcpy(Announce, greeting);
+ (void) strcpy(Announce+sizeof(greeting)-1, user);
+#undef greeting
+
+ lctrl = ctrl;
+ set(UNIX__OLD_PASSWD, lctrl);
+ retval = _unix_read_password( pamh, lctrl
+ , Announce
+ , "(current) UNIX password: "
+ , NULL
+ , _UNIX_OLD_AUTHTOK
+ , &pass_old );
+ free(Announce);
+
+ if ( retval != PAM_SUCCESS ) {
+ _log_err(LOG_NOTICE
+ , "password - (old) token not obtained");
+ return retval;
+ }
+
+ /* verify that this is the password for this user */
+
+ retval = _unix_verify_password(pamh, user, pass_old, ctrl);
+ } else {
+ D(("process run by root so do nothing this time around"));
+ pass_old = NULL;
+ retval = PAM_SUCCESS; /* root doesn't have too */
+ }
+
+ if ( retval != PAM_SUCCESS ) {
+ D(("Authentication failed"));
+ pass_old = NULL;
+ return retval;
+ }
+
+ retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old);
+ pass_old = NULL;
+ if ( retval != PAM_SUCCESS ) {
+ _log_err(LOG_CRIT, "failed to set PAM_OLDAUTHTOK");
+ }
+
+ } else if ( on( UNIX__UPDATE, ctrl ) ) {
+ /* tpass is used below to store the _pam_md() return; it
+ * should be _pam_delete()'d. */
+
+ char *tpass=NULL;
+
+ /*
+ * obtain the proposed password
+ */
+
+ D(("do update"));
+
+ /*
+ * get the old token back. NULL was ok only if root [at this
+ * point we assume that this has already been enforced on a
+ * previous call to this function].
+ */
+
+ if ( off(UNIX_NOT_SET_PASS, ctrl) ) {
+ retval = pam_get_item(pamh, PAM_OLDAUTHTOK
+ , (const void **)&pass_old);
+ } else {
+ retval = pam_get_data(pamh, _UNIX_OLD_AUTHTOK
+ , (const void **)&pass_old);
+ if (retval == PAM_NO_MODULE_DATA) {
+ retval = PAM_SUCCESS;
+ pass_old = NULL;
+ }
+ }
+
+ if (retval != PAM_SUCCESS) {
+ _log_err(LOG_NOTICE, "user not authenticated");
+ return retval;
+ }
+
+ D(("get new password now"));
+
+ lctrl = ctrl;
+
+ /*
+ * use_authtok is to force the use of a previously entered
+ * password -- needed for pluggable password strength checking
+ */
+
+ if ( on(UNIX_USE_AUTHTOK, lctrl) ) {
+ set(UNIX_USE_FIRST_PASS, lctrl);
+ }
+
+ retval = _unix_read_password( pamh, lctrl
+ , NULL
+ , "Enter new UNIX password: "
+ , "Retype new UNIX password: "
+ , _UNIX_NEW_AUTHTOK
+ , &pass_new );
+
+ if ( retval != PAM_SUCCESS ) {
+ if ( on(UNIX_DEBUG,ctrl) ) {
+ _log_err(LOG_ALERT
+ , "password - new password not obtained");
+ }
+ pass_old = NULL; /* tidy up */
+ return retval;
+ }
+
+ D(("returned to _unix_chauthtok"));
+
+ /*
+ * At this point we know who the user is and what they
+ * propose as their new password. Verify that the new
+ * password is acceptable.
+ */
+
+ if (pass_new[0] == '\0') { /* "\0" password = NULL */
+ pass_new = NULL;
+ }
+
+ retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new);
+
+ if (retval != PAM_SUCCESS) {
+ _log_err(LOG_NOTICE, "new password not acceptable");
+ pass_new = pass_old = NULL; /* tidy up */
+ return retval;
+ }
+
+ /*
+ * By reaching here we have approved the passwords and must now
+ * rebuild the password database file.
+ */
+
+ /*
+ * First we encrypt the new password.
+ *
+ * XXX - this is where we might need some code for RADIUS types
+ * of password handling... no encryption needed..
+ */
+
+ if ( on(UNIX_MD5_PASS, ctrl) ) {
+
+ /*
+ * Code lifted from Marek Michalkiewicz's shadow suite. (CG)
+ * removed use of static variables (AGM)
+ */
+
+ struct timeval tv;
+ MD5_CTX ctx;
+ unsigned char result[16];
+ char *cp = (char *)result;
+ unsigned char tmp[16];
+ int i;
+
+ GoodMD5Init(&ctx);
+ gettimeofday(&tv, (struct timezone *) 0);
+ GoodMD5Update(&ctx, (void *) &tv, sizeof tv);
+ i = getpid();
+ GoodMD5Update(&ctx, (void *) &i, sizeof i);
+ i = clock();
+ GoodMD5Update(&ctx, (void *) &i, sizeof i);
+ GoodMD5Update(&ctx, result, sizeof result);
+ GoodMD5Final(tmp, &ctx);
+ strcpy(cp, "$1$"); /* magic for the MD5 */
+ cp += strlen(cp);
+ for (i = 0; i < 8; i++)
+ *cp++ = i64c(tmp[i] & 077);
+ *cp = '\0';
+
+ /* no longer need cleartext */
+ pass_new = tpass = _pam_md(pass_new, (const char *)result);
+
+ } else {
+ /*
+ * Salt manipulation is stolen from Rick Faith's passwd
+ * program. Sorry Rick :) -- alex
+ */
+
+ time_t tm;
+ char salt[3];
+
+ time(&tm);
+ salt[0] = bin_to_ascii(tm & 0x3f);
+ salt[1] = bin_to_ascii((tm >> 6) & 0x3f);
+ salt[2] = '\0';
+
+ if ( off(UNIX_BIGCRYPT, ctrl) && strlen(pass_new) > 8 ) {
+ /* to avoid using the _extensions_ of the bigcrypt()
+ function we truncate the newly entered password */
+ char *temp = malloc(9);
+
+ if (temp == NULL) {
+ _log_err(LOG_CRIT, "out of memory for password");
+ pass_new = pass_old = NULL; /* tidy up */
+ return PAM_BUF_ERR;
+ }
+
+ /* copy first 8 bytes of password */
+ strncpy(temp, pass_new, 8);
+ temp[8] = '\0';
+
+ /* no longer need cleartext */
+ pass_new = tpass = _pam_md( temp, salt );
+
+ _pam_delete(temp); /* tidy up */
+ } else {
+ /* no longer need cleartext */
+ pass_new = tpass = _pam_md( pass_new, salt );
+ }
+ }
+
+ D(("password processed"));
+
+ /* update the password database(s) -- race conditions..? */
+
+ retval = unix_update_db(pamh, ctrl, user, pass_old, pass_new);
+ pass_old = pass_new = NULL;
+
+ } else { /* something has broken with the module */
+
+ _log_err(LOG_ALERT, "password received unknown request");
+ retval = PAM_ABORT;
+
+ }
+
+ return retval;
+}
+
+/* ******************************************************************
+ * Copyright (c) Alexander O. Yuriev (alex@bach.cis.temple.edu), 1996.
+ * Copyright (c) Andrew Morgan <morgan@parc.power.net> 1996, 1997.
+ * Copyright (c) Cristian Gafton, <gafton@redhat.com> 1996, 1997.
+ *
+ * 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, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 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 author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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.
+ */
diff --git a/modules/pam_pwdb/pam_unix_pwupd.-c b/modules/pam_pwdb/pam_unix_pwupd.-c
new file mode 100644
index 00000000..0f646369
--- /dev/null
+++ b/modules/pam_pwdb/pam_unix_pwupd.-c
@@ -0,0 +1,260 @@
+/*
+ * $Id$
+ *
+ * This file contains the routines to update the passwd databases.
+ */
+
+/* Implementation */
+
+static int unix_update_db(pam_handle_t *pamh, int ctrl, const char *user,
+ const char *pass_old, const char *pass_new)
+{
+ const struct pwdb *pw=NULL;
+ const struct pwdb_entry *pwe=NULL;
+ pwdb_flag flag;
+ int retval, i;
+
+ D(("called."));
+
+ /* obtain default user record */
+
+ retval = pwdb_locate("user", PWDB_DEFAULT, user, PWDB_ID_UNKNOWN, &pw);
+ if (retval == PWDB_PASS_PHRASE_REQD) {
+ retval = pwdb_set_entry(pw, "pass_phrase"
+ , pass_old, 1+strlen(pass_old)
+ , NULL, NULL, 0);
+ if (retval == PWDB_SUCCESS)
+ retval = pwdb_locate("user", pw->source, user
+ , PWDB_ID_UNKNOWN, &pw);
+ }
+ pass_old = NULL;
+
+ if ( retval != PWDB_SUCCESS ) {
+ _log_err(LOG_ALERT, "cannot identify user %s (uid=%d)"
+ , user, getuid() );
+ pass_new = NULL;
+ if (pw)
+ (void) pwdb_delete(&pw);
+ return PAM_USER_UNKNOWN;
+ }
+
+ /* check that we can update all of the default databases */
+
+ retval = pwdb_flags("user", pw->source, &flag);
+
+ if ( retval != PWDB_SUCCESS || ( pwdb_on(flag,PWDB_F_NOUPDATE) ) ) {
+ _log_err(LOG_ERR, "cannot update default database for user %s"
+ , user );
+ pass_new = NULL;
+ if (pw)
+ (void) pwdb_delete(&pw);
+ return PAM_PERM_DENIED;
+ }
+
+ /* If there was one, we delete the "last_change" entry */
+ retval = pwdb_get_entry(pw, "last_change", &pwe);
+ if (retval == PWDB_SUCCESS) {
+ (void) pwdb_entry_delete(&pwe);
+ pwdb_set_entry(pw, "last_change", NULL, -1, NULL, NULL, 0);
+ }
+
+ /*
+ * next check for pam.conf specified databases: shadow etc... [In
+ * other words, pam.conf indicates which database the password is
+ * to be subsequently placed in: this is password migration].
+ */
+
+ if ( on(UNIX__SET_DB, ctrl) ) {
+ const char *db_token;
+ pwdb_type pt = _PWDB_MAX_TYPES;
+
+ if ( on(UNIX_UNIX, ctrl) ) {
+ db_token = "U"; /* XXX - should be macro */
+ pt = PWDB_UNIX;
+ } else if ( on(UNIX_SHADOW, ctrl) ) {
+ db_token = "x"; /* XXX - should be macro */
+ pt = PWDB_SHADOW;
+ } else if ( on(UNIX_RADIUS, ctrl) ) {
+ db_token = "R"; /* XXX - is this ok? */
+ pt = PWDB_RADIUS;
+ } else {
+ _log_err(LOG_ALERT
+ , "cannot determine database to use for authtok");
+ pass_new = NULL;
+ if (pw)
+ (void) pwdb_delete(&pw);
+ return PAM_ABORT; /* we're in trouble */
+ }
+
+ /*
+ * Attempt to update the indicated database (only)
+ */
+
+ {
+ pwdb_type tpt[2];
+ tpt[0] = pt;
+ tpt[1] = _PWDB_MAX_TYPES;
+
+ /* Can we set entry in database? */
+ retval = pwdb_flags("user", tpt, &flag);
+ if (retval == PWDB_SUCCESS && !pwdb_on(flag,PWDB_F_NOUPDATE)) {
+ /* YES. This database is available.. */
+
+ /* Only update if it is not already in the default list */
+ for (i=0; pw->source[i] != _PWDB_MAX_TYPES
+ && pw->source[i] != pt ; ++i);
+ if (pw->source[i] == _PWDB_MAX_TYPES) {
+ const struct pwdb *tpw=NULL;
+
+ /* copy database entry */
+ if ((retval = pwdb_new(&tpw, 10)) != PWDB_SUCCESS
+ || (retval = pwdb_merge(tpw, pw, PWDB_TRUE))
+ != PWDB_SUCCESS) {
+ _log_err(LOG_CRIT, "failed to obtain new pwdb: %s"
+ , pwdb_strerror(retval));
+ retval = PAM_ABORT;
+ } else
+ retval = PAM_SUCCESS;
+
+ /* set db_token */
+ if (retval == PAM_SUCCESS) {
+ retval = pwdb_set_entry(tpw, "defer_pass", db_token
+ , 1+strlen(db_token)
+ , NULL, NULL, 0);
+ if (retval != PWDB_SUCCESS) {
+ _log_err(LOG_ALERT, "set defer_pass -> %s"
+ , pwdb_strerror(retval));
+ retval = PAM_PERM_DENIED;
+ } else
+ retval = PAM_SUCCESS;
+ }
+
+ /* update specific database */
+ if (retval == PAM_SUCCESS) {
+ retval = pwdb_replace("user", tpt
+ , user, PWDB_ID_UNKNOWN, &tpw);
+ if (retval != PWDB_SUCCESS) {
+ const char *service=NULL;
+ (void) pam_get_item(pamh, PAM_SERVICE
+ , (const void **)&service);
+ _log_err(LOG_ALERT
+ , "(%s) specified database failed: %s"
+ , service
+ , pwdb_strerror(retval));
+ retval = PAM_PERM_DENIED;
+ } else {
+ retval = PAM_SUCCESS;
+ }
+ }
+
+ /* clean up temporary pwdb */
+ if (tpw)
+ (void) pwdb_delete(&tpw);
+ }
+
+ /* we can properly adopt new defer_pass */
+ if (retval == PAM_SUCCESS) {
+ /* failing here will mean we go back to former
+ password location */
+ (void) pwdb_set_entry(pw, "defer_pass", db_token
+ , 1+strlen(db_token), NULL, NULL, 0);
+ }
+ }
+ }
+ }
+
+ /*
+ * the password will now be placed in appropriate (perhaps original) db
+ */
+
+ retval = pwdb_get_entry(pw, "uid", &pwe);
+ if (retval != PWDB_SUCCESS) {
+ _log_err(LOG_ALERT, "no uid!? (%s); %s", user, pwdb_strerror(retval));
+ pass_new = NULL;
+ if (pw)
+ (void) pwdb_delete(&pw);
+ return PAM_USER_UNKNOWN;
+ }
+
+ /* insert the passwd into the 'pw' structure */
+
+ retval = pwdb_set_entry(pw, "passwd", pass_new, 1+strlen(pass_new)
+ , NULL, NULL, 0);
+ pass_new = NULL;
+ if (retval != PWDB_SUCCESS) {
+ _log_err(LOG_ALERT, "set2 failed; %s", pwdb_strerror(retval));
+ if (pw)
+ (void) pwdb_delete(&pw);
+ return PAM_AUTHTOK_LOCK_BUSY;
+ }
+
+ retval = pwdb_replace("user", pw->source, user
+ , *((uid_t *)pwe->value), &pw);
+ if (retval != PWDB_SUCCESS) {
+ _log_err(LOG_ALERT, "user (%s/%d) update failed; %s"
+ , user, *((uid_t *)pwe->value), pwdb_strerror(retval));
+ if (pw)
+ (void) pwdb_delete(&pw);
+ (void) pwdb_entry_delete(&pwe);
+ return PAM_ABORT;
+ }
+
+ if (retval != PWDB_SUCCESS) {
+
+ _log_err(LOG_ALERT, "user (%s/%d) update failed; %s"
+ , user, *((uid_t *)pwe->value), pwdb_strerror(retval));
+ retval = PAM_ABORT;
+
+ } else {
+ /* password updated */
+
+ _log_err(LOG_INFO, "password for (%s/%d) changed by (%s/%d)"
+ , user, *((uid_t *)pwe->value), getlogin(), getuid());
+ retval = PAM_SUCCESS;
+ }
+
+ /* tidy up */
+
+ (void) pwdb_entry_delete(&pwe);
+ if (pw)
+ (void) pwdb_delete(&pw);
+
+ return retval;
+}
+
+/* ******************************************************************
+ * Copyright (c) Andrew Morgan <morgan@parc.power.net> 1996,1997.
+ * Copyright (c) Cristian Gafton, <gafton@redhat.com> 1996, 1997.
+ * 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, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 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 author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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.
+ */
diff --git a/modules/pam_pwdb/pam_unix_sess.-c b/modules/pam_pwdb/pam_unix_sess.-c
new file mode 100644
index 00000000..e1fe820b
--- /dev/null
+++ b/modules/pam_pwdb/pam_unix_sess.-c
@@ -0,0 +1,118 @@
+/*
+ * $Id$
+ *
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:51 agmorgan
+ * Initial revision
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:16 morgan
+ * Linux PAM sources pre-0.66
+ *
+ * Revision 1.4 1996/12/01 03:05:54 morgan
+ * debugging with _pam_macros.h
+ *
+ * Revision 1.3 1996/11/10 21:05:33 morgan
+ * pwdb conversion
+ *
+ * Revision 1.2 1996/09/05 06:49:02 morgan
+ * more informative logging
+ *
+ * Revision 1.1 1996/08/29 13:27:51 morgan
+ * Initial revision
+ *
+ *
+ * See end for Copyright information
+ */
+
+static const char rcsid_sess[] =
+"$Id$\n"
+" - PAM_PWDB session management. morgan@parc.power.net";
+
+/* Define internal functions */
+
+static int _unix_open_session(pam_handle_t *pamh, unsigned int ctrl)
+{
+ int retval;
+ char *user_name, *service;
+
+ D(("called."));
+
+ retval = pam_get_item( pamh, PAM_USER, (void *) &user_name );
+ if ( user_name == NULL || retval != PAM_SUCCESS ) {
+ _log_err(LOG_CRIT, "open_session - error recovering username");
+ return PAM_SESSION_ERR;
+ }
+
+ retval = pam_get_item( pamh, PAM_SERVICE, (void*) &service );
+ if ( service == NULL || retval != PAM_SUCCESS ) {
+ _log_err(LOG_CRIT, "open_session - error recovering service");
+ return PAM_SESSION_ERR;
+ }
+
+ _log_err(LOG_INFO, "(%s) session opened for user %s by %s(uid=%d)"
+ , service, user_name
+ , getlogin() == NULL ? "":getlogin(), getuid() );
+
+ return PAM_SUCCESS;
+}
+
+static int _unix_close_session(pam_handle_t *pamh, unsigned int ctrl)
+{
+ int retval;
+ char *user_name, *service;
+
+ D(("called."));
+
+ retval = pam_get_item( pamh, PAM_USER, (void*) &user_name );
+ if ( user_name == NULL || retval != PAM_SUCCESS ) {
+ _log_err(LOG_CRIT, "close_session - error recovering username");
+ return PAM_SESSION_ERR;
+ }
+
+ retval = pam_get_item( pamh, PAM_SERVICE, (void*) &service );
+ if ( service == NULL || retval != PAM_SUCCESS ) {
+ _log_err(LOG_CRIT, "close_session - error recovering service");
+ return PAM_SESSION_ERR;
+ }
+
+ _log_err(LOG_INFO, "(%s) session closed for user %s"
+ , service, user_name );
+
+ return PAM_SUCCESS;
+}
+
+/*
+ * Copyright (c) Alexander O. Yuriev, 1996. All rights reserved.
+ * Copyright (c) Andrew G. Morgan, 1996, <morgan@parc.power.net>
+ *
+ * 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, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 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 author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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.
+ */
diff --git a/modules/pam_pwdb/pwdb_chkpwd.c b/modules/pam_pwdb/pwdb_chkpwd.c
new file mode 100644
index 00000000..2c7ba29c
--- /dev/null
+++ b/modules/pam_pwdb/pwdb_chkpwd.c
@@ -0,0 +1,204 @@
+/*
+ * $Id$
+ *
+ * This program is designed to run setuid(root) or with sufficient
+ * privilege to read all of the unix password databases. It is designed
+ * to provide a mechanism for the current user (defined by this
+ * process' real uid) to verify their own password.
+ *
+ * The password is read from the standard input. The exit status of
+ * this program indicates whether the user is authenticated or not.
+ *
+ * Copyright information is located at the end of the file.
+ *
+ */
+
+#define _BSD_SOURCE
+
+#ifdef linux
+# define _GNU_SOURCE
+# include <features.h>
+#endif
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <security/_pam_macros.h>
+
+#define MAXPASS 200 /* the maximum length of a password */
+
+#define UNIX_PASSED (PWDB_SUCCESS)
+#define UNIX_FAILED (PWDB_SUCCESS+1)
+
+#include <pwdb/pwdb_public.h>
+
+/* syslogging function for errors and other information */
+
+static void _log_err(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("pwdb_chkpwd", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+#define PWDB_NO_MD_COMPAT
+#include "pam_unix_md.-c"
+
+static int _unix_verify_passwd(const char *salt, const char *p)
+{
+ char *pp=NULL;
+ int retval;
+
+ if (p == NULL) {
+ if (*salt == '\0') {
+ retval = UNIX_PASSED;
+ } else {
+ retval = UNIX_FAILED;
+ }
+ } else {
+ pp = _pam_md(p, salt);
+ p = NULL; /* no longer needed here */
+
+ if ( strcmp( pp, salt ) == 0 ) {
+ retval = UNIX_PASSED;
+ } else {
+ retval = UNIX_FAILED;
+ }
+ }
+
+ /* clean up */
+ {
+ char *tp = pp;
+ if (pp != NULL) {
+ while(tp && *tp)
+ *tp++ = '\0';
+ free(pp);
+ pp = tp = NULL;
+ }
+ }
+
+ return retval;
+}
+
+int main(void)
+{
+ const struct pwdb *pw=NULL;
+ const struct pwdb_entry *pwe=NULL;
+ char pass[MAXPASS+1];
+ int npass;
+ int retval=UNIX_FAILED;
+
+ /*
+ * we establish that this program is running with non-tty stdin.
+ * this is to discourage casual use. It does *NOT* prevent an
+ * intruder from repeatadly running this program to determine the
+ * password of the current user (brute force attack, but one for
+ * which the attacker must already have gained access to the user's
+ * account).
+ */
+
+ if ( isatty(STDIN_FILENO) ) {
+ _log_err(LOG_NOTICE
+ , "inappropriate use of PWDB helper binary [UID=%d]"
+ , getuid() );
+ fprintf(stderr,
+ "This program is not designed for running in this way\n"
+ "-- the system administrator has been informed\n");
+ exit(UNIX_FAILED);
+ }
+
+ /*
+ * determine the current user's name:
+ */
+
+ retval = pwdb_start();
+ if (retval != PWDB_SUCCESS) {
+ _log_err(LOG_ALERT, "failed to open pwdb");
+ retval = UNIX_FAILED;
+ }
+ if (retval != UNIX_FAILED) {
+ retval = pwdb_locate("user", PWDB_DEFAULT, PWDB_NAME_UNKNOWN
+ , getuid(), &pw);
+ }
+ if (retval != PWDB_SUCCESS) {
+ _log_err(LOG_ALERT, "could not identify user");
+ while (pwdb_end() != PWDB_SUCCESS);
+ exit(UNIX_FAILED);
+ }
+
+ /* read the password from stdin (a pipe from the pam_pwdb module) */
+
+ npass = read(STDIN_FILENO, pass, MAXPASS);
+
+ if (npass < 0) { /* is it a valid password? */
+ _log_err(LOG_DEBUG, "no password supplied");
+ retval = UNIX_FAILED;
+ } else if (npass >= MAXPASS-1) {
+ _log_err(LOG_DEBUG, "password too long");
+ retval = UNIX_FAILED;
+ } else if (pwdb_get_entry(pw, "passwd", &pwe) != PWDB_SUCCESS) {
+ _log_err(LOG_WARNING, "password not found");
+ retval = UNIX_FAILED;
+ } else {
+ if (npass <= 0) {
+ /* the password is NULL */
+
+ retval = _unix_verify_passwd((const char *)(pwe->value), NULL);
+ } else {
+ /* does pass agree with the official one? */
+
+ pass[npass] = '\0'; /* NUL terminate */
+ retval = _unix_verify_passwd((const char *)(pwe->value), pass);
+ }
+ }
+
+ memset(pass, '\0', MAXPASS); /* clear memory of the password */
+ while (pwdb_end() != PWDB_SUCCESS);
+
+ /* return pass or fail */
+
+ exit(retval);
+}
+
+/*
+ * Copyright (c) Andrew G. Morgan, 1997. 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, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 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 author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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.
+ */
diff --git a/modules/pam_pwdb/support.-c b/modules/pam_pwdb/support.-c
new file mode 100644
index 00000000..2cbcb576
--- /dev/null
+++ b/modules/pam_pwdb/support.-c
@@ -0,0 +1,938 @@
+/*
+ * $Id$
+ *
+ * Copyright information at end of file.
+ */
+
+/*
+ * here is the string to inform the user that the new passwords they
+ * typed were not the same.
+ */
+
+#define MISTYPED_PASS "Sorry, passwords do not match"
+
+/* type definition for the control options */
+
+typedef struct {
+ const char *token;
+ unsigned int mask; /* shall assume 32 bits of flags */
+ unsigned int flag;
+} UNIX_Ctrls;
+
+/*
+ * macro to determine if a given flag is on
+ */
+
+#define on(x,ctrl) (unix_args[x].flag & ctrl)
+
+/*
+ * macro to determine that a given flag is NOT on
+ */
+
+#define off(x,ctrl) (!on(x,ctrl))
+
+/*
+ * macro to turn on/off a ctrl flag manually
+ */
+
+#define set(x,ctrl) (ctrl = ((ctrl)&unix_args[x].mask)|unix_args[x].flag)
+#define unset(x,ctrl) (ctrl &= ~(unix_args[x].flag))
+
+/* the generic mask */
+
+#define _ALL_ON_ (~0U)
+
+/* end of macro definitions definitions for the control flags */
+
+/* ****************************************************************** *
+ * ctrl flags proper..
+ */
+
+/*
+ * here are the various options recognized by the unix module. They
+ * are enumerated here and then defined below. Internal arguments are
+ * given NULL tokens.
+ */
+
+#define UNIX__OLD_PASSWD 0 /* internal */
+#define UNIX__VERIFY_PASSWD 1 /* internal */
+#define UNIX__IAMROOT 2 /* internal */
+
+#define UNIX_AUDIT 3 /* print more things than debug..
+ some information may be sensitive */
+#define UNIX_USE_FIRST_PASS 4
+#define UNIX_TRY_FIRST_PASS 5
+#define UNIX_NOT_SET_PASS 6 /* don't set the AUTHTOK items */
+
+#define UNIX__PRELIM 7 /* internal */
+#define UNIX__UPDATE 8 /* internal */
+#define UNIX__NONULL 9 /* internal */
+#define UNIX__QUIET 10 /* internal */
+#define UNIX_USE_AUTHTOK 11 /* insist on reading PAM_AUTHTOK */
+#define UNIX_SHADOW 12 /* signal shadow on */
+#define UNIX_MD5_PASS 13 /* force the use of MD5 passwords */
+#define UNIX__NULLOK 14 /* Null token ok */
+#define UNIX_RADIUS 15 /* wish to use RADIUS for password */
+#define UNIX__SET_DB 16 /* internal - signals redirect to db */
+#define UNIX_DEBUG 17 /* send more info to syslog(3) */
+#define UNIX_NODELAY 18 /* admin does not want a fail-delay */
+#define UNIX_UNIX 19 /* wish to use /etc/passwd for pwd */
+#define UNIX_BIGCRYPT 20 /* use DEC-C2 crypt()^x function */
+#define UNIX_LIKE_AUTH 21 /* need to auth for setcred to work */
+/* -------------- */
+#define UNIX_CTRLS_ 22 /* number of ctrl arguments defined */
+
+
+static const UNIX_Ctrls unix_args[UNIX_CTRLS_] = {
+/* symbol token name ctrl mask ctrl *
+ * ------------------ ------------------ -------------- ---------- */
+
+/* UNIX__OLD_PASSWD */ { NULL, _ALL_ON_, 01 },
+/* UNIX__VERIFY_PASSWD */ { NULL, _ALL_ON_, 02 },
+/* UNIX__IAMROOT */ { NULL, _ALL_ON_, 04 },
+/* UNIX_AUDIT */ { "audit", _ALL_ON_, 010 },
+/* UNIX_USE_FIRST_PASS */ { "use_first_pass", _ALL_ON_^(060), 020 },
+/* UNIX_TRY_FIRST_PASS */ { "try_first_pass", _ALL_ON_^(060), 040 },
+/* UNIX_NOT_SET_PASS */ { "not_set_pass", _ALL_ON_, 0100 },
+/* UNIX__PRELIM */ { NULL, _ALL_ON_^(0600), 0200 },
+/* UNIX__UPDATE */ { NULL, _ALL_ON_^(0600), 0400 },
+/* UNIX__NONULL */ { NULL, _ALL_ON_, 01000 },
+/* UNIX__QUIET */ { NULL, _ALL_ON_, 02000 },
+/* UNIX_USE_AUTHTOK */ { "use_authtok", _ALL_ON_, 04000 },
+/* UNIX_SHADOW */ { "shadow", _ALL_ON_^(0140000), 010000 },
+/* UNIX_MD5_PASS */ { "md5", _ALL_ON_^(02000000), 020000 },
+/* UNIX__NULLOK */ { "nullok", _ALL_ON_^(01000), 0 },
+/* UNIX_RADIUS */ { "radius", _ALL_ON_^(0110000), 040000 },
+/* UNIX__SET_DB */ { NULL, _ALL_ON_, 0100000 },
+/* UNIX_DEBUG */ { "debug", _ALL_ON_, 0200000 },
+/* UNIX_NODELAY */ { "nodelay", _ALL_ON_, 0400000 },
+/* UNIX_UNIX */ { "unix", _ALL_ON_^(050000), 01000000 },
+/* UNIX_BIGCRYPT */ { "bigcrypt", _ALL_ON_^(020000), 02000000 },
+/* UNIX_LIKE_AUTH */ { "likeauth", _ALL_ON_, 04000000 },
+};
+
+#define UNIX_DEFAULTS (unix_args[UNIX__NONULL].flag)
+
+/* syslogging function for errors and other information */
+
+static void _log_err(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("PAM_pwdb", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+/* this is a front-end for module-application conversations */
+
+static int converse(pam_handle_t *pamh, int ctrl, int nargs
+ , struct pam_message **message
+ , struct pam_response **response)
+{
+ int retval;
+ struct pam_conv *conv;
+
+ D(("begin to converse"));
+
+ retval = pam_get_item( pamh, PAM_CONV, (const void **) &conv ) ;
+ if ( retval == PAM_SUCCESS ) {
+
+ retval = conv->conv(nargs, ( const struct pam_message ** ) message
+ , response, conv->appdata_ptr);
+
+ D(("returned from application's conversation function"));
+
+ if (retval != PAM_SUCCESS && on(UNIX_DEBUG,ctrl) ) {
+ _log_err(LOG_DEBUG, "conversation failure [%s]"
+ , pam_strerror(pamh, retval));
+ }
+
+ } else if (retval != PAM_CONV_AGAIN) {
+ _log_err(LOG_ERR, "couldn't obtain coversation function [%s]"
+ , pam_strerror(pamh, retval));
+ }
+
+ D(("ready to return from module conversation"));
+
+ return retval; /* propagate error status */
+}
+
+static int make_remark(pam_handle_t *pamh, unsigned int ctrl
+ , int type, const char *text)
+{
+ int retval=PAM_SUCCESS;
+
+ if ( off(UNIX__QUIET, ctrl) ) {
+ struct pam_message *pmsg[1], msg[1];
+ struct pam_response *resp;
+
+ pmsg[0] = &msg[0];
+ msg[0].msg = text;
+ msg[0].msg_style = type;
+
+ resp = NULL;
+ retval = converse(pamh, ctrl, 1, pmsg, &resp);
+
+ if (resp) {
+ _pam_drop_reply(resp, 1);
+ }
+ }
+ return retval;
+}
+
+/*
+ * set the control flags for the UNIX module.
+ */
+
+static int set_ctrl(int flags, int argc, const char **argv)
+{
+ unsigned int ctrl;
+
+ D(("called."));
+
+ ctrl = UNIX_DEFAULTS; /* the default selection of options */
+
+ /* set some flags manually */
+
+ if ( getuid() == 0 && !(flags & PAM_CHANGE_EXPIRED_AUTHTOK) ) {
+ set(UNIX__IAMROOT, ctrl);
+ }
+ if ( flags & PAM_UPDATE_AUTHTOK ) {
+ set(UNIX__UPDATE, ctrl);
+ }
+ if ( flags & PAM_PRELIM_CHECK ) {
+ set(UNIX__PRELIM, ctrl);
+ }
+ if ( flags & PAM_DISALLOW_NULL_AUTHTOK ) {
+ set(UNIX__NONULL, ctrl);
+ }
+ if ( flags & PAM_SILENT ) {
+ set(UNIX__QUIET, ctrl);
+ }
+
+ /* now parse the arguments to this module */
+
+ while (argc-- > 0) {
+ int j;
+
+ D(("pam_pwdb arg: %s",*argv));
+
+ for (j=0; j<UNIX_CTRLS_; ++j) {
+ if (unix_args[j].token
+ && ! strcmp(*argv, unix_args[j].token) ) {
+ break;
+ }
+ }
+
+ if ( j >= UNIX_CTRLS_ ) {
+ _log_err(LOG_ERR, "unrecognized option [%s]",*argv);
+ } else {
+ ctrl &= unix_args[j].mask; /* for turning things off */
+ ctrl |= unix_args[j].flag; /* for turning things on */
+ }
+
+ ++argv; /* step to next argument */
+ }
+
+ /* these are used for updating passwords in specific places */
+
+ if (on(UNIX_SHADOW,ctrl) || on(UNIX_RADIUS,ctrl) || on(UNIX_UNIX,ctrl)) {
+ set(UNIX__SET_DB, ctrl);
+ }
+
+ /* auditing is a more sensitive version of debug */
+
+ if ( on(UNIX_AUDIT,ctrl) ) {
+ set(UNIX_DEBUG, ctrl);
+ }
+
+ /* return the set of flags */
+
+ D(("done."));
+ return ctrl;
+}
+
+/* use this to free strings. ESPECIALLY password strings */
+
+static char *_pam_delete(register char *xx)
+{
+ _pam_overwrite(xx);
+ _pam_drop(xx);
+ return NULL;
+}
+
+static void _cleanup(pam_handle_t *pamh, void *x, int error_status)
+{
+ x = _pam_delete( (char *) x );
+}
+
+/* ************************************************************** *
+ * Useful non-trivial functions *
+ * ************************************************************** */
+
+#include "pam_unix_md.-c"
+
+/*
+ * the following is used to keep track of the number of times a user fails
+ * to authenticate themself.
+ */
+
+#define FAIL_PREFIX "-UN*X-FAIL-"
+#define UNIX_MAX_RETRIES 3
+
+struct _pam_failed_auth {
+ char *user; /* user that's failed to be authenticated */
+ char *name; /* attempt from user with name */
+ int id; /* uid of name'd user */
+ int count; /* number of failures so far */
+};
+
+#ifndef PAM_DATA_REPLACE
+#error "Need to get an updated libpam 0.52 or better"
+#endif
+
+static void _cleanup_failures(pam_handle_t *pamh, void *fl, int err)
+{
+ int quiet;
+ const char *service=NULL;
+ struct _pam_failed_auth *failure;
+
+ D(("called"));
+
+ quiet = err & PAM_DATA_SILENT; /* should we log something? */
+ err &= PAM_DATA_REPLACE; /* are we just replacing data? */
+ failure = (struct _pam_failed_auth *) fl;
+
+ if ( failure != NULL ) {
+
+ if ( !quiet && !err ) { /* under advisement from Sun,may go away */
+
+ /* log the number of authentication failures */
+ if ( failure->count > 1 ) {
+ (void) pam_get_item(pamh, PAM_SERVICE
+ , (const void **)&service);
+ _log_err(LOG_NOTICE
+ , "%d more authentication failure%s; %s(uid=%d) -> "
+ "%s for %s service"
+ , failure->count-1, failure->count==2 ? "":"s"
+ , failure->name
+ , failure->id
+ , failure->user
+ , service == NULL ? "**unknown**":service
+ );
+ if ( failure->count > UNIX_MAX_RETRIES ) {
+ _log_err(LOG_ALERT
+ , "service(%s) ignoring max retries; %d > %d"
+ , service == NULL ? "**unknown**":service
+ , failure->count
+ , UNIX_MAX_RETRIES );
+ }
+ }
+ }
+ failure->user = _pam_delete(failure->user); /* tidy up */
+ failure->name = _pam_delete(failure->name); /* tidy up */
+ free(failure);
+ }
+}
+
+/*
+ * verify the password of a user
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+static int pwdb_run_helper_binary(pam_handle_t *pamh, const char *passwd)
+{
+ int retval, child, fds[2];
+
+ D(("called."));
+ /* create a pipe for the password */
+ if (pipe(fds) != 0) {
+ D(("could not make pipe"));
+ return PAM_AUTH_ERR;
+ }
+
+ /* fork */
+ child = fork();
+ if (child == 0) {
+ static char *args[] = { NULL, NULL };
+ static char *envp[] = { NULL };
+
+ /* XXX - should really tidy up PAM here too */
+ while (pwdb_end() == PWDB_SUCCESS);
+
+ /* reopen stdin as pipe */
+ close(fds[1]);
+ dup2(fds[0], STDIN_FILENO);
+
+ /* exec binary helper */
+ args[0] = x_strdup(CHKPWD_HELPER);
+ execve(CHKPWD_HELPER, args, envp);
+
+ /* should not get here: exit with error */
+ D(("helper binary is not available"));
+ exit(PWDB_SUCCESS+1);
+ } else if (child > 0) {
+ /* wait for child */
+ close(fds[0]);
+ if (passwd != NULL) { /* send the password to the child */
+ write(fds[1], passwd, strlen(passwd)+1);
+ passwd = NULL;
+ } else {
+ write(fds[1], "", 1); /* blank password */
+ }
+ close(fds[1]);
+ (void) waitpid(child, &retval, 0); /* wait for helper to complete */
+ retval = (retval == PWDB_SUCCESS) ? PAM_SUCCESS:PAM_AUTH_ERR;
+ } else {
+ D(("fork failed"));
+ retval = PAM_AUTH_ERR;
+ }
+
+ D(("returning %d", retval));
+ return retval;
+}
+
+static int _unix_verify_password(pam_handle_t *pamh, const char *name
+ , const char *p, unsigned int ctrl)
+{
+ const struct pwdb *pw=NULL;
+ const struct pwdb_entry *pwe=NULL;
+
+ const char *salt;
+ char *pp;
+ char *data_name;
+ int retval;
+ int verify_result;
+
+ D(("called"));
+
+#ifdef HAVE_PAM_FAIL_DELAY
+ if ( off(UNIX_NODELAY, ctrl) ) {
+ D(("setting delay"));
+ (void) pam_fail_delay(pamh, 1000000); /* 1 sec delay for on failure */
+ }
+#endif
+
+ /* locate the entry for this user */
+
+ D(("locating user's record"));
+ retval = pwdb_locate("user", PWDB_DEFAULT, name, PWDB_ID_UNKNOWN, &pw);
+ if (retval == PWDB_PASS_PHRASE_REQD) {
+ /*
+ * give the password to the pwdb library. It may be needed to
+ * access the database
+ */
+
+ retval = pwdb_set_entry( pw, "pass_phrase", p, 1+strlen(p)
+ , NULL, NULL, 0);
+ if (retval != PWDB_SUCCESS) {
+ _log_err(LOG_ALERT, "find pass; %s", pwdb_strerror(retval));
+ (void) pwdb_delete(&pw);
+ p = NULL;
+ return PAM_CRED_INSUFFICIENT;
+ }
+
+ retval = pwdb_locate("user", pw->source, name, PWDB_ID_UNKNOWN, &pw);
+ }
+
+ if (retval != PWDB_SUCCESS) {
+ D(("user's record unavailable"));
+ if ( on(UNIX_AUDIT, ctrl) ) {
+ /* this might be a typo and the user has given a password
+ instead of a username. Careful with this. */
+ _log_err(LOG_ALERT, "check pass; user (%s) unknown", name);
+ } else {
+ _log_err(LOG_ALERT, "check pass; user unknown");
+ }
+ (void) pwdb_delete(&pw);
+ p = NULL;
+ return PAM_USER_UNKNOWN;
+ }
+
+ /*
+ * courtesy of PWDB the password for the user is stored in
+ * encrypted form in the "passwd" entry of pw.
+ */
+
+ retval = pwdb_get_entry(pw, "passwd", &pwe);
+ if (retval != PWDB_SUCCESS) {
+ if (geteuid()) {
+ /* we are not root perhaps this is the reason? Run helper */
+ D(("running helper binary"));
+ retval = pwdb_run_helper_binary(pamh, p);
+ } else {
+ retval = PAM_AUTHINFO_UNAVAIL;
+ _log_err(LOG_ALERT, "get passwd; %s", pwdb_strerror(retval));
+ }
+ (void) pwdb_delete(&pw);
+ p = NULL;
+ return retval;
+ }
+ salt = (const char *) pwe->value;
+
+ /*
+ * XXX: Cristian, the above is not the case for RADIUS(?) Some
+ * lines should be added for RADIUS to verify the password in
+ * clear text...
+ */
+
+ data_name = (char *) malloc(sizeof(FAIL_PREFIX)+strlen(name));
+ if ( data_name == NULL ) {
+ _log_err(LOG_CRIT, "no memory for data-name");
+ }
+ strcpy(data_name, FAIL_PREFIX);
+ strcpy(data_name + sizeof(FAIL_PREFIX)-1, name);
+
+ if ( !( (salt && *salt) || (p && *p) ) ) {
+
+ D(("two null passwords to compare"));
+
+ /* the stored password is NULL */
+ pp = NULL;
+ if ( off(UNIX__NONULL, ctrl ) ) { /* this means we've succeeded */
+ verify_result = PAM_SUCCESS;
+ } else {
+ verify_result = PAM_AUTH_ERR;
+ }
+
+ } else if ( !( salt && p ) ) {
+
+ D(("one of the two to compare are NULL"));
+
+ pp = NULL;
+ verify_result = PAM_AUTH_ERR;
+
+ } else {
+
+ pp = _pam_md(p, salt);
+
+ /* the moment of truth -- do we agree with the password? */
+ D(("comparing state of pp[%s] and salt[%s]", pp, salt));
+
+ if ( strcmp( pp, salt ) == 0 ) {
+ verify_result = PAM_SUCCESS;
+ } else {
+ _pam_delete(pp);
+ pp = _pam_md_compat(p, salt);
+ if ( strcmp( pp, salt ) == 0 ) {
+ verify_result = PAM_SUCCESS;
+ } else {
+ verify_result = PAM_AUTH_ERR;
+ }
+ }
+
+ p = NULL; /* no longer needed here */
+
+ }
+
+ if ( verify_result == PAM_SUCCESS ) {
+
+ retval = PAM_SUCCESS;
+ if (data_name) { /* reset failures */
+ pam_set_data(pamh, data_name, NULL, _cleanup_failures);
+ }
+
+ } else {
+
+ retval = PAM_AUTH_ERR;
+ if (data_name != NULL) {
+ struct _pam_failed_auth *new=NULL;
+ const struct _pam_failed_auth *old=NULL;
+
+ /* get a failure recorder */
+
+ new = (struct _pam_failed_auth *)
+ malloc(sizeof(struct _pam_failed_auth));
+
+ if (new != NULL) {
+
+ new->user = x_strdup(name);
+ new->id = getuid();
+ new->name = x_strdup(getlogin() ? getlogin():"" );
+
+ /* any previous failures for this user ? */
+ pam_get_data(pamh, data_name, (const void **)&old );
+
+ if (old != NULL) {
+ new->count = old->count +1;
+ if (new->count >= UNIX_MAX_RETRIES) {
+ retval = PAM_MAXTRIES;
+ }
+ } else {
+ const char *service=NULL;
+ (void) pam_get_item(pamh, PAM_SERVICE
+ , (const void **)&service);
+ _log_err(LOG_NOTICE
+ , "authentication failure; %s(uid=%d) -> "
+ "%s for %s service"
+ , new->name
+ , new->id
+ , new->user
+ , service == NULL ? "**unknown**":service
+ );
+ new->count = 1;
+ }
+
+ pam_set_data(pamh, data_name, new, _cleanup_failures);
+
+ } else {
+ _log_err(LOG_CRIT, "no memory for failure recorder");
+ }
+ }
+
+ }
+
+ (void) pwdb_entry_delete(&pwe);
+ (void) pwdb_delete(&pw);
+ salt = NULL;
+ _pam_delete(data_name);
+ _pam_delete(pp);
+
+ D(("done [%d].", retval));
+
+ return retval;
+}
+
+/*
+ * this function obtains the name of the current user and ensures
+ * that the PAM_USER item is set to this value
+ */
+
+static int _unix_get_user(pam_handle_t *pamh, unsigned int ctrl
+ , const char *prompt, const char **user)
+{
+ int retval;
+
+ D(("called"));
+
+ retval = pam_get_user(pamh, user, prompt);
+ if (retval != PAM_SUCCESS) {
+ D(("trouble reading username"));
+ return retval;
+ }
+
+ /*
+ * Various libraries at various times have had bugs related to
+ * '+' or '-' as the first character of a user name. Don't take
+ * any chances here. Require that the username starts with an
+ * alphanumeric character.
+ */
+
+ if (*user == NULL || !isalnum(**user)) {
+ D(("bad username"));
+ if (on(UNIX_DEBUG,ctrl)) {
+ _log_err(LOG_ERR, "bad username [%s]", *user);
+ }
+ return PAM_USER_UNKNOWN;
+ }
+
+ if (retval == PAM_SUCCESS && on(UNIX_DEBUG,ctrl)) {
+ _log_err(LOG_DEBUG, "username [%s] obtained", *user);
+ }
+
+ return retval;
+}
+
+/*
+ * _unix_blankpasswd() is a quick check for a blank password
+ *
+ * returns TRUE if user does not have a password
+ * - to avoid prompting for one in such cases (CG)
+ */
+
+static int _unix_blankpasswd(unsigned int ctrl, const char *name)
+{
+ const struct pwdb *pw=NULL;
+ const struct pwdb_entry *pwe=NULL;
+ int retval;
+
+ D(("called"));
+
+ /*
+ * This function does not have to be too smart if something goes
+ * wrong, return FALSE and let this case to be treated somewhere
+ * else (CG)
+ */
+
+ if ( on(UNIX__NONULL, ctrl) )
+ return 0; /* will fail but don't let on yet */
+
+ /* find the user's database entry */
+
+ retval = pwdb_locate("user", PWDB_DEFAULT, name, PWDB_ID_UNKNOWN, &pw);
+ if (retval != PWDB_SUCCESS || pw == NULL ) {
+
+ retval = 0;
+
+ } else {
+
+ /* Does this user have a password? */
+
+ retval = pwdb_get_entry(pw, "passwd", &pwe);
+ if ( retval != PWDB_SUCCESS || pwe == NULL )
+ retval = 0;
+ else if ( pwe->value == NULL || ((char *)pwe->value)[0] == '\0' )
+ retval = 1;
+ else
+ retval = 0;
+
+ }
+
+ /* tidy up */
+
+ if ( pw ) {
+ (void) pwdb_delete(&pw);
+ if ( pwe )
+ (void) pwdb_entry_delete(&pwe);
+ }
+
+ return retval;
+}
+
+/*
+ * obtain a password from the user
+ */
+
+static int _unix_read_password( pam_handle_t *pamh
+ , unsigned int ctrl
+ , const char *comment
+ , const char *prompt1
+ , const char *prompt2
+ , const char *data_name
+ , const char **pass )
+{
+ int authtok_flag;
+ int retval;
+ const char *item;
+ char *token;
+
+ D(("called"));
+
+ /*
+ * make sure nothing inappropriate gets returned
+ */
+
+ *pass = token = NULL;
+
+ /*
+ * which authentication token are we getting?
+ */
+
+ authtok_flag = on(UNIX__OLD_PASSWD,ctrl) ? PAM_OLDAUTHTOK:PAM_AUTHTOK ;
+
+ /*
+ * should we obtain the password from a PAM item ?
+ */
+
+ if ( on(UNIX_TRY_FIRST_PASS,ctrl) || on(UNIX_USE_FIRST_PASS,ctrl) ) {
+ retval = pam_get_item(pamh, authtok_flag, (const void **) &item);
+ if (retval != PAM_SUCCESS ) {
+ /* very strange. */
+ _log_err(LOG_ALERT
+ , "pam_get_item returned error to unix-read-password"
+ );
+ return retval;
+ } else if (item != NULL) { /* we have a password! */
+ *pass = item;
+ item = NULL;
+ return PAM_SUCCESS;
+ } else if (on(UNIX_USE_FIRST_PASS,ctrl)) {
+ return PAM_AUTHTOK_RECOVER_ERR; /* didn't work */
+ } else if (on(UNIX_USE_AUTHTOK, ctrl)
+ && off(UNIX__OLD_PASSWD, ctrl)) {
+ return PAM_AUTHTOK_RECOVER_ERR;
+ }
+ }
+
+ /*
+ * getting here implies we will have to get the password from the
+ * user directly.
+ */
+
+ {
+ struct pam_message msg[3],*pmsg[3];
+ struct pam_response *resp;
+ int i, replies;
+
+ /* prepare to converse */
+
+ if ( comment != NULL && off(UNIX__QUIET, ctrl) ) {
+ pmsg[0] = &msg[0];
+ msg[0].msg_style = PAM_TEXT_INFO;
+ msg[0].msg = comment;
+ i = 1;
+ } else {
+ i = 0;
+ }
+
+ pmsg[i] = &msg[i];
+ msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[i++].msg = prompt1;
+ replies = 1;
+
+ if ( prompt2 != NULL ) {
+ pmsg[i] = &msg[i];
+ msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[i++].msg = prompt2;
+ ++replies;
+ }
+
+ /* so call the conversation expecting i responses */
+ resp = NULL;
+ retval = converse(pamh, ctrl, i, pmsg, &resp);
+
+ if (resp != NULL) {
+
+ /* interpret the response */
+
+ if (retval == PAM_SUCCESS) { /* a good conversation */
+
+ token = x_strdup(resp[i-replies].resp);
+ if (token != NULL) {
+ if (replies == 2) {
+
+ /* verify that password entered correctly */
+ if (!resp[i-1].resp
+ || strcmp(token,resp[i-1].resp)) {
+ token = _pam_delete(token); /* mistyped */
+ retval = PAM_AUTHTOK_RECOVER_ERR;
+ make_remark(pamh, ctrl
+ , PAM_ERROR_MSG, MISTYPED_PASS);
+ }
+ }
+
+ } else {
+ _log_err(LOG_NOTICE
+ , "could not recover authentication token");
+ }
+
+ }
+
+ /*
+ * tidy up the conversation (resp_retcode) is ignored
+ * -- what is it for anyway? AGM
+ */
+
+ _pam_drop_reply(resp, i);
+
+ } else {
+ retval = (retval == PAM_SUCCESS)
+ ? PAM_AUTHTOK_RECOVER_ERR:retval ;
+ }
+ }
+
+ if (retval != PAM_SUCCESS) {
+ if ( on(UNIX_DEBUG,ctrl) )
+ _log_err(LOG_DEBUG,"unable to obtain a password");
+ return retval;
+ }
+
+ /* 'token' is the entered password */
+
+ if ( off(UNIX_NOT_SET_PASS, ctrl) ) {
+
+ /* we store this password as an item */
+
+ retval = pam_set_item(pamh, authtok_flag, token);
+ token = _pam_delete(token); /* clean it up */
+ if ( retval != PAM_SUCCESS
+ || (retval = pam_get_item(pamh, authtok_flag
+ , (const void **)&item))
+ != PAM_SUCCESS ) {
+
+ _log_err(LOG_CRIT, "error manipulating password");
+ return retval;
+
+ }
+
+ } else {
+ /*
+ * then store it as data specific to this module. pam_end()
+ * will arrange to clean it up.
+ */
+
+ retval = pam_set_data(pamh, data_name, (void *) token, _cleanup);
+ if (retval != PAM_SUCCESS) {
+ _log_err(LOG_CRIT, "error manipulating password data [%s]"
+ , pam_strerror(pamh, retval) );
+ token = _pam_delete(token);
+ return retval;
+ }
+ item = token;
+ token = NULL; /* break link to password */
+ }
+
+ *pass = item;
+ item = NULL; /* break link to password */
+
+ return PAM_SUCCESS;
+}
+
+static int _pam_unix_approve_pass(pam_handle_t *pamh
+ , unsigned int ctrl
+ , const char *pass_old
+ , const char *pass_new)
+{
+ D(("&new=%p, &old=%p",pass_old,pass_new));
+ D(("new=[%s]",pass_new));
+ D(("old=[%s]",pass_old));
+
+ if (pass_new == NULL || (pass_old && !strcmp(pass_old,pass_new))) {
+ if ( on(UNIX_DEBUG, ctrl) ) {
+ _log_err(LOG_DEBUG, "bad authentication token");
+ }
+ make_remark(pamh, ctrl, PAM_ERROR_MSG, pass_new == NULL ?
+ "No password supplied":"Password unchanged" );
+ return PAM_AUTHTOK_ERR;
+ }
+
+ /*
+ * if one wanted to hardwire authentication token strength
+ * checking this would be the place - AGM
+ */
+
+ return PAM_SUCCESS;
+}
+
+/* ****************************************************************** *
+ * Copyright (c) Andrew G. Morgan 1996-8.
+ * Copyright (c) Alex O. Yuriev, 1996.
+ * Copyright (c) Cristian Gafton 1996.
+ *
+ * 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, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 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 author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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.
+ */
+
diff --git a/modules/pam_radius/.cvsignore b/modules/pam_radius/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_radius/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_radius/Makefile b/modules/pam_radius/Makefile
new file mode 100644
index 00000000..a74b911f
--- /dev/null
+++ b/modules/pam_radius/Makefile
@@ -0,0 +1,99 @@
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# Created by Cristian Gafton <gafton@redhat.com> 1996/09/10
+#
+# STATIC modules are not supported
+#
+
+TITLE=pam_radius
+CONFD=$(CONFIGED)/security
+export CONFD
+CONFILE=$(CONFD)/radius.conf
+export CONFILE
+
+ifeq ($(HAVE_PWDBLIB),yes)
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+#LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+#static/%.o : %.c
+# $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+#ifdef STATIC
+#LIBSTATIC = lib$(TITLE).o
+#endif
+
+####################### don't edit below #######################
+
+dummy:
+
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+#ifdef STATIC
+# $(MKDIR) ./static
+#endif
+
+register:
+#ifdef STATIC
+# ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+#endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD) -lpwdb
+endif
+
+#ifdef STATIC
+#$(LIBOBJS): $(LIBSRC)
+#
+#$(LIBSTATIC): $(LIBOBJS)
+# $(LD) -r -o $@ $(LIBOBJS) -lpwdb
+#endif
+
+install: all
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak dynamic/* static/*
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+else
+
+include ../dont_makefile
+
+endif
diff --git a/modules/pam_radius/README b/modules/pam_radius/README
new file mode 100644
index 00000000..253308fd
--- /dev/null
+++ b/modules/pam_radius/README
@@ -0,0 +1,58 @@
+
+pam_radius module:
+ RADIUS session module.
+
+WHAT IT DOES:
+ This module is intended to provide the session service for users
+autheticated with a RADIUS server. At the present stage, the only option
+supported is the use of the RADIUS server as an accounting server. There are
+few things which needs to be cleared out first in the PAM project until one
+will be able to use this module and expect it to magically start pppd in
+response to a RADIUS server command to use PPP for this user, or to initiate
+a telnet connection to another host, or to hang and call back the user using
+parameters provided in the RADIUS server response. Most of these things are
+better suited for the radius login application. I hope to make available
+Real Soon (tm) patches for the login apps to make it work this way.
+
+
+ARGUMENTS RECOGNIZED:
+ debug verbose logging
+
+MODULE SERVICES PROVIDED:
+ session _open_session and _close_session
+
+ When opening a session, this module sends an Accounting-Start
+message to the RADIUS server, which will log/update/whatever a database for
+this user. On close, an Accounting-Stop message is sent to the RADIUS
+server.
+
+This module have no other pre-requisites for making it work. One can install
+a RADIUS server just for fun and use it as a centralized accounting server and
+forget about wtmp/last/sac&comp :-)
+
+USAGE:
+ For the services you need this module (login for example) put
+ the following line in /etc/pam.conf as the last line for that
+ service (usually after the pam_unix session line):
+
+ login session required /lib/security/pam_radius.so
+
+ Replace "login" for each service you are using this module.
+
+ This module make extensive use of the API provided in libpwdb
+ 0.54preB or later. By default, it will read the radius server
+ configuration (hostname and secret) from /etc/raddb/server. This is
+ a default compiled into libpwdb, and curently there is no way to
+ modify this default without recompiling libpwdb. I am working on
+ extending the radius support from libpwdb to provide a possibility
+ to make this runtime-configurable.
+
+ Also please note that libpwdb will require also the RADIUS
+ dictionary to be present (/etc/raddb/dictionary).
+
+TODO:
+ The work is far from complete. Deal with "real" session things.
+
+AUTHOR:
+ Cristian Gafton <gafton@redhat.com>
+
diff --git a/modules/pam_radius/pam_radius.c b/modules/pam_radius/pam_radius.c
new file mode 100644
index 00000000..b412edf9
--- /dev/null
+++ b/modules/pam_radius/pam_radius.c
@@ -0,0 +1,193 @@
+/*
+ * pam_radius
+ * Process an user session according to a RADIUS server response
+ *
+ * 1.0 - initial release - Linux ONLY
+ * 1.1 - revised and reorganized for libpwdb 0.54preB or higher
+ * - removed the conf= parameter, since we use libpwdb exclusively now
+ *
+ * See end for Copyright information
+ */
+
+#if !(defined(linux))
+#error THIS CODE IS KNOWN TO WORK ONLY ON LINUX !!!
+#endif
+
+/* Module defines */
+#define BUFFER_SIZE 1024
+#define LONG_VAL_PTR(ptr) ((*(ptr)<<24)+(*((ptr)+1)<<16)+(*((ptr)+2)<<8)+(*((ptr)+3)))
+
+#define PAM_SM_SESSION
+
+#include "pam_radius.h"
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+static time_t session_time;
+
+/* we need to save these from open_session to close_session, since
+ * when close_session will be called we won't be root anymore and
+ * won't be able to access again the radius server configuration file
+ * -- cristiang */
+
+static RADIUS_SERVER rad_server;
+static char hostname[BUFFER_SIZE];
+static char secret[BUFFER_SIZE];
+
+/* logging */
+static void _pam_log(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("pam_radius", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+/* argument parsing */
+
+#define PAM_DEBUG_ARG 0x0001
+
+static int _pam_parse(int argc, const char **argv)
+{
+ int ctrl=0;
+
+ /* step through arguments */
+ for (ctrl=0; argc-- > 0; ++argv) {
+
+ /* generic options */
+
+ if (!strcmp(*argv,"debug"))
+ ctrl |= PAM_DEBUG_ARG;
+ else {
+ _pam_log(LOG_ERR,"pam_parse: unknown option; %s",*argv);
+ }
+ }
+
+ return ctrl;
+}
+
+/* now the session stuff */
+PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ int retval;
+ char *user_name;
+ int ctrl;
+
+ ctrl = _pam_parse(argc, argv);
+ retval = pam_get_item( pamh, PAM_USER, (void*) &user_name );
+ if ( user_name == NULL || retval != PAM_SUCCESS ) {
+ _pam_log(LOG_CRIT, "open_session - error recovering username");
+ return PAM_SESSION_ERR;
+ }
+
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_DEBUG, "starting RADIUS user session for '%s'",
+ user_name);
+
+ retval = get_server_entries(hostname, secret);
+ if ((retval != PWDB_RADIUS_SUCCESS) ||
+ !strlen(hostname) || !strlen(secret)) {
+ _pam_log(LOG_CRIT, "Could not determine the radius server to talk to");
+ return PAM_IGNORE;
+ }
+ session_time = time(NULL);
+ rad_server.hostname = hostname;
+ rad_server.secret = secret;
+ retval = radius_acct_start(rad_server, user_name);
+ if (retval != PWDB_RADIUS_SUCCESS) {
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_DEBUG, "ERROR communicating with the RADIUS server");
+ return PAM_IGNORE;
+ }
+
+ return PAM_SUCCESS;
+}
+
+PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ int ctrl;
+ char *user_name;
+ int retval;
+
+ ctrl = _pam_parse(argc, argv);
+ retval = pam_get_item( pamh, PAM_USER, (void*) &user_name );
+ if ( user_name == NULL || retval != PAM_SUCCESS ) {
+ _pam_log(LOG_CRIT, "open_session - error recovering username");
+ return PAM_SESSION_ERR;
+ }
+
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_DEBUG, "closing RADIUS user session for '%s'",
+ user_name);
+
+ if (!strlen(hostname) || !strlen(secret)) {
+ _pam_log(LOG_CRIT, "Could not determine the radius server to talk to");
+ return PAM_IGNORE;
+ }
+ retval = radius_acct_stop(rad_server, user_name,
+ time(NULL) - session_time);
+ if (retval != PWDB_RADIUS_SUCCESS) {
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_DEBUG, "ERROR communicating with the RADIUS server");
+ return PAM_IGNORE;
+ }
+
+ return PAM_SUCCESS;
+}
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_radius_modstruct = {
+ "pam_radius",
+ NULL,
+ NULL,
+ NULL,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ NULL
+};
+#endif
+
+/*
+ * Copyright (c) Cristian Gafton, 1996, <gafton@redhat.com>
+ * 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, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 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 author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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.
+ */
diff --git a/modules/pam_radius/pam_radius.h b/modules/pam_radius/pam_radius.h
new file mode 100644
index 00000000..1f860eb5
--- /dev/null
+++ b/modules/pam_radius/pam_radius.h
@@ -0,0 +1,38 @@
+
+#ifndef PAM_RADIUS_H
+#define PAM_RADIUS_H
+
+#define _GNU_SOURCE
+#include <features.h>
+
+#include <stdio.h>
+
+#ifndef __USE_POSIX2
+#define __USE_POSIX2
+#endif /* __USE_POSIX2 */
+
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/resource.h>
+
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <utmp.h>
+#include <time.h>
+#include <netdb.h>
+
+#include <netinet/in.h>
+#include <rpcsvc/ypclnt.h>
+#include <rpc/rpc.h>
+
+#include <pwdb/radius.h>
+#include <pwdb/pwdb_radius.h>
+
+/******************************************************************/
+
+#endif /* PAM_RADIUS_H */
diff --git a/modules/pam_rhosts/.cvsignore b/modules/pam_rhosts/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_rhosts/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_rhosts/Makefile b/modules/pam_rhosts/Makefile
new file mode 100644
index 00000000..431842b2
--- /dev/null
+++ b/modules/pam_rhosts/Makefile
@@ -0,0 +1,98 @@
+# This Makefile controls a build process of the pam_rhosts modules
+# for Linux-PAM. You should not modify this Makefile.
+
+LIBAUTHOBJ = pam_rhosts_auth.o
+LIBAUTHSRC = pam_rhosts_auth.c
+LIBSESSOBJ =
+LIBSESSSRC =
+LIBPASSWDSRC =
+LIBPASSWDOBJ =
+LIBOBJ = $(LIBAUTHOBJ) $(LIBSESSOBJ) $(LIBPASSWDOBJ)
+LIBSRC = $(LIBAUTHSRC) $(LIBSESSSRC) $(LIBPASSWDSRC)
+
+ifdef STATIC
+LIBSTATIC = libpam_rhosts.o
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+endif
+
+ifdef DYNAMIC
+LIBSESSSH =
+LIBAUTHSH = pam_rhosts_auth.so
+LIBPASSWDSH =
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBSHARED = $(LIBSESSSH) $(LIBAUTHSH) $(LIBPASSWDSH)
+endif
+
+ifeq ($(shell if [ -f /usr/include/fsuid.h ]; then echo yes ; fi),yes)
+ CFLAGS += -DNEED_FSUID_H
+endif
+
+####################### don't edit below #######################
+
+dummy:
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; \
+ ./register_static pam_rhosts_auth pam_rhosts/libpam_rhosts.o )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+endif
+
+ifdef DYNAMIC
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+endif
+
+ifdef STATIC
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+
+endif
+
+#.c.o:
+# $(CC) -c $(CFLAGS) $<
+
+install: all
+ifdef DYNAMIC
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+# tidy up
+
+remove:
+ cd $(FAKEROOT)$(SECUREDIR) && rm -f $(LIBSHARED)
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) a.out core *~
+
+extraclean:
+ rm -f *.a *.out *.o *.so *.bak dynamic/* static/*
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
diff --git a/modules/pam_rhosts/README b/modules/pam_rhosts/README
new file mode 100644
index 00000000..527dfd38
--- /dev/null
+++ b/modules/pam_rhosts/README
@@ -0,0 +1,57 @@
+arguments recognized:
+
+"no_hosts_equiv"
+"no_rhosts"
+"debug"
+"nowarn"
+"suppress"
+"promiscuous"
+
+.rhosts/hosts.equiv format:
+
+There are positive entries, when one is matched authentication
+succeeds and terminates. There are negative entries, when one is
+matched authentication fails and terminates. Thus order is
+significant.
+
+Entry hosts.equiv .rhosts
+<host> All users on <host> are ok Same username from <host> is ok
+<host> <user> <user> from <host> is ok ditto
+-<host> No users from <host> are ok ditto
+<host> -<user> <user> from <host> is not ok ditto
+
+<host> can be ip (IPv4) numbers.
+
+Netgroups may be used in either host or user fields, and then applies
+to all hosts, or users, in the netgroup. The syntax is
+
+ +@<ng>
+
+The entries
+
+ <host> +@<ng>
+ +@<ng> +@<ng>
+ +@<ng> <user>
+
+means exactly what you think it does. Negative entries are of the
+form
+
+ -@<ng>
+
+When the "promiscuous" option is given the special character + may be
+used as a wildcard in any field.
+
+ + Allow anyone from any host to connect. DANGEROUS.
+ + + Ditto.
+ + <user> Allow the user to connect from anywhere. DANGEROUS.
+ <host> + Allow any user from the host. Dangerous.
+
+These, perhaps more usefull, forms of the + form is also disallowed
+unless "promiscuous" is specified:
+
+ + -<user> Disallow the user from any host
+ + -@<ng> Disallow all members of the netgroup from any host
+
+When "promiscuous" is not specified a '+' is handled as a negative
+match.
+
diff --git a/modules/pam_rhosts/pam_rhosts_auth.c b/modules/pam_rhosts/pam_rhosts_auth.c
new file mode 100644
index 00000000..f9707cfc
--- /dev/null
+++ b/modules/pam_rhosts/pam_rhosts_auth.c
@@ -0,0 +1,829 @@
+/*----------------------------------------------------------------------
+ * Modified for Linux-PAM by Al Longyear <longyear@netcom.com> 96/5/5
+ * Modifications, Cristian Gafton 97/2/8
+ * Modifications, Peter Allgeyer 97/3
+ * Modifications (netgroups and fixes), Nicolai Langfeldt 97/3/21
+ * Security fix: 97/10/2 - gethostbyname called repeatedly without care
+ * Modification (added privategroup option) Andrew <morgan@transmeta.com>
+ *----------------------------------------------------------------------
+ * Copyright (c) 1983, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define _BSD_SOURCE
+
+#define USER_RHOSTS_FILE "/.rhosts" /* prefixed by user's home dir */
+
+#ifdef linux
+#include <endian.h>
+#endif
+
+#ifdef NEED_FSUID_H
+#include <sys/fsuid.h>
+#endif /* NEED_FSUID_H */
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h> /* This is supposed(?) to contain the following */
+int innetgr(const char *, const char *, const char *,const char *);
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+
+#ifndef MAXDNAME
+#define MAXDNAME 256
+#endif
+
+#include <stdarg.h>
+#include <ctype.h>
+
+#include <net/if.h>
+#ifdef linux
+# include <linux/sockios.h>
+# ifndef __USE_MISC
+# define __USE_MISC
+# include <sys/fsuid.h>
+# endif /* __USE_MISC */
+#endif
+
+#include <pwd.h>
+#include <grp.h>
+#include <sys/file.h>
+#include <sys/signal.h>
+#include <sys/stat.h>
+#include <syslog.h>
+#ifndef _PATH_HEQUIV
+#define _PATH_HEQUIV "/etc/hosts.equiv"
+#endif /* _PATH_HEQUIV */
+
+#define PAM_SM_AUTH /* only defines this management group */
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+/* to the best of my knowledge, all modern UNIX boxes have 32 bit integers */
+#define U32 unsigned int
+
+
+/*
+ * Options for this module
+ */
+
+struct _options {
+ int opt_no_hosts_equiv;
+ int opt_hosts_equiv_rootok;
+ int opt_no_rhosts;
+ int opt_debug;
+ int opt_nowarn;
+ int opt_disallow_null_authtok;
+ int opt_silent;
+ int opt_promiscuous;
+ int opt_suppress;
+ int opt_private_group;
+ int opt_no_uid_check;
+ const char *superuser;
+ const char *last_error;
+};
+
+/* logging */
+static void _pam_log(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("pam_rhosts_auth", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+static void set_option (struct _options *opts, const char *arg)
+{
+ if (strcmp(arg, "no_hosts_equiv") == 0) {
+ opts->opt_no_hosts_equiv = 1;
+ return;
+ }
+
+ if (strcmp(arg, "hosts_equiv_rootok") == 0) {
+ opts->opt_hosts_equiv_rootok = 1;
+ return;
+ }
+
+ if (strcmp(arg, "no_rhosts") == 0) {
+ opts->opt_no_rhosts = 1;
+ return;
+ }
+
+ if (strcmp(arg, "debug") == 0) {
+ D(("debugging enabled"));
+ opts->opt_debug = 1;
+ return;
+ }
+
+ if (strcmp(arg, "no_warn") == 0) {
+ opts->opt_nowarn = 1;
+ return;
+ }
+
+ if (strcmp(arg, "promiscuous") == 0) {
+ opts->opt_promiscuous = 1; /* used to permit '+' in ...hosts file */
+ return;
+ }
+
+ if (strcmp(arg, "suppress") == 0) {
+ opts->opt_suppress = 1; /* used to suppress failure warning message */
+ return;
+ }
+
+ if (strcmp(arg, "privategroup") == 0) {
+ opts->opt_private_group = 1; /* used to permit group write on .rhosts
+ file if group has same name as owner */
+ return;
+ }
+
+ if (strcmp(arg, "no_uid_check") == 0) {
+ opts->opt_no_uid_check = 1; /* NIS optimization */
+ return;
+ }
+
+ if (strcmp(arg, "superuser=") == 0) {
+ opts->superuser = arg+sizeof("superuser=")-1;
+ return;
+ }
+ /*
+ * All other options are ignored at the present time.
+ */
+ _pam_log(LOG_WARNING, "unrecognized option '%s'", arg);
+}
+
+static void set_parameters (struct _options *opts, int flags,
+ int argc, const char **argv)
+{
+ opts->opt_silent = flags & PAM_SILENT;
+ opts->opt_disallow_null_authtok = flags & PAM_DISALLOW_NULL_AUTHTOK;
+
+ while (argc-- > 0) {
+ set_option (opts, *argv);
+ ++argv;
+ }
+}
+
+/*
+ * Obtain the name of the remote host. Currently, this is simply by
+ * requesting the contents of the PAM_RHOST item.
+ */
+
+static int pam_get_rhost(pam_handle_t *pamh, const char **rhost
+ , const char *prompt)
+{
+ int retval;
+ const char *current;
+
+ retval = pam_get_item (pamh, PAM_RHOST, (const void **)&current);
+ if (retval != PAM_SUCCESS)
+ return retval;
+
+ if (current == NULL) {
+ return PAM_AUTH_ERR;
+ }
+ *rhost = current;
+
+ return retval; /* pass on any error from conversation */
+}
+
+/*
+ * Obtain the name of the remote user. Currently, this is simply by
+ * requesting the contents of the PAM_RUSER item.
+ */
+
+static int pam_get_ruser(pam_handle_t *pamh, const char **ruser
+ , const char *prompt)
+{
+ int retval;
+ const char *current;
+
+ retval = pam_get_item (pamh, PAM_RUSER, (const void **)&current);
+ if (retval != PAM_SUCCESS)
+ return retval;
+
+ if (current == NULL) {
+ return PAM_AUTH_ERR;
+ }
+ *ruser = current;
+
+ return retval; /* pass on any error from conversation */
+}
+
+/*
+ * Returns 1 if positive match, 0 if no match, -1 if negative match.
+ */
+
+static int
+__icheckhost (pam_handle_t *pamh, struct _options *opts, U32 raddr
+ , register char *lhost, const char *rhost)
+{
+ struct hostent *hp;
+ U32 laddr;
+ int negate=1; /* Multiply return with this to get -1 instead of 1 */
+ char **pp, *user;
+
+ /* Check nis netgroup. We assume that pam has done all needed
+ paranoia checking before we are handed the rhost */
+ if (strncmp("+@",lhost,2) == 0)
+ return(innetgr(&lhost[2],rhost,NULL,NULL));
+
+ if (strncmp("-@",lhost,2) == 0)
+ return(-innetgr(&lhost[2],rhost,NULL,NULL));
+
+ /* -host */
+ if (strncmp("-",lhost,1) == 0) {
+ negate=-1;
+ lhost++;
+ } else if (strcmp("+",lhost) == 0) {
+ (void) pam_get_item(pamh, PAM_USER, (const void **)&user);
+ D(("user %s has a `+' host entry", user));
+ if (opts->opt_promiscuous)
+ return (1); /* asking for trouble, but ok.. */
+ /* If not promiscuous: handle as negative */
+ return (-1);
+ }
+
+ /* Try for raw ip address first. */
+ if (isdigit(*lhost) && (long)(laddr = inet_addr(lhost)) != -1)
+ return (negate*(! (raddr ^ laddr)));
+
+ /* Better be a hostname. */
+ hp = gethostbyname(lhost);
+ if (hp == NULL)
+ return (0);
+
+ /* Spin through ip addresses. */
+ for (pp = hp->h_addr_list; *pp; ++pp)
+ if (!memcmp (&raddr, *pp, sizeof (U32)))
+ return (negate);
+
+ /* No match. */
+ return (0);
+}
+
+/* Returns 1 on positive match, 0 on no match, -1 on negative match */
+
+static int __icheckuser(pam_handle_t *pamh, struct _options *opts
+ , const char *luser, const char *ruser
+ , const char *rhost)
+{
+ /*
+ luser is user entry from .rhosts/hosts.equiv file
+ ruser is user id on remote host
+ rhost is the remote host name
+ */
+ char *user;
+
+ /* [-+]@netgroup */
+ if (strncmp("+@",luser,2) == 0)
+ return (innetgr(&luser[2],NULL,ruser,NULL));
+
+ if (strncmp("-@",luser,2) == 0)
+ return (-innetgr(&luser[2],NULL,ruser,NULL));
+
+ /* -user */
+ if (strncmp("-",luser,1) == 0)
+ return(-(strcmp(&luser[1],ruser) == 0));
+
+ /* + */
+ if (strcmp("+",luser) == 0) {
+ (void) pam_get_item(pamh, PAM_USER, (const void **)&user);
+ _pam_log(LOG_WARNING, "user %s has a `+' user entry", user);
+ if (opts->opt_promiscuous)
+ return(1);
+ /* If not promiscuous we handle it as a negative match */
+ return(-1);
+ }
+
+ /* simple string match */
+ return (strcmp(ruser, luser) == 0);
+}
+
+/*
+ * Returns 1 for blank lines (or only comment lines) and 0 otherwise
+ */
+
+static int __isempty(char *p)
+{
+ while (*p && isspace(*p)) {
+ ++p;
+ }
+
+ return (*p == '\0' || *p == '#') ? 1:0 ;
+}
+
+/*
+ * Returns 0 if positive match, 1 if _not_ ok.
+ */
+
+static int
+__ivaliduser (pam_handle_t *pamh, struct _options *opts,
+ FILE *hostf, U32 raddr,
+ const char *luser, const char *ruser, const char *rhost)
+{
+ register const char *user;
+ register char *p;
+ int hcheck, ucheck;
+ char buf[MAXHOSTNAMELEN + 128]; /* host + login */
+
+ buf[sizeof (buf)-1] = '\0'; /* terminate line */
+
+ while (fgets(buf, sizeof(buf), hostf) != NULL) { /* hostf file line */
+ p = buf; /* from beginning of file.. */
+
+ /* Skip empty or comment lines */
+ if (__isempty(p)) {
+ continue;
+ }
+
+ /* Skip lines that are too long. */
+ if (strchr(p, '\n') == NULL) {
+ int ch = getc(hostf);
+
+ while (ch != '\n' && ch != EOF)
+ ch = getc(hostf);
+ continue;
+ }
+
+ /*
+ * If there is a hostname at the start of the line. Set it to
+ * lower case. A leading ' ' or '\t' indicates no hostname
+ */
+
+ for (;*p && !isspace(*p); ++p) {
+ *p = tolower(*p);
+ }
+
+ /*
+ * next we want to find the permitted name for the remote user
+ */
+
+ if (*p == ' ' || *p == '\t') {
+
+ /* <nul> terminate hostname and skip spaces */
+ for (*p++='\0'; *p && isspace(*p); ++p);
+
+ user = p; /* this is the user's name */
+ while (*p && !isspace(*p))
+ ++p; /* find end of user's name */
+ } else
+ user = p;
+
+ *p = '\0'; /* <nul> terminate username (+host?) */
+
+ /* buf -> host(?) ; user -> username(?) */
+
+ /* First check host part */
+ hcheck=__icheckhost(pamh, opts, raddr, buf, rhost);
+
+ if (hcheck<0)
+ return(1);
+
+ if (hcheck) {
+ /* Then check user part */
+ if (! (*user))
+ user = luser;
+
+ ucheck=__icheckuser(pamh, opts, user, ruser, rhost);
+
+ /* Positive 'host user' match? */
+ if (ucheck>0)
+ return(0);
+
+ /* Negative 'host -user' match? */
+ if (ucheck<0)
+ return(1);
+
+ /* Neither, go on looking for match */
+ }
+ }
+
+ return (1);
+}
+
+/*
+ * New .rhosts strategy: We are passed an ip address. We spin through
+ * hosts.equiv and .rhosts looking for a match. When the .rhosts only
+ * has ip addresses, we don't have to trust a nameserver. When it
+ * contains hostnames, we spin through the list of addresses the nameserver
+ * gives us and look for a match.
+ *
+ * Returns 0 if ok, -1 if not ok.
+ */
+
+static int
+pam_iruserok(pam_handle_t *pamh,
+ struct _options *opts, U32 raddr, int superuser,
+ const char *ruser, const char *luser, const char *rhost)
+{
+ const char *cp;
+ struct stat sbuf;
+ struct passwd *pwd;
+ FILE *hostf;
+ uid_t uid;
+ int answer;
+ char pbuf[MAXPATHLEN]; /* potential buffer overrun */
+
+ if ((!superuser||opts->opt_hosts_equiv_rootok) && !opts->opt_no_hosts_equiv ) {
+
+ /* try to open system hosts.equiv file */
+ hostf = fopen (_PATH_HEQUIV, "r");
+ if (hostf) {
+ answer = __ivaliduser(pamh, opts, hostf, raddr, luser
+ , ruser, rhost);
+ (void) fclose(hostf);
+ if (answer == 0)
+ return 0; /* remote host is equivalent to localhost */
+ } /* else {
+ No hosts.equiv file on system.
+ } */
+ }
+
+ if ( opts->opt_no_rhosts )
+ return 1;
+
+ /*
+ * Identify user's local .rhosts file
+ */
+
+ pwd = getpwnam(luser);
+ if (pwd == NULL) {
+ /*
+ * luser is assumed to be valid because of an earlier check for uid = 0
+ * we don't log this error twice. However, this shouldn't happen !
+ * --cristiang
+ */
+ return(1);
+ }
+
+ /* check for buffer overrun */
+ if (strlen(pwd->pw_dir) + sizeof(USER_RHOSTS_FILE) + 2 >= MAXPATHLEN) {
+ if (opts->opt_debug)
+ _pam_log(LOG_DEBUG,"home directory for `%s' is too long", luser);
+ return 1; /* to dangerous to try */
+ }
+
+ (void) strcpy(pbuf, pwd->pw_dir);
+ (void) strcat(pbuf, USER_RHOSTS_FILE);
+
+ /*
+ * Change effective uid while _reading_ .rhosts. (not just
+ * opening). If root and reading an NFS mounted file system,
+ * can't read files that are 0600 as .rhosts files should be.
+ */
+
+ /* We are root, this will not fail */
+#ifdef linux
+ /* If we are on linux the better way is setfsuid */
+ uid = setfsuid(pwd->pw_uid);
+ hostf = fopen(pbuf, "r");
+#else
+ uid = geteuid();
+ (void) seteuid(pwd->pw_uid);
+ hostf = fopen(pbuf, "r");
+#endif
+
+ if (hostf == NULL) {
+ if (opts->opt_debug)
+ _pam_log(LOG_DEBUG,"Could not open %s file",pbuf);
+ answer = 1;
+ goto exit_function;
+ }
+
+ /*
+ * If not a regular file, or is owned by someone other than
+ * user or root or if writeable by anyone but the owner, quit.
+ */
+
+ cp = NULL;
+ if (lstat(pbuf, &sbuf) < 0 || !S_ISREG(sbuf.st_mode))
+ cp = ".rhosts not regular file";
+ else if (fstat(fileno(hostf), &sbuf) < 0)
+ cp = ".rhosts fstat failed";
+ else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid)
+ cp = "bad .rhosts owner";
+ else if (sbuf.st_mode & S_IWOTH)
+ cp = ".rhosts writable by other!";
+ else if (sbuf.st_mode & S_IWGRP) {
+
+ /* private group caveat */
+ if (opts->opt_private_group) {
+ struct group *grp = getgrgid(sbuf.st_gid);
+
+ if (NULL == grp || NULL == grp->gr_name
+ || strcmp(luser,grp->gr_name)) {
+ cp = ".rhosts writable by public group";
+ } else if (grp->gr_mem) {
+ int gcount;
+
+ /* require at most one member (luser) of this group */
+ for (gcount=0; grp->gr_mem[gcount]; ++gcount) {
+ if (strcmp(grp->gr_mem[gcount], luser)) {
+ gcount = -1;
+ break;
+ }
+ }
+ if (gcount < 0) {
+ cp = ".rhosts writable by other members of group";
+ }
+ }
+ } else {
+ cp = ".rhosts writable by group";
+ }
+
+ } /* It is _NOT_ safe to append an else here... Do so prior to
+ * S_IWGRP check */
+
+ /* If there were any problems, quit. */
+ if (cp) {
+ opts->last_error = cp;
+ answer = 1;
+ goto exit_function;
+ }
+
+ answer = __ivaliduser (pamh, opts, hostf, raddr, luser, ruser, rhost);
+
+exit_function:
+ /*
+ * Go here to exit after the fsuid/euid has been adjusted so that
+ * they are reset before we exit.
+ */
+
+#ifdef linux
+ setfsuid(uid);
+#else
+ (void)seteuid(uid);
+#endif
+
+ if (hostf != NULL)
+ (void) fclose(hostf);
+
+ return answer;
+}
+
+static int
+pam_ruserok (pam_handle_t *pamh,
+ struct _options *opts, const char *rhost, int superuser,
+ const char *ruser, const char *luser)
+{
+ struct hostent *hp;
+ int answer = 1; /* default to failure */
+ U32 *addrs;
+ int n, i;
+
+ opts->last_error = (char *) 0;
+ hp = gethostbyname(rhost); /* identify host */
+
+ if (hp != NULL) {
+ /* First of all check the address length */
+ if (hp->h_length != 4) {
+ _pam_log(LOG_ALERT, "pam_rhosts module can't work with not IPv4 "
+ "addresses");
+ return 1; /* not allowed */
+ }
+
+ /* loop though address list */
+ for (n = 0; hp->h_addr_list[n]; n++);
+ D(("rhosts: %d addresses", n));
+
+ if (n) {
+ addrs = calloc (n, hp->h_length);
+ for (i = 0; i < n; i++)
+ memcpy (addrs+i, hp->h_addr_list[i], hp->h_length);
+
+ for (i = 0; i < n && answer; i++) {
+ D(("rhosts: address %d is %04x", i, addrs[i]));
+ answer = pam_iruserok(pamh, opts, addrs[i], superuser,
+ ruser, luser, rhost);
+ /* answer == 0 means success */
+ }
+
+ free (addrs);
+ }
+ }
+
+ return answer;
+}
+
+/*
+ * Internal function to do authentication
+ */
+
+static int _pam_auth_rhosts (pam_handle_t *pamh,
+ int flags,
+ int argc,
+ const char **argv)
+{
+ int retval;
+ const char *luser;
+ const char *ruser,*rhost;
+ struct _options opts;
+ int as_root = 0;
+ /*
+ * Look at the options and set the flags accordingly.
+ */
+ memset (&opts, 0, sizeof (opts));
+ set_parameters (&opts, flags, argc, argv);
+ /*
+ * Obtain the parameters for the various items
+ */
+ for (;;) { /* abuse loop to avoid goto */
+
+ /* get the remotehost */
+ retval = pam_get_rhost(pamh, &rhost, NULL);
+ (void) pam_set_item(pamh, PAM_RHOST, rhost);
+ if (retval != PAM_SUCCESS) {
+ if (opts.opt_debug) {
+ _pam_log(LOG_DEBUG, "could not get the remote host name");
+ }
+ break;
+ }
+
+ /* get the remote user */
+ retval = pam_get_ruser(pamh, &ruser, NULL);
+ (void) pam_set_item(pamh, PAM_RUSER, ruser);
+ if (retval != PAM_SUCCESS) {
+ if (opts.opt_debug)
+ _pam_log(LOG_DEBUG, "could not get the remote username");
+ break;
+ }
+
+ /* get the local user */
+ retval = pam_get_user(pamh, &luser, NULL);
+
+ if (retval != PAM_SUCCESS) {
+ if (opts.opt_debug)
+ _pam_log(LOG_DEBUG, "could not determine name of local user");
+ break;
+ }
+
+ if (opts.superuser && !strcmp(opts.superuser, luser)) {
+ as_root = 1;
+ }
+
+ /* check if the luser uid == 0... --cristiang */
+ if (! opts.opt_no_uid_check) {
+ struct passwd *luser_pwd;
+
+ luser_pwd = getpwnam(luser);
+ if (luser_pwd == NULL) {
+ if (opts.opt_debug)
+ _pam_log(LOG_DEBUG, "user '%s' unknown to this system",
+ luser);
+ retval = PAM_AUTH_ERR;
+ break;
+ }
+ if (luser_pwd->pw_uid == 0)
+ as_root = 1;
+ luser_pwd = NULL; /* forget */
+ }
+/*
+ * Validate the account information.
+ */
+ if (pam_ruserok (pamh, &opts, rhost, as_root, ruser, luser) != 0) {
+ if ( !opts.opt_suppress ) {
+ _pam_log(LOG_WARNING, "denied to %s@%s as %s: %s",
+ ruser, rhost, luser, (opts.last_error==NULL) ?
+ "access not allowed":opts.last_error);
+ }
+ retval = PAM_AUTH_ERR;
+ } else {
+ _pam_log(LOG_NOTICE, "allowed to %s@%s as %s",
+ ruser, rhost, luser);
+ }
+ break;
+ }
+
+ return retval;
+}
+
+/* --- authentication management functions --- */
+
+PAM_EXTERN
+int pam_sm_authenticate (pam_handle_t *pamh,
+ int flags,
+ int argc,
+ const char **argv)
+{
+ int retval;
+
+ if (sizeof(U32) != 4) {
+ _pam_log (LOG_ALERT, "pam_rhosts module can\'t work on this hardware "
+ "(yet)");
+ return PAM_AUTH_ERR;
+ }
+ sethostent(1);
+ retval = _pam_auth_rhosts (pamh, flags, argc, argv);
+ endhostent();
+ return retval;
+}
+
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc,
+ const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+/* end of module definition */
+
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_rhosts_auth_modstruct = {
+ "pam_rhosts_auth",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+#endif
+
+/*
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:56 agmorgan
+ * Initial revision
+ *
+ * Revision 1.4 1999/11/08 05:46:58 morgan
+ * fixes for .71
+ *
+ * Revision 1.3 1999/10/09 05:12:49 morgan
+ * added hosts_equiv_rootok support
+ *
+ * Revision 1.2 1998/12/14 05:47:58 morgan
+ * added a couple of options: specify the name of root, and don't do the
+ * user-known check.
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:16 morgan
+ * Linux PAM sources pre-0.66
+ *
+ * Revision 1.12 1997/09/27 14:34:01 morgan
+ * fixed comment and renamed iruserok to pam_iruserok.
+ *
+ * Revision 1.11 1997/04/05 06:26:39 morgan
+ * fairly major fixes and enhancements (see CHANGELOG for 0.57 release)
+ *
+ * Revision 1.10 1997/02/09 02:09:30 morgan
+ * - implementation of 'debug' argument (Cristian Gafton)
+ * - we check for uid=0 accounts instead of hardcoded 'root' (Cristian Gafton)
+ *
+ * Revision 1.9 1996/12/01 03:09:47 morgan
+ * *** empty log message ***
+ *
+ * Revision 1.8 1996/11/12 06:08:59 morgan
+ * Oliver Crow's "rootok" patch plus a little clean up of set_option
+ * (AGM)
+ *
+ * Revision 1.7 1996/11/10 20:15:56 morgan
+ * cross platform support
+ *
+ * Revision 1.6 1996/08/09 05:46:29 morgan
+ * removed code for manually setting the remote username etc..
+ *
+ */
diff --git a/modules/pam_rootok/.cvsignore b/modules/pam_rootok/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_rootok/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_rootok/Makefile b/modules/pam_rootok/Makefile
new file mode 100644
index 00000000..f3f2e53d
--- /dev/null
+++ b/modules/pam_rootok/Makefile
@@ -0,0 +1,117 @@
+#
+# $Id$
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# $Log$
+# Revision 1.1 2000/06/20 22:11:56 agmorgan
+# Initial revision
+#
+# Revision 1.1.1.1 1998/07/12 05:17:16 morgan
+# Linux PAM sources pre-0.66
+#
+# Revision 1.7 1997/04/05 06:25:20 morgan
+# fakeroot
+#
+# Revision 1.6 1997/02/15 19:15:50 morgan
+# fixed email
+#
+# Revision 1.5 1996/11/10 20:16:10 morgan
+# cross platform support
+#
+# Revision 1.4 1996/09/05 06:29:36 morgan
+# ld --> gcc
+#
+# Revision 1.3 1996/05/26 15:47:46 morgan
+# make dynamic/static dirs!
+#
+# Revision 1.2 1996/05/26 04:04:53 morgan
+# automated static support
+#
+# Revision 1.1 1996/05/05 17:14:15 morgan
+# Initial revision
+#
+#
+# Created by Andrew Morgan <morgan@parc.power.net> 1996/5/5
+#
+
+TITLE=pam_rootok
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak dynamic/* static/*
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_rootok/README b/modules/pam_rootok/README
new file mode 100644
index 00000000..7120b164
--- /dev/null
+++ b/modules/pam_rootok/README
@@ -0,0 +1,18 @@
+# $Id$
+#
+
+this module is an authentication module that performs one task: if the
+id of the user is '0' then it returns 'PAM_SUCCESS' with the
+'sufficient' /etc/pam.conf control flag it can be used to allow
+password free access to some service for 'root'
+
+Recognized arguments:
+
+ debug write a message to syslog indicating success or
+ failure.
+
+module services provided:
+
+ auth _authetication and _setcred (blank)
+
+Andrew Morgan
diff --git a/modules/pam_rootok/pam_rootok.c b/modules/pam_rootok/pam_rootok.c
new file mode 100644
index 00000000..a275e6eb
--- /dev/null
+++ b/modules/pam_rootok/pam_rootok.c
@@ -0,0 +1,109 @@
+/* pam_rootok module */
+
+/*
+ * $Id$
+ *
+ * Written by Andrew Morgan <morgan@linux.kernel.org> 1996/3/11
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <stdarg.h>
+
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_AUTH
+
+#include <security/pam_modules.h>
+
+/* some syslogging */
+
+static void _pam_log(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("PAM-rootok", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+
+/* argument parsing */
+
+#define PAM_DEBUG_ARG 01
+
+static int _pam_parse(int argc, const char **argv)
+{
+ int ctrl=0;
+
+ /* step through arguments */
+ for (ctrl=0; argc-- > 0; ++argv) {
+
+ /* generic options */
+
+ if (!strcmp(*argv,"debug"))
+ ctrl |= PAM_DEBUG_ARG;
+ else {
+ _pam_log(LOG_ERR,"pam_parse: unknown option; %s",*argv);
+ }
+ }
+
+ return ctrl;
+}
+
+/* --- authentication management functions (only) --- */
+
+PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ int ctrl;
+ int retval = PAM_AUTH_ERR;
+
+ ctrl = _pam_parse(argc, argv);
+ if (getuid() == 0)
+ retval = PAM_SUCCESS;
+
+ if (ctrl & PAM_DEBUG_ARG) {
+ _pam_log(LOG_DEBUG, "authetication %s"
+ , retval==PAM_SUCCESS ? "succeeded":"failed" );
+ }
+
+ return retval;
+}
+
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_rootok_modstruct = {
+ "pam_rootok",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+#endif
+
+/* end of module definition */
diff --git a/modules/pam_securetty/.cvsignore b/modules/pam_securetty/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_securetty/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_securetty/Makefile b/modules/pam_securetty/Makefile
new file mode 100644
index 00000000..d8a09ea1
--- /dev/null
+++ b/modules/pam_securetty/Makefile
@@ -0,0 +1,83 @@
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+
+TITLE=pam_securetty
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak dynamic/* static/*
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
diff --git a/modules/pam_securetty/README b/modules/pam_securetty/README
new file mode 100644
index 00000000..1df095c9
--- /dev/null
+++ b/modules/pam_securetty/README
@@ -0,0 +1,9 @@
+pam_securetty:
+ Allows root logins only if the user is logging in on a
+ "secure" tty, as defined by the listing in /etc/securetty
+
+ Also checks to make sure that /etc/securetty is a plain
+ file and not world writable.
+
+ - Elliot Lee <sopwith@redhat.com>, Red Hat Software.
+ July 25, 1996.
diff --git a/modules/pam_securetty/pam_securetty.c b/modules/pam_securetty/pam_securetty.c
new file mode 100644
index 00000000..9e6121e8
--- /dev/null
+++ b/modules/pam_securetty/pam_securetty.c
@@ -0,0 +1,191 @@
+/* pam_securetty module */
+
+#define SECURETTY_FILE "/etc/securetty"
+#define TTY_PREFIX "/dev/"
+
+/*
+ * by Elliot Lee <sopwith@redhat.com>, Red Hat Software.
+ * July 25, 1996.
+ * This code shamelessly ripped from the pam_rootok module.
+ * Slight modifications AGM. 1996/12/3
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <pwd.h>
+#include <string.h>
+
+#define PAM_SM_AUTH
+
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_AUTH
+
+#include <security/pam_modules.h>
+
+/* some syslogging */
+
+static void _pam_log(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("PAM-securetty", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+/* argument parsing */
+
+#define PAM_DEBUG_ARG 0x0001
+
+static int _pam_parse(int argc, const char **argv)
+{
+ int ctrl=0;
+
+ /* step through arguments */
+ for (ctrl=0; argc-- > 0; ++argv) {
+
+ /* generic options */
+
+ if (!strcmp(*argv,"debug"))
+ ctrl |= PAM_DEBUG_ARG;
+ else {
+ _pam_log(LOG_ERR,"pam_parse: unknown option; %s",*argv);
+ }
+ }
+
+ return ctrl;
+}
+
+/* --- authentication management functions (only) --- */
+
+PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ int retval = PAM_AUTH_ERR;
+ const char *username;
+ char *uttyname;
+ char ttyfileline[256];
+ struct stat ttyfileinfo;
+ struct passwd *user_pwd;
+ FILE *ttyfile;
+ int ctrl;
+
+ /* parse the arguments */
+ ctrl = _pam_parse(argc, argv);
+
+ retval = pam_get_user(pamh, &username, NULL);
+ if (retval != PAM_SUCCESS || username == NULL) {
+ if (ctrl & PAM_DEBUG_ARG) {
+ _pam_log(LOG_WARNING, "cannot determine username");
+ }
+ return (retval == PAM_CONV_AGAIN
+ ? PAM_INCOMPLETE:PAM_SERVICE_ERR);
+ }
+
+ retval = pam_get_item(pamh, PAM_TTY, (const void **)&uttyname);
+ if (retval != PAM_SUCCESS || uttyname == NULL) {
+ if (ctrl & PAM_DEBUG_ARG) {
+ _pam_log(LOG_WARNING, "cannot determine user's tty");
+ }
+ return PAM_SERVICE_ERR;
+ }
+
+ /* The PAM_TTY item may be prefixed with "/dev/" - skip that */
+ if (strncmp(TTY_PREFIX, uttyname, sizeof(TTY_PREFIX)-1) == 0)
+ uttyname += sizeof(TTY_PREFIX)-1;
+
+ user_pwd = getpwnam(username);
+ if (user_pwd == NULL) {
+ return PAM_IGNORE;
+ } else if (user_pwd->pw_uid != 0) { /* If the user is not root,
+ securetty's does not apply
+ to them */
+ return PAM_SUCCESS;
+ }
+
+ if (stat(SECURETTY_FILE, &ttyfileinfo)) {
+ _pam_log(LOG_NOTICE, "Couldn't open " SECURETTY_FILE);
+ return PAM_SUCCESS; /* for compatibility with old securetty handling,
+ this needs to succeed. But we still log the
+ error. */
+ }
+
+ if ((ttyfileinfo.st_mode & S_IWOTH)
+ || !S_ISREG(ttyfileinfo.st_mode)) {
+ /* If the file is world writable or is not a
+ normal file, return error */
+ _pam_log(LOG_ERR, SECURETTY_FILE
+ " is either world writable or not a normal file");
+ return PAM_AUTH_ERR;
+ }
+
+ ttyfile = fopen(SECURETTY_FILE,"r");
+ if(ttyfile == NULL) { /* Check that we opened it successfully */
+ _pam_log(LOG_ERR,
+ "Error opening " SECURETTY_FILE);
+ return PAM_SERVICE_ERR;
+ }
+ /* There should be no more errors from here on */
+ retval=PAM_AUTH_ERR;
+ /* This loop assumes that PAM_SUCCESS == 0
+ and PAM_AUTH_ERR != 0 */
+ while((fgets(ttyfileline,sizeof(ttyfileline)-1, ttyfile) != NULL)
+ && retval) {
+ if(ttyfileline[strlen(ttyfileline) - 1] == '\n')
+ ttyfileline[strlen(ttyfileline) - 1] = '\0';
+ retval = strcmp(ttyfileline,uttyname);
+ }
+ fclose(ttyfile);
+ if(retval) {
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_WARNING, "access denied: tty '%s' is not secure !",
+ uttyname);
+ retval = PAM_AUTH_ERR;
+ }
+ if ((retval == PAM_SUCCESS) && (ctrl & PAM_DEBUG_ARG))
+ _pam_log(LOG_DEBUG, "access allowed for '%s' on '%s'",
+ username, uttyname);
+ return retval;
+}
+
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_securetty_modstruct = {
+ "pam_securetty",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+#endif
+
+/* end of module definition */
diff --git a/modules/pam_shells/.cvsignore b/modules/pam_shells/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_shells/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_shells/Makefile b/modules/pam_shells/Makefile
new file mode 100644
index 00000000..121b19a0
--- /dev/null
+++ b/modules/pam_shells/Makefile
@@ -0,0 +1,84 @@
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+
+TITLE=pam_shells
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak dynamic/* static/*
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_shells/README b/modules/pam_shells/README
new file mode 100644
index 00000000..cbd5bfb5
--- /dev/null
+++ b/modules/pam_shells/README
@@ -0,0 +1,10 @@
+pam_shells:
+ Authentication is granted if the users shell is listed in
+ /etc/shells. If no shell is in /etc/passwd (empty), the
+ /bin/sh is used (following ftpd's convention).
+
+ Also checks to make sure that /etc/shells is a plain
+ file and not world writable.
+
+ - Erik Troan <ewt@redhat.com>, Red Hat Software.
+ August 5, 1996.
diff --git a/modules/pam_shells/pam_shells.c b/modules/pam_shells/pam_shells.c
new file mode 100644
index 00000000..f460ee5f
--- /dev/null
+++ b/modules/pam_shells/pam_shells.c
@@ -0,0 +1,133 @@
+/* pam_shells module */
+
+#define SHELL_FILE "/etc/shells"
+
+/*
+ * by Erik Troan <ewt@redhat.com>, Red Hat Software.
+ * August 5, 1996.
+ * This code shamelessly ripped from the pam_securetty module.
+ */
+
+#define _BSD_SOURCE
+
+#include <pwd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <syslog.h>
+#include <unistd.h>
+
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_AUTH
+
+#include <security/pam_modules.h>
+
+/* some syslogging */
+
+static void _pam_log(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("PAM-shells", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+/* --- authentication management functions (only) --- */
+
+PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ int retval = PAM_AUTH_ERR;
+ const char *userName;
+ char *userShell;
+ char shellFileLine[256];
+ struct stat sb;
+ struct passwd * pw;
+ FILE * shellFile;
+
+ retval = pam_get_user(pamh,&userName,NULL);
+ if(retval != PAM_SUCCESS)
+ return PAM_SERVICE_ERR;
+
+ if(!userName || (strlen(userName) <= 0)) {
+ /* Don't let them use a NULL username... */
+ pam_get_user(pamh,&userName,NULL);
+ if (retval != PAM_SUCCESS)
+ return PAM_SERVICE_ERR;
+ }
+
+ pw = getpwnam(userName);
+ if (!pw)
+ return PAM_AUTH_ERR; /* user doesn't exist */
+ userShell = pw->pw_shell;
+
+ if(stat(SHELL_FILE,&sb)) {
+ _pam_log(LOG_ERR, SHELL_FILE, " cannot be stat'd (it probably does "
+ "not exist)");
+ return PAM_AUTH_ERR; /* must have /etc/shells */
+ }
+
+ if((sb.st_mode & S_IWOTH) || !S_ISREG(sb.st_mode)) {
+ _pam_log(LOG_ERR,
+ SHELL_FILE " is either world writable or not a normal file");
+ return PAM_AUTH_ERR;
+ }
+
+ shellFile = fopen(SHELL_FILE,"r");
+ if(shellFile == NULL) { /* Check that we opened it successfully */
+ _pam_log(LOG_ERR,
+ "Error opening " SHELL_FILE);
+ return PAM_SERVICE_ERR;
+ }
+ /* There should be no more errors from here on */
+ retval=PAM_AUTH_ERR;
+ /* This loop assumes that PAM_SUCCESS == 0
+ and PAM_AUTH_ERR != 0 */
+ while((fgets(shellFileLine,255,shellFile) != NULL)
+ && retval) {
+ if (shellFileLine[strlen(shellFileLine) - 1] == '\n')
+ shellFileLine[strlen(shellFileLine) - 1] = '\0';
+ retval = strcmp(shellFileLine, userShell);
+ }
+ fclose(shellFile);
+ if(retval)
+ retval = PAM_AUTH_ERR;
+ return retval;
+}
+
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_shells_modstruct = {
+ "pam_shells",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+#endif
+
+/* end of module definition */
diff --git a/modules/pam_stress/.cvsignore b/modules/pam_stress/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_stress/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_stress/Makefile b/modules/pam_stress/Makefile
new file mode 100644
index 00000000..1bcfa502
--- /dev/null
+++ b/modules/pam_stress/Makefile
@@ -0,0 +1,115 @@
+#
+# $Id$
+#
+# Created by Andrew Morgan <morgan@parc.power.net> 1996/3/11
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# $Log$
+# Revision 1.1 2000/06/20 22:11:57 agmorgan
+# Initial revision
+#
+# Revision 1.1.1.1 1998/07/12 05:17:16 morgan
+# Linux PAM sources pre-0.66
+#
+# Revision 1.7 1997/04/05 06:23:08 morgan
+# fakeroot
+#
+# Revision 1.6 1997/02/15 19:05:55 morgan
+# fixed email
+#
+# Revision 1.5 1996/11/10 20:17:55 morgan
+# cross platform support
+#
+# Revision 1.4 1996/09/05 06:31:09 morgan
+# ld --> gcc
+#
+# Revision 1.3 1996/05/26 15:50:43 morgan
+# make dynamic and static dirs
+#
+# Revision 1.2 1996/05/26 04:11:56 morgan
+# automated static support
+#
+#
+#
+
+TITLE=pam_stress
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_stress/README b/modules/pam_stress/README
new file mode 100644
index 00000000..74a297b2
--- /dev/null
+++ b/modules/pam_stress/README
@@ -0,0 +1,66 @@
+#
+# $Id$
+#
+# This describes the behavior of this module with respect to the
+# /etc/pam.conf file.
+#
+# writen by Andrew Morgan <morgan@parc.power.net>
+#
+
+This module recognizes the following arguments.
+
+debug put lots of information in syslog.
+ *NOTE* this option writes passwords to syslog, so
+ don't use anything sensitive when testing.
+
+no_warn don't give warnings about things (otherwise warnings are issued
+ via the conversation function)
+
+use_first_pass don't prompt for a password, for pam_sm_authentication
+ function just use item PAM_AUTHTOK.
+
+try_first_pass don't prompt for a password unless there has been no
+ previous authentication token (item PAM_AUTHTOK is NULL)
+
+rootok This is intended for the pam_sm_chauthtok function and
+ it instructs this function to permit root to change
+ the user's password without entering the old password.
+
+The following arguments are acted on by the module. They are intended
+to make the module give the impression of failing as a fully
+functioning module might.
+
+expired an argument intended for the account and chauthtok module
+ parts. It instructs the module to act as if the user's
+ password has expired
+
+fail_1 this instructs the module to make its first function fail.
+
+fail_2 this instructs the module to make its second function (if there
+ is one) fail.
+
+ The function break up is indicated in the Module
+ Developers' Guide. Listed here it is:
+
+ service function 1 function 2
+ ------- ---------- ----------
+ auth pam_sm_authenticate pam_sm_setcred
+ password pam_sm_chauthtok
+ session pam_sm_open_session pam_sm_close_session
+ account pam_sm_acct_mgmt
+
+prelim for pam_sm_chauthtok, means fail on PAM_PRELIM_CHECK.
+
+required for pam_sm_chauthtok, means fail if the user hasn't already
+ been authenticated by this module. (See stress_new_pwd data
+ item below.)
+
+#
+# data strings that this module uses are the following:
+#
+
+data name value(s) Comments
+--------- -------- --------
+stress_new_pwd yes tells pam_sm_chauthtok that
+ pam_sm_acct_mgmt says we need a new
+ password
diff --git a/modules/pam_stress/pam_stress.c b/modules/pam_stress/pam_stress.c
new file mode 100644
index 00000000..e5813579
--- /dev/null
+++ b/modules/pam_stress/pam_stress.c
@@ -0,0 +1,565 @@
+/* pam_stress module */
+
+/* $Id$
+ *
+ * created by Andrew Morgan <morgan@linux.kernel.org> 1996/3/12
+ */
+
+#ifdef linux
+# define _GNU_SOURCE
+# include <features.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * here, we make definitions for the externally accessible functions
+ * in this file (these definitions are required for static modules
+ * but strongly encouraged generally) they are used to instruct the
+ * modules include file to define their prototypes.
+ */
+
+#define PAM_SM_AUTH
+#define PAM_SM_ACCOUNT
+#define PAM_SM_SESSION
+#define PAM_SM_PASSWORD
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+static char *_strdup(const char *x)
+{
+ char *new;
+ new = malloc(strlen(x)+1);
+ strcpy(new,x);
+ return new;
+}
+
+/* log errors */
+
+static void _pam_log(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("PAM-stress", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+/* ---------- */
+
+/* an internal function to turn all possible test arguments into bits
+ of a ctrl number */
+
+/* generic options */
+
+#define PAM_ST_DEBUG 01
+#define PAM_ST_NO_WARN 02
+#define PAM_ST_USE_PASS1 04
+#define PAM_ST_TRY_PASS1 010
+#define PAM_ST_ROOTOK 020
+
+/* simulation options */
+
+#define PAM_ST_EXPIRED 040
+#define PAM_ST_FAIL_1 0100
+#define PAM_ST_FAIL_2 0200
+#define PAM_ST_PRELIM 0400
+#define PAM_ST_REQUIRE_PWD 01000
+
+/* some syslogging */
+
+static void _pam_report(int ctrl, const char *name, int flags,
+ int argc, const char **argv)
+{
+ if (ctrl & PAM_ST_DEBUG) {
+ _pam_log(LOG_DEBUG, "CALLED: %s", name);
+ _pam_log(LOG_DEBUG, "FLAGS : 0%o%s", flags,
+ (flags & PAM_SILENT) ? " (silent)":"");
+ _pam_log(LOG_DEBUG, "CTRL = 0%o",ctrl);
+ _pam_log(LOG_DEBUG, "ARGV :");
+ while (argc--) {
+ _pam_log(LOG_DEBUG, " \"%s\"", *argv++);
+ }
+ }
+}
+
+static int _pam_parse(int argc, const char **argv)
+{
+ int ctrl=0;
+
+ /* step through arguments */
+ for (ctrl=0; argc-- > 0; ++argv) {
+
+ /* generic options */
+
+ if (!strcmp(*argv,"debug"))
+ ctrl |= PAM_ST_DEBUG;
+ else if (!strcmp(*argv,"no_warn"))
+ ctrl |= PAM_ST_NO_WARN;
+ else if (!strcmp(*argv,"use_first_pass"))
+ ctrl |= PAM_ST_USE_PASS1;
+ else if (!strcmp(*argv,"try_first_pass"))
+ ctrl |= PAM_ST_TRY_PASS1;
+ else if (!strcmp(*argv,"rootok"))
+ ctrl |= PAM_ST_ROOTOK;
+
+ /* simulation options */
+
+ else if (!strcmp(*argv,"expired")) /* signal password needs
+ renewal */
+ ctrl |= PAM_ST_EXPIRED;
+ else if (!strcmp(*argv,"fail_1")) /* instruct fn 1 to fail */
+ ctrl |= PAM_ST_FAIL_1;
+ else if (!strcmp(*argv,"fail_2")) /* instruct fn 2 to fail */
+ ctrl |= PAM_ST_FAIL_2;
+ else if (!strcmp(*argv,"prelim")) /* instruct pam_sm_setcred
+ to fail on first call */
+ ctrl |= PAM_ST_PRELIM;
+ else if (!strcmp(*argv,"required")) /* module is fussy about the
+ user being authenticated */
+ ctrl |= PAM_ST_REQUIRE_PWD;
+
+ else {
+ _pam_log(LOG_ERR,"pam_parse: unknown option; %s",*argv);
+ }
+ }
+
+ return ctrl;
+}
+
+static int converse(pam_handle_t *pamh, int nargs
+ , struct pam_message **message
+ , struct pam_response **response)
+{
+ int retval;
+ struct pam_conv *conv;
+
+ if ((retval = pam_get_item(pamh,PAM_CONV,(const void **)&conv))
+ == PAM_SUCCESS) {
+ retval = conv->conv(nargs, (const struct pam_message **) message
+ , response, conv->appdata_ptr);
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_ERR,"(pam_stress) converse returned %d",retval);
+ _pam_log(LOG_ERR,"that is: %s",pam_strerror(pamh, retval));
+ }
+ } else {
+ _pam_log(LOG_ERR,"(pam_stress) converse failed to get pam_conv");
+ }
+
+ return retval;
+}
+
+/* authentication management functions */
+
+static int stress_get_password(pam_handle_t *pamh, int flags
+ , int ctrl, char **password)
+{
+ char *pass;
+
+ if ( (ctrl & (PAM_ST_TRY_PASS1|PAM_ST_USE_PASS1))
+ && (pam_get_item(pamh,PAM_AUTHTOK,(const void **)&pass)
+ == PAM_SUCCESS)
+ && (pass != NULL) ) {
+ pass = _strdup(pass);
+ } else if ((ctrl & PAM_ST_USE_PASS1)) {
+ _pam_log(LOG_WARNING, "pam_stress: no forwarded password");
+ return PAM_PERM_DENIED;
+ } else { /* we will have to get one */
+ struct pam_message msg[1],*pmsg[1];
+ struct pam_response *resp;
+ int retval;
+
+ /* set up conversation call */
+
+ pmsg[0] = &msg[0];
+ msg[0].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[0].msg = "STRESS Password: ";
+ resp = NULL;
+
+ if ((retval = converse(pamh,1,pmsg,&resp)) != PAM_SUCCESS) {
+ return retval;
+ }
+
+ if (resp) {
+ if ((resp[0].resp == NULL) && (ctrl & PAM_ST_DEBUG)) {
+ _pam_log(LOG_DEBUG,
+ "pam_sm_authenticate: NULL authtok given");
+ }
+ if ((flags & PAM_DISALLOW_NULL_AUTHTOK)
+ && resp[0].resp == NULL) {
+ free(resp);
+ return PAM_AUTH_ERR;
+ }
+
+ pass = resp[0].resp; /* remember this! */
+
+ resp[0].resp = NULL;
+ } else if (ctrl & PAM_ST_DEBUG) {
+ _pam_log(LOG_DEBUG,"pam_sm_authenticate: no error reported");
+ _pam_log(LOG_DEBUG,"getting password, but NULL returned!?");
+ return PAM_CONV_ERR;
+ }
+ free(resp);
+ }
+
+ *password = pass; /* this *MUST* be free()'d by this module */
+
+ return PAM_SUCCESS;
+}
+
+/* function to clean up data items */
+
+static void wipe_up(pam_handle_t *pamh, void *data, int error)
+{
+ free(data);
+}
+
+PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ const char *username;
+ int retval=PAM_SUCCESS;
+ char *pass;
+ int ctrl;
+
+ D(("called."));
+
+ ctrl = _pam_parse(argc,argv);
+ _pam_report(ctrl, "pam_sm_authenticate", flags, argc, argv);
+
+ /* try to get the username */
+
+ retval = pam_get_user(pamh, &username, "username: ");
+ if ((ctrl & PAM_ST_DEBUG) && (retval == PAM_SUCCESS)) {
+ _pam_log(LOG_DEBUG, "pam_sm_authenticate: username = %s", username);
+ } else if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_WARNING, "pam_sm_authenticate: failed to get username");
+ return retval;
+ }
+
+ /* now get the password */
+
+ retval = stress_get_password(pamh,flags,ctrl,&pass);
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_WARNING, "pam_sm_authenticate: "
+ "failed to get a password");
+ return retval;
+ }
+
+ /* try to set password item */
+
+ retval = pam_set_item(pamh,PAM_AUTHTOK,pass);
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_WARNING, "pam_sm_authenticate: "
+ "failed to store new password");
+ _pam_overwrite(pass);
+ free(pass);
+ return retval;
+ }
+
+ /* clean up local copy of password */
+
+ _pam_overwrite(pass);
+ free(pass);
+ pass = NULL;
+
+ /* if we are debugging then we print the password */
+
+ if (ctrl & PAM_ST_DEBUG) {
+ (void) pam_get_item(pamh,PAM_AUTHTOK,(const void **)&pass);
+ _pam_log(LOG_DEBUG,
+ "pam_st_authenticate: password entered is: [%s]\n",pass);
+ pass = NULL;
+ }
+
+ /* if we signal a fail for this function then fail */
+
+ if ((ctrl & PAM_ST_FAIL_1) && retval == PAM_SUCCESS)
+ return PAM_PERM_DENIED;
+
+ return retval;
+}
+
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ int ctrl = _pam_parse(argc,argv);
+
+ D(("called. [post parsing]"));
+
+ _pam_report(ctrl, "pam_sm_setcred", flags, argc, argv);
+
+ if (ctrl & PAM_ST_FAIL_2)
+ return PAM_CRED_ERR;
+
+ return PAM_SUCCESS;
+}
+
+/* account management functions */
+
+PAM_EXTERN
+int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ int ctrl = _pam_parse(argc,argv);
+
+ D(("called. [post parsing]"));
+
+ _pam_report(ctrl,"pam_sm_acct_mgmt", flags, argc, argv);
+
+ if (ctrl & PAM_ST_FAIL_1)
+ return PAM_PERM_DENIED;
+ else if (ctrl & PAM_ST_EXPIRED) {
+ void *text = malloc(sizeof("yes")+1);
+ strcpy(text,"yes");
+ pam_set_data(pamh,"stress_new_pwd",text,wipe_up);
+ if (ctrl & PAM_ST_DEBUG) {
+ _pam_log(LOG_DEBUG,"pam_sm_acct_mgmt: need a new password");
+ }
+ return PAM_NEW_AUTHTOK_REQD;
+ }
+
+ return PAM_SUCCESS;
+}
+
+PAM_EXTERN
+int pam_sm_open_session(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ char *username,*service;
+ int ctrl = _pam_parse(argc,argv);
+
+ D(("called. [post parsing]"));
+
+ _pam_report(ctrl,"pam_sm_open_session", flags, argc, argv);
+
+ if ((pam_get_item(pamh, PAM_USER, (const void **) &username)
+ != PAM_SUCCESS)
+ || (pam_get_item(pamh, PAM_SERVICE, (const void **) &service)
+ != PAM_SUCCESS)) {
+ _pam_log(LOG_WARNING,"pam_sm_open_session: for whom?");
+ return PAM_SESSION_ERR;
+ }
+
+ _pam_log(LOG_NOTICE,"pam_stress: opened [%s] session for user [%s]"
+ , service, username);
+
+ if (ctrl & PAM_ST_FAIL_1)
+ return PAM_SESSION_ERR;
+
+ return PAM_SUCCESS;
+}
+
+PAM_EXTERN
+int pam_sm_close_session(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ const char *username,*service;
+ int ctrl = _pam_parse(argc,argv);
+
+ D(("called. [post parsing]"));
+
+ _pam_report(ctrl,"pam_sm_close_session", flags, argc, argv);
+
+ if ((pam_get_item(pamh, PAM_USER, (const void **)&username)
+ != PAM_SUCCESS)
+ || (pam_get_item(pamh, PAM_SERVICE, (const void **)&service)
+ != PAM_SUCCESS)) {
+ _pam_log(LOG_WARNING,"pam_sm_close_session: for whom?");
+ return PAM_SESSION_ERR;
+ }
+
+ _pam_log(LOG_NOTICE,"pam_stress: closed [%s] session for user [%s]"
+ , service, username);
+
+ if (ctrl & PAM_ST_FAIL_2)
+ return PAM_SESSION_ERR;
+
+ return PAM_SUCCESS;
+}
+
+PAM_EXTERN
+int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ int retval;
+ int ctrl = _pam_parse(argc,argv);
+
+ D(("called. [post parsing]"));
+
+ _pam_report(ctrl,"pam_sm_chauthtok", flags, argc, argv);
+
+ /* this function should be called twice by the Linux-PAM library */
+
+ if (flags & PAM_PRELIM_CHECK) { /* first call */
+ if (ctrl & PAM_ST_DEBUG) {
+ _pam_log(LOG_DEBUG,"pam_sm_chauthtok: prelim check");
+ }
+ if (ctrl & PAM_ST_PRELIM)
+ return PAM_TRY_AGAIN;
+
+ return PAM_SUCCESS;
+ } else if (flags & PAM_UPDATE_AUTHTOK) { /* second call */
+ struct pam_message msg[3],*pmsg[3];
+ struct pam_response *resp;
+ const char *text;
+ char *txt=NULL;
+ int i;
+
+ if (ctrl & PAM_ST_DEBUG) {
+ _pam_log(LOG_DEBUG,"pam_sm_chauthtok: alter password");
+ }
+
+ if (ctrl & PAM_ST_FAIL_1)
+ return PAM_AUTHTOK_LOCK_BUSY;
+
+ if ( !(ctrl && PAM_ST_EXPIRED)
+ && (flags & PAM_CHANGE_EXPIRED_AUTHTOK)
+ && (pam_get_data(pamh,"stress_new_pwd",(const void **)&text)
+ != PAM_SUCCESS || strcmp(text,"yes"))) {
+ return PAM_SUCCESS; /* the token has not expired */
+ }
+
+ /* the password should be changed */
+
+ if ((ctrl & PAM_ST_REQUIRE_PWD)
+ && !(getuid() == 0 && (ctrl & PAM_ST_ROOTOK))
+ ) { /* first get old one? */
+ char *pass;
+
+ if (ctrl & PAM_ST_DEBUG) {
+ _pam_log(LOG_DEBUG
+ ,"pam_sm_chauthtok: getting old password");
+ }
+ retval = stress_get_password(pamh,flags,ctrl,&pass);
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_DEBUG
+ ,"pam_sm_chauthtok: no password obtained");
+ return retval;
+ }
+ retval = pam_set_item(pamh, PAM_OLDAUTHTOK, pass);
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_DEBUG
+ ,"pam_sm_chauthtok: could not set OLDAUTHTOK");
+ _pam_overwrite(pass);
+ free(pass);
+ return retval;
+ }
+ _pam_overwrite(pass);
+ free(pass);
+ }
+
+ /* set up for conversation */
+
+ if (!(flags & PAM_SILENT)) {
+ char *username;
+
+ if ( pam_get_item(pamh, PAM_USER, (const void **)&username)
+ || username == NULL ) {
+ _pam_log(LOG_ERR,"no username set");
+ return PAM_USER_UNKNOWN;
+ }
+ pmsg[0] = &msg[0];
+ msg[0].msg_style = PAM_TEXT_INFO;
+#define _LOCAL_STRESS_COMMENT "Changing STRESS password for "
+ txt = (char *) malloc(sizeof(_LOCAL_STRESS_COMMENT)
+ +strlen(username)+1);
+ strcpy(txt, _LOCAL_STRESS_COMMENT);
+#undef _LOCAL_STRESS_COMMENT
+ strcat(txt, username);
+ msg[0].msg = txt;
+ i = 1;
+ } else {
+ i = 0;
+ }
+
+ pmsg[i] = &msg[i];
+ msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[i++].msg = "Enter new STRESS password: ";
+ pmsg[i] = &msg[i];
+ msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[i++].msg = "Retype new STRESS password: ";
+ resp = NULL;
+
+ retval = converse(pamh,i,pmsg,&resp);
+ if (txt) {
+ free(txt);
+ txt = NULL; /* clean up */
+ }
+ if (retval != PAM_SUCCESS) {
+ return retval;
+ }
+
+ if (resp == NULL) {
+ _pam_log(LOG_ERR, "pam_sm_chauthtok: no response from conv");
+ return PAM_CONV_ERR;
+ }
+
+ /* store the password */
+
+ if (resp[i-2].resp && resp[i-1].resp) {
+ if (strcmp(resp[i-2].resp,resp[i-1].resp)) {
+ /* passwords are not the same; forget and return error */
+
+ _pam_drop_reply(resp, i);
+
+ if (!(flags & PAM_SILENT) && !(ctrl & PAM_ST_NO_WARN)) {
+ pmsg[0] = &msg[0];
+ msg[0].msg_style = PAM_ERROR_MSG;
+ msg[0].msg = "Verification mis-typed; "
+ "password unchaged";
+ resp = NULL;
+ (void) converse(pamh,1,pmsg,&resp);
+ if (resp) {
+ _pam_drop_reply(resp, 1);
+ }
+ }
+ return PAM_AUTHTOK_ERR;
+ }
+
+ if (pam_get_item(pamh,PAM_AUTHTOK,(const void **)&text)
+ == PAM_SUCCESS) {
+ (void) pam_set_item(pamh,PAM_OLDAUTHTOK,text);
+ text = NULL;
+ }
+ (void) pam_set_item(pamh,PAM_AUTHTOK,resp[0].resp);
+ } else {
+ _pam_log(LOG_DEBUG,"pam_sm_chauthtok: problem with resp");
+ retval = PAM_SYSTEM_ERR;
+ }
+
+ _pam_drop_reply(resp, i); /* clean up the passwords */
+ } else {
+ _pam_log(LOG_ERR,"pam_sm_chauthtok: this must be a Linux-PAM error");
+ return PAM_SYSTEM_ERR;
+ }
+
+ return retval;
+}
+
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_stress_modstruct = {
+ "pam_stress",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ pam_sm_acct_mgmt,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ pam_sm_chauthtok
+};
+
+#endif
diff --git a/modules/pam_tally/.cvsignore b/modules/pam_tally/.cvsignore
new file mode 100644
index 00000000..e1a4f48f
--- /dev/null
+++ b/modules/pam_tally/.cvsignore
@@ -0,0 +1,2 @@
+dynamic
+pam_tally
diff --git a/modules/pam_tally/Makefile b/modules/pam_tally/Makefile
new file mode 100644
index 00000000..78ce621d
--- /dev/null
+++ b/modules/pam_tally/Makefile
@@ -0,0 +1,109 @@
+#
+# $Id$
+#
+# This Makefile controls a build process of $(TITLE) module and
+# application for Linux-PAM. You should not modify this Makefile
+# (unless you know what you are doing!).
+#
+#
+
+TITLE=pam_tally
+
+#
+## Additional rules for making (and moving) the application added.
+## Assuming that all modules' applications are called $TITLE
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+APPSRC = $(TITLE)_app.c
+APPOBJ = $(TITLE)_app.o
+APPOBJD = $(addprefix dynamic/,$(APPOBJ))
+APPOBJS = $(addprefix static/,$(APPOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+APPLICATION = $(TITLE)
+APPMODE = 755
+
+####################### don't edit below #######################
+
+dummy:
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+# we're not yet in a position to build this. If you want it, build it
+# separately...
+# $(APPLICATION)
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD)
+
+$(APPLICATION): $(APPOBJD)
+ $(CC) $(CFLAGS) -o $@ $< $(LOADLIBES)
+
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+
+$(APPLICATION): $(APPOBJS)
+ $(CC) $(CFLAGS) -o $@ $< $(LOADLIBES)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+ $(MKDIR) $(FAKEROOT)$(SUPLEMENTED)
+# $(INSTALL) -m $(APPMODE) $(APPLICATION) $(FAKEROOT)$(SUPLEMENTED)
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+ rm -f $(FAKEROOT)$(SUPLEMENTED)/$(TITLE)
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) $(APPOBJD) $(APPOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak dynamic/* static/* $(APPLICATION)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
diff --git a/modules/pam_tally/README b/modules/pam_tally/README
new file mode 100644
index 00000000..b58b24e4
--- /dev/null
+++ b/modules/pam_tally/README
@@ -0,0 +1,95 @@
+SUMMARY:
+ pam_tally:
+
+ Maintains a count of attempted accesses, can reset count on success,
+ can deny access if too many attempts fail.
+
+ Options:
+
+ * onerr=[succeed|fail] (if something weird happens
+ such as unable to open the file, what to do?)
+ * file=/where/to/keep/counts (default /var/log/faillog)
+
+ (auth)
+ Authentication phase increments attempted login counter.
+ * no_magic_root (root DOES increment counter. Use for
+ daemon-based stuff, like telnet/rsh/login)
+
+ (account)
+ Account phase can deny access and/or reset attempts counter.
+ * deny=n (deny access if tally for this user exceeds n;
+ The presence of deny=n changes the default for
+ reset/no_reset to reset, unless the user trying to
+ gain access is root and the no_magic_root option
+ has NOT been specified.)
+
+ * no_magic_root (access attempts by root DON'T ignore deny.
+ Use this for daemon-based stuff, like telnet/rsh/login)
+ * even_deny_root_account (Root can become unavailable. BEWARE.
+ Note that magic root trying to gain root bypasses this,
+ but normal users can be locked out.)
+
+ * reset (reset count to 0 on successful entry, even for
+ magic root)
+ * no_reset (don't reset count on successful entry)
+ This is the default unless deny exists and the
+ user attempting access is NOT magic root.
+
+ * per_user (If /var/log/faillog contains a non-zero
+ .fail_max field for this user then use it
+ instead of deny=n parameter)
+
+ * no_lock_time (Don't use .fail_locktime filed in
+ /var/log/faillog for this user)
+
+ Also checks to make sure that the counts file is a plain
+ file and not world writable.
+
+ - Tim Baverstock <warwick@sable.demon.co.uk>, v0.1 5 March 1997
+
+LONGER:
+
+pam_tally comes in two parts: pam_tally.so and pam_tally.
+
+pam_tally.so sits in a pam config file, in the auth and account sections.
+
+In the auth section, it increments a per-uid counter for each attempted
+login, in the account section, it denies access if attempted logins
+exceed some threashold and/or resets that counter to zero on successful
+login.
+
+Root is treated specially:
+
+1. When a process already running as root tries to access some service, the
+access is `magic', and bypasses pam_tally's checks: handy for `su'ing from
+root into an account otherwise blocked. However, for services like telnet or
+login which always effectively run from the root account, root (ie everyone)
+shouldn't be granted this magic status, and the flag `no_magic_root' should
+be set in this situation, as noted in the summary above. [This option may
+be obsolete, with `sufficient root' processing.]
+
+2. Normally, failed attempts to access root will NOT cause the root
+account to become blocked, to prevent denial-of-service: if your users aren't
+given shell accounts and root may only login via `su' or at the machine
+console (not telnet/rsh, etc), this is safe. If you really want root to be
+blocked for some given service, use even_deny_root_account.
+
+pam_tally is an (optional) application which can be used to interrogate and
+manipulate the counter file. It can display users' counts, set individual
+counts, or clear all counts. Setting artificially high counts may be useful
+for blocking users without changing their passwords. I found it useful to
+clear all counts every midnight from a cron..
+
+The counts file is organised as a binary-word array, indexed by uid. You
+can probably make sense of it with `od', if you don't want to use the
+supplied appliction.
+
+BUGS:
+
+pam_tally is very dependant on getpw*(): a database of usernames
+would be much more flexible.
+
+The (4.0 Redhat) utilities seem to do funny things with uid, and I'm
+not wholly sure I understood what I should have been doing anyway so
+the `keep a count of current logins' bit has been #ifdef'd out and you
+can only reset the counter on successful authentication, for now.
diff --git a/modules/pam_tally/faillog.h b/modules/pam_tally/faillog.h
new file mode 100644
index 00000000..7f704713
--- /dev/null
+++ b/modules/pam_tally/faillog.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 1989 - 1994, Julianne Frances Haugh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH 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 JULIE HAUGH 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.
+ */
+
+/*
+ * 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 occured here */
+ time_t fail_time; /* last failure occured 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/modules/pam_tally/pam_tally.c b/modules/pam_tally/pam_tally.c
new file mode 100644
index 00000000..ff75697d
--- /dev/null
+++ b/modules/pam_tally/pam_tally.c
@@ -0,0 +1,689 @@
+/*
+ * pam_tally.c
+ *
+ * $Id$
+ */
+
+
+/* By Tim Baverstock <warwick@mmm.co.uk>, Multi Media Machine Ltd.
+ * 5 March 1997
+ *
+ * Stuff stolen from pam_rootok and pam_listfile
+ */
+
+#ifdef linux
+# define _GNU_SOURCE
+# include <features.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <pwd.h>
+#include <time.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include "faillog.h"
+
+#ifndef TRUE
+#define TRUE 1L
+#define FALSE 0L
+#endif
+
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_AUTH
+#define PAM_SM_ACCOUNT
+/* #define PAM_SM_SESSION */
+/* #define PAM_SM_PASSWORD */
+
+#include <security/pam_modules.h>
+
+/*---------------------------------------------------------------------*/
+
+#define DEFAULT_LOGFILE "/var/log/faillog"
+#define MODULE_NAME "pam_tally"
+
+enum TALLY_RESET {
+ TALLY_RESET_DEFAULT,
+ TALLY_RESET_RESET,
+ TALLY_RESET_NO_RESET
+};
+
+#define tally_t unsigned short int
+#define TALLY_FMT "%hu"
+#define TALLY_HI ((tally_t)~0L)
+
+#define UID_FMT "%hu"
+
+#ifndef FILENAME_MAX
+# define FILENAME_MAX MAXPATHLEN
+#endif
+
+static struct faillog faillog;
+static time_t fail_time;
+
+/*---------------------------------------------------------------------*/
+
+/* some syslogging */
+
+static void _pam_log(int err, const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+
+#ifdef MAIN
+ vfprintf(stderr,format,args);
+#else
+ openlog(MODULE_NAME, LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ closelog();
+#endif
+ va_end(args);
+}
+
+/*---------------------------------------------------------------------*/
+
+/* --- Support function: get uid (and optionally username) from PAM or
+ cline_user --- */
+
+#ifdef MAIN
+static char *cline_user=0; /* cline_user is used in the administration prog */
+#endif
+
+static int pam_get_uid( pam_handle_t *pamh, uid_t *uid, const char **userp )
+ {
+ const char *user;
+ struct passwd *pw;
+
+#ifdef MAIN
+ user = cline_user;
+#else
+ pam_get_user( pamh, &user, NULL );
+#endif
+
+ if ( !user || !*user ) {
+ _pam_log(LOG_ERR, MODULE_NAME ": pam_get_uid; user?");
+ return PAM_AUTH_ERR;
+ }
+
+ if ( ! ( pw = getpwnam( user ) ) ) {
+ _pam_log(LOG_ERR,MODULE_NAME ": pam_get_uid; no such user %s",user);
+ return PAM_USER_UNKNOWN;
+ }
+
+ if ( uid ) *uid = pw->pw_uid;
+ if ( userp ) *userp = user;
+ return PAM_SUCCESS;
+ }
+
+/*---------------------------------------------------------------------*/
+
+/* --- Support function: open/create tallyfile and return tally for uid --- */
+
+/* If on entry *tally==TALLY_HI, tallyfile is opened READONLY */
+/* Otherwise, if on entry tallyfile doesn't exist, creation is attempted. */
+
+static int get_tally( tally_t *tally,
+ uid_t uid,
+ const char *filename,
+ FILE **TALLY )
+ {
+ struct stat fileinfo;
+ int lstat_ret = lstat(filename,&fileinfo);
+
+ if ( lstat_ret && *tally!=TALLY_HI ) {
+ int oldmask = umask(077);
+ *TALLY=fopen(filename, "a");
+ /* Create file, or append-open in pathological case. */
+ umask(oldmask);
+ if ( !*TALLY ) {
+ _pam_log(LOG_ALERT, "Couldn't create %s",filename);
+ return PAM_AUTH_ERR;
+ }
+ lstat_ret = fstat(fileno(*TALLY),&fileinfo);
+ fclose(*TALLY);
+ }
+
+ if ( lstat_ret ) {
+ _pam_log(LOG_ALERT, "Couldn't stat %s",filename);
+ return PAM_AUTH_ERR;
+ }
+
+ if((fileinfo.st_mode & S_IWOTH) || !S_ISREG(fileinfo.st_mode)) {
+ /* If the file is world writable or is not a
+ normal file, return error */
+ _pam_log(LOG_ALERT,
+ "%s is either world writable or not a normal file",
+ filename);
+ return PAM_AUTH_ERR;
+ }
+
+ if ( ! ( *TALLY = fopen(filename,(*tally!=TALLY_HI)?"r+":"r") ) ) {
+ _pam_log(LOG_ALERT, "Error opening %s for update", filename);
+
+/* Discovering why account service fails: e/uid are target user.
+ *
+ * perror(MODULE_NAME);
+ * fprintf(stderr,"uid %d euid %d\n",getuid(), geteuid());
+ */
+ return PAM_AUTH_ERR;
+ }
+
+ if ( fseek( *TALLY, uid * sizeof faillog, SEEK_SET ) ) {
+ _pam_log(LOG_ALERT, "fseek failed %s", filename);
+ return PAM_AUTH_ERR;
+ }
+
+
+ if ( ( fread((char *) &faillog, sizeof faillog, 1, *TALLY) )==0 ) {
+ *tally=0; /* Assuming a gappy filesystem */
+ }
+ *tally = faillog.fail_cnt;
+
+ return PAM_SUCCESS;
+ }
+
+/*---------------------------------------------------------------------*/
+
+/* --- Support function: update and close tallyfile with tally!=TALLY_HI --- */
+
+static int set_tally( tally_t tally,
+ uid_t uid,
+ const char *filename,
+ FILE **TALLY )
+ {
+ if ( tally!=TALLY_HI )
+ {
+ if ( fseek( *TALLY, uid * sizeof faillog, SEEK_SET ) ) {
+ _pam_log(LOG_ALERT, "fseek failed %s", filename);
+ return PAM_AUTH_ERR;
+ }
+ faillog.fail_cnt = tally;
+ if ( fwrite((char *) &faillog, sizeof faillog, 1, *TALLY)==0 ) {
+ _pam_log(LOG_ALERT, "tally update (fputc) failed.", filename);
+ return PAM_AUTH_ERR;
+ }
+ }
+
+ if ( fclose(*TALLY) ) {
+ _pam_log(LOG_ALERT, "tally update (fclose) failed.", filename);
+ return PAM_AUTH_ERR;
+ }
+ *TALLY=NULL;
+ return PAM_SUCCESS;
+ }
+
+/*---------------------------------------------------------------------*/
+
+/* --- PAM bits --- */
+
+#ifndef MAIN
+
+#define PAM_FUNCTION(name) \
+ PAM_EXTERN int name (pam_handle_t *pamh,int flags,int argc,const char **argv)
+
+#define RETURN_ERROR(i) return ((fail_on_error)?(i):(PAM_SUCCESS))
+
+/*---------------------------------------------------------------------*/
+
+/* --- tally bump function: bump tally for uid by (signed) inc --- */
+
+static int tally_bump (int inc,
+ pam_handle_t *pamh,
+ int flags,
+ int argc,
+ const char **argv) {
+ uid_t uid;
+
+ int
+ fail_on_error = FALSE;
+ tally_t
+ tally = 0; /* !TALLY_HI --> Log opened for update */
+
+ char
+ no_magic_root = FALSE;
+
+ char
+ filename[ FILENAME_MAX ] = DEFAULT_LOGFILE;
+
+ /* Should probably decode the parameters before anything else. */
+
+ {
+ for ( ; argc-- > 0; ++argv ) {
+
+ /* generic options.. um, ignored. :] */
+
+ if ( ! strcmp( *argv, "no_magic_root" ) ) {
+ no_magic_root = TRUE;
+ }
+ else if ( ! strncmp( *argv, "file=", 5 ) ) {
+ char const
+ *from = (*argv)+5;
+ char
+ *to = filename;
+ if ( *from!='/' || strlen(from)>FILENAME_MAX-1 ) {
+ _pam_log(LOG_ERR,
+ MODULE_NAME ": filename not /rooted or too long; ",
+ *argv);
+ RETURN_ERROR( PAM_AUTH_ERR );
+ }
+ while ( ( *to++ = *from++ ) );
+ }
+ else if ( ! strcmp( *argv, "onerr=fail" ) ) {
+ fail_on_error=TRUE;
+ }
+ else if ( ! strcmp( *argv, "onerr=succeed" ) ) {
+ fail_on_error=FALSE;
+ }
+ else {
+ _pam_log(LOG_ERR, MODULE_NAME ": unknown option; %s",*argv);
+ }
+ } /* for() */
+ }
+
+ {
+ FILE
+ *TALLY = NULL;
+ const char
+ *user = NULL,
+ *remote_host = NULL;
+
+ int i=pam_get_uid(pamh, &uid, &user);
+ if ( i != PAM_SUCCESS ) RETURN_ERROR( i );
+
+ i=get_tally( &tally, uid, filename, &TALLY );
+ fail_time = faillog.fail_time; /* to remember old fail time (for locktime) */
+ faillog.fail_time = (time_t) time( (time_t *) 0);
+ (void) pam_get_item(pamh, PAM_RHOST, (const void **)&remote_host);
+ if (!remote_host)
+ {
+ strcpy(faillog.fail_line, "unknown");
+ }
+ else
+ {
+ strncpy(faillog.fail_line, remote_host, (size_t)sizeof(faillog.fail_line));
+ faillog.fail_line[sizeof(faillog.fail_line)-1] = 0;
+ }
+ if ( i != PAM_SUCCESS ) { if (TALLY) fclose(TALLY); RETURN_ERROR( i ); }
+
+ if ( no_magic_root || getuid() ) { /* no_magic_root kills uid test */
+
+ tally+=inc;
+
+ if ( tally==TALLY_HI ) { /* Overflow *and* underflow. :) */
+ tally-=inc;
+ _pam_log(LOG_ALERT,"Tally %sflowed for user %s",
+ (inc<0)?"under":"over",user);
+ }
+ }
+
+ i=set_tally( tally, uid, filename, &TALLY );
+ if ( i != PAM_SUCCESS ) { if (TALLY) fclose(TALLY); RETURN_ERROR( i ); }
+ }
+
+ return PAM_SUCCESS;
+}
+
+/*---------------------------------------------------------------------*/
+
+/* --- authentication management functions (only) --- */
+
+#ifdef PAM_SM_AUTH
+
+PAM_FUNCTION( pam_sm_authenticate ) {
+ return tally_bump( 1, pamh, flags, argc, argv);
+}
+
+/* --- Seems to need this function. Ho hum. --- */
+
+PAM_FUNCTION( pam_sm_setcred ) { return PAM_SUCCESS; }
+
+#endif
+
+/*---------------------------------------------------------------------*/
+
+/* --- session management functions (only) --- */
+
+/*
+ * Unavailable until .so files can be suid
+ */
+
+#ifdef PAM_SM_SESSION
+
+/* To maintain a balance-tally of successful login/outs */
+
+PAM_FUNCTION( pam_sm_open_session ) {
+ return tally_bump( 1, pamh, flags, argc, argv);
+}
+
+PAM_FUNCTION( pam_sm_close_session ) {
+ return tally_bump(-1, pamh, flags, argc, argv);
+}
+
+#endif
+
+/*---------------------------------------------------------------------*/
+
+/* --- authentication management functions (only) --- */
+
+#ifdef PAM_SM_AUTH
+
+/* To lock out a user with an unacceptably high tally */
+
+PAM_FUNCTION( pam_sm_acct_mgmt ) {
+ uid_t
+ uid;
+
+ int
+ fail_on_error = FALSE;
+ tally_t
+ deny = 0;
+ tally_t
+ tally = 0; /* !TALLY_HI --> Log opened for update */
+
+ char
+ no_magic_root = FALSE,
+ even_deny_root_account = FALSE;
+ char per_user = FALSE; /* if true then deny=.fail_max for user */
+ char no_lock_time = FALSE; /* if true then don't use .fail_locktime */
+
+ const char
+ *user = NULL;
+
+ enum TALLY_RESET
+ reset = TALLY_RESET_DEFAULT;
+
+ char
+ filename[ FILENAME_MAX ] = DEFAULT_LOGFILE;
+
+ /* Should probably decode the parameters before anything else. */
+
+ {
+ for ( ; argc-- > 0; ++argv ) {
+
+ /* generic options.. um, ignored. :] */
+
+ if ( ! strcmp( *argv, "no_magic_root" ) ) {
+ no_magic_root = TRUE;
+ }
+ else if ( ! strcmp( *argv, "even_deny_root_account" ) ) {
+ even_deny_root_account = TRUE;
+ }
+ else if ( ! strcmp( *argv, "reset" ) ) {
+ reset = TALLY_RESET_RESET;
+ }
+ else if ( ! strcmp( *argv, "no_reset" ) ) {
+ reset = TALLY_RESET_NO_RESET;
+ }
+ else if ( ! strncmp( *argv, "file=", 5 ) ) {
+ char const
+ *from = (*argv)+5;
+ char
+ *to = filename;
+ if ( *from != '/' || strlen(from) > FILENAME_MAX-1 ) {
+ _pam_log(LOG_ERR,
+ MODULE_NAME ": filename not /rooted or too long; ",
+ *argv);
+ RETURN_ERROR( PAM_AUTH_ERR );
+ }
+ while ( ( *to++ = *from++ ) );
+ }
+ else if ( ! strncmp( *argv, "deny=", 5 ) ) {
+ if ( sscanf((*argv)+5,TALLY_FMT,&deny) != 1 ) {
+ _pam_log(LOG_ERR,"bad number supplied; %s",*argv);
+ RETURN_ERROR( PAM_AUTH_ERR );
+ }
+ }
+ else if ( ! strcmp( *argv, "onerr=fail" ) ) {
+ fail_on_error=TRUE;
+ }
+ else if ( ! strcmp( *argv, "onerr=succeed" ) ) {
+ fail_on_error=FALSE;
+ }
+ else if ( ! strcmp( *argv, "per_user" ) )
+ {
+ per_user = TRUE;
+ }
+ else if ( ! strcmp( *argv, "no_lock_time") )
+ {
+ no_lock_time = TRUE;
+ }
+ else {
+ _pam_log(LOG_ERR, MODULE_NAME ": unknown option; %s",*argv);
+ }
+ } /* for() */
+ }
+
+ {
+ FILE *TALLY=0;
+ int i=pam_get_uid(pamh, &uid, &user);
+ if ( i != PAM_SUCCESS ) RETURN_ERROR( i );
+
+ i=get_tally( &tally, uid, filename, &TALLY );
+ if ( i != PAM_SUCCESS ) { if (TALLY) fclose(TALLY); RETURN_ERROR( i ); }
+
+ if ( no_magic_root || getuid() ) { /* no_magic_root kills uid test */
+
+ /* To deny or not to deny; that is the question */
+
+ /* if there's .fail_max entry and per_user=TRUE then deny=.fail_max */
+
+ if ( (faillog.fail_max) && (per_user) ) {deny = faillog.fail_max;}
+ if (faillog.fail_locktime && fail_time && (!no_lock_time) )
+ {
+ if ( (faillog.fail_locktime + fail_time) > (time_t)time((time_t)0) )
+ {
+ _pam_log(LOG_NOTICE,"user %s ("UID_FMT") has time limit [%lds left] since last failure.",user,uid,fail_time+faillog.fail_locktime-(time_t)time((time_t)0));
+ return PAM_AUTH_ERR;
+ }
+ }
+ if (
+ ( deny != 0 ) && /* deny==0 means no deny */
+ ( tally > deny ) && /* tally>deny means exceeded */
+ ( even_deny_root_account || uid ) /* even_deny stops uid check */
+ ) {
+ _pam_log(LOG_NOTICE,"user %s ("UID_FMT") tally "TALLY_FMT", deny "TALLY_FMT,
+ user, uid, tally, deny);
+ return PAM_AUTH_ERR; /* Only unconditional failure */
+ }
+
+ /* resets for explicit reset
+ * or by default if deny exists and not magic-root
+ */
+
+ if ( ( reset == TALLY_RESET_RESET ) ||
+ ( reset == TALLY_RESET_DEFAULT && deny ) ) { tally=0; }
+ }
+ else /* is magic root */ {
+
+ /* Magic root skips deny test... */
+
+ /* Magic root only resets on explicit reset, regardless of deny */
+
+ if ( reset == TALLY_RESET_RESET ) { tally=0; }
+ }
+ if (tally == 0)
+ {
+ faillog.fail_time = (time_t) 0;
+ strcpy(faillog.fail_line, "");
+ }
+ i=set_tally( tally, uid, filename, &TALLY );
+ if ( i != PAM_SUCCESS ) { if (TALLY) fclose(TALLY); RETURN_ERROR( i ); }
+ }
+
+ return PAM_SUCCESS;
+}
+
+#endif /* #ifdef PAM_SM_AUTH */
+
+/*-----------------------------------------------------------------------*/
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_tally_modstruct = {
+ MODULE_NAME,
+#ifdef PAM_SM_AUTH
+ pam_sm_authenticate,
+ pam_sm_setcred,
+#else
+ NULL,
+ NULL,
+#endif
+#ifdef PAM_SM_ACCOUNT
+ pam_sm_acct_mgmt,
+#else
+ NULL,
+#endif
+#ifdef PAM_SM_SESSION
+ pam_sm_open_session,
+ pam_sm_close_session,
+#else
+ NULL,
+ NULL,
+#endif
+#ifdef PAM_SM_PASSWORD
+ pam_sm_chauthtok,
+#else
+ NULL,
+#endif
+};
+
+#endif /* #ifdef PAM_STATIC */
+
+/*-----------------------------------------------------------------------*/
+
+#else /* #ifndef MAIN */
+
+static const char *cline_filename = DEFAULT_LOGFILE;
+static tally_t cline_reset = TALLY_HI; /* Default is `interrogate only' */
+static int cline_quiet = 0;
+
+/*
+ * Not going to link with pamlib just for these.. :)
+ */
+
+static const char * pam_errors( int i ) {
+ switch (i) {
+ case PAM_AUTH_ERR: return "Authentication error";
+ case PAM_SERVICE_ERR: return "Service error";
+ case PAM_USER_UNKNOWN: return "Unknown user";
+ default: return "Unknown error";
+ }
+}
+
+static int getopts( int argc, char **argv ) {
+ const char *pname = *argv;
+ for ( ; *argv ; (void)(*argv && ++argv) ) {
+ if ( !strcmp (*argv,"--file") ) cline_filename=*++argv;
+ else if ( !strncmp(*argv,"--file=",7) ) cline_filename=*argv+7;
+ else if ( !strcmp (*argv,"--user") ) cline_user=*++argv;
+ else if ( !strncmp(*argv,"--user=",7) ) cline_user=*argv+7;
+ else if ( !strcmp (*argv,"--reset") ) cline_reset=0;
+ else if ( !strncmp(*argv,"--reset=",8)) {
+ if ( sscanf(*argv+8,TALLY_FMT,&cline_reset) != 1 )
+ fprintf(stderr,"%s: Bad number given to --reset=\n",pname), exit(0);
+ }
+ else if ( !strcmp (*argv,"--quiet") ) cline_quiet=1;
+ else {
+ fprintf(stderr,"%s: Unrecognised option %s\n",pname,*argv);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+int main ( int argc, char **argv ) {
+
+ if ( ! getopts( argc, argv+1 ) ) {
+ printf("%s: [--file rooted-filename] [--user username] "
+ "[--reset[=n]] [--quiet]\n",
+ *argv);
+ exit(0);
+ }
+
+ umask(077);
+
+ /*
+ * Major difference between individual user and all users:
+ * --user just handles one user, just like PAM.
+ * --user=* handles all users, sniffing cline_filename for nonzeros
+ */
+
+ if ( cline_user ) {
+ uid_t uid;
+ tally_t tally=cline_reset;
+ FILE *TALLY=0;
+ int i=pam_get_uid( NULL, &uid, NULL);
+ if ( i != PAM_SUCCESS ) {
+ fprintf(stderr,"%s: %s\n",*argv,pam_errors(i));
+ exit(0);
+ }
+
+ i=get_tally( &tally, uid, cline_filename, &TALLY );
+ if ( i != PAM_SUCCESS ) {
+ if (TALLY) fclose(TALLY);
+ fprintf(stderr,"%s: %s\n",*argv,pam_errors(i));
+ exit(0);
+ }
+
+ if ( !cline_quiet )
+ printf("User %s\t("UID_FMT")\t%s "TALLY_FMT"\n",cline_user,uid,
+ (cline_reset!=TALLY_HI)?"had":"has",tally);
+
+ i=set_tally( cline_reset, uid, cline_filename, &TALLY );
+ if ( i != PAM_SUCCESS ) {
+ if (TALLY) fclose(TALLY);
+ fprintf(stderr,"%s: %s\n",*argv,pam_errors(i));
+ exit(0);
+ }
+ }
+ else /* !cline_user (ie, operate on all users) */ {
+ FILE *TALLY=fopen(cline_filename, "r");
+ uid_t uid=0;
+ if ( !TALLY ) perror(*argv), exit(0);
+
+ for ( ; !feof(TALLY); uid++ ) {
+ tally_t tally;
+ struct passwd *pw;
+ if ( ! fread((char *) &faillog, sizeof faillog, 1, TALLY) || ! faillog.fail_cnt ) {
+ tally=faillog.fail_cnt;
+ continue;
+ }
+ tally = faillog.fail_cnt;
+
+ if ( ( pw=getpwuid(uid) ) ) {
+ printf("User %s\t("UID_FMT")\t%s "TALLY_FMT"\n",pw->pw_name,uid,
+ (cline_reset!=TALLY_HI)?"had":"has",tally);
+ }
+ else {
+ printf("User [NONAME]\t("UID_FMT")\t%s "TALLY_FMT"\n",uid,
+ (cline_reset!=TALLY_HI)?"had":"has",tally);
+ }
+ }
+ fclose(TALLY);
+ if ( cline_reset!=0 && cline_reset!=TALLY_HI ) {
+ fprintf(stderr,"%s: Can't reset all users to non-zero\n",*argv);
+ }
+ else if ( !cline_reset ) {
+ TALLY=fopen(cline_filename, "w");
+ if ( !TALLY ) perror(*argv), exit(0);
+ fclose(TALLY);
+ }
+ }
+ return 0;
+}
+
+
+#endif
diff --git a/modules/pam_tally/pam_tally_app.c b/modules/pam_tally/pam_tally_app.c
new file mode 100644
index 00000000..9e6e1faf
--- /dev/null
+++ b/modules/pam_tally/pam_tally_app.c
@@ -0,0 +1,7 @@
+/*
+ # This seemed like such a good idea at the time. :)
+ */
+
+#define MAIN
+#include "pam_tally.c"
+
diff --git a/modules/pam_time/.cvsignore b/modules/pam_time/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_time/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_time/Makefile b/modules/pam_time/Makefile
new file mode 100644
index 00000000..03105bb1
--- /dev/null
+++ b/modules/pam_time/Makefile
@@ -0,0 +1,98 @@
+#
+# $Id$
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# Created by Andrew Morgan <morgan@linux.kernel.org> 1996/6/11
+#
+
+TITLE=pam_time
+CONFD=$(CONFIGED)/security
+export CONFD
+CONFILE=$(CONFD)/time.conf
+export CONFILE
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+DEFS=-DCONFILE=\"$(CONFILE)\"
+
+CFLAGS += $(DEFS)
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ifdef DYNAMIC
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+ $(MKDIR) $(FAKEROOT)$(SCONFIGED)
+ bash -f ./install_conf
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+ rm -f $(FAKEROOT)$(CONFILE)
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+ rm -f ./.ignore_age
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_time/README b/modules/pam_time/README
new file mode 100644
index 00000000..dcbe35e4
--- /dev/null
+++ b/modules/pam_time/README
@@ -0,0 +1,43 @@
+$Id$
+
+This is a help file for the pam_time module. It explains the need for
+pam_time and also the syntax of the /etc/security/time.conf file.
+[a lot of the syntax is freely adapted from the porttime file of the
+shadow suite.]
+
+1. Introduction
+===============
+
+It is desirable to restrict access to a system and or specific
+applications at various times of the day and on specific days or over
+various terminal lines.
+
+The pam_time module is intended to offer a configurable module that
+satisfies this purpose, within the context of Linux-PAM.
+
+2. the /etc/security/time.conf file
+===================================
+
+This file is the configuration script for defining time/port access
+control to the system/applications.
+
+Its syntax is described in the sample ./time.conf provided in this
+directory.
+
+unrecognised rules are ignored (but an error is logged to syslog(3))
+
+--------------------
+Bugs to Andrew <morgan@parc.power.net> or the list <pam-list@redhat.com>
+
+########################################################################
+# $Log$
+# Revision 1.1 2000/06/20 22:12:00 agmorgan
+# Initial revision
+#
+# Revision 1.1.1.1 1998/07/12 05:17:16 morgan
+# Linux PAM sources pre-0.66
+#
+# Revision 1.3 1997/01/04 20:42:43 morgan
+# I want email on parc now
+#
+# \ No newline at end of file
diff --git a/modules/pam_time/install_conf b/modules/pam_time/install_conf
new file mode 100755
index 00000000..051d8b70
--- /dev/null
+++ b/modules/pam_time/install_conf
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+CONFILE=$FAKEROOT"$CONFILE"
+IGNORE_AGE=./.ignore_age
+QUIET_INSTALL=../../.quiet_install
+CONF=./time.conf
+MODULE=pam_time
+
+echo
+
+if [ -f "$QUIET_INSTALL" ]; then
+ if [ ! -f "$CONFILE" ]; then
+ yes="y"
+ else
+ yes="skip"
+ fi
+elif [ -f "$IGNORE_AGE" ]; then
+ echo "you don't want to be bothered with the age of your $CONFILE file"
+ yes="n"
+elif [ ! -f "$CONFILE" ] || [ "$CONF" -nt "$CONFILE" ]; then
+ if [ -f "$CONFILE" ]; then
+ echo "An older $MODULE configuration file already exists ($CONFILE)"
+ echo "Do you wish to copy the $CONF file in this distribution"
+ echo "to $CONFILE ? (y/n) [skip] "
+ read yes
+ else
+ yes="y"
+ fi
+else
+ yes="skip"
+fi
+
+if [ "$yes" = "y" ]; then
+ mkdir -p $FAKEROOT$CONFD
+ echo " copying $CONF to $CONFILE"
+ cp $CONF $CONFILE
+else
+ echo " Skipping $CONF installation"
+ if [ "$yes" = "n" ]; then
+ touch "$IGNORE_AGE"
+ fi
+fi
+
+echo
+
+exit 0
diff --git a/modules/pam_time/pam_time.c b/modules/pam_time/pam_time.c
new file mode 100644
index 00000000..2ca51859
--- /dev/null
+++ b/modules/pam_time/pam_time.c
@@ -0,0 +1,612 @@
+/* pam_time module */
+
+/*
+ * $Id$
+ *
+ * Written by Andrew Morgan <morgan@linux.kernel.org> 1996/6/22
+ * (File syntax and much other inspiration from the shadow package
+ * shadow-960129)
+ */
+
+const static char rcsid[] =
+"$Id$;\n"
+"\t\tVersion 0.22 for Linux-PAM\n"
+"Copyright (C) Andrew G. Morgan 1996 <morgan@linux.kernel.org>\n";
+
+#ifdef linux
+# define _GNU_SOURCE
+# include <features.h>
+#endif
+
+#include <sys/file.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <time.h>
+#include <syslog.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define PAM_TIME_CONF CONFILE /* from external define */
+#define PAM_TIME_BUFLEN 1000
+#define FIELD_SEPARATOR ';' /* this is new as of .02 */
+
+typedef enum { FALSE, TRUE } boolean;
+typedef enum { AND, OR } operator;
+
+/*
+ * here, we make definitions for the externally accessible functions
+ * in this file (these definitions are required for static modules
+ * but strongly encouraged generally) they are used to instruct the
+ * modules include file to define their prototypes.
+ */
+
+#define PAM_SM_ACCOUNT
+
+#include <security/_pam_macros.h>
+#include <security/pam_modules.h>
+
+/* --- static functions for checking whether the user should be let in --- */
+
+static void _log_err(const char *format, ... )
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("pam_time", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(LOG_CRIT, format, args);
+ va_end(args);
+ closelog();
+}
+
+static void shift_bytes(char *mem, int from, int by)
+{
+ while (by-- > 0) {
+ *mem = mem[from];
+ ++mem;
+ }
+}
+
+static int read_field(int fd, char **buf, int *from, int *to)
+{
+ /* is buf set ? */
+
+ if (! *buf) {
+ *buf = (char *) malloc(PAM_TIME_BUFLEN);
+ if (! *buf) {
+ _log_err("out of memory");
+ D(("no memory"));
+ return -1;
+ }
+ *from = *to = 0;
+ fd = open(PAM_TIME_CONF, O_RDONLY);
+ }
+
+ /* do we have a file open ? return error */
+
+ if (fd < 0 && *to <= 0) {
+ _log_err( PAM_TIME_CONF " not opened");
+ memset(*buf, 0, PAM_TIME_BUFLEN);
+ _pam_drop(*buf);
+ return -1;
+ }
+
+ /* check if there was a newline last time */
+
+ if ((*to > *from) && (*to > 0)
+ && ((*buf)[*from] == '\0')) { /* previous line ended */
+ (*from)++;
+ (*buf)[0] = '\0';
+ return fd;
+ }
+
+ /* ready for more data: first shift the buffer's remaining data */
+
+ *to -= *from;
+ shift_bytes(*buf, *from, *to);
+ *from = 0;
+ (*buf)[*to] = '\0';
+
+ while (fd >= 0 && *to < PAM_TIME_BUFLEN) {
+ int i;
+
+ /* now try to fill the remainder of the buffer */
+
+ i = read(fd, *to + *buf, PAM_TIME_BUFLEN - *to);
+ if (i < 0) {
+ _log_err("error reading " PAM_TIME_CONF);
+ return -1;
+ } else if (!i) {
+ close(fd);
+ fd = -1; /* end of file reached */
+ } else
+ *to += i;
+
+ /*
+ * contract the buffer. Delete any comments, and replace all
+ * multiple spaces with single commas
+ */
+
+ i = 0;
+#ifdef DEBUG_DUMP
+ D(("buffer=<%s>",*buf));
+#endif
+ while (i < *to) {
+ if ((*buf)[i] == ',') {
+ int j;
+
+ for (j=++i; j<*to && (*buf)[j] == ','; ++j);
+ if (j!=i) {
+ shift_bytes(i + (*buf), j-i, (*to) - j);
+ *to -= j-i;
+ }
+ }
+ switch ((*buf)[i]) {
+ int j,c;
+ case '#':
+ for (j=i; j < *to && (c = (*buf)[j]) != '\n'; ++j);
+ if (j >= *to) {
+ (*buf)[*to = ++i] = '\0';
+ } else if (c == '\n') {
+ shift_bytes(i + (*buf), j-i, (*to) - j);
+ *to -= j-i;
+ ++i;
+ } else {
+ _log_err("internal error in " __FILE__
+ " at line %d", __LINE__ );
+ return -1;
+ }
+ break;
+ case '\\':
+ if ((*buf)[i+1] == '\n') {
+ shift_bytes(i + *buf, 2, *to - (i+2));
+ *to -= 2;
+ }
+ break;
+ case '!':
+ case ' ':
+ case '\t':
+ if ((*buf)[i] != '!')
+ (*buf)[i] = ',';
+ /* delete any trailing spaces */
+ for (j=++i; j < *to && ( (c = (*buf)[j]) == ' '
+ || c == '\t' ); ++j);
+ shift_bytes(i + *buf, j-i, (*to)-j );
+ *to -= j-i;
+ break;
+ default:
+ ++i;
+ }
+ }
+ }
+
+ (*buf)[*to] = '\0';
+
+ /* now return the next field (set the from/to markers) */
+ {
+ int i;
+
+ for (i=0; i<*to; ++i) {
+ switch ((*buf)[i]) {
+ case '#':
+ case '\n': /* end of the line/file */
+ (*buf)[i] = '\0';
+ *from = i;
+ return fd;
+ case FIELD_SEPARATOR: /* end of the field */
+ (*buf)[i] = '\0';
+ *from = ++i;
+ return fd;
+ }
+ }
+ *from = i;
+ (*buf)[*from] = '\0';
+ }
+
+ if (*to <= 0) {
+ D(("[end of text]"));
+ *buf = NULL;
+ }
+
+ return fd;
+}
+
+/* read a member from a field */
+
+static int logic_member(const char *string, int *at)
+{
+ int len,c,to;
+ int done=0;
+ int token=0;
+
+ len=0;
+ to=*at;
+ do {
+ c = string[to++];
+
+ switch (c) {
+
+ case '\0':
+ --to;
+ done = 1;
+ break;
+
+ case '&':
+ case '|':
+ case '!':
+ if (token) {
+ --to;
+ }
+ done = 1;
+ break;
+
+ default:
+ if (isalpha(c) || c == '*' || isdigit(c) || c == '_'
+ || c == '-' || c == '.') {
+ token = 1;
+ } else if (token) {
+ --to;
+ done = 1;
+ } else {
+ ++*at;
+ }
+ }
+ } while (!done);
+
+ return to - *at;
+}
+
+typedef enum { VAL, OP } expect;
+
+static boolean logic_field(const void *me, const char *x, int rule,
+ boolean (*agrees)(const void *, const char *
+ , int, int))
+{
+ boolean left=FALSE, right, not=FALSE;
+ operator oper=OR;
+ int at=0, l;
+ expect next=VAL;
+
+ while ((l = logic_member(x,&at))) {
+ int c = x[at];
+
+ if (next == VAL) {
+ if (c == '!')
+ not = !not;
+ else if (isalpha(c) || c == '*') {
+ right = not ^ agrees(me, x+at, l, rule);
+ if (oper == AND)
+ left &= right;
+ else
+ left |= right;
+ next = OP;
+ } else {
+ _log_err("garbled syntax; expected name (rule #%d)", rule);
+ return FALSE;
+ }
+ } else { /* OP */
+ switch (c) {
+ case '&':
+ oper = AND;
+ break;
+ case '|':
+ oper = OR;
+ break;
+ default:
+ _log_err("garbled syntax; expected & or | (rule #%d)"
+ , rule);
+ D(("%c at %d",c,at));
+ return FALSE;
+ }
+ next = VAL;
+ }
+ at += l;
+ }
+
+ return left;
+}
+
+static boolean is_same(const void *A, const char *b, int len, int rule)
+{
+ int i;
+ const char *a;
+
+ a = A;
+ for (i=0; len > 0; ++i, --len) {
+ if (b[i] != a[i]) {
+ if (b[i++] == '*') {
+ return (!--len || !strncmp(b+i,a+strlen(a)-len,len));
+ } else
+ return FALSE;
+ }
+ }
+ return ( !len );
+}
+
+typedef struct {
+ int day; /* array of 7 bits, one set for today */
+ int minute; /* integer, hour*100+minute for now */
+} TIME;
+
+struct day {
+ const char *d;
+ int bit;
+} static const days[11] = {
+ { "su", 01 },
+ { "mo", 02 },
+ { "tu", 04 },
+ { "we", 010 },
+ { "th", 020 },
+ { "fr", 040 },
+ { "sa", 0100 },
+ { "wk", 076 },
+ { "wd", 0101 },
+ { "al", 0177 },
+ { NULL, 0 }
+};
+
+static TIME time_now(void)
+{
+ struct tm *local;
+ time_t the_time;
+ TIME this;
+
+ the_time = time((time_t *)0); /* get the current time */
+ local = localtime(&the_time);
+ this.day = days[local->tm_wday].bit;
+ this.minute = local->tm_hour*100 + local->tm_min;
+
+ D(("day: 0%o, time: %.4d", this.day, this.minute));
+ return this;
+}
+
+/* take the current date and see if the range "date" passes it */
+static boolean check_time(const void *AT, const char *times, int len, int rule)
+{
+ boolean not,pass;
+ int marked_day, time_start, time_end;
+ const TIME *at;
+ int i,j=0;
+
+ at = AT;
+ D(("chcking: 0%o/%.4d vs. %s", at->day, at->minute, times));
+
+ if (times == NULL) {
+ /* this should not happen */
+ _log_err("internal error: " __FILE__ " line %d", __LINE__);
+ return FALSE;
+ }
+
+ if (times[j] == '!') {
+ ++j;
+ not = TRUE;
+ } else {
+ not = FALSE;
+ }
+
+ for (marked_day = 0; len > 0 && isalpha(times[j]); --len) {
+ int this_day=-1;
+
+ D(("%c%c ?", times[j], times[j+1]));
+ for (i=0; days[i].d != NULL; ++i) {
+ if (tolower(times[j]) == days[i].d[0]
+ && tolower(times[j+1]) == days[i].d[1] ) {
+ this_day = days[i].bit;
+ break;
+ }
+ }
+ j += 2;
+ if (this_day == -1) {
+ _log_err("bad day specified (rule #%d)", rule);
+ return FALSE;
+ }
+ marked_day ^= this_day;
+ }
+ if (marked_day == 0) {
+ _log_err("no day specified");
+ return FALSE;
+ }
+ D(("day range = 0%o", marked_day));
+
+ time_start = 0;
+ for (i=0; len > 0 && i < 4 && isdigit(times[i+j]); ++i, --len) {
+ time_start *= 10;
+ time_start += times[i+j]-'0'; /* is this portable? */
+ }
+ j += i;
+
+ if (times[j] == '-') {
+ time_end = 0;
+ for (i=1; len > 0 && i < 5 && isdigit(times[i+j]); ++i, --len) {
+ time_end *= 10;
+ time_end += times[i+j]-'0'; /* is this portable */
+ }
+ j += i;
+ } else
+ time_end = -1;
+
+ D(("i=%d, time_end=%d, times[j]='%c'", i, time_end, times[j]));
+ if (i != 5 || time_end == -1) {
+ _log_err("no/bad times specified (rule #%d)", rule);
+ return TRUE;
+ }
+ D(("times(%d to %d)", time_start,time_end));
+ D(("marked_day = 0%o", marked_day));
+
+ /* compare with the actual time now */
+
+ pass = FALSE;
+ if (time_start < time_end) { /* start < end ? --> same day */
+ if ((at->day & marked_day) && (at->minute >= time_start)
+ && (at->minute < time_end)) {
+ D(("time is listed"));
+ pass = TRUE;
+ }
+ } else { /* spans two days */
+ if ((at->day & marked_day) && (at->minute >= time_start)) {
+ D(("caught on first day"));
+ pass = TRUE;
+ } else {
+ marked_day <<= 1;
+ marked_day |= (marked_day & 0200) ? 1:0;
+ D(("next day = 0%o", marked_day));
+ if ((at->day & marked_day) && (at->minute <= time_end)) {
+ D(("caught on second day"));
+ pass = TRUE;
+ }
+ }
+ }
+
+ return (not ^ pass);
+}
+
+static int check_account(const char *service
+ , const char *tty, const char *user)
+{
+ int from=0,to=0,fd=-1;
+ char *buffer=NULL;
+ int count=0;
+ TIME here_and_now;
+ int retval=PAM_SUCCESS;
+
+ here_and_now = time_now(); /* find current time */
+ do {
+ boolean good=TRUE,intime;
+
+ /* here we get the service name field */
+
+ fd = read_field(fd,&buffer,&from,&to);
+
+ if (!buffer || !buffer[0]) {
+ /* empty line .. ? */
+ continue;
+ }
+ ++count;
+
+ good = logic_field(service, buffer, count, is_same);
+ D(("with service: %s", good ? "passes":"fails" ));
+
+ /* here we get the terminal name field */
+
+ fd = read_field(fd,&buffer,&from,&to);
+ if (!buffer || !buffer[0]) {
+ _log_err(PAM_TIME_CONF "; no tty entry #%d", count);
+ continue;
+ }
+ good &= logic_field(tty, buffer, count, is_same);
+ D(("with tty: %s", good ? "passes":"fails" ));
+
+ /* here we get the username field */
+
+ fd = read_field(fd,&buffer,&from,&to);
+ if (!buffer || !buffer[0]) {
+ _log_err(PAM_TIME_CONF "; no user entry #%d", count);
+ continue;
+ }
+ good &= logic_field(user, buffer, count, is_same);
+ D(("with user: %s", good ? "passes":"fails" ));
+
+ /* here we get the time field */
+
+ fd = read_field(fd,&buffer,&from,&to);
+ if (!buffer || !buffer[0]) {
+ _log_err(PAM_TIME_CONF "; no time entry #%d", count);
+ continue;
+ }
+
+ intime = logic_field(&here_and_now, buffer, count, check_time);
+ D(("with time: %s", intime ? "passes":"fails" ));
+
+ fd = read_field(fd,&buffer,&from,&to);
+ if (buffer && buffer[0]) {
+ _log_err(PAM_TIME_CONF "; poorly terminated rule #%d", count);
+ continue;
+ }
+
+ if (good && !intime) {
+ /*
+ * for security parse whole file.. also need to ensure
+ * that the buffer is free()'d and the file is closed.
+ */
+ retval = PAM_PERM_DENIED;
+ } else {
+ D(("rule passed"));
+ }
+ } while (buffer);
+
+ return retval;
+}
+
+/* --- public account management functions --- */
+
+PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ const char *service=NULL, *tty=NULL;
+ const char *user=NULL;
+
+ /* set service name */
+
+ if (pam_get_item(pamh, PAM_SERVICE, (const void **)&service)
+ != PAM_SUCCESS || service == NULL) {
+ _log_err("cannot find the current service name");
+ return PAM_ABORT;
+ }
+
+ /* set username */
+
+ if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS || user == NULL
+ || *user == '\0') {
+ _log_err("cannot determine the user's name");
+ return PAM_USER_UNKNOWN;
+ }
+
+ /* set tty name */
+
+ if (pam_get_item(pamh, PAM_TTY, (const void **)&tty) != PAM_SUCCESS
+ || tty == NULL) {
+ D(("PAM_TTY not set, probing stdin"));
+ tty = ttyname(STDIN_FILENO);
+ if (tty == NULL) {
+ _log_err("couldn't get the tty name");
+ return PAM_ABORT;
+ }
+ if (pam_set_item(pamh, PAM_TTY, tty) != PAM_SUCCESS) {
+ _log_err("couldn't set tty name");
+ return PAM_ABORT;
+ }
+ }
+
+ if (strncmp("/dev/",tty,5) == 0) { /* strip leading /dev/ */
+ tty += 5;
+ }
+
+ /* good, now we have the service name, the user and the terminal name */
+
+ D(("service=%s", service));
+ D(("user=%s", user));
+ D(("tty=%s", tty));
+
+ return check_account(service,tty,user);
+}
+
+/* end of module definition */
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_time_modstruct = {
+ "pam_time",
+ NULL,
+ NULL,
+ pam_sm_acct_mgmt,
+ NULL,
+ NULL,
+ NULL
+};
+#endif
diff --git a/modules/pam_time/time.conf b/modules/pam_time/time.conf
new file mode 100644
index 00000000..d2062fdb
--- /dev/null
+++ b/modules/pam_time/time.conf
@@ -0,0 +1,64 @@
+# this is an example configuration file for the pam_time module. Its syntax
+# was initially based heavily on that of the shadow package (shadow-960129).
+#
+# the syntax of the lines is as follows:
+#
+# services;ttys;users;times
+#
+# white space is ignored and lines maybe extended with '\\n' (escaped
+# newlines). As should be clear from reading these comments,
+# text following a '#' is ignored to the end of the line.
+#
+# the combination of individual users/terminals etc is a logic list
+# namely individual tokens that are optionally prefixed with '!' (logical
+# not) and separated with '&' (logical and) and '|' (logical or).
+#
+# services
+# is a logic list of PAM service names that the rule applies to.
+#
+# ttys
+# is a logic list of terminal names that this rule applies to.
+#
+# users
+# is a logic list of users to whom this rule applies.
+#
+# NB. For these items the simple wildcard '*' may be used only once.
+#
+# times
+# the format here is a logic list of day/time-range
+# entries the days are specified by a sequence of two character
+# entries, MoTuSa for example is Monday Tuesday and Saturday. Note
+# that repeated days are unset MoMo = no day, and MoWk = all weekdays
+# bar Monday. The two character combinations accepted are
+#
+# Mo Tu We Th Fr Sa Su Wk Wd Al
+#
+# the last two being week-end days and all 7 days of the week
+# respectively. As a final example, AlFr means all days except Friday.
+#
+# each day/time-range can be prefixed with a '!' to indicate "anything
+# but"
+#
+# The time-range part is two 24-hour times HHMM separated by a hyphen
+# indicating the start and finish time (if the finish time is smaller
+# than the start time it is deemed to apply on the following day).
+#
+# for a rule to be active, ALL of service+ttys+users must be satisfied
+# by the applying process.
+#
+
+#
+# Here is a simple example: running blank on tty* (any ttyXXX device),
+# the users 'you' and 'me' are denied service all of the time
+#
+
+#blank;tty* & !ttyp*;you|me;!Al0000-2400
+
+# Another silly example, user 'root' is denied xsh access
+# from pseudo terminals at the weekend and on mondays.
+
+#xsh;ttyp*;root;!WdMo0000-2400
+
+#
+# End of example file.
+# \ No newline at end of file
diff --git a/modules/pam_unix/.cvsignore b/modules/pam_unix/.cvsignore
new file mode 100644
index 00000000..64c5ce5c
--- /dev/null
+++ b/modules/pam_unix/.cvsignore
@@ -0,0 +1,4 @@
+dynamic
+unix_chkpwd
+*.so
+*~
diff --git a/modules/pam_unix/CHANGELOG b/modules/pam_unix/CHANGELOG
new file mode 100644
index 00000000..1476b579
--- /dev/null
+++ b/modules/pam_unix/CHANGELOG
@@ -0,0 +1,55 @@
+$Id$
+
+* Mon Aug 16 1999 Jan RÍkorajski <baggins@pld.org.pl>
+- fixed reentrancy problems
+
+* Sun Jul 4 21:03:42 PDT 1999
+
+- temporarily removed the crypt16 stuff. I'm really paranoid about
+ crypto stuff and exporting it, and there are a few too many 's-box'
+ references in the code for my liking..
+
+* Wed Jun 30 1999 Steve Langasek <vorlon@netexpress.net>
+- further NIS+ fixes
+
+* Sun Jun 27 1999 Steve Langasek <vorlon@netexpress.net>
+- fix to uid-handling code for NIS+
+
+* Sat Jun 26 1999 Jan RÍkorajski <baggins@mimuw.edu.pl>
+- merged MD5 fix and early failure syslog
+ by Andrey Vladimirovich Savochkin <saw@msu.ru>
+- minor fixes
+- added signal handler to unix_chkpwd
+
+* Fri Jun 25 1999 Stephen Langasek <vorlon@netexpress.net>
+- reorganized the code to let it build as separate C files
+
+* Sun Jun 20 1999 Jan RÍkorajski <baggins@mimuw.edu.pl>
+- fixes in pam_unix_auth, it incorrectly saved and restored return
+ value when likeauth option was used
+
+* Tue Jun 15 1999 Jan RÍkorajski <baggins@mimuw.edu.pl>
+- added NIS+ support
+
+* Mon Jun 14 1999 Jan RÍkorajski <baggins@mimuw.edu.pl>
+- total rewrite based on pam_pwdb module, now there is ONE pam_unix.so
+ module, it accepts the same options as pam_pwdb - all of them correctly ;)
+ (pam_pwdb dosn't understand what DISALLOW_NULL_AUTHTOK means)
+
+* Tue Apr 20 1999 Jan RÍkorajski <baggins@mimuw.edu.pl>
+- Arghhh, pam_unix_passwd was not updating /etc/shadow when used with
+ pam_cracklib.
+
+* Mon Apr 19 1999 Jan RÍkorajski <baggins@mimuw.edu.pl>
+- added "remember=XXX" option that means 'remember XXX old passwords'
+ Old passwords are stored in /etc/security/opasswd, there can be
+ maximum of 400 passwords per user.
+
+* Sat Mar 27 1999 Jan RÍkorajski <baggins@mimuw.edu.pl>
+- added crypt16 to pam_unix_auth and pam_unix_passwd (check only, this algorithm
+ is too lame to use it in real life)
+
+* Sun Mar 21 1999 Jan RÍkorajski <baggins@mimuw.edu.pl>
+- pam_unix_auth now correctly behave when user has NULL AUTHTOK
+- pam_unix_auth returns PAM_PERM_DENIED when seteuid fails
+
diff --git a/modules/pam_unix/Makefile b/modules/pam_unix/Makefile
new file mode 100644
index 00000000..89b33cfd
--- /dev/null
+++ b/modules/pam_unix/Makefile
@@ -0,0 +1,155 @@
+# $Id$
+#
+# This Makefile controls a build process of the pam_unix modules
+# for Linux-PAM. You should not modify this Makefile.
+#
+
+########################################################################
+# some options... uncomment to take effect
+########################################################################
+
+# do you want cracklib?
+ifeq ($(HAVE_CRACKLIB),yes)
+USE_CRACKLIB=-D"USE_CRACKLIB"
+endif
+
+# do you want to use lckpwdf?
+USE_LCKPWDF=-D"USE_LCKPWDF"
+
+# do you need to include the locking functions in the source?
+#NEED_LCKPWDF=-D"NEED_LCKPWDF"
+
+ifeq ($(shell ./need_nsl.sh),yes)
+LIBNSL = -lnsl
+endif
+
+CHKPWD=unix_chkpwd
+
+EXTRAS += -DCHKPWD_HELPER=\"$(SUPLEMENTED)/$(CHKPWD)\"
+
+########################################################################
+
+CFLAGS += $(USE_CRACKLIB) $(USE_LCKPWDF) $(NEED_LCKPWDF) $(EXTRAS)
+LDLIBS = $(EXTRALS)
+
+ifdef USE_CRACKLIB
+CRACKLIB = -lcrack
+endif
+
+
+LIBOBJ = pam_unix_auth.o pam_unix_acct.o pam_unix_sess.o pam_unix_passwd.o \
+ support.o
+LIBSRC = pam_unix_auth.c pam_unix_acct.c pam_unix_sess.c pam_unix_passwd.c \
+ support.c
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+PLUS = md5_good.o md5_broken.o md5_crypt_good.o md5_crypt_broken.o \
+ yppasswd_xdr.o bigcrypt.o
+
+ifdef DYNAMIC
+LIBSHARED = pam_unix.so
+endif
+ifdef STATIC
+LIBSTATIC = libpam_unix.o
+endif
+
+
+########################### don't edit below #######################
+
+all: dirs info $(PLUS) $(LIBSHARED) $(LIBSTATIC) $(CHKPWD) register
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o: %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+dummy:
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+info:
+ @echo
+ @echo "*** Building pam-unix module of the framework..."
+ @echo
+
+dirs:
+ifdef DYNAMIC
+ mkdir -p ./dynamic
+endif
+ifdef STATIC
+ mkdir -p ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static pam_unix_auth pam_unix/$(LIBSTATIC) ; \
+ ./register_static pam_unix_acct "" ; \
+ ./register_static pam_unix_session "" ; \
+ ./register_static pam_unix_passwd "" ; \
+ )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD) $(PLUS) $(CRACKLIB) $(LDLIBS) $(LIBNSL)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS) $(PLUS) $(CRACKLIB) $(LDLIBS) $(LIBNSL)
+endif
+
+$(CHKPWD): unix_chkpwd.o md5_good.o md5_broken.o \
+ md5_crypt_good.o md5_crypt_broken.o \
+ bigcrypt.o
+ $(CC) -o $(CHKPWD) $^ $(LDLIBS)
+
+unix_chkpwd.o: unix_chkpwd.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+md5_good.o: md5.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -DHIGHFIRST -D'MD5Name(x)=Good##x' \
+ $(TARGET_ARCH) -c $< -o $@
+
+md5_broken.o: md5.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -D'MD5Name(x)=Broken##x' \
+ $(TARGET_ARCH) -c $< -o $@
+
+md5_crypt_good.o: md5_crypt.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -D'MD5Name(x)=Good##x' \
+ $(TARGET_ARCH) -c $< -o $@
+
+md5_crypt_broken.o: md5_crypt.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -D'MD5Name(x)=Broken##x' \
+ $(TARGET_ARCH) -c $< -o $@
+
+install: all
+ mkdir -p $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ install -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+ ln -sf $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)/pam_unix_auth.so
+ ln -sf $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)/pam_unix_acct.so
+ ln -sf $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)/pam_unix_passwd.so
+ ln -sf $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)/pam_unix_session.so
+endif
+ install $(CHKPWD) $(FAKEROOT)$(SUPLEMENTED)
+
+remove:
+ cd $(FAKEROOT)$(SECUREDIR) && rm -f $(LIBSHARED)
+ rm -f $(FAKEROOT)$(SUPLEMENTED)/$(CHKPWD)
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) $(CHKPWD) *.o *.so core
+
+extraclean: clean
+ rm -f *~ *.a *.out *.bak
+
+.c.o:
+ $(CC) -c $(CFLAGS) $<
+
diff --git a/modules/pam_unix/README b/modules/pam_unix/README
new file mode 100644
index 00000000..ad4bc35e
--- /dev/null
+++ b/modules/pam_unix/README
@@ -0,0 +1,39 @@
+This is the README for pam_unix in Linux-PAM-0.67.
+--------------------------------------------------
+
+pam_unix now comes as one module pam_unix.so.
+
+The following links are left for compatibility with old versions:
+pam_unix_auth: authentication module providing
+ pam_authenticate() and pam_setcred() hooks
+pam_unix_sess: session module, providing session logging
+pam_unix_acct: account management, providing shadow account
+ managment features, password aging etc..
+pam_unix_passwd: password updating facilities providing
+ cracklib password strength checking facilities.
+
+The following options are recognized:
+ debug - log more debugging info
+ audit - a little more extreme than debug
+ use_first_pass - don 't prompt the user for passwords
+ take them from PAM_ items instead
+ try_first_pass - don 't prompt the user for the passwords
+ unless PAM_(OLD)AUTHTOK is unset
+ use_authtok - like try_first_pass, but * fail * if the new
+ PAM_AUTHTOK has not been previously set.
+ (intended for stacking password modules only)
+ not_set_pass - don 't set the PAM_ items with the passwords
+ used by this module.
+ shadow - try to maintian a shadow based system.
+ md5 - when a user changes their password next,
+ encrypt it with the md5 algorithm.
+ bigcrypt - when a user changes their password next,
+ excrypt it with the DEC C2 - algorithm(0).
+ nodelay - used to prevent failed authentication
+ resulting in a delay of about 1 second.
+ nis - use NIS RPC for setting new password
+ remember=X - remember X old passwords, they are kept in
+ /etc/security/opasswd in MD5 crypted form
+
+ invalid arguments are logged to syslog.
+
diff --git a/modules/pam_unix/bigcrypt.c b/modules/pam_unix/bigcrypt.c
new file mode 100644
index 00000000..b1568d6b
--- /dev/null
+++ b/modules/pam_unix/bigcrypt.c
@@ -0,0 +1,119 @@
+/*
+ * This function implements the "bigcrypt" algorithm specifically for
+ * Linux-PAM.
+ *
+ * This algorithm is algorithm 0 (default) shipped with the C2 secure
+ * implementation of Digital UNIX.
+ *
+ * Disclaimer: This work is not based on the source code to Digital
+ * UNIX, nor am I connected to Digital Equipment Corp, in any way
+ * other than as a customer. This code is based on published
+ * interfaces and reasonable guesswork.
+ *
+ * Description: The cleartext is divided into blocks of SEGMENT_SIZE=8
+ * characters or less. Each block is encrypted using the standard UNIX
+ * libc crypt function. The result of the encryption for one block
+ * provides the salt for the suceeding block.
+ *
+ * Restrictions: The buffer used to hold the encrypted result is
+ * statically allocated. (see MAX_PASS_LEN below). This is necessary,
+ * as the returned pointer points to "static data that are overwritten
+ * by each call", (XPG3: XSI System Interface + Headers pg 109), and
+ * this is a drop in replacement for crypt();
+ *
+ * Andy Phillips <atp@mssl.ucl.ac.uk>
+ */
+
+#include <string.h>
+#include <security/_pam_macros.h>
+
+char *crypt(const char *key, const char *salt);
+char *bigcrypt(const char *key, const char *salt);
+
+/*
+ * Max cleartext password length in segments of 8 characters this
+ * function can deal with (16 segments of 8 chars= max 128 character
+ * password).
+ */
+
+#define MAX_PASS_LEN 16
+#define SEGMENT_SIZE 8
+#define SALT_SIZE 2
+#define KEYBUF_SIZE ((MAX_PASS_LEN*SEGMENT_SIZE)+SALT_SIZE)
+#define ESEGMENT_SIZE 11
+#define CBUF_SIZE ((MAX_PASS_LEN*ESEGMENT_SIZE)+SALT_SIZE+1)
+
+char *bigcrypt(const char *key, const char *salt)
+{
+ static char dec_c2_cryptbuf[CBUF_SIZE]; /* static storage area */
+
+ unsigned long int keylen, n_seg, j;
+ char *cipher_ptr, *plaintext_ptr, *tmp_ptr, *salt_ptr;
+ char keybuf[KEYBUF_SIZE + 1];
+
+ D(("called with key='%s', salt='%s'.", key, salt));
+
+ /* reset arrays */
+ memset(keybuf, 0, KEYBUF_SIZE + 1);
+ memset(dec_c2_cryptbuf, 0, CBUF_SIZE);
+
+ /* fill KEYBUF_SIZE with key */
+ strncpy(keybuf, key, KEYBUF_SIZE);
+
+ /* deal with case that we are doing a password check for a
+ conventially encrypted password: the salt will be
+ SALT_SIZE+ESEGMENT_SIZE long. */
+ if (strlen(salt) == (SALT_SIZE + ESEGMENT_SIZE))