summaryrefslogtreecommitdiff
path: root/modules/pam_filter
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/pam_filter
Initial revision
Diffstat (limited to 'modules/pam_filter')
-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
9 files changed, 1246 insertions, 0 deletions
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);
+}
+
+
+