diff options
author | Andrew G. Morgan <morgan@kernel.org> | 2000-06-20 22:10:38 +0000 |
---|---|---|
committer | Andrew G. Morgan <morgan@kernel.org> | 2000-06-20 22:10:38 +0000 |
commit | ea488580c42e8918445a945484de3c8a5addc761 (patch) | |
tree | c992f3ba699caafedfadc16af38e6359c3c24698 /libpam | |
download | pam-ea488580c42e8918445a945484de3c8a5addc761.tar.gz pam-ea488580c42e8918445a945484de3c8a5addc761.tar.bz2 pam-ea488580c42e8918445a945484de3c8a5addc761.zip |
Initial revision
Diffstat (limited to 'libpam')
29 files changed, 5533 insertions, 0 deletions
diff --git a/libpam/.cvsignore b/libpam/.cvsignore new file mode 100644 index 00000000..dd17fdcf --- /dev/null +++ b/libpam/.cvsignore @@ -0,0 +1,2 @@ +dynamic +static diff --git a/libpam/Makefile b/libpam/Makefile new file mode 100644 index 00000000..66b2a705 --- /dev/null +++ b/libpam/Makefile @@ -0,0 +1,162 @@ +# +# $Id$ +# +# + +# need to tell libpam about the default directory for PAMs +MOREFLAGS=-D"DEFAULT_MODULE_PATH=\"$(SECUREDIR)/\"" + +# you may uncomment the following to build libpam in modified ways + +# lots of debugging information goes to /tmp/pam-debug.log +#MOREFLAGS += -D"DEBUG" + +# pay attention to locked /etc/pam.conf or /etc/pam.d/* files +#MOREFLAGS += -D"PAM_LOCKING" + +# read both the /etc/pam.d/ and pam.conf files specific to the deisred service +#MOREFLAGS += -D"PAM_READ_BOTH_CONFS" + +# make a kludge attempt to be compatible with the old pam_strerror +# calling convention +#MOREFLAGS += -D"UGLY_HACK_FOR_PRIOR_BEHAVIOR_SUPPORT" + +# libpam.so needs -ldl, too. +LINKLIBS += $(LIBDL) + +ifeq ($(DEBUG_REL),yes) + LIBNAME=libpamd +else + LIBNAME=libpam +endif +VERSION=.$(MAJOR_REL) +MODIFICATION=.$(MINOR_REL) + +# --------------------------------------------- + +dummy: + @echo "*** This is not a top-level Makefile!" + +# --------------------------------------------- + +CFLAGS += $(DYNAMIC) $(STATIC) $(MOREFLAGS) + +# dynamic library names + +LIBPAM = $(LIBNAME).$(DYNTYPE) +LIBPAMNAME = $(LIBPAM)$(VERSION) +LIBPAMFULL = $(LIBPAMNAME)$(MODIFICATION) + +# static library name + +LIBPAMSTATIC = $(LIBNAME).a + +ifdef STATIC +MODULES = $(shell cat ../modules/_static_module_objects) +STATICOBJ = pam_static.o +endif + +ifdef MEMORY_DEBUG +EXTRAS += pam_malloc.o +endif + +LIBOBJECTS = pam_item.o pam_strerror.o pam_end.o pam_start.o pam_data.o \ + pam_delay.o pam_dispatch.o pam_handlers.o pam_misc.o \ + pam_account.o pam_auth.o pam_session.o pam_password.o \ + pam_env.o pam_log.o $(EXTRAS) + +ifdef DYNAMIC_LIBPAM +DLIBOBJECTS = $(addprefix dynamic/,$(LIBOBJECTS) $(STATICOBJ)) +ifdef STATICOBJ +dynamic/pam_static.o: pam_static.c ../modules/_static_module_objects + $(CC) $(CFLAGS) -c pam_static.c -o $@ +endif +endif + +ifdef STATIC_LIBPAM +SLIBOBJECTS = $(addprefix static/,$(LIBOBJECTS) $(STATICOBJ)) +ifdef STATICOBJ +static/pam_static.o: pam_static.c ../modules/_static_module_objects + $(CC) $(CFLAGS) -c pam_static.c -o $@ +endif +endif + +# --------------------------------------------- +## rules + +all: dirs $(LIBPAM) $(LIBPAMSTATIC) + +dirs: +ifdef DYNAMIC_LIBPAM + mkdir -p dynamic +endif +ifdef STATIC_LIBPAM + mkdir -p static +endif + +dynamic/%.o : %.c + $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@ + +static/%.o : %.c + $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@ + +$(LIBPAM): $(DLIBOBJECTS) +ifdef DYNAMIC_LIBPAM + ifeq ($(USESONAME),yes) + $(LD_L) $(SOSWITCH) $(LIBPAMNAME) -o $@ $(DLIBOBJECTS) $(MODULES) $(LINKLIBS) + else + $(LD_L) -o $@ $(DLIBOBJECTS) $(MODULES) + endif + ifeq ($(NEEDSONAME),yes) + rm -f $(LIBPAMFULL) + ln -s $(LIBPAM) $(LIBPAMFULL) + rm -f $(LIBPAMNAME) + ln -s $(LIBPAM) $(LIBPAMNAME) + endif +endif + +$(LIBPAMSTATIC): $(SLIBOBJECTS) +ifdef STATIC_LIBPAM + $(AR) $@ $(SLIBOBJECTS) $(MODULES) + $(RANLIB) $@ +endif + +install: all + $(MKDIR) $(FAKEROOT)$(INCLUDED) $(FAKEROOT)$(LIBDIR) + $(INSTALL) -m 644 include/security/pam_appl.h $(FAKEROOT)$(INCLUDED) + $(INSTALL) -m 644 include/security/pam_modules.h $(FAKEROOT)$(INCLUDED) + $(INSTALL) -m 644 include/security/_pam_macros.h $(FAKEROOT)$(INCLUDED) + $(INSTALL) -m 644 include/security/_pam_types.h $(FAKEROOT)$(INCLUDED) + $(INSTALL) -m 644 include/security/_pam_compat.h $(FAKEROOT)$(INCLUDED) +ifdef MEMORY_DEBUG + $(INSTALL) -m 644 include/security/pam_malloc.h $(FAKEROOT)$(INCLUDED) +endif +ifdef DYNAMIC_LIBPAM + $(INSTALL) -m $(SHLIBMODE) $(LIBPAM) $(FAKEROOT)$(LIBDIR)/$(LIBPAMFULL) + $(LDCONFIG) + ifneq ($(DYNTYPE),"sl") + ( cd $(FAKEROOT)$(LIBDIR) ; rm -f $(LIBPAM) ; ln -s $(LIBPAMNAME) $(LIBPAM) ) + endif +endif +ifdef STATIC_LIBPAM + $(INSTALL) -m 644 $(LIBPAMSTATIC) $(FAKEROOT)$(LIBDIR) +endif + +remove: + rm -f $(FAKEROOT)$(INCLUDED)/_pam_types.h + rm -f $(FAKEROOT)$(INCLUDED)/_pam_macros.h + rm -f $(FAKEROOT)$(INCLUDED)/pam_appl.h + rm -f $(FAKEROOT)$(INCLUDED)/pam_modules.h + rm -f $(FAKEROOT)$(INCLUDED)/pam_malloc.h + rm -f $(FAKEROOT)$(LIBDIR)/$(LIBPAM).* + rm -f $(FAKEROOT)$(LIBDIR)/$(LIBPAM) + $(LDCONFIG) + rm -f $(FAKEROOT)$(LIBDIR)/$(LIBPAMSTATIC) + +clean: + rm -f a.out core *~ static/*.o dynamic/*.o + +extraclean: clean + rm -f *.a *.out *.o *.so ./include/security/*~ + if [ -d dynamic ]; then rmdir dynamic ; fi + if [ -d static ]; then rmdir static ; fi diff --git a/libpam/include/security/_pam_compat.h b/libpam/include/security/_pam_compat.h new file mode 100644 index 00000000..a5f77e7a --- /dev/null +++ b/libpam/include/security/_pam_compat.h @@ -0,0 +1,122 @@ +#ifndef _PAM_COMPAT_H +#define _PAM_COMPAT_H + +/* + * $Id$ + * + * This file was contributed by Derrick J Brashear <shadow@dementia.org> + * slight modification by Brad M. Garcia <bgarcia@fore.com> + * + * A number of operating systems have started to implement PAM. + * unfortunately, they have a different set of numeric values for + * certain constants. This file is included for compatibility's sake. + */ + +/* Solaris uses different constants. We redefine to those here */ +#if defined(solaris) || (defined(__SVR4) && defined(sun)) + +#ifndef _SECURITY__PAM_TYPES_H + +# ifdef _SECURITY_PAM_MODULES_H + +/* flags for pam_chauthtok() */ +# undef PAM_PRELIM_CHECK +# define PAM_PRELIM_CHECK 0x1 + +# undef PAM_UPDATE_AUTHTOK +# define PAM_UPDATE_AUTHTOK 0x2 + +# endif /* _SECURITY_PAM_MODULES_H */ + +#else /* _SECURITY__PAM_TYPES_H */ + +/* generic for pam_* functions */ +# undef PAM_SILENT +# define PAM_SILENT 0x80000000 + +/* flags for pam_setcred() */ +# undef PAM_ESTABLISH_CRED +# define PAM_ESTABLISH_CRED 0x1 + +# undef PAM_DELETE_CRED +# define PAM_DELETE_CRED 0x2 + +# undef PAM_REINITIALIZE_CRED +# define PAM_REINITIALIZE_CRED 0x4 + +# undef PAM_REFRESH_CRED +# define PAM_REFRESH_CRED 0x8 + +/* another binary incompatibility comes from the return codes! */ + +# undef PAM_CONV_ERR +# define PAM_CONV_ERR 6 + +# undef PAM_PERM_DENIED +# define PAM_PERM_DENIED 7 + +# undef PAM_MAXTRIES +# define PAM_MAXTRIES 8 + +# undef PAM_AUTH_ERR +# define PAM_AUTH_ERR 9 + +# undef PAM_NEW_AUTHTOK_REQD +# define PAM_NEW_AUTHTOK_REQD 10 + +# undef PAM_CRED_INSUFFICIENT +# define PAM_CRED_INSUFFICIENT 11 + +# undef PAM_AUTHINFO_UNAVAIL +# define PAM_AUTHINFO_UNAVAIL 12 + +# undef PAM_USER_UNKNOWN +# define PAM_USER_UNKNOWN 13 + +# undef PAM_CRED_UNAVAIL +# define PAM_CRED_UNAVAIL 14 + +# undef PAM_CRED_EXPIRED +# define PAM_CRED_EXPIRED 15 + +# undef PAM_CRED_ERR +# define PAM_CRED_ERR 16 + +# undef PAM_ACCT_EXPIRED +# define PAM_ACCT_EXPIRED 17 + +# undef PAM_AUTHTOK_EXPIRED +# define PAM_AUTHTOK_EXPIRED 18 + +# undef PAM_SESSION_ERR +# define PAM_SESSION_ERR 19 + +# undef PAM_AUTHTOK_ERR +# define PAM_AUTHTOK_ERR 20 + +# undef PAM_AUTHTOK_RECOVERY_ERR +# define PAM_AUTHTOK_RECOVERY_ERR 21 + +# undef PAM_AUTHTOK_LOCK_BUSY +# define PAM_AUTHTOK_LOCK_BUSY 22 + +# undef PAM_AUTHTOK_DISABLE_AGING +# define PAM_AUTHTOK_DISABLE_AGING 23 + +# undef PAM_NO_MODULE_DATA +# define PAM_NO_MODULE_DATA 24 + +# undef PAM_IGNORE +# define PAM_IGNORE 25 + +# undef PAM_ABORT +# define PAM_ABORT 26 + +# undef PAM_TRY_AGAIN +# define PAM_TRY_AGAIN 27 + +#endif /* _SECURITY__PAM_TYPES_H */ + +#endif /* defined(solaris) || (defined(__SVR4) && defined(sun)) */ + +#endif /* _PAM_COMPAT_H */ diff --git a/libpam/include/security/_pam_macros.h b/libpam/include/security/_pam_macros.h new file mode 100644 index 00000000..7c3dde1d --- /dev/null +++ b/libpam/include/security/_pam_macros.h @@ -0,0 +1,166 @@ +#ifndef PAM_MACROS_H +#define PAM_MACROS_H + +/* + * All kind of macros used by PAM, but usable in some other + * programs too. + * Organized by Cristian Gafton <gafton@redhat.com> + */ + +/* a 'safe' version of strdup */ + +#include <string.h> +#include <stdlib.h> + +#define x_strdup(s) ( (s) ? strdup(s):NULL ) + +/* Good policy to strike out passwords with some characters not just + free the memory */ + +#define _pam_overwrite(x) \ +do { \ + register char *__xx__; \ + if ((__xx__=(x))) \ + while (*__xx__) \ + *__xx__++ = '\0'; \ +} while (0) + +/* + * Don't just free it, forget it too. + */ + +#define _pam_drop(X) \ +do { \ + if (X) { \ + free(X); \ + X=NULL; \ + } \ +} while (0) + +#define _pam_drop_reply(/* struct pam_response * */ reply, /* int */ replies) \ +do { \ + int reply_i; \ + \ + for (reply_i=0; reply_i<replies; ++reply_i) { \ + if (reply[reply_i].resp) { \ + _pam_overwrite(reply[reply_i].resp); \ + free(reply[reply_i].resp); \ + } \ + } \ + if (reply) \ + free(reply); \ +} while (0) + +/* some debugging code */ + +#ifdef DEBUG + +/* + * This provides the necessary function to do debugging in PAM. + * Cristian Gafton <gafton@redhat.com> + */ + +#include <stdio.h> +#include <sys/types.h> +#include <stdarg.h> +#include <errno.h> + +/* + * This is for debugging purposes ONLY. DO NOT use on live systems !!! + * You have been warned :-) - CG + * + * to get automated debugging to the log file, it must be created manually. + * _PAM_LOGFILE must exist, mode 666 + */ + +#ifndef _PAM_LOGFILE +#define _PAM_LOGFILE "/tmp/pam-debug.log" +#endif + +static void _pam_output_debug_info(const char *file, const char *fn + , const int line) +{ + FILE *logfile; + int must_close = 1; + + if (!(logfile = fopen(_PAM_LOGFILE,"a"))) { + logfile = stderr; + must_close = 0; + } + fprintf(logfile,"[%s:%s(%d)] ",file, fn, line); + if (must_close) { + fflush(logfile); + fclose(logfile); + } +} + +static void _pam_output_debug(const char *format, ...) +{ + va_list args; + FILE *logfile; + int must_close = 1; + + va_start(args, format); + + if (!(logfile = fopen(_PAM_LOGFILE,"a"))) { + logfile = stderr; + must_close = 0; + } + vfprintf(logfile, format, args); + fprintf(logfile, "\n"); + if (must_close) { + fflush(logfile); + fclose(logfile); + } + + va_end(args); +} + +#define D(x) do { \ + _pam_output_debug_info(__FILE__, __FUNCTION__, __LINE__); \ + _pam_output_debug x ; \ +} while (0) + +#define _pam_show_mem(X,XS) do { \ + int i; \ + register unsigned char *x; \ + x = (unsigned char *)X; \ + fprintf(stderr, " <start at %p>\n", X); \ + for (i = 0; i < XS ; ++x, ++i) { \ + fprintf(stderr, " %02X. <%p:%02X>\n", i, x, *x); \ + } \ + fprintf(stderr, " <end for %p after %d bytes>\n", X, XS); \ +} while (0) + +#define _pam_show_reply(/* struct pam_response * */reply, /* int */replies) \ +do { \ + int reply_i; \ + setbuf(stderr, NULL); \ + fprintf(stderr, "array at %p of size %d\n",reply,replies); \ + fflush(stderr); \ + if (reply) { \ + for (reply_i = 0; reply_i < replies; reply_i++) { \ + fprintf(stderr, " elem# %d at %p: resp = %p, retcode = %d\n", \ + reply_i, reply+reply_i, reply[reply_i].resp, \ + reply[reply_i].resp, _retcode); \ + fflush(stderr); \ + if (reply[reply_i].resp) { \ + fprintf(stderr, " resp[%d] = '%s'\n", \ + strlen(reply[reply_i].resp), reply[reply_i].resp); \ + fflush(stderr); \ + } \ + } \ + } \ + fprintf(stderr, "done here\n"); \ + fflush(stderr); \ +} while (0) + +#else + +#define D(x) do { } while (0) +#define _pam_show_mem(X,XS) do { } while (0) +#define _pam_show_reply(reply, replies) do { } while (0) + +#endif /* DEBUG */ + +#endif /* PAM_MACROS_H */ diff --git a/libpam/include/security/_pam_types.h b/libpam/include/security/_pam_types.h new file mode 100644 index 00000000..fe4ada39 --- /dev/null +++ b/libpam/include/security/_pam_types.h @@ -0,0 +1,316 @@ +/* + * <security/_pam_types.h> + * + * $Id$ + * + * This file defines all of the types common to the Linux-PAM library + * applications and modules. + * + * Note, the copyright+license information is at end of file. + * + * Created: 1996/3/5 by AGM + */ + +#ifndef _SECURITY__PAM_TYPES_H +#define _SECURITY__PAM_TYPES_H + +/* + * include local definition for POSIX - NULL + */ + +#include <locale.h> + +/* This is a blind structure; users aren't allowed to see inside a + * pam_handle_t, so we don't define struct pam_handle here. This is + * defined in a file private to the PAM library. (i.e., it's private + * to PAM service modules, too!) */ + +typedef struct pam_handle pam_handle_t; + +/* ----------------- The Linux-PAM return values ------------------ */ + +#define PAM_SUCCESS 0 /* Successful function return */ +#define PAM_OPEN_ERR 1 /* dlopen() failure when dynamically */ + /* loading a service module */ +#define PAM_SYMBOL_ERR 2 /* Symbol not found */ +#define PAM_SERVICE_ERR 3 /* Error in service module */ +#define PAM_SYSTEM_ERR 4 /* System error */ +#define PAM_BUF_ERR 5 /* Memory buffer error */ +#define PAM_PERM_DENIED 6 /* Permission denied */ +#define PAM_AUTH_ERR 7 /* Authentication failure */ +#define PAM_CRED_INSUFFICIENT 8 /* Can not access authentication data */ + /* due to insufficient credentials */ +#define PAM_AUTHINFO_UNAVAIL 9 /* Underlying authentication service */ + /* can not retrieve authenticaiton */ + /* information */ +#define PAM_USER_UNKNOWN 10 /* User not known to the underlying */ + /* authenticaiton module */ +#define PAM_MAXTRIES 11 /* An authentication service has */ + /* maintained a retry count which has */ + /* been reached. No further retries */ + /* should be attempted */ +#define PAM_NEW_AUTHTOK_REQD 12 /* New authentication token required. */ + /* This is normally returned if the */ + /* machine security policies require */ + /* that the password should be changed */ + /* beccause the password is NULL or it */ + /* has aged */ +#define PAM_ACCT_EXPIRED 13 /* User account has expired */ +#define PAM_SESSION_ERR 14 /* Can not make/remove an entry for */ + /* the specified session */ +#define PAM_CRED_UNAVAIL 15 /* Underlying authentication service */ + /* can not retrieve user credentials */ + /* unavailable */ +#define PAM_CRED_EXPIRED 16 /* User credentials expired */ +#define PAM_CRED_ERR 17 /* Failure setting user credentials */ +#define PAM_NO_MODULE_DATA 18 /* No module specific data is present */ +#define PAM_CONV_ERR 19 /* Conversation error */ +#define PAM_AUTHTOK_ERR 20 /* Authentication token manipulation error */ +#define PAM_AUTHTOK_RECOVER_ERR 21 /* Authentication information */ + /* cannot be recovered */ +#define PAM_AUTHTOK_LOCK_BUSY 22 /* Authentication token lock busy */ +#define PAM_AUTHTOK_DISABLE_AGING 23 /* Authentication token aging disabled */ +#define PAM_TRY_AGAIN 24 /* Preliminary check by password service */ +#define PAM_IGNORE 25 /* Ingore underlying account module */ + /* regardless of whether the control */ + /* flag is required, optional, or sufficient */ +#define PAM_ABORT 26 /* Critical error (?module fail now request) */ +#define PAM_AUTHTOK_EXPIRED 27 /* user's authentication token has expired */ +#define PAM_MODULE_UNKNOWN 28 /* module is not known */ + +#define PAM_BAD_ITEM 29 /* Bad item passed to pam_*_item() */ +#define PAM_CONV_AGAIN 30 /* conversation function is event driven + and data is not available yet */ +#define PAM_INCOMPLETE 31 /* please call this function again to + complete authentication stack. Before + calling again, verify that conversation + is completed */ + +/* Add new #define's here */ + +#define _PAM_RETURN_VALUES 32 /* this is the number of return values */ + + +/* ---------------------- The Linux-PAM flags -------------------- */ + +/* Authentication service should not generate any messages */ +#define PAM_SILENT 0x8000U + +/* Note: these flags are used by pam_authenticate{,_secondary}() */ + +/* The authentication service should return PAM_AUTH_ERROR if the + * user has a null authentication token */ +#define PAM_DISALLOW_NULL_AUTHTOK 0x0001U + +/* Note: these flags are used for pam_setcred() */ + +/* Set user credentials for an authentication service */ +#define PAM_ESTABLISH_CRED 0x0002U + +/* Delete user credentials associated with an authentication service */ +#define PAM_DELETE_CRED 0x0004U + +/* Reinitialize user credentials */ +#define PAM_REINITIALIZE_CRED 0x0008U + +/* Extend lifetime of user credentials */ +#define PAM_REFRESH_CRED 0x0010U + +/* Note: these flags are used by pam_chauthtok */ + +/* The password service should only update those passwords that have + * aged. If this flag is not passed, the password service should + * update all passwords. */ +#define PAM_CHANGE_EXPIRED_AUTHTOK 0x0020U + +/* ------------------ The Linux-PAM item types ------------------- */ + +/* these defines are used by pam_set_item() and pam_get_item() */ + +#define PAM_SERVICE 1 /* The service name */ +#define PAM_USER 2 /* The user name */ +#define PAM_TTY 3 /* The tty name */ +#define PAM_RHOST 4 /* The remote host name */ +#define PAM_CONV 5 /* The pam_conv structure */ + +/* missing entries found in <security/pam_modules.h> for modules only! */ + +#define PAM_RUSER 8 /* The remote user name */ +#define PAM_USER_PROMPT 9 /* the prompt for getting a username */ +#define PAM_FAIL_DELAY 10 /* app supplied function to override failure + delays */ + +/* ---------- Common Linux-PAM application/module PI ----------- */ + +extern int pam_set_item(pam_handle_t *pamh, int item_type, const void *item); +extern int pam_get_item(const pam_handle_t *pamh, int item_type, + const void **item); +extern const char *pam_strerror(pam_handle_t *pamh, int errnum); + +extern int pam_putenv(pam_handle_t *pamh, const char *name_value); +extern const char *pam_getenv(pam_handle_t *pamh, const char *name); +extern char **pam_getenvlist(pam_handle_t *pamh); + +/* ---------- Common Linux-PAM application/module PI ----------- */ + +/* + * here are some proposed error status definitions for the + * 'error_status' argument used by the cleanup function associated + * with data items they should be logically OR'd with the error_status + * of the latest return from libpam -- new with .52 and positive + * impression from Sun although not official as of 1996/9/4 + * [generally the other flags are to be found in pam_modules.h] + */ + +#define PAM_DATA_SILENT 0x40000000 /* used to suppress messages... */ + +/* + * here we define an externally (by apps or modules) callable function + * that primes the libpam library to delay when a stacked set of + * modules results in a failure. In the case of PAM_SUCCESS this delay + * is ignored. + * + * Note, the pam_[gs]et_item(... PAM_FAIL_DELAY ...) can be used to set + * a function pointer which can override the default fail-delay behavior. + * This item was added to accommodate event driven programs that need to + * manage delays more carefully. The function prototype for this data + * item is + * void (*fail_delay)(int status, unsigned int delay); + */ + +#define HAVE_PAM_FAIL_DELAY +extern int pam_fail_delay(pam_handle_t *pamh, unsigned int musec_delay); + +#include <syslog.h> +#ifndef LOG_AUTHPRIV +# ifdef LOG_PRIV +# define LOG_AUTHPRIV LOG_PRIV +# endif /* LOG_PRIV */ +#endif /* !LOG_AUTHPRIV */ + +#ifdef MEMORY_DEBUG +/* + * this defines some macros that keep track of what memory has been + * allocated and indicates leakage etc... It should not be included in + * production application/modules. + */ +#include <security/pam_malloc.h> +#endif + +/* ------------ The Linux-PAM conversation structures ------------ */ + +/* Message styles */ + +#define PAM_PROMPT_ECHO_OFF 1 +#define PAM_PROMPT_ECHO_ON 2 +#define PAM_ERROR_MSG 3 +#define PAM_TEXT_INFO 4 + +/* Linux-PAM specific types */ + +#define PAM_RADIO_TYPE 5 /* yes/no/maybe conditionals */ + +/* This is for server client non-human interaction.. these are NOT + part of the X/Open PAM specification. */ + +#define PAM_BINARY_PROMPT 7 + +/* maximum size of messages/responses etc.. (these are mostly + arbitrary so Linux-PAM should handle longer values). */ + +#define PAM_MAX_NUM_MSG 32 +#define PAM_MAX_MSG_SIZE 512 +#define PAM_MAX_RESP_SIZE 512 + +/* Used to pass prompting text, error messages, or other informatory + * text to the user. This structure is allocated and freed by the PAM + * library (or loaded module). */ + +struct pam_message { + int msg_style; + const char *msg; +}; + +/* if the pam_message.msg_style = PAM_BINARY_PROMPT + the 'pam_message.msg' is a pointer to a 'const *' for the following + pseudo-structure. When used with a PAM_BINARY_PROMPT, the returned + pam_response.resp pointer points to an object with the following + structure: + + struct { + u32 length; # network byte order + unsigned char type; + unsigned char data[length-5]; + }; + + The 'libpamc' library is designed around this flavor of + message and should be used to handle this flavor of msg_style. + */ + +/* Used to return the user's response to the PAM library. This + structure is allocated by the application program, and free()'d by + the Linux-PAM library (or calling module). */ + +struct pam_response { + char *resp; + int resp_retcode; /* currently un-used, zero expected */ +}; + +/* The actual conversation structure itself */ + +struct pam_conv { + int (*conv)(int num_msg, const struct pam_message **msg, + struct pam_response **resp, void *appdata_ptr); + void *appdata_ptr; +}; + +#ifndef LINUX_PAM +/* + * the following few lines represent a hack. They are there to make + * the Linux-PAM headers more compatible with the Sun ones, which have a + * less strictly separated notion of module specific and application + * specific definitions. + */ +#include <security/pam_appl.h> +#include <security/pam_modules.h> +#endif + + +/* ... adapted from the pam_appl.h file created by Theodore Ts'o and + * + * Copyright Theodore Ts'o, 1996. All rights reserved. + * Copyright (c) Andrew G. Morgan <morgan@linux.kernel.org>, 1996-8 + * + * 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. */ + +#endif /* _SECURITY__PAM_TYPES_H */ + diff --git a/libpam/include/security/pam_appl.h b/libpam/include/security/pam_appl.h new file mode 100644 index 00000000..2849970a --- /dev/null +++ b/libpam/include/security/pam_appl.h @@ -0,0 +1,92 @@ +/* + * <security/pam_appl.h> + * + * This header file collects definitions for the PAM API --- that is, + * public interface between the PAM library and an application program + * that wishes to use it. + * + * Note, the copyright information is at end of file. + * + * Created: 15-Jan-96 by TYT + * Last modified: 1996/3/5 by AGM + * + * $Id$ + */ + +#ifndef _SECURITY_PAM_APPL_H +#define _SECURITY_PAM_APPL_H + +#include <security/_pam_types.h> /* Linux-PAM common defined types */ + +/* -------------- The Linux-PAM Framework layer API ------------- */ + +#ifdef __cplusplus +extern "C" { +#endif + +extern int pam_start(const char *service_name, const char *user, + const struct pam_conv *pam_conversation, + pam_handle_t **pamh); +extern int pam_end(pam_handle_t *pamh, int pam_status); + +/* Authentication API's */ + +extern int pam_authenticate(pam_handle_t *pamh, int flags); +extern int pam_setcred(pam_handle_t *pamh, int flags); + +/* Account Management API's */ + +extern int pam_acct_mgmt(pam_handle_t *pamh, int flags); + +/* Session Management API's */ + +extern int pam_open_session(pam_handle_t *pamh, int flags); +extern int pam_close_session(pam_handle_t *pamh, int flags); + +/* Password Management API's */ + +extern int pam_chauthtok(pam_handle_t *pamh, int flags); + +#ifdef __cplusplus +} +#endif + +/* take care of any compatibility issues */ +#include <security/_pam_compat.h> + +/* + * Copyright Theodore Ts'o, 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. + */ + +#endif /* _SECURITY_PAM_APPL_H */ diff --git a/libpam/include/security/pam_malloc.h b/libpam/include/security/pam_malloc.h new file mode 100644 index 00000000..8daf3f7c --- /dev/null +++ b/libpam/include/security/pam_malloc.h @@ -0,0 +1,79 @@ +/* $Id$ + * + * $Log$ + * Revision 1.1 2000/06/20 22:11:23 agmorgan + * Initial revision + * + * Revision 1.1.1.1 1998/07/12 05:17:15 morgan + * Linux PAM sources pre-0.66 + * + * Revision 1.1 1996/11/10 21:23:14 morgan + * Initial revision + * + */ + +/* + * This file (via the use of macros) defines a wrapper for the malloc + * family of calls. It logs where the memory was requested and also + * where it was free()'d and keeps a list of currently requested memory. + * + * It is hoped that it will provide some help in locating memory leaks. + */ + +#ifndef PAM_MALLOC_H +#define PAM_MALLOC_H + +/* these are the macro definitions for the stdlib.h memory functions */ + +#define malloc(s) pam_malloc(s,__FILE__,__FUNCTION__,__LINE__) +#define calloc(n,s) pam_calloc(n,s,__FILE__,__FUNCTION__,__LINE__) +#define free(x) pam_free(x,__FILE__,__FUNCTION__,__LINE__) +/* #define memalign(a,s) pam_memalign(a,s,__FILE__,__FUNCTION__,__LINE__) */ +#define realloc(x,s) pam_realloc(x,s,__FILE__,__FUNCTION__,__LINE__) +/* #define valloc(s) pam_valloc(s,__FILE__,__FUNCTION__,__LINE__) */ +/* #define alloca(s) pam_alloca(s,__FILE__,__FUNCTION__,__LINE__) */ +#define exit(i) pam_exit(i,__FILE__,__FUNCTION__,__LINE__) + +/* these are the prototypes for the wrapper functions */ + +#include <sys/types.h> + +extern void *pam_malloc(size_t s,const char *,const char *,const int); +extern void *pam_calloc(size_t n,size_t s,const char *,const char *,const int); +extern void pam_free(void *x,const char *,const char *,const int); +extern void *pam_memalign(size_t a,size_t s + ,const char *,const char *,const int); +extern void *pam_realloc(void *x,size_t s,const char *,const char *,const int); +extern void *pam_valloc(size_t s,const char *,const char *,const int); +extern void *pam_alloca(size_t s,const char *,const char *,const int); +extern void pam_exit(int i,const char *,const char *,const int); + +/* these are the flags used to turn on and off diagnostics */ + +#define PAM_MALLOC_LEAKED 01 +#define PAM_MALLOC_REQUEST 02 +#define PAM_MALLOC_FREE 04 +#define PAM_MALLOC_EXCH (PAM_MALLOC_FREED|PAM_MALLOC_EXCH) +#define PAM_MALLOC_RESIZE 010 +#define PAM_MALLOC_FAIL 020 +#define PAM_MALLOC_NULL 040 +#define PAM_MALLOC_VERIFY 0100 +#define PAM_MALLOC_FUNC 0200 +#define PAM_MALLOC_PAUSE 0400 +#define PAM_MALLOC_STOP 01000 + +#define PAM_MALLOC_ALL 0777 + +#define PAM_MALLOC_DEFAULT \ + (PAM_MALLOC_LEAKED|PAM_MALLOC_PAUSE|PAM_MALLOC_FAIL) + +#include <stdio.h> + +extern FILE *pam_malloc_outfile; /* defaults to stdout */ + +/* how much output do you want? */ + +extern int pam_malloc_flags; +extern int pam_malloc_delay_length; /* how long to pause on errors */ + +#endif /* PAM_MALLOC_H */ diff --git a/libpam/include/security/pam_modules.h b/libpam/include/security/pam_modules.h new file mode 100644 index 00000000..945c1711 --- /dev/null +++ b/libpam/include/security/pam_modules.h @@ -0,0 +1,195 @@ +/* + * <security/pam_modules.h> + * + * $Id$ + * + * This header file documents the PAM SPI --- that is, interface + * between the PAM library and a PAM service library which is called + * by the PAM library. + * + * Note, the copyright information is at end of file. + * + * $Log$ + * Revision 1.1 2000/06/20 22:11:23 agmorgan + * Initial revision + * + * Revision 1.1.1.1 1998/07/12 05:17:15 morgan + * Linux PAM sources pre-0.66 + * + * Revision 1.8 1997/01/04 20:14:42 morgan + * moved PAM_DATA_SILENT to _pam_types.h so applications can use it too + * + * Revision 1.7 1996/11/10 19:57:08 morgan + * pam_get_user prototype. + * + * Revision 1.6 1996/09/05 06:18:45 morgan + * added some data error_status masks, changed prototype for cleanup() + * + * Revision 1.5 1996/06/02 07:58:37 morgan + * altered the way in which modules obtain static prototypes for + * functions + * + */ + +#ifndef _SECURITY_PAM_MODULES_H +#define _SECURITY_PAM_MODULES_H + +#include <security/_pam_types.h> /* Linux-PAM common defined types */ + +/* these defines are used by pam_set_item() and pam_get_item() and are + * in addition to those found in <security/_pam_types.h> */ + +#define PAM_AUTHTOK 6 /* The authentication token (password) */ +#define PAM_OLDAUTHTOK 7 /* The old authentication token */ + +/* -------------- The Linux-PAM Module PI ------------- */ + +extern int pam_set_data(pam_handle_t *pamh, const char *module_data_name, + void *data, + void (*cleanup)(pam_handle_t *pamh, void *data, + int error_status)); +extern int pam_get_data(const pam_handle_t *pamh, + const char *module_data_name, const void **data); + +extern int pam_get_user(pam_handle_t *pamh, const char **user + , const char *prompt); + +#ifdef PAM_STATIC + +#define PAM_EXTERN static + +struct pam_module { + const char *name; /* Name of the module */ + + /* These are function pointers to the module's key functions. */ + + int (*pam_sm_authenticate)(pam_handle_t *pamh, int flags, + int argc, const char **argv); + int (*pam_sm_setcred)(pam_handle_t *pamh, int flags, + int argc, const char **argv); + int (*pam_sm_acct_mgmt)(pam_handle_t *pamh, int flags, + int argc, const char **argv); + int (*pam_sm_open_session)(pam_handle_t *pamh, int flags, + int argc, const char **argv); + int (*pam_sm_close_session)(pam_handle_t *pamh, int flags, + int argc, const char **argv); + int (*pam_sm_chauthtok)(pam_handle_t *pamh, int flags, + int argc, const char **argv); +}; + +#else /* !PAM_STATIC */ + +#define PAM_EXTERN extern + +#endif /* PAM_STATIC */ + +/* Lots of files include pam_modules.h that don't need these + * declared. However, when they are declared static, they + * need to be defined later. So we have to protect C files + * that include these without wanting these functions defined.. */ + +#if (defined(PAM_STATIC) && defined(PAM_SM_AUTH)) || !defined(PAM_STATIC) + +/* Authentication API's */ +PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, + int argc, const char **argv); +PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags, + int argc, const char **argv); + +#endif /*(defined(PAM_STATIC) && defined(PAM_SM_AUTH)) + || !defined(PAM_STATIC)*/ + +#if (defined(PAM_STATIC) && defined(PAM_SM_ACCOUNT)) || !defined(PAM_STATIC) + +/* Account Management API's */ +PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, + int argc, const char **argv); + +#endif /*(defined(PAM_STATIC) && defined(PAM_SM_ACCOUNT)) + || !defined(PAM_STATIC)*/ + +#if (defined(PAM_STATIC) && defined(PAM_SM_SESSION)) || !defined(PAM_STATIC) + +/* Session Management API's */ +PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags, + int argc, const char **argv); + +PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags, + int argc, const char **argv); + +#endif /*(defined(PAM_STATIC) && defined(PAM_SM_SESSION)) + || !defined(PAM_STATIC)*/ + +#if (defined(PAM_STATIC) && defined(PAM_SM_PASSWORD)) || !defined(PAM_STATIC) + +/* Password Management API's */ +PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags, + int argc, const char **argv); + +#endif /*(defined(PAM_STATIC) && defined(PAM_SM_PASSWORD)) + || !defined(PAM_STATIC)*/ + +/* The following two flags are for use across the Linux-PAM/module + * interface only. The Application is not permitted to use these + * tokens. + * + * The password service should only perform preliminary checks. No + * passwords should be updated. */ +#define PAM_PRELIM_CHECK 0x4000 + +/* The password service should update passwords Note: PAM_PRELIM_CHECK + * and PAM_UPDATE_AUTHTOK can not both be set simultaneously! */ +#define PAM_UPDATE_AUTHTOK 0x2000 + + +/* + * here are some proposed error status definitions for the + * 'error_status' argument used by the cleanup function associated + * with data items they should be logically OR'd with the error_status + * of the latest return from libpam -- new with .52 and positive + * impression from Sun although not official as of 1996/9/4 there are + * others in _pam_types.h -- they are for common module/app use. + */ + +#define PAM_DATA_REPLACE 0x20000000 /* used when replacing a data item */ + +/* take care of any compatibility issues */ +#include <security/_pam_compat.h> + +/* Copyright (C) Theodore Ts'o, 1996. + * Copyright (C) Andrew Morgan, 1996-8. + * 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 General Public License, in which case the provisions of the + * GNU GPL are required INSTEAD OF the above restrictions. (This + * clause is necessary due to a potential bad interaction between the + * GNU 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. */ + +#endif /* _SECURITY_PAM_MODULES_H */ + diff --git a/libpam/pam_account.c b/libpam/pam_account.c new file mode 100644 index 00000000..ffc01acd --- /dev/null +++ b/libpam/pam_account.c @@ -0,0 +1,13 @@ +/* pam_account.c - PAM Account Management */ + +#include <stdio.h> + +#include "pam_private.h" + +int pam_acct_mgmt(pam_handle_t *pamh, int flags) +{ + D(("called")); + + IF_NO_PAMH("pam_acct_mgmt",pamh,PAM_SYSTEM_ERR); + return _pam_dispatch(pamh, flags, PAM_ACCOUNT); +} diff --git a/libpam/pam_auth.c b/libpam/pam_auth.c new file mode 100644 index 00000000..c946eaab --- /dev/null +++ b/libpam/pam_auth.c @@ -0,0 +1,67 @@ +/* + * pam_auth.c -- PAM authentication + * + * $Id$ + * + * $Log$ + * Revision 1.1 2000/06/20 22:11:13 agmorgan + * Initial revision + * + * Revision 1.1.1.1 1998/07/12 05:17:15 morgan + * Linux PAM sources pre-0.66 + * + * Revision 1.7 1997/04/05 06:53:52 morgan + * fail-delay changes + * + */ + +#include <stdio.h> +#include <stdlib.h> + +#include "pam_private.h" + +int pam_authenticate(pam_handle_t *pamh, int flags) +{ + int retval; + + D(("pam_authenticate called")); + + if (pamh->former.choice == PAM_NOT_STACKED) { + _pam_sanitize(pamh); + _pam_start_timer(pamh); /* we try to make the time for a failure + independent of the time it takes to + fail */ + } + + IF_NO_PAMH("pam_authenticate",pamh,PAM_SYSTEM_ERR); + retval = _pam_dispatch(pamh, flags, PAM_AUTHENTICATE); + + if (retval != PAM_INCOMPLETE) { + _pam_sanitize(pamh); + _pam_await_timer(pamh, retval); /* if unsuccessful then wait now */ + D(("pam_authenticate exit")); + } else { + D(("will resume when ready")); + } + + return retval; +} + +int pam_setcred(pam_handle_t *pamh, int flags) +{ + int retval; + + IF_NO_PAMH("pam_setcred", pamh, PAM_SYSTEM_ERR); + + D(("pam_setcred called")); + + if (! flags) { + flags = PAM_ESTABLISH_CRED; + } + + retval = _pam_dispatch(pamh, flags, PAM_SETCRED); + + D(("pam_setcred exit")); + + return retval; +} diff --git a/libpam/pam_data.c b/libpam/pam_data.c new file mode 100644 index 00000000..6422b10e --- /dev/null +++ b/libpam/pam_data.c @@ -0,0 +1,105 @@ +/* pam_data.c */ + +/* + * $Id$ + */ + +#include <stdlib.h> +#include <string.h> + +#include "pam_private.h" + +struct pam_data *_pam_locate_data(const pam_handle_t *pamh, const char *name); + +int pam_set_data( + pam_handle_t *pamh, + const char *module_data_name, + void *data, + void (*cleanup)(pam_handle_t *pamh, void *data, int error_status)) +{ + struct pam_data *data_entry; + + IF_NO_PAMH("pam_set_data",pamh,PAM_SYSTEM_ERR); + + /* first check if there is some data already. If so clean it up */ + + if ((data_entry = _pam_locate_data(pamh, module_data_name))) { + if (data_entry->cleanup) { + data_entry->cleanup(pamh, data_entry->data, + PAM_DATA_REPLACE | PAM_SUCCESS ); + } + } else if ((data_entry = malloc(sizeof(*data_entry)))) { + char *tname; + + if ((tname = _pam_strdup(module_data_name)) == NULL) { + _pam_system_log(LOG_CRIT, "pam_set_data: no memory for data name"); + _pam_drop(data_entry); + return PAM_BUF_ERR; + } + data_entry->next = pamh->data; + pamh->data = data_entry; + data_entry->name = tname; + } else { + _pam_system_log(LOG_CRIT, "pam_set_data: cannot allocate data entry"); + return PAM_BUF_ERR; + } + + data_entry->data = data; /* note this could be NULL */ + data_entry->cleanup = cleanup; + + return PAM_SUCCESS; +} + +int pam_get_data( + const pam_handle_t *pamh, + const char *module_data_name, + const void **datap) +{ + struct pam_data *data; + + IF_NO_PAMH("pam_get_data",pamh,PAM_SYSTEM_ERR); + + data = _pam_locate_data(pamh, module_data_name); + if (data) { + *datap = data->data; + return PAM_SUCCESS; + } + + return PAM_NO_MODULE_DATA; +} + +struct pam_data *_pam_locate_data(const pam_handle_t *pamh, const char *name) +{ + struct pam_data *data; + + IF_NO_PAMH("_pam_locate_data",pamh,NULL); + data = pamh->data; + + while (data) { + if (!strcmp(data->name, name)) { + return data; + } + data = data->next; + } + + return NULL; +} + +void _pam_free_data(pam_handle_t *pamh, int status) +{ + struct pam_data *last; + struct pam_data *data; + + IF_NO_PAMH("_pam_free_data",pamh,/* no return value for void fn */); + data = pamh->data; + + while (data) { + last = data; + data = data->next; + if (last->cleanup) { + last->cleanup(pamh, last->data, status); + } + _pam_drop(last->name); + _pam_drop(last); + } +} diff --git a/libpam/pam_delay.c b/libpam/pam_delay.c new file mode 100644 index 00000000..b7c6604d --- /dev/null +++ b/libpam/pam_delay.c @@ -0,0 +1,168 @@ +/* + * pam_delay.c + * + * Copyright (c) Andrew G. Morgan <morgan@linux.kernel.org> 1996-9 + * All rights reserved. + * + * $Id$ + * + * $Log$ + * Revision 1.1 2000/06/20 22:11:14 agmorgan + * Initial revision + * + * Revision 1.2 1999/07/04 23:23:42 morgan + * add appdata_ptr to app callback function + * + * Revision 1.1.1.1 1998/07/12 05:17:15 morgan + * Linux PAM sources pre-0.66 + * + */ + +/* + * This is a simple implementation of a delay on failure mechanism; an + * attempt to overcome authentication-time attacks in a simple manner. + */ + +#include <unistd.h> +#include "pam_private.h" + +/* ********************************************************************** + * initialize the time as unset, this is set on the return from the + * authenticating pair of of the libpam pam_XXX calls. + */ + +void _pam_reset_timer(pam_handle_t *pamh) +{ + D(("setting pamh->fail_delay.set to FALSE")); + pamh->fail_delay.set = PAM_FALSE; +} + +/* ********************************************************************** + * this function sets the start time for possible delayed failing. + * + * Eventually, it may set the timer so libpam knows how long the program + * has already been executing. Currently, this value is used to seed + * a pseudo-random number generator... + */ + +void _pam_start_timer(pam_handle_t *pamh) +{ + pamh->fail_delay.begin = time(NULL); + D(("starting timer...")); +} + +/* ******************************************************************* + * Compute a pseudo random time. The value is base*(1 +/- 1/5) where + * the distribution is pseudo gausian (the sum of three evenly + * distributed random numbers -- central limit theorem and all ;^) The + * linear random numbers are based on a formulae given in Knuth's + * Seminumerical recipies that was reproduced in `Numerical Recipies + * in C'. It is *not* a cryptographically strong generator, but it is + * probably "good enough" for our purposes here. + * + * /dev/random might be a better place to look for some numbers... + */ + +static unsigned int _pam_rand(unsigned int seed) +{ +#define N1 1664525 +#define N2 1013904223 + return N1*seed + N2; +} + +static unsigned int _pam_compute_delay(unsigned int seed, unsigned int base) +{ + int i; + double sum; + unsigned int ans; + + for (sum=i=0; i<3; ++i) { + seed = _pam_rand(seed); + sum += (double) ((seed / 10) % 1000000); + } + sum = (sum/3.)/1e6 - .5; /* rescale */ + ans = (unsigned int) ( base*(1.+sum) ); + D(("random number: base=%u -> ans=%u\n", base, ans)); + + return ans; +} + +/* ********************************************************************** + * the following function sleeps for a random time. The actual time + * slept is computed above.. It is based on the requested time but will + * differ by up to +/- 25%. + */ + +void _pam_await_timer(pam_handle_t *pamh, int status) +{ + unsigned int delay; + D(("waiting?...")); + + delay = _pam_compute_delay(pamh->fail_delay.begin, + pamh->fail_delay.delay); + if (pamh->fail_delay.delay_fn_ptr) { + union { + const void *value; + void (*fn)(int, unsigned, void *); + } hack_fn_u; + void *appdata_ptr; + + if (pamh->pam_conversation) { + appdata_ptr = pamh->pam_conversation->appdata_ptr; + } else { + appdata_ptr = NULL; + } + + /* always call the applications delay function, even if + the delay is zero - indicate status */ + hack_fn_u.value = pamh->fail_delay.delay_fn_ptr; + hack_fn_u.fn(status, delay, appdata_ptr); + + } else if (status != PAM_SUCCESS && pamh->fail_delay.set) { + + D(("will wait %u usec", delay)); + + if (delay > 0) { + struct timeval tval; + + tval.tv_sec = delay / 1000000; + tval.tv_usec = delay % 1000000; + select(0, NULL, NULL, NULL, &tval); + } + } + + _pam_reset_timer(pamh); + D(("waiting done")); +} + +/* ********************************************************************** + * this function is known to both the module and the application, it + * keeps a running score of the largest-requested delay so far, as + * specified by either modules or an application. + */ + +int pam_fail_delay(pam_handle_t *pamh, unsigned int usec) +{ + int largest; + + IF_NO_PAMH("pam_fail_delay", pamh, PAM_SYSTEM_ERR); + + D(("setting delay to %u",usec)); + + if (pamh->fail_delay.set) { + largest = pamh->fail_delay.delay; + } else { + pamh->fail_delay.set = PAM_TRUE; + largest = 0; + } + + D(("largest = %u",largest)); + + if (largest < usec) { + D(("resetting largest delay")); + pamh->fail_delay.delay = usec; + } + + return PAM_SUCCESS; +} + diff --git a/libpam/pam_dispatch.c b/libpam/pam_dispatch.c new file mode 100644 index 00000000..285f3316 --- /dev/null +++ b/libpam/pam_dispatch.c @@ -0,0 +1,282 @@ +/* pam_dispatch.c - handles module function dispatch */ + +/* + * Copyright (c) 1998 Andrew G. Morgan <morgan@linux.kernel.org> + * + * $Id$ + */ + +#include <stdlib.h> +#include <stdio.h> + +#include "pam_private.h" + +/* + * this is the return code we return when a function pointer is NULL + * or, the handler structure indicates a broken module config line + */ +#define PAM_MUST_FAIL_CODE PAM_PERM_DENIED + +/* impression codes - this gives some sense to the logical choices */ +#define _PAM_UNDEF 0 +#define _PAM_POSITIVE +1 +#define _PAM_NEGATIVE -1 + +/* + * walk a stack of modules. Interpret the administrator's instructions + * when combining the return code of each module. + */ + +static int _pam_dispatch_aux(pam_handle_t *pamh, int flags, struct handler *h, + _pam_boolean resumed) +{ + int depth, impression, status, skip_depth; + + IF_NO_PAMH("_pam_dispatch_aux", pamh, PAM_SYSTEM_ERR); + + if (h == NULL) { + const char *service=NULL; + + (void) pam_get_item(pamh, PAM_SERVICE, (const void **)&service); + _pam_system_log(LOG_ERR, "no modules loaded for `%s' service", + service ? service:"<unknown>" ); + service = NULL; + return PAM_MUST_FAIL_CODE; + } + + /* if we are recalling this module stack because a former call did + not complete, we restore the state of play from pamh. */ + if (resumed) { + skip_depth = pamh->former.depth; + status = pamh->former.status; + impression = pamh->former.impression; + /* forget all that */ + pamh->former.impression = _PAM_UNDEF; + pamh->former.status = PAM_MUST_FAIL_CODE; + pamh->former.depth = 0; + } else { + skip_depth = 0; + impression = _PAM_UNDEF; + status = PAM_MUST_FAIL_CODE; + } + + /* Loop through module logic stack */ + for (depth=0 ; h != NULL ; h = h->next, ++depth) { + int retval, action; + + /* skip leading modules if they have already returned */ + if (depth < skip_depth) { + continue; + } + + /* attempt to call the module */ + if (h->func == NULL) { + D(("module function is not defined, indicating failure")); + retval = PAM_MODULE_UNKNOWN; + } else { + D(("passing control to module...")); + retval = h->func(pamh, flags, h->argc, h->argv); + D(("module returned: %s", pam_strerror(pamh, retval))); + if (h->must_fail) { + D(("module poorly listed in pam.conf; forcing failure")); + retval = PAM_MUST_FAIL_CODE; + } + } + + /* + * PAM_INCOMPLETE return is special. It indicates that the + * module wants to wait for the application before continuing. + * In order to return this, the module will have saved its + * state so it can resume from an equivalent position when it + * is called next time. (This was added as of 0.65) + */ + if (retval == PAM_INCOMPLETE) { + pamh->former.impression = impression; + pamh->former.status = status; + pamh->former.depth = depth; + + D(("module %d returned PAM_INCOMPLETE", depth)); + return retval; + } + + /* verify that the return value is a valid one */ + if (retval < PAM_SUCCESS || retval >= _PAM_RETURN_VALUES) { + retval = PAM_MUST_FAIL_CODE; + action = _PAM_ACTION_BAD; + } else { + action = h->actions[retval]; + } + + /* decide what to do */ + switch (action) { + case _PAM_ACTION_RESET: + impression = _PAM_UNDEF; + status = PAM_MUST_FAIL_CODE; + break; + + case _PAM_ACTION_OK: + case _PAM_ACTION_DONE: + if ( impression == _PAM_UNDEF + || (impression == _PAM_POSITIVE && status == PAM_SUCCESS) ) { + impression = _PAM_POSITIVE; + status = retval; + } + if ( impression == _PAM_POSITIVE && action == _PAM_ACTION_DONE ) { + goto decision_made; + } + break; + + case _PAM_ACTION_BAD: + case _PAM_ACTION_DIE: +#ifdef PAM_FAIL_NOW_ON + if ( retval == PAM_ABORT ) { + impression = _PAM_NEGATIVE; + status = PAM_PERM_DENIED; + goto decision_made; + } +#endif /* PAM_FAIL_NOW_ON */ + if ( impression != _PAM_NEGATIVE ) { + impression = _PAM_NEGATIVE; + status = retval; + } + if ( action == _PAM_ACTION_DIE ) { + goto decision_made; + } + break; + + case _PAM_ACTION_IGNORE: + break; + + /* if we get here, we expect action is a positive number -- + this is what the ...JUMP macro checks. */ + + default: + if ( _PAM_ACTION_IS_JUMP(action) ) { + /* this means that we need to skip #action stacked modules */ + do { + h = h->next; + } while ( --action > 0 && h != NULL ); + + /* note if we try to skip too many modules action is + still non-zero and we snag the next if. */ + } + + /* this case is a syntax error: we can't succeed */ + if (action) { + D(("action syntax error")); + impression = _PAM_NEGATIVE; + status = PAM_MUST_FAIL_CODE; + } + } + } + +decision_made: /* by getting here we have made a decision */ + + /* Sanity check */ + if ( status == PAM_SUCCESS && impression != _PAM_POSITIVE ) { + D(("caught on sanity check -- this is probably a config error!")); + status = PAM_MUST_FAIL_CODE; + } + + /* We have made a decision about the modules executed */ + return status; +} + +/* + * This function translates the module dispatch request into a pointer + * to the stack of modules that will actually be run. the + * _pam_dispatch_aux() function (above) is responsible for walking the + * module stack. + */ + +int _pam_dispatch(pam_handle_t *pamh, int flags, int choice) +{ + struct handler *h = NULL; + int retval; + _pam_boolean resumed; + + IF_NO_PAMH("_pam_dispatch",pamh,PAM_SYSTEM_ERR); + + /* Load all modules, resolve all symbols */ + + if ((retval = _pam_init_handlers(pamh)) != PAM_SUCCESS) { + _pam_system_log(LOG_ERR, "unable to dispatch function"); + return retval; + } + + switch (choice) { + case PAM_AUTHENTICATE: + h = pamh->handlers.conf.authenticate; + break; + case PAM_SETCRED: + h = pamh->handlers.conf.setcred; + break; + case PAM_ACCOUNT: + h = pamh->handlers.conf.acct_mgmt; + break; + case PAM_OPEN_SESSION: + h = pamh->handlers.conf.open_session; + break; + case PAM_CLOSE_SESSION: + h = pamh->handlers.conf.close_session; + break; + case PAM_CHAUTHTOK: + h = pamh->handlers.conf.chauthtok; + break; + default: + _pam_system_log(LOG_ERR, "undefined fn choice; %d", choice); + return PAM_ABORT; + } + + if (h == NULL) { /* there was no handlers.conf... entry; will use + * handlers.other... */ + switch (choice) { + case PAM_AUTHENTICATE: + h = pamh->handlers.other.authenticate; + break; + case PAM_SETCRED: + h = pamh->handlers.other.setcred; + break; + case PAM_ACCOUNT: + h = pamh->handlers.other.acct_mgmt; + break; + case PAM_OPEN_SESSION: + h = pamh->handlers.other.open_session; + break; + case PAM_CLOSE_SESSION: + h = pamh->handlers.other.close_session; + break; + case PAM_CHAUTHTOK: + h = pamh->handlers.other.chauthtok; + break; + } + } + + /* Did a module return an "incomplete state" last time? */ + if (pamh->former.choice != PAM_NOT_STACKED) { + if (pamh->former.choice != choice) { + _pam_system_log(LOG_ERR, + "application failed to re-exec stack [%d:%d]", + pamh->former.choice, choice); + return PAM_ABORT; + } + resumed = PAM_TRUE; + } else { + resumed = PAM_FALSE; + } + + /* call the list of module functions */ + retval = _pam_dispatch_aux(pamh, flags, h, resumed); + resumed = PAM_FALSE; + + /* Should we recall where to resume next time? */ + if (retval == PAM_INCOMPLETE) { + D(("module [%d] returned PAM_INCOMPLETE")); + pamh->former.choice = choice; + } else { + pamh->former.choice = PAM_NOT_STACKED; + } + + return retval; +} + diff --git a/libpam/pam_end.c b/libpam/pam_end.c new file mode 100644 index 00000000..b389038f --- /dev/null +++ b/libpam/pam_end.c @@ -0,0 +1,72 @@ +/* pam_end.c */ + +/* + * $Id$ + */ + +#include <stdlib.h> + +#include "pam_private.h" + +int pam_end(pam_handle_t *pamh, int pam_status) +{ + int ret; + + IF_NO_PAMH("pam_end", pamh, PAM_SYSTEM_ERR); + + D(("entering pam_end()")); + + /* first liberate the modules (it is not inconcevible that the + modules may need to use the service_name etc. to clean up) */ + + _pam_free_data(pamh, pam_status); + + /* now drop all modules */ + + if ((ret = _pam_free_handlers(pamh)) != PAM_SUCCESS) { + return ret; /* error occurred */ + } + + /* from this point we cannot call the modules any more. Free the remaining + memory used by the Linux-PAM interface */ + + _pam_drop_env(pamh); /* purge the environment */ + + _pam_overwrite(pamh->authtok); /* blank out old token */ + _pam_drop(pamh->authtok); + + _pam_overwrite(pamh->oldauthtok); /* blank out old token */ + _pam_drop(pamh->oldauthtok); + + _pam_overwrite(pamh->former.prompt); + _pam_drop(pamh->former.prompt); /* drop saved prompt */ + + _pam_overwrite(pamh->service_name); + _pam_drop(pamh->service_name); + + _pam_overwrite(pamh->user); + _pam_drop(pamh->user); + + _pam_overwrite(pamh->prompt); + _pam_drop(pamh->prompt); /* prompt for pam_get_user() */ + + _pam_overwrite(pamh->tty); + _pam_drop(pamh->tty); + + _pam_overwrite(pamh->rhost); + _pam_drop(pamh->rhost); + + _pam_overwrite(pamh->ruser); + _pam_drop(pamh->ruser); + + _pam_drop(pamh->pam_conversation); + pamh->fail_delay.delay_fn_ptr = NULL; + + /* and finally liberate the memory for the pam_handle structure */ + + _pam_drop(pamh); + + D(("exiting pam_end() successfully")); + + return PAM_SUCCESS; +} diff --git a/libpam/pam_env.c b/libpam/pam_env.c new file mode 100644 index 00000000..a1e744b7 --- /dev/null +++ b/libpam/pam_env.c @@ -0,0 +1,389 @@ +/* + * pam_env.c + * + * Copyright (c) Andrew G. Morgan <morgan@parc.power.net> 1996,1997 + * All rights reserved. + * + * This file was written from a "hint" provided by the people at SUN. + * and the X/Open XSSO draft of March 1997. + * + * $Id$ + */ + +#include <string.h> +#include <stdlib.h> +#ifdef sunos +#define memmove(x,y,z) bcopy(y,x,z) +#endif + +#include "pam_private.h" + +/* helper functions */ + +#ifdef DEBUG +static void _pam_dump_env(pam_handle_t *pamh) +{ + int i; + + D(("Listing environment of pamh=%p", pamh)); + D(("pamh->env = %p", pamh->env)); + D(("environment entries used = %d [of %d allocated]" + , pamh->env->requested, pamh->env->entries)); + + for (i=0; i<pamh->env->requested; ++i) { + _pam_output_debug(">%-3d [%9p]:[%s]" + , i, pamh->env->list[i], pamh->env->list[i]); + } + _pam_output_debug("*NOTE* the last item should be (nil)"); +} +#else +#define _pam_dump_env(x) +#endif + +/* + * Create the environment + */ + +int _pam_make_env(pam_handle_t *pamh) +{ + D(("called.")); + IF_NO_PAMH("_pam_make_env", pamh, PAM_ABORT); + + /* + * get structure memory + */ + + pamh->env = (struct pam_environ *) malloc(sizeof(struct pam_environ)); + if (pamh->env == NULL) { + _pam_system_log(LOG_CRIT, "_pam_make_env: out of memory"); + return PAM_BUF_ERR; + } + + /* + * get list memory + */ + + pamh->env->list = (char **)calloc( PAM_ENV_CHUNK, sizeof(char *) ); + if (pamh->env->list == NULL) { + _pam_system_log(LOG_CRIT, "_pam_make_env: no memory for list"); + _pam_drop(pamh->env); + return PAM_BUF_ERR; + } + + /* + * fill entries in pamh->env + */ + + pamh->env->entries = PAM_ENV_CHUNK; + pamh->env->requested = 1; + pamh->env->list[0] = NULL; + + _pam_dump_env(pamh); /* only active when debugging */ + + return PAM_SUCCESS; +} + +/* + * purge the environment + */ + +void _pam_drop_env(pam_handle_t *pamh) +{ + D(("called.")); + IF_NO_PAMH("_pam_make_env", pamh, /* nothing to return */); + + if (pamh->env != NULL) { + int i; + /* we will only purge the pamh->env->requested number of elements */ + + for (i=pamh->env->requested-1; i-- > 0; ) { + D(("dropping #%3d>%s<", i, pamh->env->list[i])); + _pam_overwrite(pamh->env->list[i]); /* clean */ + _pam_drop(pamh->env->list[i]); /* forget */ + } + pamh->env->requested = 0; + pamh->env->entries = 0; + _pam_drop(pamh->env->list); /* forget */ + _pam_drop(pamh->env); /* forget */ + } else { + D(("no environment present in pamh?")); + } +} + +/* + * Return the item number of the given variable = first 'length' chars + * of 'name_value'. Since this is a static function, it is safe to + * assume its supplied arguments are well defined. + */ + +static int _pam_search_env(const struct pam_environ *env + , const char *name_value, int length) +{ + int i; + + for (i=env->requested-1; i-- > 0; ) { + if (strncmp(name_value,env->list[i],length) == 0 + && env->list[i][length] == '=') { + + return i; /* Got it! */ + + } + } + + return -1; /* no luck */ +} + +/* + * externally visible functions + */ + +/* + * pam_putenv(): Add/replace/delete a PAM-environment variable. + * + * Add/replace: + * name_value = "NAME=VALUE" or "NAME=" (for empty value="\0") + * + * delete: + * name_value = "NAME" + */ + +int pam_putenv(pam_handle_t *pamh, const char *name_value) +{ + int l2eq, item, retval; + + D(("called.")); + IF_NO_PAMH("pam_putenv", pamh, PAM_ABORT); + + if (name_value == NULL) { + _pam_system_log(LOG_ERR, "pam_putenv: no variable indicated"); + return PAM_PERM_DENIED; + } + + /* + * establish if we are setting or deleting; scan for '=' + */ + + for (l2eq=0; name_value[l2eq] && name_value[l2eq] != '='; ++l2eq); + if (l2eq <= 0) { + _pam_system_log(LOG_ERR, "pam_putenv: bad variable"); + return PAM_BAD_ITEM; + } + + /* + * Look first for environment. + */ + + if (pamh->env == NULL || pamh->env->list == NULL) { + _pam_system_log(LOG_ERR, "pam_putenv: no env%s found", + pamh->env == NULL ? "":"-list"); + return PAM_ABORT; + } + + /* find the item to replace */ + + item = _pam_search_env(pamh->env, name_value, l2eq); + + if (name_value[l2eq]) { /* (re)setting */ + + if (item == -1) { /* new variable */ + D(("adding item: %s", name_value)); + /* enough space? */ + if (pamh->env->entries <= pamh->env->requested) { + register int i; + register char **tmp; + + /* get some new space */ + tmp = calloc( pamh->env->entries + PAM_ENV_CHUNK + , sizeof(char *) ); + if (tmp == NULL) { + /* nothing has changed - old env intact */ + _pam_system_log(LOG_CRIT, + "pam_putenv: cannot grow environment"); + return PAM_BUF_ERR; + } + + /* copy old env-item pointers/forget old */ + for (i=0; i<pamh->env->requested; ++i) { + tmp[i] = pamh->env->list[i]; + pamh->env->list[i] = NULL; + } + + /* drop old list and replace with new */ + _pam_drop(pamh->env->list); + pamh->env->list = tmp; + pamh->env->entries += PAM_ENV_CHUNK; + + D(("resized env list")); + _pam_dump_env(pamh); /* only when debugging */ + } + + item = pamh->env->requested-1; /* old last item (NULL) */ + + /* add a new NULL entry at end; increase counter */ + pamh->env->list[pamh->env->requested++] = NULL; + + } else { /* replace old */ + D(("replacing item: %s\n with: %s" + , pamh->env->list[item], name_value)); + _pam_overwrite(pamh->env->list[item]); + _pam_drop(pamh->env->list[item]); + } + + /* + * now we have a place to put the new env-item, insert at 'item' + */ + + pamh->env->list[item] = _pam_strdup(name_value); + if (pamh->env->list[item] != NULL) { + _pam_dump_env(pamh); /* only when debugging */ + return PAM_SUCCESS; + } + + /* something went wrong; we should delete the item - fall through */ + + retval = PAM_BUF_ERR; /* an error occurred */ + } else { + retval = PAM_SUCCESS; /* we requested delete */ + } + + /* getting to here implies we are deleting an item */ + + if (item < 0) { + _pam_system_log(LOG_ERR, "pam_putenv: delete non-existent entry; %s", + name_value); + return PAM_BAD_ITEM; + } + + /* + * remove item: purge memory; reset counter; resize [; display-env] + */ + + D(("deleting: env#%3d:[%s]", item, pamh->env->list[item])); + _pam_overwrite(pamh->env->list[item]); + _pam_drop(pamh->env->list[item]); + --(pamh->env->requested); + D(("mmove: item[%d]+%d -> item[%d]" + , item+1, ( pamh->env->requested - item ), item)); + (void) memmove(&pamh->env->list[item], &pamh->env->list[item+1] + , ( pamh->env->requested - item )*sizeof(char *) ); + + _pam_dump_env(pamh); /* only when debugging */ + + /* + * deleted. + */ + + return retval; +} + +/* + * Return the value of the requested environment variable + */ + +const char *pam_getenv(pam_handle_t *pamh, const char *name) +{ + int item; + + D(("called.")); + IF_NO_PAMH("pam_getenv", pamh, NULL); + + if (name == NULL) { + _pam_system_log(LOG_ERR, "pam_getenv: no variable indicated"); + return NULL; + } + + if (pamh->env == NULL || pamh->env->list == NULL) { + _pam_system_log(LOG_ERR, "pam_getenv: no env%s found", + pamh->env == NULL ? "":"-list" ); + return NULL; + } + + /* find the requested item */ + + item = _pam_search_env(pamh->env, name, strlen(name)); + if (item != -1) { + + D(("env-item: %s, found!", name)); + return (pamh->env->list[item] + 1 + strlen(name)); + + } else { + + D(("env-item: %s, not found", name)); + return NULL; + + } +} + +static char **_copy_env(pam_handle_t *pamh) +{ + char **dump; + int i = pamh->env->requested; /* reckon size of environment */ + char *const *env = pamh->env->list; + + D(("now get some memory for dump")); + + /* allocate some memory for this (plus the null tail-pointer) */ + dump = (char **) calloc(i, sizeof(char *)); + D(("dump = %p", dump)); + if (dump == NULL) { + return NULL; + } + + /* now run through entries and copy the variables over */ + dump[--i] = NULL; + while (i-- > 0) { + D(("env[%d]=`%s'", i,env[i])); + dump[i] = _pam_strdup(env[i]); + D(("->dump[%d]=`%s'", i,dump[i])); + if (dump[i] == NULL) { + /* out of memory */ + + while (dump[++i]) { + _pam_overwrite(dump[i]); + _pam_drop(dump[i]); + } + return NULL; + } + } + + env = NULL; /* forget now */ + + /* return transcribed environment */ + return dump; +} + +char **pam_getenvlist(pam_handle_t *pamh) +{ + int i; + + D(("called.")); + IF_NO_PAMH("pam_getenvlist", pamh, NULL); + + if (pamh->env == NULL || pamh->env->list == NULL) { + _pam_system_log(LOG_ERR, "pam_getenvlist: no env%s found", + pamh->env == NULL ? "":"-list" ); + return NULL; + } + + /* some quick checks */ + + if (pamh->env->requested > pamh->env->entries) { + _pam_system_log(LOG_ERR, "pam_getenvlist: environment corruption"); + _pam_dump_env(pamh); /* only active when debugging */ + return NULL; + } + + for (i=pamh->env->requested-1; i-- > 0; ) { + if (pamh->env->list[i] == NULL) { + _pam_system_log(LOG_ERR, "pam_getenvlist: environment broken"); + _pam_dump_env(pamh); /* only active when debugging */ + return NULL; /* somehow we've broken the environment!? */ + } + } + + /* Seems fine; copy environment */ + + _pam_dump_env(pamh); /* only active when debugging */ + + return _copy_env(pamh); +} diff --git a/libpam/pam_handlers.c b/libpam/pam_handlers.c new file mode 100644 index 00000000..569335e6 --- /dev/null +++ b/libpam/pam_handlers.c @@ -0,0 +1,896 @@ +/* pam_handlers.c -- pam config file parsing and module loading */ + +/* + * created by Marc Ewing. + * Currently maintained by Andrew G. Morgan <morgan@linux.kernel.org> + * + * $Id$ + * + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#ifdef PAM_DYNAMIC +# ifdef PAM_SHL +# include <dl.h> +# else /* PAM_SHL */ +# include <dlfcn.h> +# endif /* PAM_SHL */ +#endif /* PAM_DYNAMIC */ +#include <fcntl.h> +#include <unistd.h> + +#include "pam_private.h" + +/* FreeBSD doesn't define this */ +#ifndef RTLD_NOW +# define RTLD_NOW 1 +#endif + +/* If not required, define as nothing - FreeBSD needs it to be "_"... */ +#ifndef SHLIB_SYM_PREFIX +# define SHLIB_SYM_PREFIX "" +#endif + +#define BUF_SIZE 1024 +#define MODULE_CHUNK 4 +#define UNKNOWN_MODULE_PATH "<*unknown module path*>" + +static int _pam_assemble_line(FILE *f, char *buf, int buf_len); + +static void _pam_free_handlers_aux(struct handler **hp); + +static int _pam_add_handler(pam_handle_t *pamh + , int must_fail, int other, int type + , int *actions, const char *mod_path + , int argc, char **argv, int argvlen); + +/* Values for module type */ + +#define PAM_T_AUTH 1 +#define PAM_T_SESS 2 +#define PAM_T_ACCT 4 +#define PAM_T_PASS 8 + +static int _pam_parse_conf_file(pam_handle_t *pamh, FILE *f + , const char *known_service /* specific file */ +#ifdef PAM_READ_BOTH_CONFS + , int not_other +#endif /* PAM_READ_BOTH_CONFS */ + ) +{ + char buf[BUF_SIZE]; + int x; /* read a line from the FILE *f ? */ + /* + * read a line from the configuration (FILE *) f + */ + while ((x = _pam_assemble_line(f, buf, BUF_SIZE)) > 0) { + char *tok, *nexttok=NULL; + const char *this_service; + const char *mod_path; + int module_type, actions[_PAM_RETURN_VALUES]; + int other; /* set if module is for PAM_DEFAULT_SERVICE */ + int res; /* module added successfully? */ + int must_fail=0; /* a badly formatted line must fail when used */ + int argc; + char **argv; + int argvlen; + + D(("_pam_init_handler: LINE: %s", buf)); + if (known_service != NULL) { + nexttok = buf; + /* No service field: all lines are for the known service. */ + this_service = known_service; + } else { + this_service = tok = _pam_StrTok(buf, " \n\t", &nexttok); + } + +#ifdef PAM_READ_BOTH_CONFS + if (not_other) + other = 0; + else +#endif /* PAM_READ_BOTH_CONFS */ + other = !_pam_strCMP(this_service, PAM_DEFAULT_SERVICE); + + /* accept "service name" or PAM_DEFAULT_SERVICE modules */ + if (!_pam_strCMP(this_service, pamh->service_name) || other) { + /* This is a service we are looking for */ + D(("_pam_init_handlers: Found PAM config entry for: %s" + , this_service)); + + tok = _pam_StrTok(NULL, " \n\t", &nexttok); + if (!_pam_strCMP("auth", tok)) { + module_type = PAM_T_AUTH; + } else if (!_pam_strCMP("session", tok)) { + module_type = PAM_T_SESS; + } else if (!_pam_strCMP("account", tok)) { + module_type = PAM_T_ACCT; + } else if (!_pam_strCMP("password", tok)) { + module_type = PAM_T_PASS; + } else { + /* Illegal module type */ + D(("_pam_init_handlers: bad module type: %s", tok)); + _pam_system_log(LOG_ERR, "(%s) illegal module type: %s", + this_service, tok); + module_type = PAM_T_AUTH; /* most sensitive */ + must_fail = 1; /* install as normal but fail when dispatched */ + } + D(("Using %s config entry: %s", must_fail?"BAD ":"", tok)); + + /* reset the actions to .._UNDEF's -- this is so that + we can work out which entries are not yet set (for default). */ + { + int i; + for (i=0; i<_PAM_RETURN_VALUES; + actions[i++] = _PAM_ACTION_UNDEF); + } + tok = _pam_StrTok(NULL, " \n\t", &nexttok); + if (!_pam_strCMP("required", tok)) { + D(("*PAM_F_REQUIRED*")); + actions[PAM_SUCCESS] = _PAM_ACTION_OK; + actions[PAM_NEW_AUTHTOK_REQD] = _PAM_ACTION_OK; + actions[PAM_IGNORE] = _PAM_ACTION_IGNORE; + _pam_set_default_control(actions, _PAM_ACTION_BAD); + } else if (!_pam_strCMP("requisite", tok)) { + D(("*PAM_F_REQUISITE*")); + actions[PAM_SUCCESS] = _PAM_ACTION_OK; + actions[PAM_NEW_AUTHTOK_REQD] = _PAM_ACTION_OK; + actions[PAM_IGNORE] = _PAM_ACTION_IGNORE; + _pam_set_default_control(actions, _PAM_ACTION_DIE); + } else if (!_pam_strCMP("optional", tok)) { + D(("*PAM_F_OPTIONAL*")); + actions[PAM_SUCCESS] = _PAM_ACTION_OK; + actions[PAM_NEW_AUTHTOK_REQD] = _PAM_ACTION_OK; + _pam_set_default_control(actions, _PAM_ACTION_IGNORE); + } else if (!_pam_strCMP("sufficient", tok)) { + D(("*PAM_F_SUFFICIENT*")); + actions[PAM_SUCCESS] = _PAM_ACTION_DONE; + actions[PAM_NEW_AUTHTOK_REQD] = _PAM_ACTION_DONE; + _pam_set_default_control(actions, _PAM_ACTION_IGNORE); + } else { + D(("will need to parse %s", tok)); + _pam_parse_control(actions, tok); + /* by default the default is to treat as failure */ + _pam_set_default_control(actions, _PAM_ACTION_BAD); + } + + tok = _pam_StrTok(NULL, " \n\t", &nexttok); + if (tok != NULL) { + mod_path = tok; + D(("mod_path = %s",mod_path)); + } else { + /* no module name given */ + D(("_pam_init_handlers: no module name supplied")); + _pam_system_log(LOG_ERR, + "(%s) no module name supplied", this_service); + mod_path = NULL; + must_fail = 1; + } + + /* nexttok points to remaining arguments... */ + + if (nexttok != NULL) { + D(("list: %s",nexttok)); + argvlen = _pam_mkargv(nexttok, &argv, &argc); + D(("argvlen = %d",argvlen)); + } else { /* there are no arguments so fix by hand */ + D(("_pam_init_handlers: empty argument list")); + argvlen = argc = 0; + argv = NULL; + } + +#ifdef DEBUG + { + int y; + + D(("CONF%s: %s%s %d %s %d" + , must_fail?"<*will fail*>":"" + , this_service, other ? "(backup)":"" + , module_type + , mod_path, argc)); + for (y = 0; y < argc; y++) { + D(("CONF: %s", argv[y])); + } + for (y = 0; y<_PAM_RETURN_VALUES; ++y) { + D(("RETURN %s(%d) -> %d %s", + _pam_token_returns[y], y, actions[y], + actions[y]>0 ? "jump": + _pam_token_actions[-actions[y]])); + } + } +#endif + + res = _pam_add_handler(pamh, must_fail, other + , module_type, actions, mod_path + , argc, argv, argvlen); + if (res != PAM_SUCCESS) { + _pam_system_log(LOG_ERR, "error loading %s", mod_path); + D(("failed to load module - aborting")); + return PAM_ABORT; + } + } + } + + return ( (x < 0) ? PAM_ABORT:PAM_SUCCESS ); +} + +/* Parse config file, allocate handler structures, dlopen() */ +int _pam_init_handlers(pam_handle_t *pamh) +{ + FILE *f; + int retval; + + D(("_pam_init_handlers called")); + IF_NO_PAMH("_pam_init_handlers",pamh,PAM_SYSTEM_ERR); + + /* Return immediately if everything is already loaded */ + if (pamh->handlers.handlers_loaded) { + return PAM_SUCCESS; + } + + D(("_pam_init_handlers: initializing")); + + /* First clean the service structure */ + + _pam_free_handlers(pamh); + if (! pamh->handlers.module) { + if ((pamh->handlers.module = + malloc(MODULE_CHUNK * sizeof(struct loaded_module))) == NULL) { + _pam_system_log(LOG_CRIT, + "_pam_init_handlers: no memory loading module"); + return PAM_BUF_ERR; + } + pamh->handlers.modules_allocated = MODULE_CHUNK; + pamh->handlers.modules_used = 0; + } + + if (pamh->service_name == NULL) { + return PAM_BAD_ITEM; /* XXX - better error? */ + } + +#ifdef PAM_LOCKING + /* Is the PAM subsystem locked? */ + { + int fd_tmp; + + if ((fd_tmp = open( PAM_LOCK_FILE, O_RDONLY )) != -1) { + _pam_system_log(LOG_ERR, "_pam_init_handlers: PAM lockfile (" + PAM_LOCK_FILE ") exists - aborting"); + (void) close(fd_tmp); + /* + * to avoid swamping the system with requests + */ + _pam_start_timer(pamh); + pam_fail_delay(pamh, 5000000); + _pam_await_timer(pamh, PAM_ABORT); + + return PAM_ABORT; + } + } +#endif /* PAM_LOCKING */ + + /* + * Now parse the config file(s) and add handlers + */ + { + struct stat test_d; + + /* Is there a PAM_CONFIG_D directory? */ + if ( stat(PAM_CONFIG_D, &test_d) == 0 && S_ISDIR(test_d.st_mode) ) { + char *filename; + int read_something=0; + + D(("searching " PAM_CONFIG_D " for config files")); + filename = malloc(sizeof(PAM_CONFIG_DF) + +strlen(pamh->service_name)); + if (filename == NULL) { + _pam_system_log(LOG_ERR, + "_pam_init_handlers: no memory; service %s", + pamh->service_name); + return PAM_BUF_ERR; + } + sprintf(filename, PAM_CONFIG_DF, pamh->service_name); + D(("opening %s", filename)); + f = fopen(filename, "r"); + if (f != NULL) { + /* would test magic here? */ + retval = _pam_parse_conf_file(pamh, f, pamh->service_name +#ifdef PAM_READ_BOTH_CONFS + , 0 +#endif /* PAM_READ_BOTH_CONFS */ + ); + fclose(f); + if (retval != PAM_SUCCESS) { + _pam_system_log(LOG_ERR, + "_pam_init_handlers: error reading %s", + filename); + _pam_system_log(LOG_ERR, "_pam_init_handlers: [%s]", + pam_strerror(pamh, retval)); + } else { + read_something = 1; + } + } else { + D(("unable to open %s", filename)); +#ifdef PAM_READ_BOTH_CONFS + D(("checking %s", PAM_CONFIG)); + + if ((f = fopen(PAM_CONFIG,"r")) != NULL) { + retval = _pam_parse_conf_file(pamh, f, NULL, 1); + fclose(f); + } else +#endif /* PAM_READ_BOTH_CONFS */ + retval = PAM_SUCCESS; + /* + * XXX - should we log an error? Some people want to always + * use "other" + */ + } + _pam_drop(filename); + + if (retval == PAM_SUCCESS) { + /* now parse the PAM_DEFAULT_SERVICE_FILE */ + + D(("opening %s", PAM_DEFAULT_SERVICE_FILE)); + f = fopen(PAM_DEFAULT_SERVICE_FILE, "r"); + if (f != NULL) { + /* would test magic here? */ + retval = _pam_parse_conf_file(pamh, f + , PAM_DEFAULT_SERVICE +#ifdef PAM_READ_BOTH_CONFS + , 0 +#endif /* PAM_READ_BOTH_CONFS */ + ); + fclose(f); + if (retval != PAM_SUCCESS) { + _pam_system_log(LOG_ERR, + "_pam_init_handlers: error reading %s", + PAM_DEFAULT_SERVICE_FILE); + _pam_system_log(LOG_ERR, + "_pam_init_handlers: [%s]", + pam_strerror(pamh, retval)); + } else { + read_something = 1; + } + } else { + D(("unable to open %s", PAM_DEFAULT_SERVICE_FILE)); + _pam_system_log(LOG_ERR, + "_pam_init_handlers: no default config %s", + PAM_DEFAULT_SERVICE_FILE); + } + if (!read_something) { /* nothing read successfully */ + retval = PAM_ABORT; + } + } + } else { + if ((f = fopen(PAM_CONFIG, "r")) == NULL) { + _pam_system_log(LOG_ERR, "_pam_init_handlers: could not open " + PAM_CONFIG ); + return PAM_ABORT; + } + + retval = _pam_parse_conf_file(pamh, f, NULL +#ifdef PAM_READ_BOTH_CONFS + , 0 +#endif /* PAM_READ_BOTH_CONFS */ + ); + + D(("closing configuration file")); + fclose(f); + } + } + + if (retval != PAM_SUCCESS) { + /* Read error */ + _pam_system_log(LOG_ERR, "error reading PAM configuration file"); + return PAM_ABORT; + } + + pamh->handlers.handlers_loaded = 1; + + D(("_pam_init_handlers exiting")); + return 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" + */ + +int _pam_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(("_pam_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++ = ' '; /* replace backslash with ' ' */ + *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; +} + +typedef int (*servicefn)(pam_handle_t *, int, int, char **); + +int _pam_add_handler(pam_handle_t *pamh + , int must_fail, int other, int type + , int *actions, const char *mod_path + , int argc, char **argv, int argvlen) +{ + struct loaded_module *mod; + int x = 0; + struct handler **handler_p; + struct handler **handler_p2; + struct handlers *the_handlers; + const char *sym, *sym2; +#ifdef PAM_SHL + const char *_sym, *_sym2; +#endif + char *mod_full_path=NULL; + servicefn func, func2; + int success; + + D(("called.")); + IF_NO_PAMH("_pam_add_handler",pamh,PAM_SYSTEM_ERR); + + /* if NULL set to something that can be searched for */ + switch (mod_path != NULL) { + default: + if (mod_path[0] == '/') { + break; + } + mod_full_path = malloc(sizeof(DEFAULT_MODULE_PATH)+strlen(mod_path)); + if (mod_full_path) { + sprintf(mod_full_path, DEFAULT_MODULE_PATH "%s", mod_path); + mod_path = mod_full_path; + break; + } + _pam_system_log(LOG_CRIT, "cannot malloc full mod path"); + case 0: + mod_path = UNKNOWN_MODULE_PATH; + } + + D(("_pam_add_handler: adding type %d, module `%s'",type,mod_path)); + mod = pamh->handlers.module; + + /* First, ensure the module is loaded */ + while (x < pamh->handlers.modules_used) { + if (!strcmp(mod[x].name, mod_path)) { /* case sensitive ! */ + break; + } + x++; + } + if (x == pamh->handlers.modules_used) { + /* Not found */ + if (pamh->handlers.modules_allocated == pamh->handlers.modules_used) { + /* will need more memory */ + void *tmp = realloc(pamh->handlers.module, + (pamh->handlers.modules_allocated+MODULE_CHUNK) + *sizeof(struct loaded_module)); + if (tmp == NULL) { + D(("cannot enlarge module pointer memory")); + _pam_system_log(LOG_ERR, + "realloc returned NULL in _pam_add_handler"); + _pam_drop(mod_full_path); + return PAM_ABORT; + } + pamh->handlers.module = tmp; + pamh->handlers.modules_allocated += MODULE_CHUNK; + } + mod = &(pamh->handlers.module[x]); + /* Be pessimistic... */ + success = PAM_ABORT; + +#ifdef PAM_DYNAMIC + D(("_pam_add_handler: dlopen(%s) -> %lx", mod_path, &mod->dl_handle)); + mod->dl_handle = +# ifdef PAM_SHL + shl_load(mod_path, BIND_IMMEDIATE, 0L); +# else /* PAM_SHL */ + dlopen(mod_path, RTLD_NOW); +# endif /* PAM_SHL */ + D(("_pam_add_handler: dlopen'ed")); + if (mod->dl_handle == NULL) { + D(("_pam_add_handler: dlopen(%s) failed", mod_path)); + _pam_system_log(LOG_ERR, "unable to dlopen(%s)", mod_path); +# ifndef PAM_SHL + _pam_system_log(LOG_ERR, "[dlerror: %s]", dlerror()); +# endif /* PAM_SHL */ + /* Don't abort yet; static code may be able to find function. + * But defaults to abort if nothing found below... */ + } else { + D(("module added successfully")); + success = PAM_SUCCESS; + mod->type = PAM_MT_DYNAMIC_MOD; + pamh->handlers.modules_used++; + } +#endif +#ifdef PAM_STATIC + /* Only load static function if function was not found dynamically. + * This code should work even if no dynamic loading is available. */ + if (success != PAM_SUCCESS) { + D(("_pam_add_handler: open static handler %s", mod_path)); + mod->dl_handle = _pam_open_static_handler(mod_path); + if (mod->dl_handle == NULL) { + D(("_pam_add_handler: unable to find static handler %s", + mod_path)); + _pam_system_log(LOG_ERR, + "unable to open static handler %s", mod_path); + /* Didn't find module in dynamic or static..will mark bad */ + } else { + D(("static module added successfully")); + success = PAM_SUCCESS; + mod->type = PAM_MT_STATIC_MOD; + pamh->handlers.modules_used++; + } + } +#endif + + if (success != PAM_SUCCESS) { /* add a malformed module */ + mod->dl_handle = NULL; + mod->type = PAM_MT_FAULTY_MOD; + pamh->handlers.modules_used++; + _pam_system_log(LOG_ERR, "adding faulty module: %s", mod_path); + success = PAM_SUCCESS; /* We have successfully added a module */ + } + + /* indicate its name - later we will search for it by this */ + if ((mod->name = _pam_strdup(mod_path)) == NULL) { + D(("_pam_handler: couldn't get memory for mod_path")); + _pam_system_log(LOG_ERR, "no memory for module path", mod_path); + success = PAM_ABORT; + } + + } else { /* x != pamh->handlers.modules_used */ + mod += x; /* the located module */ + success = PAM_SUCCESS; + } + + _pam_drop(mod_full_path); + mod_path = NULL; /* no longer needed or trusted */ + + /* Now return error if necessary after trying all possible ways... */ + if (success != PAM_SUCCESS) + return(success); + + /* + * At this point 'mod' points to the stored/loaded module. If its + * dl_handle is unknown, then we must be able to indicate dispatch + * failure with 'must_fail' + */ + + /* Now define the handler(s) based on mod->dlhandle and type */ + + /* decide which list of handlers to use */ + the_handlers = (other) ? &pamh->handlers.other : &pamh->handlers.conf; + + handler_p = handler_p2 = NULL; + func = func2 = NULL; +#ifdef PAM_SHL + _sym2 = +#endif /* PAM_SHL */ + sym2 = NULL; + + /* point handler_p's at the root addresses of the function stacks */ + switch (type) { + case PAM_T_AUTH: + handler_p = &the_handlers->authenticate; + sym = SHLIB_SYM_PREFIX "pam_sm_authenticate"; + handler_p2 = &the_handlers->setcred; + sym2 = SHLIB_SYM_PREFIX "pam_sm_setcred"; +#ifdef PAM_SHL + _sym = "_pam_sm_authenticate"; + _sym2 = "_pam_sm_setcred"; +#endif + break; + case PAM_T_SESS: + handler_p = &the_handlers->open_session; + sym = SHLIB_SYM_PREFIX "pam_sm_open_session"; + handler_p2 = &the_handlers->close_session; + sym2 = SHLIB_SYM_PREFIX "pam_sm_close_session"; +#ifdef PAM_SHL + _sym = "_pam_sm_open_session"; + _sym2 = "_pam_sm_close_session"; +#endif + break; + case PAM_T_ACCT: + handler_p = &the_handlers->acct_mgmt; + sym = SHLIB_SYM_PREFIX "pam_sm_acct_mgmt"; +#ifdef PAM_SHL + _sym = "_pam_sm_acct_mgmt"; +#endif + break; + case PAM_T_PASS: + handler_p = &the_handlers->chauthtok; + sym = SHLIB_SYM_PREFIX "pam_sm_chauthtok"; +#ifdef PAM_SHL + _sym = "_pam_sm_chauthtok"; +#endif + break; + default: + /* Illegal module type */ + D(("_pam_add_handler: illegal module type %d", type)); + return PAM_ABORT; + } + + /* are the modules reliable? */ + if ( +#ifdef PAM_DYNAMIC + mod->type != PAM_MT_DYNAMIC_MOD + && +#endif /* PAM_DYNAMIC */ +#ifdef PAM_STATIC + mod->type != PAM_MT_STATIC_MOD + && +#endif /* PAM_STATIC */ + mod->type != PAM_MT_FAULTY_MOD + ) { + D(("_pam_add_handlers: illegal module library type; %d", mod->type)); + _pam_system_log(LOG_ERR, + "internal error: module library type not known: %s;%d", + sym, mod->type); + return PAM_ABORT; + } + + /* now identify this module's functions - for non-faulty modules */ + +#ifdef PAM_DYNAMIC + if ((mod->type == PAM_MT_DYNAMIC_MOD) && +# ifdef PAM_SHL + (shl_findsym(&mod->dl_handle, sym, (short) TYPE_PROCEDURE, &func) && + shl_findsym(&mod->dl_handle, _sym, (short) TYPE_PROCEDURE, &func)) +# else /* PAM_SHL */ + (func = (servicefn) dlsym(mod->dl_handle, sym)) == NULL +# endif /* PAM_SHL */ + ) { + _pam_system_log(LOG_ERR, "unable to resolve symbol: %s", sym); + } +#endif +#ifdef PAM_STATIC + if ((mod->type == PAM_MT_STATIC_MOD) && + (func = (servicefn)_pam_get_static_sym(mod->dl_handle, sym)) == NULL) { + _pam_system_log(LOG_ERR, "unable to resolve static symbol: %s", sym); + } +#endif + if (sym2) { +#ifdef PAM_DYNAMIC + if ((mod->type == PAM_MT_DYNAMIC_MOD) && +# ifdef PAM_SHL + (shl_findsym(&mod->dl_handle,sym2,(short)TYPE_PROCEDURE, &func2)&& + shl_findsym(&mod->dl_handle,_sym2,(short)TYPE_PROCEDURE, &func2)) +# else /* PAM_SHL */ + (func2 = (servicefn) dlsym(mod->dl_handle, sym2)) == NULL +# endif /* PAM_SHL */ + ) { + _pam_system_log(LOG_ERR, "unable to resolve symbol: %s", sym2); + } +#endif +#ifdef PAM_STATIC + if ((mod->type == PAM_MT_STATIC_MOD) && + (func2 = (servicefn)_pam_get_static_sym(mod->dl_handle, sym2)) + == NULL) { + _pam_system_log(LOG_ERR, "unable to resolve symbol: %s", sym2); + } +#endif + } + + /* here func (and perhaps func2) point to the appropriate functions */ + + /* add new handler to end of existing list */ + while (*handler_p != NULL) { + handler_p = &((*handler_p)->next); + } + + if ((*handler_p = malloc(sizeof(struct handler))) == NULL) { + _pam_system_log(LOG_CRIT, "cannot malloc struct handler #1"); + return (PAM_ABORT); + } + + (*handler_p)->must_fail = must_fail; /* failure forced? */ + (*handler_p)->func = func; + memcpy((*handler_p)->actions,actions,sizeof((*handler_p)->actions)); + (*handler_p)->argc = argc; + (*handler_p)->argv = argv; /* not a copy */ + (*handler_p)->next = NULL; + + /* some of the modules have a second calling function */ + if (handler_p2) { + /* add new handler to end of existing list */ + while (*handler_p2) { + handler_p2 = &((*handler_p2)->next); + } + + if ((*handler_p2 = malloc(sizeof(struct handler))) == NULL) { + _pam_system_log(LOG_CRIT, "cannot malloc struct handler #2"); + return (PAM_ABORT); + } + + (*handler_p2)->must_fail = must_fail; /* failure forced? */ + (*handler_p2)->func = func2; + memcpy((*handler_p2)->actions,actions,sizeof((*handler_p2)->actions)); + (*handler_p2)->argc = argc; + if (argv) { + if (((*handler_p2)->argv = malloc(argvlen)) == NULL) { + _pam_system_log(LOG_CRIT, "cannot malloc argv for handler #2"); + return (PAM_ABORT); + } + memcpy((*handler_p2)->argv, argv, argvlen); + } else { + (*handler_p2)->argv = NULL; /* no arguments */ + } + (*handler_p2)->next = NULL; + } + + D(("_pam_add_handler: returning successfully")); + + return PAM_SUCCESS; +} + +/* Free various allocated structures and dlclose() the libs */ +int _pam_free_handlers(pam_handle_t *pamh) +{ + struct loaded_module *mod; + + D(("called.")); + IF_NO_PAMH("_pam_free_handlers",pamh,PAM_SYSTEM_ERR); + + mod = pamh->handlers.module; + + /* Close all loaded modules */ + + while (pamh->handlers.modules_used) { + D(("_pam_free_handlers: dlclose(%s)", mod->name)); + free(mod->name); +#ifdef PAM_DYNAMIC + if (mod->type == PAM_MT_DYNAMIC_MOD) { +# ifdef PAM_SHL + shl_unload(mod->dl_handle); +# else + dlclose(mod->dl_handle); +# endif + } +#endif + mod++; + pamh->handlers.modules_used--; + } + + /* Free all the handlers */ + + _pam_free_handlers_aux(&(pamh->handlers.conf.authenticate)); + _pam_free_handlers_aux(&(pamh->handlers.conf.setcred)); + _pam_free_handlers_aux(&(pamh->handlers.conf.acct_mgmt)); + _pam_free_handlers_aux(&(pamh->handlers.conf.open_session)); + _pam_free_handlers_aux(&(pamh->handlers.conf.close_session)); + _pam_free_handlers_aux(&(pamh->handlers.conf.chauthtok)); + + _pam_free_handlers_aux(&(pamh->handlers.other.authenticate)); + _pam_free_handlers_aux(&(pamh->handlers.other.setcred)); + _pam_free_handlers_aux(&(pamh->handlers.other.acct_mgmt)); + _pam_free_handlers_aux(&(pamh->handlers.other.open_session)); + _pam_free_handlers_aux(&(pamh->handlers.other.close_session)); + _pam_free_handlers_aux(&(pamh->handlers.other.chauthtok)); + + /* no more loaded modules */ + + _pam_drop(pamh->handlers.module); + + /* Indicate that handlers are not initialized for this pamh */ + + pamh->handlers.handlers_loaded = 0; + + return PAM_SUCCESS; +} + +void _pam_start_handlers(pam_handle_t *pamh) +{ + D(("called.")); + /* NB. There is no check for a NULL pamh here, since no return + * value to communicate the fact! */ + + /* Indicate that handlers are not initialized for this pamh */ + pamh->handlers.handlers_loaded = 0; + + pamh->handlers.modules_allocated = 0; + pamh->handlers.modules_used = 0; + pamh->handlers.module = NULL; + + /* initialize the .conf and .other entries */ + + pamh->handlers.conf.authenticate = NULL; + pamh->handlers.conf.setcred = NULL; + pamh->handlers.conf.acct_mgmt = NULL; + pamh->handlers.conf.open_session = NULL; + pamh->handlers.conf.close_session = NULL; + pamh->handlers.conf.chauthtok = NULL; + + pamh->handlers.other.authenticate = NULL; + pamh->handlers.other.setcred = NULL; + pamh->handlers.other.acct_mgmt = NULL; + pamh->handlers.other.open_session = NULL; + pamh->handlers.other.close_session = NULL; + pamh->handlers.other.chauthtok = NULL; +} + +void _pam_free_handlers_aux(struct handler **hp) +{ + struct handler *h = *hp; + struct handler *last; + + D(("called.")); + while (h) { + last = h; + _pam_drop(h->argv); /* This is all alocated in a single chunk */ + h = h->next; + memset(last, 0, sizeof(*last)); + free(last); + } + + *hp = NULL; +} diff --git a/libpam/pam_item.c b/libpam/pam_item.c new file mode 100644 index 00000000..f595a0b4 --- /dev/null +++ b/libpam/pam_item.c @@ -0,0 +1,304 @@ +/* pam_item.c */ + +/* + * $Id$ + * + * $Log$ + * Revision 1.1 2000/06/20 22:11:17 agmorgan + * Initial revision + * + * Revision 1.3 1999/11/08 05:41:05 morgan + * pam_log - extra paranoia + * otherwise debugging changes + * + * Revision 1.2 1998/12/27 04:34:22 morgan + * reverting logging functions within libpam. Gone are the externally + * advertised API replaced by a more simple (libpam internal) one. + * + * Revision 1.1.1.1 1998/07/12 05:17:15 morgan + * Linux PAM sources pre-0.66 + * + */ + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> + +#include "pam_private.h" + +#define RESET(X, Y) \ +{ \ + char *_TMP_ = (X); \ + if (_TMP_ != (Y)) { \ + (X) = (Y) ? _pam_strdup(Y) : NULL; \ + if (_TMP_) \ + free(_TMP_); \ + } \ +} + +/* functions */ + +int pam_set_item ( + pam_handle_t *pamh, + int item_type, + const void *item) +{ + int retval; + + D(("called")); + + IF_NO_PAMH("pam_set_item", pamh, PAM_SYSTEM_ERR); + + retval = PAM_SUCCESS; + + switch (item_type) { + case PAM_SERVICE: + /* Setting handlers_loaded to 0 will cause the handlers + * to be reloaded on the next call to a service module. + */ + pamh->handlers.handlers_loaded = 0; + RESET(pamh->service_name, item); + { + char *tmp; + for (tmp=pamh->service_name; *tmp; ++tmp) + *tmp = tolower(*tmp); /* require lower case */ + } + break; + case PAM_USER: + RESET(pamh->user, item); + break; + case PAM_USER_PROMPT: + RESET(pamh->prompt, item); + break; + case PAM_TTY: + D(("setting tty to %s", item)); + RESET(pamh->tty, item); + break; + case PAM_RUSER: + RESET(pamh->ruser, item); + break; + case PAM_RHOST: + RESET(pamh->rhost, item); + break; + case PAM_AUTHTOK: + /* + * The man page says this is only supposed to be available to + * the module providers. In order to use this item the app + * has to #include <security/pam_modules.h>. This is something + * it is *not* supposed to do with "Linux-"PAM! - AGM. + */ + { + char *_TMP_ = pamh->authtok; + if (_TMP_ == item) /* not changed so leave alone */ + break; + pamh->authtok = (item) ? _pam_strdup(item) : NULL; + if (_TMP_) { + _pam_overwrite(_TMP_); + free(_TMP_); + } + break; + } + case PAM_OLDAUTHTOK: + /* See note above. */ + { + char *_TMP_ = pamh->oldauthtok; + if (_TMP_ == item) /* not changed so leave alone */ + break; + pamh->oldauthtok = (item) ? _pam_strdup(item) : NULL; + if (_TMP_) { + _pam_overwrite(_TMP_); + free(_TMP_); + } + break; + } + case PAM_CONV: /* want to change the conversation function */ + if (item == NULL) { + _pam_system_log(LOG_ERR, + "pam_set_item: attempt to set conv() to NULL"); + retval = PAM_PERM_DENIED; + } else { + struct pam_conv *tconv; + + if ((tconv= + (struct pam_conv *) malloc(sizeof(struct pam_conv)) + ) == NULL) { + _pam_system_log(LOG_CRIT, + "pam_set_item: malloc failed for pam_conv"); + retval = PAM_BUF_ERR; + } else { + memcpy(tconv, item, sizeof(struct pam_conv)); + _pam_drop(pamh->pam_conversation); + pamh->pam_conversation = tconv; + } + } + break; + case PAM_FAIL_DELAY: + pamh->fail_delay.delay_fn_ptr = item; + break; + default: + retval = PAM_BAD_ITEM; + } + + return (retval); +} + +int pam_get_item ( + const pam_handle_t *pamh, + int item_type, + const void **item) +{ + D(("called.")); + IF_NO_PAMH("pam_get_item",pamh,PAM_SYSTEM_ERR); + + if (item == NULL) { + _pam_system_log(LOG_ERR, + "pam_get_item: nowhere to place requested item"); + return PAM_PERM_DENIED; + } + + switch (item_type) { + case PAM_SERVICE: + *item = pamh->service_name; + break; + case PAM_USER: + D(("returning user=%s", pamh->user)); + *item = pamh->user; + break; + case PAM_USER_PROMPT: + D(("returning userprompt=%s", pamh->user)); + *item = pamh->prompt; + break; + case PAM_TTY: + D(("returning tty=%s", pamh->tty)); + *item = pamh->tty; + break; + case PAM_RUSER: + *item = pamh->ruser; + break; + case PAM_RHOST: + *item = pamh->rhost; + break; + case PAM_AUTHTOK: + *item = pamh->authtok; + break; + case PAM_OLDAUTHTOK: + *item = pamh->oldauthtok; + break; + case PAM_CONV: + *item = pamh->pam_conversation; + break; + case PAM_FAIL_DELAY: + *item = pamh->fail_delay.delay_fn_ptr; + break; + default: + /* XXX - I made this up */ + return PAM_BAD_ITEM; + } + + return PAM_SUCCESS; +} + +/* added by AGM 1996/3/2 */ + +int pam_get_user(pam_handle_t *pamh, const char **user, const char *prompt) +{ + const char *use_prompt; + int retval; + struct pam_message msg,*pmsg; + struct pam_response *resp; + + D(("called.")); + IF_NO_PAMH("pam_get_user", pamh, PAM_SYSTEM_ERR); + + if (pamh->pam_conversation == NULL) { + _pam_system_log(LOG_ERR, "pam_get_user: no conv element in pamh"); + return PAM_SERVICE_ERR; + } + + if (user == NULL) { /* ensure the the module has suplied a destination */ + _pam_system_log(LOG_ERR, "pam_get_user: nowhere to record username"); + return PAM_PERM_DENIED; + } else + *user = NULL; + + if (pamh->user) { /* have one so return it */ + *user = pamh->user; + return PAM_SUCCESS; + } + + /* will need a prompt */ + use_prompt = prompt; + if (use_prompt == NULL) { + use_prompt = pamh->prompt; + if (use_prompt == NULL) { + use_prompt = PAM_DEFAULT_PROMPT; + } + } + + /* If we are resuming an old conversation, we verify that the prompt + is the same. Anything else is an error. */ + if (pamh->former.want_user) { + /* must have a prompt to resume with */ + if (! pamh->former.prompt) { + _pam_system_log(LOG_ERR, + "pam_get_user: failed to resume with prompt" + ); + return PAM_ABORT; + } + + /* must be the same prompt as last time */ + if (strcmp(pamh->former.prompt, use_prompt)) { + _pam_system_log(LOG_ERR, + "pam_get_user: resumed with different prompt"); + return PAM_ABORT; + } + + /* ok, we can resume where we left off last time */ + pamh->former.want_user = PAM_FALSE; + _pam_overwrite(pamh->former.prompt); + _pam_drop(pamh->former.prompt); + } + + /* converse with application -- prompt user for a username */ + pmsg = &msg; + msg.msg_style = PAM_PROMPT_ECHO_ON; + msg.msg = use_prompt; + resp = NULL; + + retval = pamh->pam_conversation-> + conv(1, (const struct pam_message **) &pmsg, &resp, + pamh->pam_conversation->appdata_ptr); + + if (retval == PAM_CONV_AGAIN) { + /* conversation function is waiting for an event - save state */ + D(("conversation function is not ready yet")); + pamh->former.want_user = PAM_TRUE; + pamh->former.prompt = _pam_strdup(use_prompt); + } else if (resp == NULL) { + /* + * conversation should have given a response + */ + D(("pam_get_user: no response provided")); + retval = PAM_CONV_ERR; + } else if (retval == PAM_SUCCESS) { /* copy the username */ + /* + * now we set the PAM_USER item -- this was missing from pre.53 + * releases. However, reading the Sun manual, it is part of + * the standard API. + */ + RESET(pamh->user, resp->resp); + *user = pamh->user; + } + + if (resp) { + /* + * note 'resp' is allocated by the application and is + * correctly free()'d here + */ + _pam_drop_reply(resp, 1); + } + + D(("completed")); + return retval; /* pass on any error from conversation */ +} diff --git a/libpam/pam_log.c b/libpam/pam_log.c new file mode 100644 index 00000000..d990c8c0 --- /dev/null +++ b/libpam/pam_log.c @@ -0,0 +1,382 @@ +/* + * pam_log.c -- PAM system logging + * + * $Id$ + * + */ + +#ifdef linux +# define _GNU_SOURCE +# include <features.h> +#else +# define _BSD_SOURCE +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> + +#include "pam_private.h" + +#ifdef __hpux +# include <stdio.h> +# include <syslog.h> +# ifdef __STDC__ +# ifndef __P +# define __P(p) p +# endif /* __P */ +# include <stdarg.h> +# define VA_LOCAL_DECL va_list ap; +# define VA_START(f) va_start(ap, f) +# define VA_END va_end(ap) +# else /* __STDC__ */ +# ifndef __P +# define __P(p) () +# endif /* __P */ +# include <varargs.h> +# define VA_LOCAL_DECL va_list ap; +# define VA_START(f) va_start(ap) +# define VA_END va_end(ap) +# endif /* __STDC__ */ +/************************************************************** + * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 + * A bombproof version of doprnt (dopr) included. + * Sigh. This sort of thing is always nasty do deal with. Note that + * the version here does not include floating point... + * + * snprintf() is used instead of sprintf() as it does limit checks + * for string length. This covers a nasty loophole. + * + * The other functions are there to prevent NULL pointers from + * causing nast effects. + **************************************************************/ + +static void dopr(); +static char *end; +# ifndef _SCO_DS +/* VARARGS3 */ +int +# ifdef __STDC__ +snprintf(char *str, size_t count, const char *fmt, ...) +# else /* __STDC__ */ +snprintf(str, count, fmt, va_alist) + char *str; + size_t count; + const char *fmt; + va_dcl +# endif /* __STDC__ */ +{ + int len; + VA_LOCAL_DECL + + VA_START(fmt); + len = vsnprintf(str, count, fmt, ap); + VA_END; + return len; +} +# endif /* _SCO_DS */ + +int +# ifdef __STDC__ +vsnprintf(char *str, size_t count, const char *fmt, va_list args) +# else /* __STDC__ */ +vsnprintf(str, count, fmt, args) + char *str; + int count; + char *fmt; + va_list args; +# endif /* __STDC__ */ +{ + str[0] = 0; + end = str + count - 1; + dopr( str, fmt, args ); + if (count > 0) + end[0] = 0; + return strlen(str); +} + +/* + * dopr(): poor man's version of doprintf + */ + +static void fmtstr __P((char *value, int ljust, int len, int zpad, + int maxwidth)); +static void fmtnum __P((long value, int base, int dosign, int ljust, int len, + int zpad)); +static void dostr __P(( char * , int )); +static char *output; +static void dopr_outch __P(( int c )); + +static void +# ifdef __STDC__ +dopr(char * buffer, const char * format, va_list args ) +# else /* __STDC__ */ +dopr( buffer, format, args ) + char *buffer; + char *format; + va_list args; +# endif /* __STDC__ */ +{ + int ch; + long value; + int longflag = 0; + int pointflag = 0; + int maxwidth = 0; + char *strvalue; + int ljust; + int len; + int zpad; + + output = buffer; + while( (ch = *format++) ){ + switch( ch ){ + case '%': + ljust = len = zpad = maxwidth = 0; + longflag = pointflag = 0; + nextch: + ch = *format++; + switch( ch ){ + case 0: + dostr( "**end of format**" , 0); + return; + case '-': ljust = 1; goto nextch; + case '0': /* set zero padding if len not set */ + if(len==0 && !pointflag) zpad = '0'; + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': case '8': case '9': + if (pointflag) + maxwidth = maxwidth*10 + ch - '0'; + else + len = len*10 + ch - '0'; + goto nextch; + case '*': + if (pointflag) + maxwidth = va_arg( args, int ); + else + len = va_arg( args, int ); + goto nextch; + case '.': pointflag = 1; goto nextch; + case 'l': longflag = 1; goto nextch; + case 'u': case 'U': + /*fmtnum(value,base,dosign,ljust,len,zpad) */ + if( longflag ){ + value = va_arg( args, long ); + } else { + value = va_arg( args, int ); + } + fmtnum( value, 10,0, ljust, len, zpad ); break; + case 'o': case 'O': + /*fmtnum(value,base,dosign,ljust,len,zpad) */ + if( longflag ){ + value = va_arg( args, long ); + } else { + value = va_arg( args, int ); + } + fmtnum( value, 8,0, ljust, len, zpad ); break; + case 'd': case 'D': + if( longflag ){ + value = va_arg( args, long ); + } else { + value = va_arg( args, int ); + } + fmtnum( value, 10,1, ljust, len, zpad ); break; + case 'x': + if( longflag ){ + value = va_arg( args, long ); + } else { + value = va_arg( args, int ); + } + fmtnum( value, 16,0, ljust, len, zpad ); break; + case 'X': + if( longflag ){ + value = va_arg( args, long ); + } else { + value = va_arg( args, int ); + } + fmtnum( value,-16,0, ljust, len, zpad ); break; + case 's': + strvalue = va_arg( args, char *); + if (maxwidth > 0 || !pointflag) { + if (pointflag && len > maxwidth) + len = maxwidth; /* Adjust padding */ + fmtstr( strvalue,ljust,len,zpad, maxwidth); + } + break; + case 'c': + ch = va_arg( args, int ); + dopr_outch( ch ); break; + case '%': dopr_outch( ch ); continue; + default: + dostr( "???????" , 0); + } + break; + default: + dopr_outch( ch ); + break; + } + } + *output = 0; +} + +static void +fmtstr( value, ljust, len, zpad, maxwidth ) + char *value; + int ljust, len, zpad, maxwidth; +{ + int padlen, strlen; /* amount to pad */ + + if( value == 0 ){ + value = "<NULL>"; + } + for( strlen = 0; value[strlen]; ++ strlen ); /* strlen */ + if (strlen > maxwidth && maxwidth) + strlen = maxwidth; + padlen = len - strlen; + if( padlen < 0 ) padlen = 0; + if( ljust ) padlen = -padlen; + while( padlen > 0 ) { + dopr_outch( ' ' ); + --padlen; + } + dostr( value, maxwidth ); + while( padlen < 0 ) { + dopr_outch( ' ' ); + ++padlen; + } +} + +static void +fmtnum( value, base, dosign, ljust, len, zpad ) + long value; + int base, dosign, ljust, len, zpad; +{ + int signvalue = 0; + unsigned long uvalue; + char convert[20]; + int place = 0; + int padlen = 0; /* amount to pad */ + int caps = 0; + + /* DEBUGP(("value 0x%x, base %d, dosign %d, ljust %d, len %d, zpad %d\n", + value, base, dosign, ljust, len, zpad )); */ + uvalue = value; + if( dosign ){ + if( value < 0 ) { + signvalue = '-'; + uvalue = -value; + } + } + if( base < 0 ){ + caps = 1; + base = -base; + } + do{ + convert[place++] = + (caps? "0123456789ABCDEF":"0123456789abcdef") + [uvalue % (unsigned)base ]; + uvalue = (uvalue / (unsigned)base ); + }while(uvalue); + convert[place] = 0; + padlen = len - place; + if( padlen < 0 ) padlen = 0; + if( ljust ) padlen = -padlen; + /* DEBUGP(( "str '%s', place %d, sign %c, padlen %d\n", + convert,place,signvalue,padlen)); */ + if( zpad && padlen > 0 ){ + if( signvalue ){ + dopr_outch( signvalue ); + --padlen; + signvalue = 0; + } + while( padlen > 0 ){ + dopr_outch( zpad ); + --padlen; + } + } + while( padlen > 0 ) { + dopr_outch( ' ' ); + --padlen; + } + if( signvalue ) dopr_outch( signvalue ); + while( place > 0 ) dopr_outch( convert[--place] ); + while( padlen < 0 ){ + dopr_outch( ' ' ); + ++padlen; + } +} + +static void +dostr( str , cut) + char *str; + int cut; +{ + if (cut) { + while(*str && cut-- > 0) dopr_outch(*str++); + } else { + while(*str) dopr_outch(*str++); + } +} + +static void +dopr_outch( c ) + int c; +{ + if( end == 0 || output < end ) + *output++ = c; +} + +int +# ifdef __STDC__ +vsyslog(int priority, const char *fmt, ...) +# else /* __STDC__ */ +vsyslog(priority, fmt, va_alist) + int priority; + const char *fmt; + va_dcl +# endif /* __STDC__ */ +{ + VA_LOCAL_DECL + char logbuf[BUFSIZ]; + + VA_START(fmt); + + vsnprintf(logbuf, BUFSIZ, fmt, ap); + syslog(priority, "%s", logbuf); + + VA_END; +} +#endif /* __hpux */ + +/* internal logging function */ + +void _pam_system_log(int priority, const char *format, ... ) +{ + va_list args; + char *eformat; + + D(("pam_system_log called")); + + if (format == NULL) { + D(("NULL format to _pam_system_log() call")); + return; + } + + va_start(args, format); + + eformat = malloc(sizeof(_PAM_SYSTEM_LOG_PREFIX)+strlen(format)); + if (eformat != NULL) { + strcpy(eformat, _PAM_SYSTEM_LOG_PREFIX); + strcpy(eformat + sizeof(_PAM_SYSTEM_LOG_PREFIX) - 1, format); + vsyslog(priority, eformat, args); + _pam_overwrite(eformat); + _pam_drop(eformat); + } else { + vsyslog(priority, format, args); + } + + va_end(args); + + D(("done.")); +} + diff --git a/libpam/pam_malloc.c b/libpam/pam_malloc.c new file mode 100644 index 00000000..44d583e7 --- /dev/null +++ b/libpam/pam_malloc.c @@ -0,0 +1,404 @@ +/* + * $Id$ + * + * $Log$ + * Revision 1.1 2000/06/20 22:11:18 agmorgan + * Initial revision + * + * Revision 1.2 1998/12/27 04:34:23 morgan + * reverting logging functions within libpam. Gone are the externally + * advertised API replaced by a more simple (libpam internal) one. + * + * Revision 1.1.1.1 1998/07/12 05:17:15 morgan + * Linux PAM sources pre-0.66 + * + * Revision 1.2 1996/12/01 03:14:13 morgan + * use _pam_macros.h + * + * Revision 1.1 1996/11/10 21:26:11 morgan + * Initial revision + * + */ + +/* + * This pair of files helps to locate memory leaks. It is a wrapper for + * the malloc family of calls. (Actutally, it currently only deals + * with calloc, malloc, realloc, free and exit) + * + * To use these functions the header "pam_malloc.h" must be included + * in all parts of the code (that use the malloc functions) and this + * file must be linked with the result. The pam_malloc_flags can be + * set from another function and determine the level of logging. + * + * The output is via the macros defined in _pam_macros.h + * + * It is a debugging tool and should be turned off in released code. + * + * This suite was written by Andrew Morgan <morgan@parc.power.net> for + * Linux-PAM. + */ + +#ifndef DEBUG +#define DEBUG +#endif + +#include "pam_private.h" + +#include <security/pam_malloc.h> +#include <security/_pam_macros.h> + +/* this must be done to stop infinite recursion! */ +#undef malloc +#undef calloc +#undef free +#undef realloc +#undef exit + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +/* + * default debugging level + */ + +int pam_malloc_flags = PAM_MALLOC_ALL; +int pam_malloc_delay_length = 4; + +#define on(x) ((pam_malloc_flags&(x))==(x)) + +/* + * the implementation + */ + +static const char *last_fn=NULL; +static const char *last_file=NULL; +static const char *last_call=NULL; +static int last_line = 1; + +#define err(x) { _pam_output_xdebug_info(); _pam_output_debug x ; } + +static void set_last_(const char *x, const char *f + , const char *fn, const int l) +{ + last_fn = x ? x : "error-in-pam_malloc.."; + last_file = f ? f : "*bad-file*"; + last_call = fn ? fn: "*bad-fn*"; + last_line = l; +} + +static void _pam_output_xdebug_info(void) +{ + FILE *logfile; + int must_close = 1; + + if (!(logfile = fopen(_PAM_LOGFILE,"a"))) { + logfile = stderr; + must_close = 0; + } + fprintf(logfile, "[%s:%s(%d)->%s()] ", + last_file, last_call, last_line, last_fn); + if (must_close) { + fflush(logfile); + fclose(logfile); + } +} + +static void hinder(void) +{ + if (on(PAM_MALLOC_PAUSE)) { + if (on(0)) err(("pause requested")); + sleep(pam_malloc_delay_length); + } + + if (on(PAM_MALLOC_STOP)) { + if (on(0)) err(("stop requested")); + exit(1); + } +} + +/* + * here are the memory pointer registering functions.. these actually + * use malloc(!) but that's ok! ;^) + */ + +struct reference { + void *ptr; /* pointer */ + int nelements; /* number of elements */ + int size; /* - each of this size */ + char *file; /* where it was requested - filename */ + char *function; /* - function */ + int line; /* - line number */ +/* + * linking info + */ + struct reference *next; +}; + +static void _dump(const char *say, const struct reference *ref) +{ + _pam_output_debug(" <%s: %p (#%d of %d) req. by %s(); %s line %d>\n" + , say + , ref->ptr,ref->nelements,ref->size + , ref->function,ref->file,ref->line); +} + +static struct reference *root=NULL; + +static char *_strdup(const char *x) +{ + char *s; + + s = (char *)malloc(strlen(x)+1); + if (s == NULL) { + if (on(0)) err(("_strdup failed")); + exit(1); + } + + strcpy(s,x); + return s; +} + +static void add_new_ref(void *new, int n, int size) +{ + struct reference *ref=NULL; + + ref = (struct reference *) malloc( sizeof(struct reference) ); + if (new == NULL || ref == NULL) { + if (on(0)) err(("internal error {add_new_ref}")); + exit(1); + } + + ref->ptr = new; + ref->nelements = n; + ref->size = size; + + ref->file = _strdup(last_file); + ref->function = _strdup(last_call); + ref->line = last_line; + + ref->next = root; + + if (on(PAM_MALLOC_REQUEST)) { + _dump("new_ptr", ref); + } + + root = ref; +} + +static void del_old_ref(void *old) +{ + struct reference *this,*last; + + if (old == NULL) { + if (on(0)) err(("internal error {del_old_ref}")); + exit(1); + } + + /* locate old pointer */ + + last = NULL; + this = root; + while (this) { + if (this->ptr == old) + break; + last = this; + this = this->next; + } + + /* Did we find a reference ? */ + + if (this) { + if (on(PAM_MALLOC_FREE)) { + _dump("free old_ptr", this); + } + if (last == NULL) { + root = this->next; + } else { + last->next = this->next; + } + free(this->file); + free(this->function); + free(this); + } else { + if (on(0)) err(("ERROR!: bad memory")); + hinder(); + } +} + +static void verify_old_ref(void *old) +{ + struct reference *this; + + if (old == NULL) { + if (on(0)) err(("internal error {verify_old_ref}")); + exit(1); + } + + /* locate old pointer */ + + this = root; + while (this) { + if (this->ptr == old) + break; + this = this->next; + } + + /* Did we find a reference ? */ + + if (this) { + if (on(PAM_MALLOC_VERIFY)) { + _dump("verify_ptr", this); + } + } else { + if (on(0)) err(("ERROR!: bad request")); + hinder(); + } +} + +static void dump_memory_list(const char *dump) +{ + struct reference *this; + + this = root; + if (this) { + if (on(0)) err(("un-free()'d memory")); + while (this) { + _dump(dump, this); + this = this->next; + } + } else { + if (on(0)) err(("no memory allocated")); + } +} + +/* now for the wrappers */ + +#define _fn(x) set_last_(x,file,fn,line) + +void *pam_malloc(size_t size, const char *file, const char *fn, const int line) +{ + void *new; + + _fn("malloc"); + + if (on(PAM_MALLOC_FUNC)) err(("request for %d", size)); + + new = malloc(size); + if (new == NULL) { + if (on(PAM_MALLOC_FAIL)) err(("returned NULL")); + } else { + if (on(PAM_MALLOC_REQUEST)) err(("request new")); + add_new_ref(new, 1, size); + } + + return new; +} + +void *pam_calloc(size_t nelm, size_t size + , const char *file, const char *fn, const int line) +{ + void *new; + + _fn("calloc"); + + if (on(PAM_MALLOC_FUNC)) err(("request for %d of %d", nelm, size)); + + new = calloc(nelm,size); + if (new == NULL) { + if (on(PAM_MALLOC_FAIL)) err(("returned NULL")); + } else { + if (on(PAM_MALLOC_REQUEST)) err(("request new")); + add_new_ref(new, nelm, size); + } + + return new; +} + +void pam_free(void *ptr + , const char *file, const char *fn, const int line) +{ + _fn("free"); + + if (on(PAM_MALLOC_FUNC)) err(("request to free %p", ptr)); + + if (ptr == NULL) { + if (on(PAM_MALLOC_NULL)) err(("passed NULL pointer")); + } else { + if (on(PAM_MALLOC_FREE)) err(("deleted old")); + del_old_ref(ptr); + free(ptr); + } +} + +void *pam_memalign(size_t ali, size_t size + , const char *file, const char *fn, const int line) +{ + _fn("memalign"); + if (on(0)) err(("not implemented currently (Sorry)")); + exit(1); +} + +void *pam_realloc(void *ptr, size_t size + , const char *file, const char *fn, const int line) +{ + void *new; + + _fn("realloc"); + + if (on(PAM_MALLOC_FUNC)) err(("resize %p to %d", ptr, size)); + + if (ptr == NULL) { + if (on(PAM_MALLOC_NULL)) err(("passed NULL pointer")); + } else { + verify_old_ref(ptr); + } + + new = realloc(ptr, size); + if (new == NULL) { + if (on(PAM_MALLOC_FAIL)) err(("returned NULL")); + } else { + if (ptr) { + if (on(PAM_MALLOC_FREE)) err(("deleted old")); + del_old_ref(ptr); + } else { + if (on(PAM_MALLOC_NULL)) err(("old is NULL")); + } + if (on(PAM_MALLOC_REQUEST)) err(("request new")); + add_new_ref(new, 1, size); + } + + return new; +} + +void *pam_valloc(size_t size + , const char *file, const char *fn, const int line) +{ + _fn("valloc"); + if (on(0)) err(("not implemented currently (Sorry)")); + exit(1); +} + +#include <alloca.h> + +void *pam_alloca(size_t size + , const char *file, const char *fn, const int line) +{ + _fn("alloca"); + if (on(0)) err(("not implemented currently (Sorry)")); + exit(1); +} + +void pam_exit(int i + , const char *file, const char *fn, const int line) +{ + _fn("exit"); + + if (on(0)) err(("passed (%d)", i)); + if (on(PAM_MALLOC_LEAKED)) { + dump_memory_list("leaked"); + } + exit(i); +} + +/* end of file */ diff --git a/libpam/pam_map.c b/libpam/pam_map.c new file mode 100644 index 00000000..8b969123 --- /dev/null +++ b/libpam/pam_map.c @@ -0,0 +1,85 @@ +/* pam_map.c - PAM mapping interface + * + * $Id$ + * + * This is based on the X/Open XSSO specification of March 1997. + * It is not implemented as it is going to change... after 1997/9/25. + * + * $Log$ + * Revision 1.1 2000/06/20 22:11:19 agmorgan + * Initial revision + * + * Revision 1.1.1.1 1998/07/12 05:17:15 morgan + * Linux PAM sources pre-0.66 + * + */ + +#include <stdio.h> + +#include "pam_private.h" + +/* p 54 */ + +int pam_get_mapped_authtok(pam_handle_t *pamh, + const char *target_module_username, + const char *target_module_type, + const char *target_authn_domain, + size_t *target_authtok_len + unsigned char **target_module_authtok); +{ + D(("called")); + + IF_NO_PAMH("pam_get_mapped_authtok",pamh,PAM_SYSTEM_ERR); + + return PAM_SYSTEM_ERROR; +} + +/* p 68 */ + +int pam_set_mapped_authtok(pam_handle_t *pamh, + char *target_module_username, + size_t *target_authtok_len, + unsigned char *target_module_authtok, + char *target_module_type, + char *target_authn_domain) +{ + D(("called")); + + IF_NO_PAMH("pam_set_mapped_authtok",pamh,PAM_SYSTEM_ERR); + + return PAM_SYSTEM_ERROR; +} + +/* p 56 */ + +int pam_get_mapped_username(pam_handle_t *pamh, + const char *src_username, + const char *src_module_type, + const char *src_authn_domain, + const char *target_module_type, + const char *target_authn_domain, + char **target_module_username) +{ + D(("called")); + + IF_NO_PAMH("pam_get_mapped_username",pamh,PAM_SYSTEM_ERR); + + return PAM_SYSTEM_ERROR; +} + +/* p 70 */ + +int pam_set_mapped_username(pam_handle_t *pamh, + char *src_username, + char *src_module_type, + char *src_authn_domain, + char *target_module_username, + char *target_module_type, + char *target_authn_domain) +{ + D(("called")); + + IF_NO_PAMH("pam_set_mapped_username",pamh,PAM_SYSTEM_ERR); + + return PAM_SYSTEM_ERROR; +} diff --git a/libpam/pam_misc.c b/libpam/pam_misc.c new file mode 100644 index 00000000..9bd52bfa --- /dev/null +++ b/libpam/pam_misc.c @@ -0,0 +1,305 @@ +/* pam_misc.c -- This is random stuff */ + +/* + * $Id$ + */ + +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> +#include <ctype.h> + +#include "pam_private.h" + +/* caseless string comparison: POSIX does not define this.. */ +int _pam_strCMP(const char *s, const char *t) +{ + int cf; + + do { + cf = tolower(*s) - tolower(*t); + ++t; + } while (!cf && *s++); + + return cf; +} + +char *_pam_StrTok(char *from, const char *format, char **next) +/* + * this function is a variant of the standard strtok, it differs in that + * it takes an additional argument and doesn't nul terminate tokens until + * they are actually reached. + */ +{ + char table[256], *end; + int i; + + if (from == NULL && (from = *next) == NULL) + return from; + + /* initialize table */ + for (i=1; i<256; table[i++] = '\0'); + for (i=0; format[i] ; table[(int)format[i++]] = 'y'); + + /* look for first non-blank char */ + while (*from && table[(int)*from]) { + ++from; + } + + if (*from == '[') { + /* + * special case, "[...]" is considered to be a single + * object. Note, however, if one of the format[] chars is + * '[' this single string will not be read correctly. + */ + for (end=++from; *end && *end != ']'; ++end) { + if (*end == '\\' && end[1] == ']') + ++end; + } + /* note, this string is stripped of its edges: "..." is what + remains */ + } else if (*from) { + /* simply look for next blank char */ + for (end=from; *end && !table[(int)*end]; ++end); + } else { + return (*next = NULL); /* no tokens left */ + } + + /* now terminate what we have */ + if (*end) + *end++ = '\0'; + + /* indicate what it left */ + if (*end) { + *next = end; + } else { + *next = NULL; /* have found last token */ + } + + /* return what we have */ + return from; +} + +/* + * Safe duplication of character strings. "Paranoid"; don't leave + * evidence of old token around for later stack analysis. + */ + +char *_pam_strdup(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; + _pam_system_log(LOG_CRIT, "_pam_strdup: failed to get memory"); + } else { + while (i-- > 0) { + new[i] = x[i]; + } + } + x = NULL; + } + + return new; /* return the duplicate or NULL on error */ +} + +/* Generate argv, argc from s */ +/* caller must free(argv) */ + +int _pam_mkargv(char *s, char ***argv, int *argc) +{ + int l; + int argvlen = 0; + char *sbuf, *sbuf_start; + char **our_argv = NULL; + char **argvbuf; + char *argvbufp; +#ifdef DEBUG + int count=0; +#endif + + D(("_pam_mkargv called: %s",s)); + + *argc = 0; + + l = strlen(s); + if (l) { + if ((sbuf = sbuf_start = _pam_strdup(s)) == NULL) { + _pam_system_log(LOG_CRIT, + "pam_mkargv: null returned by _pam_strdup"); + D(("arg NULL")); + } else { + /* Overkill on the malloc, but not large */ + argvlen = (l + 1) * ((sizeof(char)) + sizeof(char *)); + if ((our_argv = argvbuf = malloc(argvlen)) == NULL) { + _pam_system_log(LOG_CRIT, + "pam_mkargv: null returned by malloc"); + } else { + char *tmp=NULL; + + argvbufp = (char *) argvbuf + (l * sizeof(char *)); + D(("[%s]",sbuf)); + while ((sbuf = _pam_StrTok(sbuf, " \n\t", &tmp))) { + D(("arg #%d",++count)); + D(("->[%s]",sbuf)); + strcpy(argvbufp, sbuf); + D(("copied token")); + *argvbuf = argvbufp; + argvbufp += strlen(argvbufp) + 1; + D(("stepped in argvbufp")); + (*argc)++; + argvbuf++; + sbuf = NULL; + D(("loop again?")); + } + _pam_drop(sbuf_start); + } + } + } + + *argv = our_argv; + + D(("_pam_mkargv returned")); + + return(argvlen); +} + +/* + * this function is used to protect the modules from accidental or + * semi-mallicious harm that an application may do to confuse the API. + */ + +void _pam_sanitize(pam_handle_t *pamh) +{ + /* + * this is for security. We reset the auth-tokens here. + */ + pam_set_item(pamh,PAM_AUTHTOK,NULL); + pam_set_item(pamh,PAM_OLDAUTHTOK,NULL); +} + +/* + * This function scans the array and replaces the _PAM_ACTION_UNDEF + * entries with the default action. + */ + +void _pam_set_default_control(int *control_array, int default_action) +{ + int i; + + for (i=0; i<_PAM_RETURN_VALUES; ++i) { + if (control_array[i] == _PAM_ACTION_UNDEF) { + control_array[i] = default_action; + } + } +} + +/* + * This function is used to parse a control string. This string is a + * series of tokens of the following form: + * + * "[ ]*return_code[ ]*=[ ]*action/[ ]". + */ + +#include "pam_tokens.h" + +void _pam_parse_control(int *control_array, char *tok) +{ + const char *error; + int ret; + + while (*tok) { + int act, len; + + /* skip leading space */ + while (isspace((int)*tok) && *++tok); + if (!*tok) + break; + + /* identify return code */ + for (ret=0; ret<=_PAM_RETURN_VALUES; ++ret) { + len = strlen(_pam_token_returns[ret]); + if (!strncmp(_pam_token_returns[ret], tok, len)) { + break; + } + } + if (ret > _PAM_RETURN_VALUES || !*(tok += len)) { + error = "expecting return value"; + goto parse_error; + } + + /* observe '=' */ + while (isspace((int)*tok) && *++tok); + if (!*tok || *tok++ != '=') { + error = "expecting '='"; + goto parse_error; + } + + /* skip leading space */ + while (isspace((int)*tok) && *++tok); + if (!*tok) { + error = "expecting action"; + goto parse_error; + } + + /* observe action type */ + for (act=0; act < (-(_PAM_ACTION_UNDEF)); ++act) { + len = strlen(_pam_token_actions[act]); + if (!strncmp(_pam_token_actions[act], tok, len)) { + act *= -1; + tok += len; + break; + } + } + if (act > 0) { + /* + * Either we have a number or we have hit an error. In + * principle, there is nothing to stop us accepting + * negative offsets. (Although we would have to think of + * another way of encoding the tokens.) However, I really + * think this would be both hard to administer and easily + * cause looping problems. So, for now, we will just + * allow forward jumps. (AGM 1998/1/7) + */ + if (!isdigit((int)*tok)) { + error = "expecting jump number"; + goto parse_error; + } + /* parse a number */ + act = 0; + do { + act *= 10; + act += *tok - '0'; /* XXX - this assumes ascii behavior */ + } while (*++tok && isdigit((int)*tok)); + if (! act) { + /* we do not allow 0 jumps. There is a token ('ignore') + for that */ + error = "expecting non-zero"; + goto parse_error; + } + } + + /* set control_array element */ + if (ret != _PAM_RETURN_VALUES) { + control_array[ret] = act; + } else { + /* set the default to 'act' */ + _pam_set_default_control(control_array, act); + } + } + + /* that was a success */ + return; + +parse_error: + /* treat everything as bad */ + _pam_system_log(LOG_ERR, "pam_parse: %s; [...%s]", error, tok); + for (ret=0; ret<_PAM_RETURN_VALUES; control_array[ret++]=_PAM_ACTION_BAD); + +} diff --git a/libpam/pam_password.c b/libpam/pam_password.c new file mode 100644 index 00000000..afbaa580 --- /dev/null +++ b/libpam/pam_password.c @@ -0,0 +1,52 @@ +/* pam_password.c - PAM Password Management */ + +/* + * $Id$ + */ + +#include <stdio.h> +#include <stdlib.h> + +/* #define DEBUG */ + +#include "pam_private.h" + +int pam_chauthtok(pam_handle_t *pamh, int flags) +{ + int retval; + + D(("called.")); + + IF_NO_PAMH("pam_chauthtok", pamh, PAM_SYSTEM_ERR); + + if (pamh->former.choice == PAM_NOT_STACKED) { + _pam_start_timer(pamh); /* we try to make the time for a failure + independent of the time it takes to + fail */ + _pam_sanitize(pamh); + pamh->former.update = PAM_FALSE; + } + + /* first call to check if there will be a problem */ + if (pamh->former.update || + (retval = _pam_dispatch(pamh, flags|PAM_PRELIM_CHECK, + PAM_CHAUTHTOK)) == PAM_SUCCESS) { + D(("completed check ok: former=%d", pamh->former.update)); + pamh->former.update = PAM_TRUE; + retval = _pam_dispatch(pamh, flags|PAM_UPDATE_AUTHTOK, + PAM_CHAUTHTOK); + } + + /* if we completed we should clean up */ + if (retval != PAM_INCOMPLETE) { + _pam_sanitize(pamh); + pamh->former.update = PAM_FALSE; + _pam_await_timer(pamh, retval); /* if unsuccessful then wait now */ + D(("pam_chauthtok exit %d - %d", retval, pamh->former.choice)); + } else { + D(("will resume when ready", retval)); + } + + return retval; +} + diff --git a/libpam/pam_private.h b/libpam/pam_private.h new file mode 100644 index 00000000..51660fd1 --- /dev/null +++ b/libpam/pam_private.h @@ -0,0 +1,312 @@ +/* + * pam_private.h + * + * $Id$ + * + * This is the Linux-PAM Library Private Header. It contains things + * internal to the Linux-PAM library. Things not needed by either an + * application or module. + * + * Please see end of file for copyright. + * + * Creator: Marc Ewing. + * Maintained: AGM + */ + +#ifndef _PAM_PRIVATE_H +#define _PAM_PRIVATE_H + +/* this is not used at the moment --- AGM */ +#define LIBPAM_VERSION 67 + +#include <security/pam_appl.h> +#include <security/pam_modules.h> + +/* the Linux-PAM configuration file */ + +#define PAM_CONFIG "/etc/pam.conf" +#define PAM_CONFIG_D "/etc/pam.d" +#define PAM_CONFIG_DF "/etc/pam.d/%s" + +#define PAM_DEFAULT_SERVICE "other" /* lower case */ +#define PAM_DEFAULT_SERVICE_FILE PAM_CONFIG_D "/" PAM_DEFAULT_SERVICE + +#ifdef PAM_LOCKING +/* + * the Linux-PAM lock file. If it exists Linux-PAM will abort. Use it + * to block access to libpam + */ +#define PAM_LOCK_FILE "/var/lock/subsys/PAM" +#endif + +/* components of the pam_handle structure */ + +struct handler { + int must_fail; + int (*func)(pam_handle_t *pamh, int flags, int argc, char **argv); + int actions[_PAM_RETURN_VALUES]; + int argc; + char **argv; + struct handler *next; +}; + +struct loaded_module { + char *name; + int type; /* PAM_STATIC_MOD or PAM_DYNAMIC_MOD */ + void *dl_handle; +}; + +#define PAM_MT_DYNAMIC_MOD 0 +#define PAM_MT_STATIC_MOD 1 +#define PAM_MT_FAULTY_MOD 2 + +struct handlers { + struct handler *authenticate; + struct handler *setcred; + struct handler *acct_mgmt; + struct handler *open_session; + struct handler *close_session; + struct handler *chauthtok; +}; + +struct service { + struct loaded_module *module; /* Only used for dynamic loading */ + int modules_allocated; + int modules_used; + int handlers_loaded; + + struct handlers conf; /* the configured handlers */ + struct handlers other; /* the default handlers */ +}; + +/* + * Environment helper functions + */ + +#define PAM_ENV_CHUNK 10 /* chunks of memory calloc()'d * + * at once */ + +struct pam_environ { + int entries; /* the number of pointers available */ + int requested; /* the number of pointers used: * + * 1 <= requested <= entries */ + char **list; /* the environment storage (a list * + * of pointers to malloc() memory) */ +}; + +#include <sys/time.h> + +typedef enum { PAM_FALSE, PAM_TRUE } _pam_boolean; + +struct _pam_fail_delay { + _pam_boolean set; + unsigned int delay; + time_t begin; + const void *delay_fn_ptr; +}; + +struct _pam_former_state { +/* this is known and set by _pam_dispatch() */ + int choice; /* which flavor of module function did we call? */ + +/* state info for the _pam_dispatch_aux() function */ + int depth; /* how deep in the stack were we? */ + int impression; /* the impression at that time */ + int status; /* the status before returning incomplete */ + +/* state info used by pam_get_user() function */ + int want_user; + char *prompt; /* saved prompt information */ + +/* state info for the pam_chauthtok() function */ + _pam_boolean update; +}; + +struct pam_handle { + char *authtok; + struct pam_conv *pam_conversation; + char *oldauthtok; + char *prompt; /* for use by pam_get_user() */ + char *service_name; + char *user; + char *rhost; + char *ruser; + char *tty; + struct pam_data *data; + struct pam_environ *env; /* structure to maintain environment list */ + struct _pam_fail_delay fail_delay; /* helper function for easy delays */ + struct service handlers; + struct _pam_former_state former; /* library state - support for + event driven applications */ +}; + +/* Values for select arg to _pam_dispatch() */ +#define PAM_NOT_STACKED 0 +#define PAM_AUTHENTICATE 1 +#define PAM_SETCRED 2 +#define PAM_ACCOUNT 3 +#define PAM_OPEN_SESSION 4 +#define PAM_CLOSE_SESSION 5 +#define PAM_CHAUTHTOK 6 + +#define _PAM_ACTION_IS_JUMP(x) ((x) > 0) +#define _PAM_ACTION_IGNORE 0 +#define _PAM_ACTION_OK -1 +#define _PAM_ACTION_DONE -2 +#define _PAM_ACTION_BAD -3 +#define _PAM_ACTION_DIE -4 +#define _PAM_ACTION_RESET -5 +/* Add any new entries here. Will need to change ..._UNDEF and then + * need to change pam_tokens.h */ +#define _PAM_ACTION_UNDEF -6 /* this is treated as an error + ( = _PAM_ACTION_BAD) */ + +/* character tables for parsing config files */ +extern const char * const _pam_token_actions[-_PAM_ACTION_UNDEF]; +extern const char * const _pam_token_returns[_PAM_RETURN_VALUES+1]; + +/* + * internally defined functions --- these should not be directly + * called by applications or modules + */ +int _pam_dispatch(pam_handle_t *pamh, int flags, int choice); + +/* Free various allocated structures and dlclose() the libs */ +int _pam_free_handlers(pam_handle_t *pamh); + +/* Parse config file, allocate handler structures, dlopen() */ +int _pam_init_handlers(pam_handle_t *pamh); + +/* Set all hander stuff to 0/NULL - called once from pam_start() */ +void _pam_start_handlers(pam_handle_t *pamh); + +/* environment helper functions */ + +/* create the environment structure */ +int _pam_make_env(pam_handle_t *pamh); + +/* delete the environment structure */ +void _pam_drop_env(pam_handle_t *pamh); + +#ifdef LINUX_PAM + +/* these functions deal with failure delays as required by the + authentication modules and application. Their *interface* is likely + to remain the same although their function is hopefully going to + improve */ + +/* reset the timer to no-delay */ +void _pam_reset_timer(pam_handle_t *pamh); + +/* this sets the clock ticking */ +void _pam_start_timer(pam_handle_t *pamh); + +/* this waits for the clock to stop ticking if status != PAM_SUCCESS */ +void _pam_await_timer(pam_handle_t *pamh, int status); + + +#endif /* LINUX_PAM */ + +typedef void (*voidfunc(void))(void); +#ifdef PAM_STATIC + +/* The next two in ../modules/_pam_static/pam_static.c */ + +/* Return pointer to data structure used to define a static module */ +struct pam_module * _pam_open_static_handler(const char *path); + +/* Return pointer to function requested from static module */ + +voidfunc *_pam_get_static_sym(struct pam_module *mod, const char *symname); + +#endif + +/* For now we just use a stack and linear search for module data. */ +/* If it becomes apparent that there is a lot of data, it should */ +/* changed to either a sorted list or a hash table. */ + +struct pam_data { + char *name; + void *data; + void (*cleanup)(pam_handle_t *pamh, void *data, int error_status); + struct pam_data *next; +}; + +void _pam_free_data(pam_handle_t *pamh, int status); + +int _pam_strCMP(const char *s, const char *t); +char *_pam_StrTok(char *from, const char *format, char **next); + +char *_pam_strdup(const char *s); + +int _pam_mkargv(char *s, char ***argv, int *argc); + +void _pam_sanitize(pam_handle_t *pamh); + +void _pam_set_default_control(int *control_array, int default_action); + +void _pam_parse_control(int *control_array, char *tok); + +void _pam_system_log(int priority, const char *format, ... ); +#define _PAM_SYSTEM_LOG_PREFIX "PAM " + +/* + * XXX - Take care with this. It could confuse the logic of a trailing + * else + */ + +#define IF_NO_PAMH(X,pamh,ERR) \ +if ((pamh) == NULL) { \ + _pam_system_log(LOG_ERR, X ": NULL pam handle passed"); \ + return ERR; \ +} + +/* Definition for the default username prompt used by pam_get_user() */ + +#define PAM_DEFAULT_PROMPT "Please enter username: " + +/* + * include some helpful macros + */ + +#include <security/_pam_macros.h> + +/* + * Copyright (C) 1995 by Red Hat Software, Marc Ewing + * Copyright (c) 1996-8, Andrew G. Morgan <morgan@linux.kernel.org> + * + * 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. + */ + +#endif /* _PAM_PRIVATE_H_ */ diff --git a/libpam/pam_second.c b/libpam/pam_second.c new file mode 100644 index 00000000..614275af --- /dev/null +++ b/libpam/pam_second.c @@ -0,0 +1,46 @@ +/* + * pam_second.c -- PAM secondary authentication + * (based on XSSO draft spec of March 1997) + * + * $Id$ + * + * $Log$ + * Revision 1.1 2000/06/20 22:11:20 agmorgan + * Initial revision + * + * Revision 1.1.1.1 1998/07/12 05:17:15 morgan + * Linux PAM sources pre-0.66 + * + */ + +#include <stdio.h> +#include <stdlib.h> + +#include "pam_private.h" + +/* p 42 */ + +int pam_authenticate_secondary(pam_handle_t *pamh, + char *target_username, + char *target_module_type, + char *target_authn_domain, + char *target_supp_data, + unsigned char *target_module_authtok, + int flags) +{ + int retval=PAM_SYSTEM_ERR; + + D(("called")); + + _pam_start_timer(pamh); /* we try to make the time for a failure + independent of the time it takes to + fail */ + + IF_NO_PAMH("pam_authenticate_secondary",pamh,PAM_SYSTEM_ERR); + + _pam_await_timer(pamh, retval); /* if unsuccessful then wait now */ + + D(("pam_authenticate_secondary exit")); + + return retval; +} diff --git a/libpam/pam_session.c b/libpam/pam_session.c new file mode 100644 index 00000000..eae464c8 --- /dev/null +++ b/libpam/pam_session.c @@ -0,0 +1,41 @@ +/* pam_session.c - PAM Session Management */ + +/* + * $Id$ + * + * $Log$ + * Revision 1.1 2000/06/20 22:11:20 agmorgan + * Initial revision + * + * Revision 1.1.1.1 1998/07/12 05:17:15 morgan + * Linux PAM sources pre-0.66 + * + * Revision 1.3 1996/12/01 03:14:13 morgan + * use _pam_macros.h + * + * Revision 1.2 1996/03/10 02:19:12 morgan + * some oversight meant that this wasn't being compiled. It needed a + * couple of changes. + * + * + */ + +#include <stdio.h> + +#include "pam_private.h" + +int pam_open_session(pam_handle_t *pamh, int flags) +{ + D(("called")); + + IF_NO_PAMH("pam_open_session",pamh,PAM_SYSTEM_ERR); + return _pam_dispatch(pamh, flags, PAM_OPEN_SESSION); +} + +int pam_close_session(pam_handle_t *pamh, int flags) +{ + D(("called")); + + IF_NO_PAMH("pam_close_session",pamh,PAM_SYSTEM_ERR); + return _pam_dispatch(pamh, flags, PAM_CLOSE_SESSION); +} diff --git a/libpam/pam_start.c b/libpam/pam_start.c new file mode 100644 index 00000000..fb222d62 --- /dev/null +++ b/libpam/pam_start.c @@ -0,0 +1,112 @@ +/* pam_start.c */ + +/* Creator Marc Ewing + * Maintained by AGM + * + * $Id$ + * + */ + +#include <ctype.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <syslog.h> + +#include "pam_private.h" + +int pam_start ( + const char *service_name, + const char *user, + const struct pam_conv *pam_conversation, + pam_handle_t **pamh) +{ + D(("called pam_start: [%s] [%s] [%p] [%p]" + ,service_name, user, pam_conversation, pamh)); + + if ((*pamh = calloc(1, sizeof(**pamh))) == NULL) { + _pam_system_log(LOG_CRIT, "pam_start: calloc failed for *pamh"); + return (PAM_BUF_ERR); + } + + if (service_name) { + char *tmp; + + if (((*pamh)->service_name = _pam_strdup(service_name)) == NULL) { + _pam_system_log(LOG_CRIT, + "pam_start: _pam_strdup failed for service name"); + _pam_drop(*pamh); + return (PAM_BUF_ERR); + } + for (tmp=(*pamh)->service_name; *tmp; ++tmp) + *tmp = tolower(*tmp); /* require lower case */ + } else + (*pamh)->service_name = NULL; + + if (user) { + if (((*pamh)->user = _pam_strdup(user)) == NULL) { + _pam_system_log(LOG_CRIT, + "pam_start: _pam_strdup failed for user"); + _pam_drop((*pamh)->service_name); + _pam_drop(*pamh); + return (PAM_BUF_ERR); + } + } else + (*pamh)->user = NULL; + + (*pamh)->tty = NULL; + (*pamh)->prompt = NULL; /* prompt for pam_get_user() */ + (*pamh)->ruser = NULL; + (*pamh)->rhost = NULL; + (*pamh)->authtok = NULL; + (*pamh)->oldauthtok = NULL; + (*pamh)->fail_delay.delay_fn_ptr = NULL; + (*pamh)->former.choice = PAM_NOT_STACKED; + + if (pam_conversation == NULL + || ((*pamh)->pam_conversation = (struct pam_conv *) + malloc(sizeof(struct pam_conv))) == NULL) { + _pam_system_log(LOG_CRIT, "pam_start: malloc failed for pam_conv"); + _pam_drop((*pamh)->service_name); + _pam_drop((*pamh)->user); + _pam_drop(*pamh); + return (PAM_BUF_ERR); + } else { + memcpy((*pamh)->pam_conversation, pam_conversation, + sizeof(struct pam_conv)); + } + + (*pamh)->data = NULL; + if ( _pam_make_env(*pamh) != PAM_SUCCESS ) { + _pam_system_log(LOG_ERR,"pam_start: failed to initialize environment"); + _pam_drop((*pamh)->service_name); + _pam_drop((*pamh)->user); + _pam_drop(*pamh); + return PAM_ABORT; + } + + _pam_reset_timer(*pamh); /* initialize timer support */ + + _pam_start_handlers(*pamh); /* cannot fail */ + + /* According to the SunOS man pages, loading modules and resolving + * symbols happens on the first call from the application. */ + + /* + * XXX - should we call _pam_init_handlers() here ? The following + * is new as of Linux-PAM 0.55 + */ + + if ( _pam_init_handlers(*pamh) != PAM_SUCCESS ) { + _pam_system_log(LOG_ERR, "pam_start: failed to initialize handlers"); + _pam_drop_env(*pamh); /* purge the environment */ + _pam_drop((*pamh)->service_name); + _pam_drop((*pamh)->user); + _pam_drop(*pamh); + return PAM_ABORT; + } + + D(("exiting pam_start successfully")); + + return PAM_SUCCESS; +} diff --git a/libpam/pam_static.c b/libpam/pam_static.c new file mode 100644 index 00000000..64a3dd31 --- /dev/null +++ b/libpam/pam_static.c @@ -0,0 +1,141 @@ +/* pam_static.c -- static module loading helper functions */ + +/* created by Michael K. Johnson, johnsonm@redhat.com + * + * $Id$ + */ + +/* This whole file is only used for PAM_STATIC */ + +#ifdef PAM_STATIC + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "pam_private.h" + +/* + * Need to include pointers to static modules; this was built by each + * of the modules that register... + */ + +#include "../modules/_static_module_list" + +/* + * and here is a structure that connects libpam to the above static + * modules + */ + +static struct pam_module *static_modules[] = { + +#include "../modules/_static_module_entry" + + NULL +}; + +/* + * and now for the functions + */ + +/* Return pointer to data structure used to define a static module */ +struct pam_module * _pam_open_static_handler(const char *path) +{ + int i; + const char *clpath = path; + char *lpath, *end; + + if (strchr(clpath, '/')) { + /* ignore path and leading "/" */ + clpath = strrchr(lpath, '/') + 1; + } + /* create copy to muck with (must free before return) */ + lpath = _pam_strdup(clpath); + /* chop .so off copy if it exists (or other extension on other + platform...) */ + end = strstr(lpath, ".so"); + if (end) { + *end = '\0'; + } + + /* now go find the module */ + for (i = 0; static_modules[i] != NULL; i++) { + D(("%s=?%s\n", lpath, static_modules[i]->name)); + if (static_modules[i]->name && + ! strcmp(static_modules[i]->name, lpath)) { + break; + } + } + + if (static_modules[i] == NULL) { + _pam_system_log(NULL, NULL, LOG_ERR, "no static module named %s", + lpath); + } + + free(lpath); + return (static_modules[i]); +} + +/* Return pointer to function requested from static module + * Can't just return void *, because ANSI C disallows casting a + * pointer to a function to a void *... + * This definition means: + * _pam_get_static_sym is a function taking two arguments and + * returning a pointer to a function which takes no arguments + * and returns void... */ +voidfunc *_pam_get_static_sym(struct pam_module *mod, const char *symname) { + + if (! strcmp(symname, "pam_sm_authenticate")) { + return ((voidfunc *)mod->pam_sm_authenticate); + } else if (! strcmp(symname, "pam_sm_setcred")) { + return ((voidfunc *)mod->pam_sm_setcred); + } else if (! strcmp(symname, "pam_sm_acct_mgmt")) { + return ((voidfunc *)mod->pam_sm_acct_mgmt); + } else if (! strcmp(symname, "pam_sm_open_session")) { + return ((voidfunc *)mod->pam_sm_open_session); + } else if (! strcmp(symname, "pam_sm_close_session")) { + return ((voidfunc *)mod->pam_sm_close_session); + } else if (! strcmp(symname, "pam_sm_chauthtok")) { + return ((voidfunc *)mod->pam_sm_chauthtok); + } + /* getting to this point is an error */ + return ((voidfunc *)NULL); +} + +#endif /* PAM_STATIC */ + +/* + * Copyright (C) 1995 by Red Hat Software, Michael K. Johnson + * 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/libpam/pam_strerror.c b/libpam/pam_strerror.c new file mode 100644 index 00000000..ecf8c0bb --- /dev/null +++ b/libpam/pam_strerror.c @@ -0,0 +1,112 @@ +/* pam_strerror.c */ + +/* $Id$ + * + * $Log$ + * Revision 1.1 2000/06/20 22:11:21 agmorgan + * Initial revision + * + * Revision 1.1.1.1 1998/07/12 05:17:15 morgan + * Linux PAM sources pre-0.66 + * + * Revision 1.6 1997/01/04 20:12:02 morgan + * replaced conditional FAIL_NOW with ABORT + * + * Revision 1.5 1996/07/07 23:58:56 morgan + * corrected "... " to "..." + * + * Revision 1.4 1996/06/02 08:03:29 morgan + * spelling correction + * + * Revision 1.3 1996/03/16 23:08:54 morgan + * PAM --> Linux-PAM ;) + * + */ + +#include "pam_private.h" + +const char *pam_strerror(pam_handle_t *pamh, int errnum) +{ +#ifdef UGLY_HACK_FOR_PRIOR_BEHAVIOR_SUPPORT /* will be removed from v 1.0 */ + + int possible_error; + + possible_error = (int) pamh; + if (!(possible_error >= 0 && possible_error <= PAM_BAD_ITEM)) { + possible_error = errnum; + } + +/* mask standard behavior to use possible_error variable. */ +#define errnum possible_error + +#endif /* UGLY_HACK_FOR_PRIOR_BEHAVIOR_SUPPORT */ + + switch (errnum) { + case PAM_SUCCESS: + return "Success"; + case PAM_ABORT: + return "Critical error - immediate abort"; + case PAM_OPEN_ERR: + return "dlopen() failure"; + case PAM_SYMBOL_ERR: + return "Symbol not found"; + case PAM_SERVICE_ERR: + return "Error in service module"; + case PAM_SYSTEM_ERR: + return "System error"; + case PAM_BUF_ERR: + return "Memory buffer error"; + case PAM_PERM_DENIED: + return "Permission denied"; + case PAM_AUTH_ERR: + return "Authentication failure"; + case PAM_CRED_INSUFFICIENT: + return "Insufficient credentials to access authentication data"; + case PAM_AUTHINFO_UNAVAIL: + return "Authentication service cannot retrieve authentication info."; + case PAM_USER_UNKNOWN: + return "User not known to the underlying authentication module"; + case PAM_MAXTRIES: + return "Have exhasted maximum number of retries for service."; + case PAM_NEW_AUTHTOK_REQD: + return "Authentication token is no longer valid; new one required."; + case PAM_ACCT_EXPIRED: + return "User account has expired"; + case PAM_SESSION_ERR: + return "Cannot make/remove an entry for the specified session"; + case PAM_CRED_UNAVAIL: + return "Authentication service cannot retrieve user credentials"; + case PAM_CRED_EXPIRED: + return "User credentials expired"; + case PAM_CRED_ERR: + return "Failure setting user credentials"; + case PAM_NO_MODULE_DATA: + return "No module specific data is present"; + case PAM_BAD_ITEM: + return "Bad item passed to pam_*_item()"; + case PAM_CONV_ERR: + return "Conversation error"; + case PAM_AUTHTOK_ERR: + return "Authentication token manipulation error"; + case PAM_AUTHTOK_RECOVER_ERR: + return "Authentication information cannot be recovered"; + case PAM_AUTHTOK_LOCK_BUSY: + return "Authentication token lock busy"; + case PAM_AUTHTOK_DISABLE_AGING: + return "Authentication token aging disabled"; + case PAM_TRY_AGAIN: + return "Failed preliminary check by password service"; + case PAM_IGNORE: + return "Please ignore underlying account module"; + case PAM_MODULE_UNKNOWN: + return "Module is unknown"; + case PAM_AUTHTOK_EXPIRED: + return "Authentication token expired"; + case PAM_CONV_AGAIN: + return "Conversation is waiting for event"; + case PAM_INCOMPLETE: + return "Application needs to call libpam again"; + } + + return "Unknown Linux-PAM error (need to upgrde libpam?)"; +} diff --git a/libpam/pam_tokens.h b/libpam/pam_tokens.h new file mode 100644 index 00000000..1fd32641 --- /dev/null +++ b/libpam/pam_tokens.h @@ -0,0 +1,111 @@ +/* + * pam_tokens.h + * + * $Id$ + * + * This is a Linux-PAM Library Private Header file. It contains tokens + * that are used when we parse the configuration file(s). + * + * Please see end of file for copyright. + * + * Creator: Andrew Morgan. + * + * $Log$ + * Revision 1.1 2000/06/20 22:11:21 agmorgan + * Initial revision + * + * Revision 1.1.1.1 1998/07/12 05:17:15 morgan + * Linux PAM sources pre-0.66 + * + */ + +#ifndef _PAM_TOKENS_H +#define _PAM_TOKENS_H + +/* an array of actions */ + +const char * const _pam_token_actions[-_PAM_ACTION_UNDEF] = { + "ignore", /* 0 */ + "ok", /* -1 */ + "done", /* -2 */ + "bad", /* -3 */ + "die", /* -4 */ + "reset", /* -5 */ +}; + +/* an array of possible return values */ + +const char * const _pam_token_returns[_PAM_RETURN_VALUES+1] = { + "success", /* 0 */ + "open_err", /* 1 */ + "symbol_err", /* 2 */ + "service_err", /* 3 */ + "system_err", /* 4 */ + "buf_err", /* 5 */ + "perm_denied", /* 6 */ + "auth_err", /* 7 */ + "cred_insufficient", /* 8 */ + "authinfo_unavail", /* 9 */ + "user_unknown", /* 10 */ + "maxtries", /* 11 */ + "new_authtok_reqd", /* 12 */ + "acct_expired", /* 13 */ + "session_err", /* 14 */ + "cred_unavail", /* 15 */ + "cred_expired", /* 16 */ + "cred_err", /* 17 */ + "no_module_data", /* 18 */ + "conv_err", /* 19 */ + "authtok_err", /* 20 */ + "authtok_recover_err", /* 21 */ + "authtok_lock_busy", /* 22 */ + "authtok_disable_aging", /* 23 */ + "try_again", /* 24 */ + "ignore", /* 25 */ + "abort", /* 26 */ + "authtok_expired", /* 27 */ + "module_unknown", /* 28 */ + "bad_item", /* 29 */ +/* add new return codes here */ + "default" /* this is _PAM_RETURN_VALUES and indicates + the default return action */ +}; + +/* + * Copyright (C) 1998, Andrew G. Morgan <morgan@linux.kernel.org> + * + * 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. + */ + +#endif /* _PAM_PRIVATE_H_ */ |