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
19 #include <sys/types.h>
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"
34 * here, we make a definition for the externally accessible function
35 * in this file (this definition is required for static a module
36 * but strongly encouraged generally) it is used to instruct the
37 * modules include file to define the function prototypes.
40 #define PAM_SM_SESSION
43 #include <security/pam_modules.h>
44 #include <security/_pam_macros.h>
45 #include <security/pam_modutil.h>
46 #include <security/pam_ext.h>
48 /* argument parsing */
50 #define PAM_DEBUG_ARG 0x0001
51 #define PAM_NO_LOGIN 0x0002
52 #define PAM_LOGOUT_TOO 0x0004
53 #define PAM_NEW_MAIL_DIR 0x0010
54 #define PAM_MAIL_SILENT 0x0020
55 #define PAM_NO_ENV 0x0040
56 #define PAM_HOME_MAIL 0x0100
57 #define PAM_EMPTY_TOO 0x0200
58 #define PAM_STANDARD_MAIL 0x0400
59 #define PAM_QUIET_MAIL 0x1000
61 #define HAVE_NEW_MAIL 0x1
62 #define HAVE_OLD_MAIL 0x2
63 #define HAVE_NO_MAIL 0x3
67 _pam_parse (const pam_handle_t *pamh, int flags, int argc,
68 const char **argv, const char **maildir, size_t *hashcount)
72 if (flags & PAM_SILENT) {
73 ctrl |= PAM_MAIL_SILENT;
78 /* step through arguments */
79 for (; argc-- > 0; ++argv) {
83 if (!strcmp(*argv,"debug"))
84 ctrl |= PAM_DEBUG_ARG;
85 else if (!strcmp(*argv,"quiet"))
86 ctrl |= PAM_QUIET_MAIL;
87 else if (!strcmp(*argv,"standard"))
88 ctrl |= PAM_STANDARD_MAIL | PAM_EMPTY_TOO;
89 else if (!strncmp(*argv,"dir=",4)) {
91 if (**maildir != '\0') {
92 D(("new mail directory: %s", *maildir));
93 ctrl |= PAM_NEW_MAIL_DIR;
95 pam_syslog(pamh, LOG_ERR,
96 "dir= specification missing argument - ignored");
98 } else if (!strncmp(*argv,"hash=",5)) {
100 *hashcount = strtoul(*argv+5,&ep,10);
104 } else if (!strcmp(*argv,"close")) {
105 ctrl |= PAM_LOGOUT_TOO;
106 } else if (!strcmp(*argv,"nopen")) {
107 ctrl |= PAM_NO_LOGIN;
108 } else if (!strcmp(*argv,"noenv")) {
110 } else if (!strcmp(*argv,"empty")) {
111 ctrl |= PAM_EMPTY_TOO;
113 pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
117 if ((*hashcount != 0) && !(ctrl & PAM_NEW_MAIL_DIR)) {
118 *maildir = DEFAULT_MAIL_DIRECTORY;
119 ctrl |= PAM_NEW_MAIL_DIR;
126 get_folder(pam_handle_t *pamh, int ctrl,
127 const char *path_mail, char **folder_p, size_t hashcount,
128 const struct passwd *pwd)
134 if (ctrl & PAM_NEW_MAIL_DIR) {
136 if (*path == '~') { /* support for $HOME delivery */
138 * "~/xxx" and "~xxx" are treated as same
140 if (!*++path || (*path == '/' && !*++path)) {
141 pam_syslog(pamh, LOG_ERR,
142 "badly formed mail path [%s]", path_mail);
143 retval = PAM_SERVICE_ERR;
144 goto get_folder_cleanup;
146 ctrl |= PAM_HOME_MAIL;
147 if (hashcount != 0) {
148 pam_syslog(pamh, LOG_ERR,
149 "cannot do hash= and home directory mail");
153 path = DEFAULT_MAIL_DIRECTORY;
156 /* put folder together */
158 hashcount = hashcount < strlen(pwd->pw_name) ?
159 hashcount : strlen(pwd->pw_name);
161 retval = PAM_BUF_ERR;
162 if (ctrl & PAM_HOME_MAIL) {
163 if (asprintf(&folder, MAIL_FILE_FORMAT, pwd->pw_dir, "", path) < 0)
164 goto get_folder_cleanup;
170 if ((hash = malloc(2 * hashcount + 1)) == NULL)
171 goto get_folder_cleanup;
173 for (i = 0; i < hashcount; i++) {
175 hash[2 * i + 1] = pwd->pw_name[i];
179 rc = asprintf(&folder, MAIL_FILE_FORMAT, path, hash, pwd->pw_name);
180 _pam_overwrite(hash);
183 goto get_folder_cleanup;
185 D(("folder=[%s]", folder));
186 retval = PAM_SUCCESS;
196 if (retval == PAM_BUF_ERR)
197 pam_syslog(pamh, LOG_CRIT, "out of memory for mail folder");
203 get_mail_status(pam_handle_t *pamh, int ctrl, const char *folder)
208 if (stat(folder, &mail_st) < 0)
211 if (S_ISDIR(mail_st.st_mode)) { /* Assume Maildir format */
214 struct dirent **namelist;
216 if (asprintf(&dir, "%s/new", folder) < 0) {
217 pam_syslog(pamh, LOG_CRIT, "out of memory");
218 goto get_mail_status_cleanup;
220 i = scandir(dir, &namelist, 0, alphasort);
227 if (save_errno == ENOMEM) {
228 pam_syslog(pamh, LOG_CRIT, "out of memory");
229 goto get_mail_status_cleanup;
232 type = (i > 2) ? HAVE_NEW_MAIL : 0;
234 _pam_drop(namelist[i]);
237 if (asprintf(&dir, "%s/cur", folder) < 0) {
238 pam_syslog(pamh, LOG_CRIT, "out of memory");
239 goto get_mail_status_cleanup;
241 i = scandir(dir, &namelist, 0, alphasort);
248 if (save_errno == ENOMEM) {
249 pam_syslog(pamh, LOG_CRIT, "out of memory");
250 goto get_mail_status_cleanup;
254 type = HAVE_OLD_MAIL;
256 type = (ctrl & PAM_EMPTY_TOO) ? HAVE_NO_MAIL : 0;
258 _pam_drop(namelist[i]);
262 if (mail_st.st_size > 0) {
263 if (mail_st.st_atime < mail_st.st_mtime) /* new */
264 type = HAVE_NEW_MAIL;
266 type = (ctrl & PAM_STANDARD_MAIL) ? HAVE_MAIL : HAVE_OLD_MAIL;
267 } else if (ctrl & PAM_EMPTY_TOO) {
274 get_mail_status_cleanup:
275 memset(&mail_st, 0, sizeof(mail_st));
276 D(("user has %d mail in %s folder", type, folder));
281 report_mail(pam_handle_t *pamh, int ctrl, int type, const char *folder)
285 if ((ctrl & PAM_MAIL_SILENT) ||
286 ((ctrl & PAM_QUIET_MAIL) && type != HAVE_NEW_MAIL))
288 D(("keeping quiet"));
289 retval = PAM_SUCCESS;
293 if (ctrl & PAM_STANDARD_MAIL)
297 retval = pam_info (pamh, "%s", _("You have no mail."));
300 retval = pam_info (pamh, "%s", _("You have new mail."));
303 retval = pam_info (pamh, "%s", _("You have old mail."));
307 retval = pam_info (pamh, "%s", _("You have mail."));
314 retval = pam_info (pamh, _("You have no mail in folder %s."),
318 retval = pam_info (pamh, _("You have new mail in folder %s."),
322 retval = pam_info (pamh, _("You have old mail in folder %s."),
327 retval = pam_info (pamh, _("You have mail in folder %s."),
333 D(("returning %s", pam_strerror(pamh, retval)));
337 static int _do_mail(pam_handle_t *, int, int, const char **, int);
339 /* --- authentication functions --- */
342 pam_sm_authenticate (pam_handle_t *pamh UNUSED, int flags UNUSED,
343 int argc UNUSED, const char **argv UNUSED)
348 /* Checking mail as part of authentication */
349 int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc,
352 if (!(flags & (PAM_ESTABLISH_CRED|PAM_DELETE_CRED)))
354 return _do_mail(pamh,flags,argc,argv,(flags & PAM_ESTABLISH_CRED));
357 /* --- session management functions --- */
359 int pam_sm_close_session(pam_handle_t *pamh,int flags,int argc
362 return _do_mail(pamh,flags,argc,argv,0);
365 /* Checking mail as part of the session management */
366 int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc,
369 return _do_mail(pamh,flags,argc,argv,1);
373 /* --- The Beaf (Tm) --- */
375 static int _do_mail(pam_handle_t *pamh, int flags, int argc,
376 const char **argv, int est)
378 int retval, ctrl, type;
382 const char *path_mail = NULL;
383 const struct passwd *pwd = NULL;
386 * this module (un)sets the MAIL environment variable, and checks if
387 * the user has any new mail.
390 ctrl = _pam_parse(pamh, flags, argc, argv, &path_mail, &hashcount);
392 retval = pam_get_user(pamh, &user, NULL);
393 if (retval != PAM_SUCCESS || user == NULL) {
394 pam_syslog(pamh, LOG_ERR, "cannot determine username");
395 return PAM_USER_UNKNOWN;
398 pwd = pam_modutil_getpwnam (pamh, user);
400 pam_syslog(pamh, LOG_ERR, "user unknown");
401 return PAM_USER_UNKNOWN;
406 retval = get_folder(pamh, ctrl, path_mail, &folder, hashcount, pwd);
407 if (retval != PAM_SUCCESS) {
408 D(("failed to find folder"));
412 /* set the MAIL variable? */
414 if (!(ctrl & PAM_NO_ENV) && est) {
417 if (asprintf(&tmp, MAIL_ENV_FORMAT, folder) < 0) {
418 pam_syslog(pamh, LOG_CRIT,
419 "no memory for " MAIL_ENV_NAME " variable");
420 retval = PAM_BUF_ERR;
421 goto do_mail_cleanup;
423 D(("setting env: %s", tmp));
424 retval = pam_putenv(pamh, tmp);
427 if (retval != PAM_SUCCESS) {
428 pam_syslog(pamh, LOG_CRIT,
429 "unable to set " MAIL_ENV_NAME " variable");
430 retval = PAM_BUF_ERR;
431 goto do_mail_cleanup;
434 D(("not setting " MAIL_ENV_NAME " variable"));
438 * OK. we've got the mail folder... what about its status?
441 if ((est && !(ctrl & PAM_NO_LOGIN))
442 || (!est && (ctrl & PAM_LOGOUT_TOO))) {
443 PAM_MODUTIL_DEF_PRIVS(privs);
445 if (pam_modutil_drop_priv(pamh, &privs, pwd)) {
446 retval = PAM_SESSION_ERR;
447 goto do_mail_cleanup;
449 type = get_mail_status(pamh, ctrl, folder);
450 if (pam_modutil_regain_priv(pamh, &privs)) {
451 retval = PAM_SESSION_ERR;
452 goto do_mail_cleanup;
457 retval = report_mail(pamh, ctrl, type, folder);
462 /* Delete environment variable? */
463 if ( ! est && ! (ctrl & PAM_NO_ENV) )
464 (void) pam_putenv(pamh, MAIL_ENV_NAME);
467 _pam_overwrite(folder);
470 /* indicate success or failure */
475 /* end of module definition */