From ea488580c42e8918445a945484de3c8a5addc761 Mon Sep 17 00:00:00 2001 From: "Andrew G. Morgan" Date: Tue, 20 Jun 2000 22:10:38 +0000 Subject: Initial revision --- libpam_misc/.cvsignore | 7 + libpam_misc/Makefile | 96 ++++++++++++++ libpam_misc/help_env.c | 118 +++++++++++++++++ libpam_misc/misc_conv.c | 344 ++++++++++++++++++++++++++++++++++++++++++++++++ libpam_misc/pam_misc.h | 56 ++++++++ libpam_misc/xstrdup.c | 31 +++++ 6 files changed, 652 insertions(+) create mode 100644 libpam_misc/.cvsignore create mode 100644 libpam_misc/Makefile create mode 100644 libpam_misc/help_env.c create mode 100644 libpam_misc/misc_conv.c create mode 100644 libpam_misc/pam_misc.h create mode 100644 libpam_misc/xstrdup.c (limited to 'libpam_misc') diff --git a/libpam_misc/.cvsignore b/libpam_misc/.cvsignore new file mode 100644 index 00000000..d04bebe5 --- /dev/null +++ b/libpam_misc/.cvsignore @@ -0,0 +1,7 @@ +libpam_misc.so +libpam_misc.a +libpamd_misc.so +libpamd_misc.a +help_env.o +misc_conv.o +xstrdup.o diff --git a/libpam_misc/Makefile b/libpam_misc/Makefile new file mode 100644 index 00000000..89a27536 --- /dev/null +++ b/libpam_misc/Makefile @@ -0,0 +1,96 @@ +# $Id$ +# + +dummy: + @echo "*** This is not a top-level Makefile!" + +# /////////////////////////////////////////////////////////////////// + +# uncomment if you wnat libpam_misc to be made as a dynamic library +# AGM has had some segfaulting from libdl when I did this. I have not +# investigated the cause... + +MAKE_DYNAMIC=yes + +ifeq ($(DEBUG_REL),yes) + LIBNAME=pamd_misc +else + LIBNAME=pam_misc +endif +LIBMAJOR=$(MAJOR_REL) +LIBMINOR=$(MINOR_REL) + +FILES=misc_conv help_env + +# +# Probably no need to alter anything below here. +# + +# build dynamic library names + +LIBDYNAMIC=lib$(LIBNAME).$(DYNTYPE) +LIBDYNMAJ=$(LIBDYNAMIC).$(LIBMAJOR) +LIBDYNMIN=$(LIBDYNMAJ).$(LIBMINOR) + +# static library name + +LIBSTATIC = lib$(LIBNAME).a + +# sources and object files + +LIBSRC = $(addsuffix .c,$(FILES)) +LIBOBJ = $(addsuffix .o,$(FILES)) + +# rules + +all: $(LIBSTATIC) $(LIBDYNAMIC) + +$(LIBDYNAMIC): $(LIBOBJ) +ifdef MAKE_DYNAMIC + ifeq ($(USESONAME),yes) + $(LD_L) $(SOSWITCH) $(LIBDYNMAJ) -o $@ $(LIBOBJ) $(LINKLIBS) + else + $(LD_L) -o $@ $(LIBOBJ) + endif + ifeq ($(NEEDSONAME),yes) + rm -f $(LIBDYNMIN) + ln -s $(LIBDYNAMIC) $(LIBDYNMAJ) + rm -f $(LIBDYNMAJ) + ln -s $(LIBDYNAMIC) $(LIBDYNMIN) + endif +endif + +$(LIBSTATIC): $(LIBOBJ) + $(AR) $@ $(LIBOBJ) + $(RANLIB) $@ + +install: all + $(MKDIR) $(FAKEROOT)$(INCLUDED) + $(INSTALL) -m 644 ./pam_misc.h $(FAKEROOT)$(INCLUDED) +ifdef MAKE_DYNAMIC + $(INSTALL) -m $(SHLIBMODE) $(LIBDYNAMIC) $(FAKEROOT)$(LIBDIR)/$(LIBDYNMIN) + $(LDCONFIG) + ifneq ($(DYNTYPE),"sl") + ( cd $(FAKEROOT)$(LIBDIR) ; ln -sf $(LIBDYNMAJ) $(LIBDYNAMIC) ) + endif +endif + $(INSTALL) -m 644 $(LIBSTATIC) $(FAKEROOT)$(LIBDIR) + +clean: + rm -f *.so *.a core a.out *~ + +remove: + rm -f $(FAKEROOT)$(INCLUDED)/pam_misc.h + rm -f $(FAKEROOT)$(LIBDIR)/$(LIBDYNAMIC).* + rm -f $(FAKEROOT)$(LIBDIR)/$(LIBDYNAMIC) + $(LDCONFIG) + rm -f $(FAKEROOT)$(LIBDIR)/$(LIBSTATIC) + rm -f $(FAKEROOT)$(INCLUDED)/chk_malloc.h + +.c.o: + $(CC) -c $(DEFS) $(CFLAGS) $< + +extraclean: + @$(MAKE) clean + rm -f *.o *.bak + diff --git a/libpam_misc/help_env.c b/libpam_misc/help_env.c new file mode 100644 index 00000000..d52b3a02 --- /dev/null +++ b/libpam_misc/help_env.c @@ -0,0 +1,118 @@ +/* + * $Id$ + * + * This file was written by Andrew G. Morgan + * + * $Log$ + * Revision 1.1 2000/06/20 22:11:24 agmorgan + * Initial revision + * + * Revision 1.1.1.1 1998/07/12 05:17:15 morgan + * Linux PAM sources pre-0.66 + * + * Revision 1.2 1997/01/04 20:19:20 morgan + * added a prototype (no warning) and fixed paste function + * + * Revision 1.1 1996/12/01 03:25:37 morgan + * Initial revision + * + */ + +#include +#include +#include +#include + +/* + * This is a useful function for dumping the Linux-PAM environment + * into some local memory, prior to it all getting lost when pam_end() + * is called. + * + * Initially it was assumed that libpam did not do this part correctly + * (based on a loose email definition). The X/Open XSSO spec makes it + * clear that this function is a duplicate of the one already in + * libpam and therefore unnecessary. IT WILL BE COMPLETELY REMOVED + * IN libpam_misc 1.0 */ + +char **pam_misc_copy_env(pam_handle_t *pamh); +char **pam_misc_copy_env(pam_handle_t *pamh) +{ + return pam_getenvlist(pamh); +} + +/* + * This function should be used to carefully dispose of the copied + * environment. + * + * usage: env = pam_misc_drop_env(env); + */ + +char **pam_misc_drop_env(char **dump) +{ + int i; + + for (i=0; dump[i] != NULL; ++i) { + D(("dump[%d]=`%s'", i, dump[i])); + _pam_overwrite(dump[i]); + _pam_drop(dump[i]); + } + _pam_drop(dump); + + return NULL; +} + +/* + * This function takes the supplied environment and uploads it to be + * the PAM one. + */ + +int pam_misc_paste_env(pam_handle_t *pamh, const char * const * user_env) +{ + for (; user_env && *user_env; ++user_env) { + int retval; + + D(("uploading: %s", *user_env)); + retval = pam_putenv(pamh, *user_env); + if (retval != PAM_SUCCESS) { + D(("error setting %s: %s", *user_env, pam_strerror(pamh,retval))); + return retval; + } + } + D(("done.")); + return PAM_SUCCESS; +} + +/* + * This is a wrapper to make pam behave in the way that setenv() does. + */ + +int pam_misc_setenv(pam_handle_t *pamh, const char *name + , const char *value, int readonly) +{ + char *tmp; + int retval; + + if (readonly) { + const char *etmp; + + /* we check if the variable is there already */ + etmp = pam_getenv(pamh, name); + if (etmp != NULL) { + D(("failed to set readonly variable: %s", name)); + return PAM_PERM_DENIED; /* not allowed to overwrite */ + } + } + tmp = malloc(2+strlen(name)+strlen(value)); + if (tmp != NULL) { + sprintf(tmp,"%s=%s",name,value); + D(("pam_putt()ing: %s", tmp)); + retval = pam_putenv(pamh, tmp); + _pam_overwrite(tmp); /* purge */ + _pam_drop(tmp); /* forget */ + } else { + D(("malloc failure")); + retval = PAM_BUF_ERR; + } + + return retval; +} diff --git a/libpam_misc/misc_conv.c b/libpam_misc/misc_conv.c new file mode 100644 index 00000000..f2811a26 --- /dev/null +++ b/libpam_misc/misc_conv.c @@ -0,0 +1,344 @@ +/* + * $Id$ + * + * A generic conversation function for text based applications + * + * Written by Andrew Morgan + */ + +#ifdef linux +#define _GNU_SOURCE +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define INPUTSIZE PAM_MAX_MSG_SIZE /* maximum length of input+1 */ +#define CONV_ECHO_ON 1 /* types of echo state */ +#define CONV_ECHO_OFF 0 + +/* + * external timeout definitions - these can be overriden by the + * application. + */ + +time_t pam_misc_conv_warn_time = 0; /* time when we warn */ +time_t pam_misc_conv_die_time = 0; /* time when we timeout */ + +const char *pam_misc_conv_warn_line = "..\a.Time is running out...\n"; +const char *pam_misc_conv_die_line = "..\a.Sorry, your time is up!\n"; + +int pam_misc_conv_died=0; /* application can probe this for timeout */ + +static void pam_misc_conv_delete_binary(void **delete_me) +{ + if (delete_me && *delete_me) { + unsigned char *packet = *(unsigned char **)delete_me; + int length; + + length = (packet[0]<<24)+(packet[1]<<16)+(packet[2]<<8)+packet[3]; + memset(packet, 0, length); + free(packet); + *delete_me = packet = NULL; + } +} + +/* These function pointers are for application specific binary + conversations. One or both of the arguments to the first function + must be non-NULL. The first function must return PAM_SUCCESS or + PAM_CONV_ERR. If input is non-NULL, a response is expected, this + response should be malloc()'d and will eventually be free()'d by + the calling module. The structure of this malloc()'d response is as + follows: + + { int length, char data[length] } + + For convenience, the pointer used by the two function pointer + prototypes is 'void *'. + + The ...free() fn pointer is used to discard a binary message that + is not of the default form. It should be explicitly overwritten + when using some other convention for the structure of a binary + prompt (not recommended). */ + +int (*pam_binary_handler_fn)(const void *send, void **receive) = NULL; +void (*pam_binary_handler_free)(void **packet_p) = pam_misc_conv_delete_binary; + +/* the following code is used to get text input */ + +volatile static int expired=0; + +/* return to the previous signal handling */ +static void reset_alarm(struct sigaction *o_ptr) +{ + (void) alarm(0); /* stop alarm clock - if still ticking */ + (void) sigaction(SIGALRM, o_ptr, NULL); +} + +/* this is where we intercept the alarm signal */ +static void time_is_up(int ignore) +{ + expired = 1; +} + +/* set the new alarm to hit the time_is_up() function */ +static int set_alarm(int delay, struct sigaction *o_ptr) +{ + struct sigaction new_sig; + + sigemptyset(&new_sig.sa_mask); + new_sig.sa_flags = 0; + new_sig.sa_handler = time_is_up; + if ( sigaction(SIGALRM, &new_sig, o_ptr) ) { + return 1; /* setting signal failed */ + } + if ( alarm(delay) ) { + (void) sigaction(SIGALRM, o_ptr, NULL); + return 1; /* failed to set alarm */ + } + return 0; /* all seems to have worked */ +} + +/* return the number of seconds to next alarm. 0 = no delay, -1 = expired */ +static int get_delay(void) +{ + time_t now; + + expired = 0; /* reset flag */ + (void) time(&now); + + /* has the quit time past? */ + if (pam_misc_conv_die_time && now >= pam_misc_conv_die_time) { + fprintf(stderr,"%s",pam_misc_conv_die_line); + + pam_misc_conv_died = 1; /* note we do not reset the die_time */ + return -1; /* time is up */ + } + + /* has the warning time past? */ + if (pam_misc_conv_warn_time && now >= pam_misc_conv_warn_time) { + fprintf(stderr, "%s", pam_misc_conv_warn_line); + pam_misc_conv_warn_time = 0; /* reset warn_time */ + + /* indicate remaining delay - if any */ + + return (pam_misc_conv_die_time ? pam_misc_conv_die_time - now:0 ); + } + + /* indicate possible warning delay */ + + if (pam_misc_conv_warn_time) + return (pam_misc_conv_warn_time - now); + else if (pam_misc_conv_die_time) + return (pam_misc_conv_die_time - now); + else + return 0; +} + +/* read a line of input string, giving prompt when appropriate */ +static char *read_string(int echo, const char *prompt) +{ + struct termios term_before, term_tmp; + char line[INPUTSIZE]; + struct sigaction old_sig; + int delay, nc, have_term=0; + + D(("called with echo='%s', prompt='%s'.", echo ? "ON":"OFF" , prompt)); + + if (isatty(STDIN_FILENO)) { /* terminal state */ + + /* is a terminal so record settings and flush it */ + if ( tcgetattr(STDIN_FILENO, &term_before) != 0 ) { + D(("")); + return NULL; + } + memcpy(&term_tmp, &term_before, sizeof(term_tmp)); + if (!echo) { + term_tmp.c_lflag &= ~(ECHO); + } + have_term = 1; + + } else if (!echo) { + D(("")); + } + + /* set up the signal handling */ + delay = get_delay(); + + /* reading the line */ + while (delay >= 0) { + + fprintf(stderr, "%s", prompt); + /* this may, or may not set echo off -- drop pending input */ + if (have_term) + (void) tcsetattr(STDIN_FILENO, TCSAFLUSH, &term_tmp); + + if ( delay > 0 && set_alarm(delay, &old_sig) ) { + D(("")); + break; + } else { + nc = read(STDIN_FILENO, line, INPUTSIZE-1); + if (have_term) { + (void) tcsetattr(STDIN_FILENO, TCSADRAIN, &term_before); + if (!echo || expired) /* do we need a newline? */ + fprintf(stderr,"\n"); + } + if ( delay > 0 ) { + reset_alarm(&old_sig); + } + if (expired) { + delay = get_delay(); + } else if (nc > 0) { /* we got some user input */ + char *input; + + if (nc > 0 && line[nc-1] == '\n') { /* terminate */ + line[--nc] = '\0'; + } else { + line[nc] = '\0'; + } + input = x_strdup(line); + _pam_overwrite(line); + + return input; /* return malloc()ed string */ + } else if (nc == 0) { /* Ctrl-D */ + D(("user did not want to type anything")); + fprintf(stderr, "\n"); + break; + } + } + } + + /* getting here implies that the timer expired */ + if (have_term) + (void) tcsetattr(STDIN_FILENO, TCSADRAIN, &term_before); + + memset(line, 0, INPUTSIZE); /* clean up */ + return NULL; +} + +/* end of read_string functions */ + +int misc_conv(int num_msg, const struct pam_message **msgm, + struct pam_response **response, void *appdata_ptr) +{ + int count=0; + struct pam_response *reply; + + if (num_msg <= 0) + return PAM_CONV_ERR; + + D(("allocating empty response structure array.")); + + reply = (struct pam_response *) calloc(num_msg, + sizeof(struct pam_response)); + if (reply == NULL) { + D(("no memory for responses")); + return PAM_CONV_ERR; + } + + D(("entering conversation function.")); + + for (count=0; count < num_msg; ++count) { + char *string=NULL; + + switch (msgm[count]->msg_style) { + case PAM_PROMPT_ECHO_OFF: + string = read_string(CONV_ECHO_OFF,msgm[count]->msg); + if (string == NULL) { + goto failed_conversation; + } + break; + case PAM_PROMPT_ECHO_ON: + string = read_string(CONV_ECHO_ON,msgm[count]->msg); + if (string == NULL) { + goto failed_conversation; + } + break; + case PAM_ERROR_MSG: + if (fprintf(stderr,"%s\n",msgm[count]->msg) < 0) { + goto failed_conversation; + } + break; + case PAM_TEXT_INFO: + if (fprintf(stdout,"%s\n",msgm[count]->msg) < 0) { + goto failed_conversation; + } + break; + case PAM_BINARY_PROMPT: + { + void *pack_out=NULL; + const void *pack_in = msgm[count]->msg; + + if (!pam_binary_handler_fn + || pam_binary_handler_fn(pack_in, &pack_out) != PAM_SUCCESS + || pack_out == NULL) { + goto failed_conversation; + } + string = (char *) pack_out; + pack_out = NULL; + + break; + } + default: + fprintf(stderr, "erroneous conversation (%d)\n" + ,msgm[count]->msg_style); + goto failed_conversation; + } + + if (string) { /* must add to reply array */ + /* add string to list of responses */ + + reply[count].resp_retcode = 0; + reply[count].resp = string; + string = NULL; + } + } + + /* New (0.59+) behavior is to always have a reply - this is + compatable with the X/Open (March 1997) spec. */ + *response = reply; + reply = NULL; + + return PAM_SUCCESS; + +failed_conversation: + + if (reply) { + for (count=0; countmsg_style) { + case PAM_PROMPT_ECHO_ON: + case PAM_PROMPT_ECHO_OFF: + _pam_overwrite(reply[count].resp); + free(reply[count].resp); + break; + case PAM_BINARY_PROMPT: + pam_binary_handler_free((void **) &reply[count].resp); + break; + case PAM_ERROR_MSG: + case PAM_TEXT_INFO: + /* should not actually be able to get here... */ + free(reply[count].resp); + } + reply[count].resp = NULL; + } + /* forget reply too */ + free(reply); + reply = NULL; + } + + return PAM_CONV_ERR; +} + diff --git a/libpam_misc/pam_misc.h b/libpam_misc/pam_misc.h new file mode 100644 index 00000000..fbf7a9f1 --- /dev/null +++ b/libpam_misc/pam_misc.h @@ -0,0 +1,56 @@ +/* $Id$ */ + +#ifndef __PAMMISC_H +#define __PAMMISC_H + +#include + +/* include some useful macros */ + +#include + +/* functions defined in pam_misc.* libraries */ + +extern int misc_conv(int num_msg, const struct pam_message **msgm, + struct pam_response **response, void *appdata_ptr); + +#include + +extern time_t pam_misc_conv_warn_time; /* time that we should warn user */ +extern time_t pam_misc_conv_die_time; /* cut-off time for input */ +extern const char *pam_misc_conv_warn_line; /* warning notice */ +extern const char *pam_misc_conv_die_line; /* cut-off remark */ +extern int pam_misc_conv_died; /* 1 = cut-off time reached (0 not) */ +extern int (*pam_binary_handler_fn)(const void *send, void **receive); + +/* + * Environment helper functions + */ + +/* transcribe given environment (to pam) */ +extern int pam_misc_paste_env(pam_handle_t *pamh + , const char * const * user_env); + +/* char **pam_misc_copy_env(pam_handle_t *pamh); + + This is no longer defined as a prototype because the X/Open XSSO + spec makes it clear that PAM's pam_getenvlist() does exactly + what this was needed for. + + A wrapper is still provided in the pam_misc library - so that + legacy applications will still work. But _BE_WARNED_ it will + disappear by the release of libpam 1.0 . */ + +/* delete environment as obtained from (pam_getenvlist) */ +extern char **pam_misc_drop_env(char **env); + +/* provide something like the POSIX setenv function for the (Linux-)PAM + * environment. */ + +extern int pam_misc_setenv(pam_handle_t *pamh, const char *name + , const char *value, int readonly); + +#endif + + + diff --git a/libpam_misc/xstrdup.c b/libpam_misc/xstrdup.c new file mode 100644 index 00000000..6a4ca6f7 --- /dev/null +++ b/libpam_misc/xstrdup.c @@ -0,0 +1,31 @@ +/* $Id$ */ + +#include +#include +#include + +/* + * Safe duplication of character strings. "Paranoid"; don't leave + * evidence of old token around for later stack analysis. + */ + +char *xstrdup(const char *x) +{ + register char *new=NULL; + + if (x != NULL) { + register int i; + + for (i=0; x[i]; ++i); /* length of string */ + if ((new = malloc(++i)) == NULL) { + i = 0; + } else { + while (i-- > 0) { + new[i] = x[i]; + } + } + x = NULL; + } + + return new; /* return the duplicate or NULL on error */ +} -- cgit v1.2.3