(root)/
Linux-PAM-1.5.3/
modules/
pam_mail/
pam_mail.c
       1  /*
       2   * pam_mail module
       3   *
       4   * Written by Andrew Morgan <morgan@linux.kernel.org> 1996/3/11
       5   * $HOME additions by David Kinchlea <kinch@kinch.ark.com> 1997/1/7
       6   * mailhash additions by Chris Adams <cadams@ro.com> 1998/7/11
       7   */
       8  
       9  #include "config.h"
      10  
      11  #include <ctype.h>
      12  #include <pwd.h>
      13  #include <stdarg.h>
      14  #include <stdio.h>
      15  #include <stdlib.h>
      16  #include <string.h>
      17  #include <syslog.h>
      18  #include <sys/stat.h>
      19  #include <sys/types.h>
      20  #include <unistd.h>
      21  #include <dirent.h>
      22  #include <errno.h>
      23  
      24  #ifdef HAVE_PATHS_H
      25  #include <paths.h>
      26  #endif
      27  
      28  #define DEFAULT_MAIL_DIRECTORY    PAM_PATH_MAILDIR
      29  #define MAIL_FILE_FORMAT          "%s%s/%s"
      30  #define MAIL_ENV_NAME             "MAIL"
      31  #define MAIL_ENV_FORMAT           MAIL_ENV_NAME "=%s"
      32  
      33  #include <security/pam_modules.h>
      34  #include <security/_pam_macros.h>
      35  #include <security/pam_modutil.h>
      36  #include <security/pam_ext.h>
      37  #include "pam_inline.h"
      38  
      39  /* argument parsing */
      40  
      41  #define PAM_DEBUG_ARG		0x0001
      42  #define PAM_NO_LOGIN		0x0002
      43  #define PAM_LOGOUT_TOO		0x0004
      44  #define PAM_NEW_MAIL_DIR	0x0010
      45  #define PAM_MAIL_SILENT		0x0020
      46  #define PAM_NO_ENV		0x0040
      47  #define PAM_HOME_MAIL		0x0100
      48  #define PAM_EMPTY_TOO		0x0200
      49  #define PAM_STANDARD_MAIL	0x0400
      50  #define PAM_QUIET_MAIL		0x1000
      51  
      52  #define HAVE_NEW_MAIL           0x1
      53  #define HAVE_OLD_MAIL           0x2
      54  #define HAVE_NO_MAIL            0x3
      55  #define HAVE_MAIL               0x4
      56  
      57  static int
      58  _pam_parse (const pam_handle_t *pamh, int flags, int argc,
      59  	    const char **argv, const char **maildir, size_t *hashcount)
      60  {
      61      int ctrl=0;
      62  
      63      if (flags & PAM_SILENT) {
      64  	ctrl |= PAM_MAIL_SILENT;
      65      }
      66  
      67      *hashcount = 0;
      68  
      69      /* step through arguments */
      70      for (; argc-- > 0; ++argv) {
      71  	const char *str;
      72  
      73  	/* generic options */
      74  
      75  	if (!strcmp(*argv,"debug"))
      76  	    ctrl |= PAM_DEBUG_ARG;
      77  	else if (!strcmp(*argv,"quiet"))
      78  	    ctrl |= PAM_QUIET_MAIL;
      79  	else if (!strcmp(*argv,"standard"))
      80  	    ctrl |= PAM_STANDARD_MAIL | PAM_EMPTY_TOO;
      81  	else if ((str = pam_str_skip_prefix(*argv, "dir=")) != NULL) {
      82  	    *maildir = str;
      83  	    if (**maildir != '\0') {
      84  		D(("new mail directory: %s", *maildir));
      85  		ctrl |= PAM_NEW_MAIL_DIR;
      86  	    } else {
      87  		pam_syslog(pamh, LOG_ERR,
      88  			   "dir= specification missing argument - ignored");
      89  	    }
      90  	} else if ((str = pam_str_skip_prefix(*argv, "hash=")) != NULL) {
      91  	    char *ep = NULL;
      92  	    *hashcount = strtoul(str,&ep,10);
      93  	    if (!ep) {
      94  		*hashcount = 0;
      95  	    }
      96  	} else if (!strcmp(*argv,"close")) {
      97  	    ctrl |= PAM_LOGOUT_TOO;
      98  	} else if (!strcmp(*argv,"nopen")) {
      99  	    ctrl |= PAM_NO_LOGIN;
     100  	} else if (!strcmp(*argv,"noenv")) {
     101  	    ctrl |= PAM_NO_ENV;
     102  	} else if (!strcmp(*argv,"empty")) {
     103  	    ctrl |= PAM_EMPTY_TOO;
     104  	} else {
     105  	    pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
     106  	}
     107      }
     108  
     109      if ((*hashcount != 0) && !(ctrl & PAM_NEW_MAIL_DIR)) {
     110  	*maildir = DEFAULT_MAIL_DIRECTORY;
     111  	ctrl |= PAM_NEW_MAIL_DIR;
     112      }
     113  
     114      return ctrl;
     115  }
     116  
     117  static int
     118  get_folder(pam_handle_t *pamh, int ctrl,
     119  	   const char *path_mail, char **folder_p, size_t hashcount,
     120  	   const struct passwd *pwd)
     121  {
     122      int retval;
     123      const char *path;
     124      char *folder = NULL;
     125  
     126      if (ctrl & PAM_NEW_MAIL_DIR) {
     127  	path = path_mail;
     128  	if (*path == '~') {	/* support for $HOME delivery */
     129  	    /*
     130  	     * "~/xxx" and "~xxx" are treated as same
     131  	     */
     132  	    if (!*++path || (*path == '/' && !*++path)) {
     133  		pam_syslog(pamh, LOG_ERR,
     134  			   "badly formed mail path [%s]", path_mail);
     135  		retval = PAM_SERVICE_ERR;
     136  		goto get_folder_cleanup;
     137  	    }
     138  	    ctrl |= PAM_HOME_MAIL;
     139  	    if (hashcount != 0) {
     140  		pam_syslog(pamh, LOG_ERR,
     141  			   "cannot do hash= and home directory mail");
     142  	    }
     143  	}
     144      } else {
     145  	path = DEFAULT_MAIL_DIRECTORY;
     146      }
     147  
     148      /* put folder together */
     149  
     150      hashcount = hashcount < strlen(pwd->pw_name) ?
     151        hashcount : strlen(pwd->pw_name);
     152  
     153      retval = PAM_BUF_ERR;
     154      if (ctrl & PAM_HOME_MAIL) {
     155  	if (asprintf(&folder, MAIL_FILE_FORMAT, pwd->pw_dir, "", path) < 0)
     156  	    goto get_folder_cleanup;
     157      } else {
     158  	int rc;
     159  	size_t i;
     160  	char *hash;
     161  
     162  	if ((hash = malloc(2 * hashcount + 1)) == NULL)
     163  	    goto get_folder_cleanup;
     164  
     165  	for (i = 0; i < hashcount; i++) {
     166  	    hash[2 * i] = '/';
     167  	    hash[2 * i + 1] = pwd->pw_name[i];
     168  	}
     169  	hash[2 * i] = '\0';
     170  
     171  	rc = asprintf(&folder, MAIL_FILE_FORMAT, path, hash, pwd->pw_name);
     172  	pam_overwrite_string(hash);
     173  	_pam_drop(hash);
     174  	if (rc < 0)
     175  	    goto get_folder_cleanup;
     176      }
     177      D(("folder=[%s]", folder));
     178      retval = PAM_SUCCESS;
     179  
     180      /* tidy up */
     181  
     182    get_folder_cleanup:
     183      path = NULL;
     184  
     185      *folder_p = folder;
     186      folder = NULL;
     187  
     188      if (retval == PAM_BUF_ERR)
     189  	pam_syslog(pamh, LOG_CRIT, "out of memory for mail folder");
     190  
     191      return retval;
     192  }
     193  
     194  static int
     195  get_mail_status(pam_handle_t *pamh, int ctrl, const char *folder)
     196  {
     197      int type = 0;
     198      struct stat mail_st;
     199  
     200      if (stat(folder, &mail_st) < 0)
     201  	return 0;
     202  
     203      if (S_ISDIR(mail_st.st_mode)) {	/* Assume Maildir format */
     204  	int i, save_errno;
     205  	char *dir;
     206  	struct dirent **namelist;
     207  
     208  	if (asprintf(&dir, "%s/new", folder) < 0) {
     209  	    pam_syslog(pamh, LOG_CRIT, "out of memory");
     210  	    goto get_mail_status_cleanup;
     211  	}
     212  	i = scandir(dir, &namelist, 0, alphasort);
     213  	save_errno = errno;
     214  	pam_overwrite_string(dir);
     215  	_pam_drop(dir);
     216  	if (i < 0) {
     217  	    type = 0;
     218  	    namelist = NULL;
     219  	    if (save_errno == ENOMEM) {
     220  		pam_syslog(pamh, LOG_CRIT, "out of memory");
     221  		goto get_mail_status_cleanup;
     222  	    }
     223  	}
     224  	type = (i > 2) ? HAVE_NEW_MAIL : 0;
     225  	while (--i >= 0)
     226  	    _pam_drop(namelist[i]);
     227  	_pam_drop(namelist);
     228  	if (type == 0) {
     229  	    if (asprintf(&dir, "%s/cur", folder) < 0) {
     230  		pam_syslog(pamh, LOG_CRIT, "out of memory");
     231  		goto get_mail_status_cleanup;
     232  	    }
     233  	    i = scandir(dir, &namelist, 0, alphasort);
     234  	    save_errno = errno;
     235  	    pam_overwrite_string(dir);
     236  	    _pam_drop(dir);
     237  	    if (i < 0) {
     238  		type = 0;
     239  		namelist = NULL;
     240  		if (save_errno == ENOMEM) {
     241  		    pam_syslog(pamh, LOG_CRIT, "out of memory");
     242  		    goto get_mail_status_cleanup;
     243  		}
     244  	    }
     245  	    if (i > 2)
     246  	        type = HAVE_OLD_MAIL;
     247  	    else
     248  	        type = (ctrl & PAM_EMPTY_TOO) ? HAVE_NO_MAIL : 0;
     249  	    while (--i >= 0)
     250  		_pam_drop(namelist[i]);
     251  	    _pam_drop(namelist);
     252  	}
     253      } else {
     254  	if (mail_st.st_size > 0) {
     255  	    if (mail_st.st_atime < mail_st.st_mtime)	/* new */
     256  	        type = HAVE_NEW_MAIL;
     257  	    else		/* old */
     258  	        type = (ctrl & PAM_STANDARD_MAIL) ? HAVE_MAIL : HAVE_OLD_MAIL;
     259  	} else if (ctrl & PAM_EMPTY_TOO) {
     260  	    type = HAVE_NO_MAIL;
     261  	} else {
     262  	    type = 0;
     263  	}
     264      }
     265  
     266    get_mail_status_cleanup:
     267      pam_overwrite_object(&mail_st);
     268      D(("user has %d mail in %s folder", type, folder));
     269      return type;
     270  }
     271  
     272  static int
     273  report_mail(pam_handle_t *pamh, int ctrl, int type, const char *folder)
     274  {
     275      int retval;
     276  
     277      if ((ctrl & PAM_MAIL_SILENT) ||
     278  	((ctrl & PAM_QUIET_MAIL) && type != HAVE_NEW_MAIL))
     279        {
     280  	D(("keeping quiet"));
     281  	retval = PAM_SUCCESS;
     282        }
     283      else
     284        {
     285  	if (ctrl & PAM_STANDARD_MAIL)
     286  	  switch (type)
     287  	    {
     288  	    case HAVE_NO_MAIL:
     289  	      retval = pam_info (pamh, "%s", _("You do not have any new mail."));
     290  	      break;
     291  	    case HAVE_NEW_MAIL:
     292  	      retval = pam_info (pamh, "%s", _("You have new mail."));
     293  	      break;
     294  	    case HAVE_OLD_MAIL:
     295  	      retval = pam_info (pamh, "%s", _("You have old mail."));
     296  	      break;
     297  	    case HAVE_MAIL:
     298  	    default:
     299  	      retval = pam_info (pamh, "%s", _("You have mail."));
     300  	      break;
     301  	    }
     302  	else
     303  	  switch (type)
     304  	    {
     305  	    case HAVE_NO_MAIL:
     306  	      retval = pam_info (pamh, _("You have no mail in folder %s."),
     307  				 folder);
     308  	      break;
     309  	    case HAVE_NEW_MAIL:
     310  	      retval = pam_info (pamh, _("You have new mail in folder %s."),
     311  				 folder);
     312  	      break;
     313  	    case HAVE_OLD_MAIL:
     314  	      retval = pam_info (pamh, _("You have old mail in folder %s."),
     315  				 folder);
     316  	      break;
     317  	    case HAVE_MAIL:
     318  	    default:
     319  	      retval = pam_info (pamh, _("You have mail in folder %s."),
     320  				 folder);
     321  	      break;
     322  	    }
     323        }
     324  
     325      D(("returning %s", pam_strerror(pamh, retval)));
     326      return retval;
     327  }
     328  
     329  static int _do_mail(pam_handle_t *, int, int, const char **, int);
     330  
     331  /* --- authentication functions --- */
     332  
     333  int
     334  pam_sm_authenticate (pam_handle_t *pamh UNUSED, int flags UNUSED,
     335  		     int argc UNUSED, const char **argv UNUSED)
     336  {
     337      return PAM_IGNORE;
     338  }
     339  
     340  /* Checking mail as part of authentication */
     341  int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc,
     342      const char **argv)
     343  {
     344      if (!(flags & (PAM_ESTABLISH_CRED|PAM_DELETE_CRED)))
     345        return PAM_IGNORE;
     346      return _do_mail(pamh,flags,argc,argv,(flags & PAM_ESTABLISH_CRED));
     347  }
     348  
     349  /* --- session management functions --- */
     350  
     351  int pam_sm_close_session(pam_handle_t *pamh,int flags,int argc
     352  			 ,const char **argv)
     353  {
     354      return _do_mail(pamh,flags,argc,argv,0);
     355  }
     356  
     357  /* Checking mail as part of the session management */
     358  int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc,
     359      const char **argv)
     360  {
     361      return _do_mail(pamh,flags,argc,argv,1);
     362  }
     363  
     364  
     365  /* --- The Beaf (Tm) --- */
     366  
     367  static int _do_mail(pam_handle_t *pamh, int flags, int argc,
     368      const char **argv, int est)
     369  {
     370      int retval, ctrl, type;
     371      size_t hashcount;
     372      char *folder = NULL;
     373      const char *user;
     374      const char *path_mail = NULL;
     375      const struct passwd *pwd = NULL;
     376  
     377      /*
     378       * this module (un)sets the MAIL environment variable, and checks if
     379       * the user has any new mail.
     380       */
     381  
     382      ctrl = _pam_parse(pamh, flags, argc, argv, &path_mail, &hashcount);
     383  
     384      retval = pam_get_user(pamh, &user, NULL);
     385      if (retval != PAM_SUCCESS) {
     386  	pam_syslog(pamh, LOG_NOTICE, "cannot determine user name: %s",
     387  		   pam_strerror(pamh, retval));
     388  	return PAM_USER_UNKNOWN;
     389      }
     390  
     391      pwd = pam_modutil_getpwnam (pamh, user);
     392      if (pwd == NULL) {
     393          pam_syslog(pamh, LOG_NOTICE, "user unknown");
     394          return PAM_USER_UNKNOWN;
     395      }
     396  
     397      /* which folder? */
     398  
     399      retval = get_folder(pamh, ctrl, path_mail, &folder, hashcount, pwd);
     400      if (retval != PAM_SUCCESS) {
     401  	D(("failed to find folder"));
     402  	return retval;
     403      }
     404  
     405      /* set the MAIL variable? */
     406  
     407      if (!(ctrl & PAM_NO_ENV) && est) {
     408  	char *tmp;
     409  
     410  	if (asprintf(&tmp, MAIL_ENV_FORMAT, folder) < 0) {
     411  	    pam_syslog(pamh, LOG_CRIT,
     412  		       "no memory for " MAIL_ENV_NAME " variable");
     413  	    retval = PAM_BUF_ERR;
     414  	    goto do_mail_cleanup;
     415  	}
     416  	D(("setting env: %s", tmp));
     417  	retval = pam_putenv(pamh, tmp);
     418  	pam_overwrite_string(tmp);
     419  	_pam_drop(tmp);
     420  	if (retval != PAM_SUCCESS) {
     421  	    pam_syslog(pamh, LOG_CRIT,
     422  		       "unable to set " MAIL_ENV_NAME " variable");
     423  	    retval = PAM_BUF_ERR;
     424  	    goto do_mail_cleanup;
     425  	}
     426      } else {
     427  	D(("not setting " MAIL_ENV_NAME " variable"));
     428      }
     429  
     430      /*
     431       * OK. we've got the mail folder... what about its status?
     432       */
     433  
     434      if ((est && !(ctrl & PAM_NO_LOGIN))
     435  	|| (!est && (ctrl & PAM_LOGOUT_TOO))) {
     436  	PAM_MODUTIL_DEF_PRIVS(privs);
     437  
     438  	if (pam_modutil_drop_priv(pamh, &privs, pwd)) {
     439  	  retval = PAM_SESSION_ERR;
     440  	  goto do_mail_cleanup;
     441  	} else {
     442  	  type = get_mail_status(pamh, ctrl, folder);
     443  	  if (pam_modutil_regain_priv(pamh, &privs)) {
     444  	    retval = PAM_SESSION_ERR;
     445  	    goto do_mail_cleanup;
     446  	  }
     447  	}
     448  
     449  	if (type != 0) {
     450  	    retval = report_mail(pamh, ctrl, type, folder);
     451  	    type = 0;
     452  	}
     453      }
     454  
     455      /* Delete environment variable? */
     456      if ( ! est && ! (ctrl & PAM_NO_ENV) )
     457  	(void) pam_putenv(pamh, MAIL_ENV_NAME);
     458  
     459    do_mail_cleanup:
     460      pam_overwrite_string(folder);
     461      _pam_drop(folder);
     462  
     463      /* indicate success or failure */
     464  
     465      return retval;
     466  }
     467  
     468  /* end of module definition */