summaryrefslogtreecommitdiff
path: root/modules/pam_mail
diff options
context:
space:
mode:
Diffstat (limited to 'modules/pam_mail')
-rw-r--r--modules/pam_mail/.cvsignore1
-rw-r--r--modules/pam_mail/Makefile113
-rw-r--r--modules/pam_mail/README17
-rw-r--r--modules/pam_mail/pam_mail.c504
4 files changed, 635 insertions, 0 deletions
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 <morgan@parc.power.net> 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/<login>
+ "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 <morgan@linux.kernel.org> 1996/3/11
+ * $HOME additions by David Kinchlea <kinch@kinch.ark.com> 1997/1/7
+ * mailhash additions by Chris Adams <cadams@ro.com> 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 <features.h>
+#endif
+
+#include <ctype.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#ifdef WANT_PWDB
+#include <pwdb/pwdb_public.h>
+#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 <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+/* 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 */