123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468 |
- /*
- * pam_mail module
- *
- * 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
- */
- #include "config.h"
- #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>
- #include <errno.h>
- #ifdef HAVE_PATHS_H
- #include <paths.h>
- #endif
- #define DEFAULT_MAIL_DIRECTORY PAM_PATH_MAILDIR
- #define MAIL_FILE_FORMAT "%s%s/%s"
- #define MAIL_ENV_NAME "MAIL"
- #define MAIL_ENV_FORMAT MAIL_ENV_NAME "=%s"
- #include <security/pam_modules.h>
- #include <security/_pam_macros.h>
- #include <security/pam_modutil.h>
- #include <security/pam_ext.h>
- #include "pam_inline.h"
- /* 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
- #define HAVE_NEW_MAIL 0x1
- #define HAVE_OLD_MAIL 0x2
- #define HAVE_NO_MAIL 0x3
- #define HAVE_MAIL 0x4
- static int
- _pam_parse (const pam_handle_t *pamh, int flags, int argc,
- const char **argv, const char **maildir, size_t *hashcount)
- {
- int ctrl=0;
- if (flags & PAM_SILENT) {
- ctrl |= PAM_MAIL_SILENT;
- }
- *hashcount = 0;
- /* step through arguments */
- for (; argc-- > 0; ++argv) {
- const char *str;
- /* 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 ((str = pam_str_skip_prefix(*argv, "dir=")) != NULL) {
- *maildir = str;
- if (**maildir != '\0') {
- D(("new mail directory: %s", *maildir));
- ctrl |= PAM_NEW_MAIL_DIR;
- } else {
- pam_syslog(pamh, LOG_ERR,
- "dir= specification missing argument - ignored");
- }
- } else if ((str = pam_str_skip_prefix(*argv, "hash=")) != NULL) {
- char *ep = NULL;
- *hashcount = strtoul(str,&ep,10);
- if (!ep) {
- *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 {
- pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
- }
- }
- if ((*hashcount != 0) && !(ctrl & PAM_NEW_MAIL_DIR)) {
- *maildir = DEFAULT_MAIL_DIRECTORY;
- ctrl |= PAM_NEW_MAIL_DIR;
- }
- return ctrl;
- }
- static int
- get_folder(pam_handle_t *pamh, int ctrl,
- const char *path_mail, char **folder_p, size_t hashcount,
- const struct passwd *pwd)
- {
- int retval;
- const char *path;
- char *folder = NULL;
- if (ctrl & PAM_NEW_MAIL_DIR) {
- path = path_mail;
- if (*path == '~') { /* support for $HOME delivery */
- /*
- * "~/xxx" and "~xxx" are treated as same
- */
- if (!*++path || (*path == '/' && !*++path)) {
- pam_syslog(pamh, LOG_ERR,
- "badly formed mail path [%s]", path_mail);
- retval = PAM_SERVICE_ERR;
- goto get_folder_cleanup;
- }
- ctrl |= PAM_HOME_MAIL;
- if (hashcount != 0) {
- pam_syslog(pamh, LOG_ERR,
- "cannot do hash= and home directory mail");
- }
- }
- } else {
- path = DEFAULT_MAIL_DIRECTORY;
- }
- /* put folder together */
- hashcount = hashcount < strlen(pwd->pw_name) ?
- hashcount : strlen(pwd->pw_name);
- retval = PAM_BUF_ERR;
- if (ctrl & PAM_HOME_MAIL) {
- if (asprintf(&folder, MAIL_FILE_FORMAT, pwd->pw_dir, "", path) < 0)
- goto get_folder_cleanup;
- } else {
- int rc;
- size_t i;
- char *hash;
- if ((hash = malloc(2 * hashcount + 1)) == NULL)
- goto get_folder_cleanup;
- for (i = 0; i < hashcount; i++) {
- hash[2 * i] = '/';
- hash[2 * i + 1] = pwd->pw_name[i];
- }
- hash[2 * i] = '\0';
- rc = asprintf(&folder, MAIL_FILE_FORMAT, path, hash, pwd->pw_name);
- _pam_overwrite(hash);
- _pam_drop(hash);
- if (rc < 0)
- goto get_folder_cleanup;
- }
- D(("folder=[%s]", folder));
- retval = PAM_SUCCESS;
- /* tidy up */
- get_folder_cleanup:
- path = NULL;
- *folder_p = folder;
- folder = NULL;
- if (retval == PAM_BUF_ERR)
- pam_syslog(pamh, LOG_CRIT, "out of memory for mail folder");
- return retval;
- }
- static int
- get_mail_status(pam_handle_t *pamh, int ctrl, const char *folder)
- {
- int type = 0;
- struct stat mail_st;
- if (stat(folder, &mail_st) < 0)
- return 0;
- if (S_ISDIR(mail_st.st_mode)) { /* Assume Maildir format */
- int i, save_errno;
- char *dir;
- struct dirent **namelist;
- if (asprintf(&dir, "%s/new", folder) < 0) {
- pam_syslog(pamh, LOG_CRIT, "out of memory");
- goto get_mail_status_cleanup;
- }
- i = scandir(dir, &namelist, 0, alphasort);
- save_errno = errno;
- _pam_overwrite(dir);
- _pam_drop(dir);
- if (i < 0) {
- type = 0;
- namelist = NULL;
- if (save_errno == ENOMEM) {
- pam_syslog(pamh, LOG_CRIT, "out of memory");
- goto get_mail_status_cleanup;
- }
- }
- type = (i > 2) ? HAVE_NEW_MAIL : 0;
- while (--i >= 0)
- _pam_drop(namelist[i]);
- _pam_drop(namelist);
- if (type == 0) {
- if (asprintf(&dir, "%s/cur", folder) < 0) {
- pam_syslog(pamh, LOG_CRIT, "out of memory");
- goto get_mail_status_cleanup;
- }
- i = scandir(dir, &namelist, 0, alphasort);
- save_errno = errno;
- _pam_overwrite(dir);
- _pam_drop(dir);
- if (i < 0) {
- type = 0;
- namelist = NULL;
- if (save_errno == ENOMEM) {
- pam_syslog(pamh, LOG_CRIT, "out of memory");
- goto get_mail_status_cleanup;
- }
- }
- if (i > 2)
- type = HAVE_OLD_MAIL;
- else
- type = (ctrl & PAM_EMPTY_TOO) ? HAVE_NO_MAIL : 0;
- while (--i >= 0)
- _pam_drop(namelist[i]);
- _pam_drop(namelist);
- }
- } else {
- if (mail_st.st_size > 0) {
- if (mail_st.st_atime < mail_st.st_mtime) /* new */
- type = HAVE_NEW_MAIL;
- else /* old */
- type = (ctrl & PAM_STANDARD_MAIL) ? HAVE_MAIL : HAVE_OLD_MAIL;
- } else if (ctrl & PAM_EMPTY_TOO) {
- type = HAVE_NO_MAIL;
- } else {
- type = 0;
- }
- }
- get_mail_status_cleanup:
- memset(&mail_st, 0, sizeof(mail_st));
- D(("user has %d mail in %s folder", type, folder));
- return type;
- }
- static int
- report_mail(pam_handle_t *pamh, int ctrl, int type, const char *folder)
- {
- int retval;
- if ((ctrl & PAM_MAIL_SILENT) ||
- ((ctrl & PAM_QUIET_MAIL) && type != HAVE_NEW_MAIL))
- {
- D(("keeping quiet"));
- retval = PAM_SUCCESS;
- }
- else
- {
- if (ctrl & PAM_STANDARD_MAIL)
- switch (type)
- {
- case HAVE_NO_MAIL:
- retval = pam_info (pamh, "%s", _("You have no mail."));
- break;
- case HAVE_NEW_MAIL:
- retval = pam_info (pamh, "%s", _("You have new mail."));
- break;
- case HAVE_OLD_MAIL:
- retval = pam_info (pamh, "%s", _("You have old mail."));
- break;
- case HAVE_MAIL:
- default:
- retval = pam_info (pamh, "%s", _("You have mail."));
- break;
- }
- else
- switch (type)
- {
- case HAVE_NO_MAIL:
- retval = pam_info (pamh, _("You have no mail in folder %s."),
- folder);
- break;
- case HAVE_NEW_MAIL:
- retval = pam_info (pamh, _("You have new mail in folder %s."),
- folder);
- break;
- case HAVE_OLD_MAIL:
- retval = pam_info (pamh, _("You have old mail in folder %s."),
- folder);
- break;
- case HAVE_MAIL:
- default:
- retval = pam_info (pamh, _("You have mail in folder %s."),
- folder);
- break;
- }
- }
- D(("returning %s", pam_strerror(pamh, retval)));
- return retval;
- }
- static int _do_mail(pam_handle_t *, int, int, const char **, int);
- /* --- authentication functions --- */
- int
- pam_sm_authenticate (pam_handle_t *pamh UNUSED, int flags UNUSED,
- int argc UNUSED, const char **argv UNUSED)
- {
- return PAM_IGNORE;
- }
- /* Checking mail as part of authentication */
- 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 --- */
- 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 */
- 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, type;
- size_t hashcount;
- char *folder = NULL;
- const char *user;
- const char *path_mail = NULL;
- const struct passwd *pwd = NULL;
- /*
- * this module (un)sets the MAIL environment variable, and checks if
- * the user has any new mail.
- */
- ctrl = _pam_parse(pamh, flags, argc, argv, &path_mail, &hashcount);
- retval = pam_get_user(pamh, &user, NULL);
- if (retval != PAM_SUCCESS) {
- pam_syslog(pamh, LOG_NOTICE, "cannot determine user name: %s",
- pam_strerror(pamh, retval));
- return PAM_USER_UNKNOWN;
- }
- pwd = pam_modutil_getpwnam (pamh, user);
- if (pwd == NULL) {
- pam_syslog(pamh, LOG_NOTICE, "user unknown");
- return PAM_USER_UNKNOWN;
- }
- /* which folder? */
- retval = get_folder(pamh, ctrl, path_mail, &folder, hashcount, pwd);
- if (retval != PAM_SUCCESS) {
- D(("failed to find folder"));
- return retval;
- }
- /* set the MAIL variable? */
- if (!(ctrl & PAM_NO_ENV) && est) {
- char *tmp;
- if (asprintf(&tmp, MAIL_ENV_FORMAT, folder) < 0) {
- pam_syslog(pamh, LOG_CRIT,
- "no memory for " MAIL_ENV_NAME " variable");
- retval = PAM_BUF_ERR;
- goto do_mail_cleanup;
- }
- D(("setting env: %s", tmp));
- retval = pam_putenv(pamh, tmp);
- _pam_overwrite(tmp);
- _pam_drop(tmp);
- if (retval != PAM_SUCCESS) {
- pam_syslog(pamh, LOG_CRIT,
- "unable to set " MAIL_ENV_NAME " variable");
- retval = PAM_BUF_ERR;
- goto do_mail_cleanup;
- }
- } 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))) {
- PAM_MODUTIL_DEF_PRIVS(privs);
- if (pam_modutil_drop_priv(pamh, &privs, pwd)) {
- retval = PAM_SESSION_ERR;
- goto do_mail_cleanup;
- } else {
- type = get_mail_status(pamh, ctrl, folder);
- if (pam_modutil_regain_priv(pamh, &privs)) {
- retval = PAM_SESSION_ERR;
- goto do_mail_cleanup;
- }
- }
- if (type != 0) {
- retval = report_mail(pamh, ctrl, type, folder);
- type = 0;
- }
- }
- /* Delete environment variable? */
- if ( ! est && ! (ctrl & PAM_NO_ENV) )
- (void) pam_putenv(pamh, MAIL_ENV_NAME);
- do_mail_cleanup:
- _pam_overwrite(folder);
- _pam_drop(folder);
- /* indicate success or failure */
- return retval;
- }
- /* end of module definition */
|