From ea488580c42e8918445a945484de3c8a5addc761 Mon Sep 17 00:00:00 2001 From: "Andrew G. Morgan" Date: Tue, 20 Jun 2000 22:10:38 +0000 Subject: Initial revision --- modules/pam_mail/.cvsignore | 1 + modules/pam_mail/Makefile | 113 ++++++++++ modules/pam_mail/README | 17 ++ modules/pam_mail/pam_mail.c | 504 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 635 insertions(+) create mode 100644 modules/pam_mail/.cvsignore create mode 100644 modules/pam_mail/Makefile create mode 100644 modules/pam_mail/README create mode 100644 modules/pam_mail/pam_mail.c (limited to 'modules/pam_mail') diff --git a/modules/pam_mail/.cvsignore b/modules/pam_mail/.cvsignore new file mode 100644 index 00000000..380a834a --- /dev/null +++ b/modules/pam_mail/.cvsignore @@ -0,0 +1 @@ +dynamic diff --git a/modules/pam_mail/Makefile b/modules/pam_mail/Makefile new file mode 100644 index 00000000..64f73b0b --- /dev/null +++ b/modules/pam_mail/Makefile @@ -0,0 +1,113 @@ +# +# $Id$ +# +# This Makefile controls a build process of $(TITLE) module for +# Linux-PAM. You should not modify this Makefile (unless you know +# what you are doing!). +# +# $Log$ +# Revision 1.1 2000/06/20 22:11:45 agmorgan +# Initial revision +# +# Revision 1.1.1.1 1998/07/12 05:17:17 morgan +# Linux PAM sources pre-0.66 +# +# Revision 1.3 1997/04/05 06:37:45 morgan +# fakeroot +# +# Revision 1.2 1997/02/15 16:07:22 morgan +# optional libpwdb compilation +# +# Revision 1.1 1997/01/04 20:32:52 morgan +# Initial revision +# +# +# +# Created by Andrew Morgan 1996/12/8 +# + +TITLE=pam_mail + +ifndef STATIC +ifeq ($(HAVE_PWDBLIB),yes) +CFLAGS += -DWANT_PWDB +EXTRALIB = -lpwdb +endif +endif + +# + +LIBSRC = $(TITLE).c +LIBOBJ = $(TITLE).o +LIBOBJD = $(addprefix dynamic/,$(LIBOBJ)) +LIBOBJS = $(addprefix static/,$(LIBOBJ)) + +dynamic/%.o : %.c + $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@ + +static/%.o : %.c + $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@ + + +ifdef DYNAMIC +LIBSHARED = $(TITLE).so +endif + +ifdef STATIC +LIBSTATIC = lib$(TITLE).o +endif + +####################### don't edit below ####################### + +dummy: + + @echo "**** This is not a top-level Makefile " + exit + +all: dirs $(LIBSHARED) $(LIBSTATIC) register + +dirs: +ifdef DYNAMIC + $(MKDIR) ./dynamic +endif +ifdef STATIC + $(MKDIR) ./static +endif + +register: +ifdef STATIC + ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) ) +endif + +ifdef DYNAMIC +$(LIBOBJD): $(LIBSRC) + +$(LIBSHARED): $(LIBOBJD) + $(LD_D) -o $@ $(LIBOBJD) $(EXTRALIB) +endif + +ifdef STATIC +$(LIBOBJS): $(LIBSRC) + +$(LIBSTATIC): $(LIBOBJS) + $(LD) -r -o $@ $(LIBOBJS) $(EXTRALIB) +endif + +install: all + $(MKDIR) $(FAKEROOT)$(SECUREDIR) +ifdef DYNAMIC + $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR) +endif + +remove: + rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so + +clean: + rm -f $(LIBOBJD) $(LIBOBJS) core *~ + +extraclean: clean + rm -f *.a *.o *.so *.bak dynamic/* static/* + +.c.o: + $(CC) $(CFLAGS) -c $< + diff --git a/modules/pam_mail/README b/modules/pam_mail/README new file mode 100644 index 00000000..155bd1db --- /dev/null +++ b/modules/pam_mail/README @@ -0,0 +1,17 @@ +This is the README for pam_mail +------------------------------- + +This PAM module tells the User that he has new/unread email. + +Options for: +auth: for authentication it provides pam_authenticate() and + pam_setcred() hooks. + + "debug" write more information to syslog + "dir=maildir" users mailbox is maildir/ + "hash=count" mail directory hash depth + "close" print message also on logout + "nopen" print message not on login + "noenv" don't set the MAIL environment variable + "empty" also print message if user has no mail + diff --git a/modules/pam_mail/pam_mail.c b/modules/pam_mail/pam_mail.c new file mode 100644 index 00000000..f67b6f02 --- /dev/null +++ b/modules/pam_mail/pam_mail.c @@ -0,0 +1,504 @@ +/* pam_mail module */ + +/* + * $Id$ + * + * Written by Andrew Morgan 1996/3/11 + * $HOME additions by David Kinchlea 1997/1/7 + * mailhash additions by Chris Adams 1998/7/11 + */ + +#define DEFAULT_MAIL_DIRECTORY "/var/spool/mail" +#define MAIL_FILE_FORMAT "%s%s/%s" +#define MAIL_ENV_NAME "MAIL" +#define MAIL_ENV_FORMAT MAIL_ENV_NAME "=%s" +#define YOUR_MAIL_VERBOSE_FORMAT "You have %s mail in %s." +#define YOUR_MAIL_STANDARD_FORMAT "You have %smail." +#define NO_MAIL_STANDARD_FORMAT "No mail." + +#define _BSD_SOURCE + +#ifdef linux +# define _GNU_SOURCE +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef WANT_PWDB +#include +#endif + +/* + * here, we make a definition for the externally accessible function + * in this file (this definition is required for static a module + * but strongly encouraged generally) it is used to instruct the + * modules include file to define the function prototypes. + */ + +#define PAM_SM_SESSION +#define PAM_SM_AUTH + +#include +#include + +/* some syslogging */ + +static void _log_err(int err, const char *format, ...) +{ + va_list args; + + va_start(args, format); + openlog("PAM-mail", LOG_CONS|LOG_PID, LOG_AUTH); + vsyslog(err, format, args); + va_end(args); + closelog(); +} + +/* argument parsing */ + +#define PAM_DEBUG_ARG 0x0001 +#define PAM_NO_LOGIN 0x0002 +#define PAM_LOGOUT_TOO 0x0004 +#define PAM_NEW_MAIL_DIR 0x0010 +#define PAM_MAIL_SILENT 0x0020 +#define PAM_NO_ENV 0x0040 +#define PAM_HOME_MAIL 0x0100 +#define PAM_EMPTY_TOO 0x0200 +#define PAM_STANDARD_MAIL 0x0400 +#define PAM_QUIET_MAIL 0x1000 + +static int _pam_parse(int flags, int argc, const char **argv, char **maildir, + int *hashcount) +{ + int ctrl=0; + + if (flags & PAM_SILENT) { + ctrl |= PAM_MAIL_SILENT; + } + + *hashcount = 0; + + /* step through arguments */ + for (; argc-- > 0; ++argv) { + + /* generic options */ + + if (!strcmp(*argv,"debug")) + ctrl |= PAM_DEBUG_ARG; + else if (!strcmp(*argv,"quiet")) + ctrl |= PAM_QUIET_MAIL; + else if (!strcmp(*argv,"standard")) + ctrl |= PAM_STANDARD_MAIL | PAM_EMPTY_TOO; + else if (!strncmp(*argv,"dir=",4)) { + *maildir = x_strdup(4+*argv); + if (*maildir != NULL) { + D(("new mail directory: %s", *maildir)); + ctrl |= PAM_NEW_MAIL_DIR; + } else { + _log_err(LOG_CRIT, + "failed to duplicate mail directory - ignored"); + } + } else if (!strncmp(*argv,"hash=",5)) { + char *ep = NULL; + *hashcount = strtol(*argv+5,&ep,10); + if (!ep || (*hashcount < 0)) { + *hashcount = 0; + } + } else if (!strcmp(*argv,"close")) { + ctrl |= PAM_LOGOUT_TOO; + } else if (!strcmp(*argv,"nopen")) { + ctrl |= PAM_NO_LOGIN; + } else if (!strcmp(*argv,"noenv")) { + ctrl |= PAM_NO_ENV; + } else if (!strcmp(*argv,"empty")) { + ctrl |= PAM_EMPTY_TOO; + } else { + _log_err(LOG_ERR,"pam_parse: unknown option; %s",*argv); + } + } + + if ((*hashcount != 0) && !(ctrl & PAM_NEW_MAIL_DIR)) { + *maildir = x_strdup(DEFAULT_MAIL_DIRECTORY); + ctrl |= PAM_NEW_MAIL_DIR; + } + + return ctrl; +} + +/* a front end for conversations */ + +static int converse(pam_handle_t *pamh, int ctrl, int nargs + , struct pam_message **message + , struct pam_response **response) +{ + int retval; + struct pam_conv *conv; + + D(("begin to converse")); + + retval = pam_get_item( pamh, PAM_CONV, (const void **) &conv ) ; + if ( retval == PAM_SUCCESS ) { + + retval = conv->conv(nargs, ( const struct pam_message ** ) message + , response, conv->appdata_ptr); + + D(("returned from application's conversation function")); + + if (retval != PAM_SUCCESS && (PAM_DEBUG_ARG & ctrl) ) { + _log_err(LOG_DEBUG, "conversation failure [%s]" + , pam_strerror(pamh, retval)); + } + + } else { + _log_err(LOG_ERR, "couldn't obtain coversation function [%s]" + , pam_strerror(pamh, retval)); + } + + D(("ready to return from module conversation")); + + return retval; /* propagate error status */ +} + +static int get_folder(pam_handle_t *pamh, int ctrl, + char **path_mail, char **folder_p, int hashcount) +{ + int retval; + const char *user, *path; + char *folder; + const struct passwd *pwd=NULL; + + retval = pam_get_user(pamh, &user, NULL); + if (retval != PAM_SUCCESS || user == NULL) { + _log_err(LOG_ERR, "no user specified"); + return PAM_USER_UNKNOWN; + } + + if (ctrl & PAM_NEW_MAIL_DIR) { + path = *path_mail; + if (*path == '~') { /* support for $HOME delivery */ + pwd = getpwnam(user); + if (pwd == NULL) { + _log_err(LOG_ERR, "user [%s] unknown", user); + _pam_overwrite(*path_mail); + _pam_drop(*path_mail); + return PAM_USER_UNKNOWN; + } + /* + * "~/xxx" and "~xxx" are treated as same + */ + if (!*++path || (*path == '/' && !*++path)) { + _log_err(LOG_ALERT, "badly formed mail path [%s]", *path_mail); + _pam_overwrite(*path_mail); + _pam_drop(*path_mail); + return PAM_ABORT; + } + ctrl |= PAM_HOME_MAIL; + if (hashcount != 0) { + _log_err(LOG_ALERT, "can't do hash= and home directory mail"); + } + } + } else { + path = DEFAULT_MAIL_DIRECTORY; + } + + /* put folder together */ + + if (ctrl & PAM_HOME_MAIL) { + folder = malloc(sizeof(MAIL_FILE_FORMAT) + +strlen(pwd->pw_dir)+strlen(path)); + } else { + folder = malloc(sizeof(MAIL_FILE_FORMAT)+strlen(path)+strlen(user) + +2*hashcount); + } + + if (folder != NULL) { + if (ctrl & PAM_HOME_MAIL) { + sprintf(folder, MAIL_FILE_FORMAT, pwd->pw_dir, "", path); + } else { + int i; + char *hash = malloc(2*hashcount+1); + + if (hash) { + for (i = 0; i < hashcount; i++) { + hash[2*i] = '/'; + hash[2*i+1] = user[i]; + } + hash[2*i] = '\0'; + sprintf(folder, MAIL_FILE_FORMAT, path, hash, user); + _pam_overwrite(hash); + _pam_drop(hash); + } else { + sprintf(folder, "error"); + } + } + D(("folder =[%s]", folder)); + } + + /* tidy up */ + + _pam_overwrite(*path_mail); + _pam_drop(*path_mail); + user = NULL; + + if (folder == NULL) { + _log_err(LOG_CRIT, "out of memory for mail folder"); + return PAM_BUF_ERR; + } + + *folder_p = folder; + folder = NULL; + + return PAM_SUCCESS; +} + +static const char *get_mail_status(int ctrl, const char *folder) +{ + const char *type = NULL; + static char dir[256]; + struct stat mail_st; + struct dirent **namelist; + int i; + + if (stat(folder, &mail_st) == 0) { + if (S_ISDIR(mail_st.st_mode)) { /* Assume Maildir format */ + sprintf(dir, "%.250s/new", folder); + i = scandir(dir, &namelist, 0, alphasort); + if (i > 2) { + type = "new"; + while (--i) + free(namelist[i]); + } else { + while (--i >= 0) + free(namelist[i]); + sprintf(dir, "%.250s/cur", folder); + i = scandir(dir, &namelist, 0, alphasort); + if (i > 2) { + type = "old"; + while (--i) + free(namelist[i]); + } else if (ctrl & PAM_EMPTY_TOO) { + while (--i >= 0) + free(namelist[i]); + type = "no"; + } else { + type = NULL; + } + } + } else { + if (mail_st.st_size > 0) { + if (mail_st.st_atime < mail_st.st_mtime) /* new */ + type = (ctrl & PAM_STANDARD_MAIL) ? "new " : "new"; + else /* old */ + type = (ctrl & PAM_STANDARD_MAIL) ? "" : "old"; + } else if (ctrl & PAM_EMPTY_TOO) { + type = "no"; + } else { + type = NULL; + } + } + } + + memset(dir, 0, 256); + memset(&mail_st, 0, sizeof(mail_st)); + D(("user has %s mail in %s folder", type, folder)); + return type; +} + +static int report_mail(pam_handle_t *pamh, int ctrl + , const char *type, const char *folder) +{ + int retval; + + if (!(ctrl & PAM_MAIL_SILENT) || ((ctrl & PAM_QUIET_MAIL) && strcmp(type, "new"))) { + char *remark; + + if (ctrl & PAM_STANDARD_MAIL) + if (!strcmp(type, "no")) + remark = malloc(strlen(NO_MAIL_STANDARD_FORMAT)+1); + else + remark = malloc(strlen(YOUR_MAIL_STANDARD_FORMAT)+strlen(type)+1); + else + remark = malloc(strlen(YOUR_MAIL_VERBOSE_FORMAT)+strlen(type)+strlen(folder)+1); + if (remark == NULL) { + retval = PAM_BUF_ERR; + } else { + struct pam_message msg[1], *mesg[1]; + struct pam_response *resp=NULL; + + if (ctrl & PAM_STANDARD_MAIL) + if (!strcmp(type, "no")) + sprintf(remark, NO_MAIL_STANDARD_FORMAT); + else + sprintf(remark, YOUR_MAIL_STANDARD_FORMAT, type); + else + sprintf(remark, YOUR_MAIL_VERBOSE_FORMAT, type, folder); + + mesg[0] = &msg[0]; + msg[0].msg_style = PAM_TEXT_INFO; + msg[0].msg = remark; + + retval = converse(pamh, ctrl, 1, mesg, &resp); + + _pam_overwrite(remark); + _pam_drop(remark); + if (resp) + _pam_drop_reply(resp, 1); + } + } else { + D(("keeping quiet")); + retval = PAM_SUCCESS; + } + + D(("returning %s", pam_strerror(pamh, retval))); + return retval; +} + +static int _do_mail(pam_handle_t *, int, int, const char **, int); + +/* --- authentication functions --- */ + +PAM_EXTERN +int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc, + const char **argv) +{ + return PAM_IGNORE; +} + +/* Checking mail as part of authentication */ +PAM_EXTERN +int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + if (!(flags & (PAM_ESTABLISH_CRED|PAM_DELETE_CRED))) + return PAM_IGNORE; + return _do_mail(pamh,flags,argc,argv,(flags & PAM_ESTABLISH_CRED)); +} + +/* --- session management functions --- */ + +PAM_EXTERN +int pam_sm_close_session(pam_handle_t *pamh,int flags,int argc + ,const char **argv) +{ + return _do_mail(pamh,flags,argc,argv,0);; +} + +/* Checking mail as part of the session management */ +PAM_EXTERN +int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + return _do_mail(pamh,flags,argc,argv,1); +} + + +/* --- The Beaf (Tm) --- */ + +static int _do_mail(pam_handle_t *pamh, int flags, int argc, + const char **argv, int est) +{ + int retval, ctrl, hashcount; + char *path_mail=NULL, *folder; + const char *type; + + /* + * this module (un)sets the MAIL environment variable, and checks if + * the user has any new mail. + */ + + ctrl = _pam_parse(flags, argc, argv, &path_mail, &hashcount); + + /* Do we have anything to do? */ + + if (flags & PAM_SILENT) + return PAM_SUCCESS; + + /* which folder? */ + + retval = get_folder(pamh, ctrl, &path_mail, &folder, hashcount); + if (retval != PAM_SUCCESS) { + D(("failed to find folder")); + return retval; + } + + /* set the MAIL variable? */ + + if (!(ctrl & PAM_NO_ENV) && est) { + char *tmp; + + tmp = malloc(strlen(folder)+sizeof(MAIL_ENV_FORMAT)); + if (tmp != NULL) { + sprintf(tmp, MAIL_ENV_FORMAT, folder); + D(("setting env: %s", tmp)); + retval = pam_putenv(pamh, tmp); + _pam_overwrite(tmp); + _pam_drop(tmp); + if (retval != PAM_SUCCESS) { + _pam_overwrite(folder); + _pam_drop(folder); + _log_err(LOG_CRIT, "unable to set " MAIL_ENV_NAME " variable"); + return retval; + } + } else { + _log_err(LOG_CRIT, "no memory for " MAIL_ENV_NAME " variable"); + _pam_overwrite(folder); + _pam_drop(folder); + return retval; + } + } else { + D(("not setting " MAIL_ENV_NAME " variable")); + } + + /* + * OK. we've got the mail folder... what about its status? + */ + + if ((est && !(ctrl & PAM_NO_LOGIN)) + || (!est && (ctrl & PAM_LOGOUT_TOO))) { + type = get_mail_status(ctrl, folder); + if (type != NULL) { + retval = report_mail(pamh, ctrl, type, folder); + type = NULL; + } + } + + /* Delete environment variable? */ + if (!est) + (void) pam_putenv(pamh, MAIL_ENV_NAME); + + _pam_overwrite(folder); /* clean up */ + _pam_drop(folder); + + /* indicate success or failure */ + + return retval; +} + +#ifdef PAM_STATIC + +/* static module data */ + +struct pam_module _pam_mail_modstruct = { + "pam_mail", + pam_sm_authenticate, + pam_sm_setcred, + NULL, + pam_sm_open_session, + pam_sm_close_session, + NULL, +}; + +#endif + +/* end of module definition */ -- cgit v1.2.3