diff options
Diffstat (limited to 'modules/pam_filter')
-rw-r--r-- | modules/pam_filter/.cvsignore | 2 | ||||
-rw-r--r-- | modules/pam_filter/.upperLOWER | 1 | ||||
-rw-r--r-- | modules/pam_filter/Makefile | 140 | ||||
-rw-r--r-- | modules/pam_filter/README | 94 | ||||
-rw-r--r-- | modules/pam_filter/include/pam_filter.h | 32 | ||||
-rw-r--r-- | modules/pam_filter/pam_filter.c | 738 | ||||
-rw-r--r-- | modules/pam_filter/upperLOWER/.cvsignore | 1 | ||||
-rw-r--r-- | modules/pam_filter/upperLOWER/Makefile | 64 | ||||
-rw-r--r-- | modules/pam_filter/upperLOWER/upperLOWER.c | 174 |
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); +} + + + |