6 * Written by Andrew Morgan <morgan@linux.kernel.org> 1996/3/11
7 * $HOME additions by David Kinchlea <kinch@kinch.ark.com> 1997/1/7
8 * mailhash additions by Chris Adams <cadams@ro.com> 1998/7/11
11 #include <security/_pam_aconf.h>
21 #include <sys/types.h>
26 #include <pwdb/pwdb_public.h>
29 #define DEFAULT_MAIL_DIRECTORY PAM_PATH_MAILDIR
30 #define MAIL_FILE_FORMAT "%s%s/%s"
31 #define MAIL_ENV_NAME "MAIL"
32 #define MAIL_ENV_FORMAT MAIL_ENV_NAME "=%s"
33 #define YOUR_MAIL_VERBOSE_FORMAT "You have %s mail in %s."
34 #define YOUR_MAIL_STANDARD_FORMAT "You have %smail."
35 #define NO_MAIL_STANDARD_FORMAT "No mail."
38 * here, we make a definition for the externally accessible function
39 * in this file (this definition is required for static a module
40 * but strongly encouraged generally) it is used to instruct the
41 * modules include file to define the function prototypes.
44 #define PAM_SM_SESSION
47 #include <security/pam_modules.h>
48 #include <security/_pam_macros.h>
49 #include <security/_pam_modutil.h>
53 static void _log_err(int err, const char *format, ...)
57 va_start(args, format);
58 openlog("PAM-mail", LOG_CONS|LOG_PID, LOG_AUTH);
59 vsyslog(err, format, args);
64 /* argument parsing */
66 #define PAM_DEBUG_ARG 0x0001
67 #define PAM_NO_LOGIN 0x0002
68 #define PAM_LOGOUT_TOO 0x0004
69 #define PAM_NEW_MAIL_DIR 0x0010
70 #define PAM_MAIL_SILENT 0x0020
71 #define PAM_NO_ENV 0x0040
72 #define PAM_HOME_MAIL 0x0100
73 #define PAM_EMPTY_TOO 0x0200
74 #define PAM_STANDARD_MAIL 0x0400
75 #define PAM_QUIET_MAIL 0x1000
77 static int _pam_parse(int flags, int argc, const char **argv, char **maildir,
82 if (flags & PAM_SILENT) {
83 ctrl |= PAM_MAIL_SILENT;
88 /* step through arguments */
89 for (; argc-- > 0; ++argv) {
93 if (!strcmp(*argv,"debug"))
94 ctrl |= PAM_DEBUG_ARG;
95 else if (!strcmp(*argv,"quiet"))
96 ctrl |= PAM_QUIET_MAIL;
97 else if (!strcmp(*argv,"standard"))
98 ctrl |= PAM_STANDARD_MAIL | PAM_EMPTY_TOO;
99 else if (!strncmp(*argv,"dir=",4)) {
100 *maildir = x_strdup(4+*argv);
101 if (*maildir != NULL) {
102 D(("new mail directory: %s", *maildir));
103 ctrl |= PAM_NEW_MAIL_DIR;
106 "failed to duplicate mail directory - ignored");
108 } else if (!strncmp(*argv,"hash=",5)) {
110 *hashcount = strtol(*argv+5,&ep,10);
111 if (!ep || (*hashcount < 0)) {
114 } else if (!strcmp(*argv,"close")) {
115 ctrl |= PAM_LOGOUT_TOO;
116 } else if (!strcmp(*argv,"nopen")) {
117 ctrl |= PAM_NO_LOGIN;
118 } else if (!strcmp(*argv,"noenv")) {
120 } else if (!strcmp(*argv,"empty")) {
121 ctrl |= PAM_EMPTY_TOO;
123 _log_err(LOG_ERR,"pam_parse: unknown option; %s",*argv);
127 if ((*hashcount != 0) && !(ctrl & PAM_NEW_MAIL_DIR)) {
128 *maildir = x_strdup(DEFAULT_MAIL_DIRECTORY);
129 ctrl |= PAM_NEW_MAIL_DIR;
135 /* a front end for conversations */
137 static int converse(pam_handle_t *pamh, int ctrl, int nargs
138 , struct pam_message **message
139 , struct pam_response **response)
142 struct pam_conv *conv;
144 D(("begin to converse"));
146 retval = pam_get_item( pamh, PAM_CONV, (const void **) &conv ) ;
147 if ( retval == PAM_SUCCESS && conv ) {
149 retval = conv->conv(nargs, ( const struct pam_message ** ) message
150 , response, conv->appdata_ptr);
152 D(("returned from application's conversation function"));
154 if (retval != PAM_SUCCESS && (PAM_DEBUG_ARG & ctrl) ) {
155 _log_err(LOG_DEBUG, "conversation failure [%s]"
156 , pam_strerror(pamh, retval));
160 _log_err(LOG_ERR, "couldn't obtain coversation function [%s]"
161 , pam_strerror(pamh, retval));
162 if (retval == PAM_SUCCESS)
163 retval = PAM_BAD_ITEM; /* conv was NULL */
166 D(("ready to return from module conversation"));
168 return retval; /* propagate error status */
171 static int get_folder(pam_handle_t *pamh, int ctrl,
172 char **path_mail, char **folder_p, int hashcount)
175 const char *user, *path;
177 const struct passwd *pwd=NULL;
179 retval = pam_get_user(pamh, &user, NULL);
180 if (retval != PAM_SUCCESS || user == NULL) {
181 _log_err(LOG_ERR, "no user specified");
182 return PAM_USER_UNKNOWN;
185 if (ctrl & PAM_NEW_MAIL_DIR) {
187 if (*path == '~') { /* support for $HOME delivery */
188 pwd = _pammodutil_getpwnam(pamh, user);
190 _log_err(LOG_ERR, "user [%s] unknown", user);
191 _pam_overwrite(*path_mail);
192 _pam_drop(*path_mail);
193 return PAM_USER_UNKNOWN;
196 * "~/xxx" and "~xxx" are treated as same
198 if (!*++path || (*path == '/' && !*++path)) {
199 _log_err(LOG_ALERT, "badly formed mail path [%s]", *path_mail);
200 _pam_overwrite(*path_mail);
201 _pam_drop(*path_mail);
204 ctrl |= PAM_HOME_MAIL;
205 if (hashcount != 0) {
206 _log_err(LOG_ALERT, "can't do hash= and home directory mail");
210 path = DEFAULT_MAIL_DIRECTORY;
213 /* put folder together */
215 if (ctrl & PAM_HOME_MAIL) {
216 folder = malloc(sizeof(MAIL_FILE_FORMAT)
217 +strlen(pwd->pw_dir)+strlen(path));
219 folder = malloc(sizeof(MAIL_FILE_FORMAT)+strlen(path)+strlen(user)
223 if (folder != NULL) {
224 if (ctrl & PAM_HOME_MAIL) {
225 sprintf(folder, MAIL_FILE_FORMAT, pwd->pw_dir, "", path);
228 char *hash = malloc(2*hashcount+1);
231 for (i = 0; i < hashcount; i++) {
233 hash[2*i+1] = user[i];
236 sprintf(folder, MAIL_FILE_FORMAT, path, hash, user);
237 _pam_overwrite(hash);
241 _log_err(LOG_CRIT, "out of memory for mail folder");
245 D(("folder =[%s]", folder));
250 _pam_overwrite(*path_mail);
251 _pam_drop(*path_mail);
254 if (folder == NULL) {
255 _log_err(LOG_CRIT, "out of memory for mail folder");
265 static const char *get_mail_status(int ctrl, const char *folder)
267 const char *type = NULL;
268 static char dir[256];
270 struct dirent **namelist;
273 if (stat(folder, &mail_st) == 0) {
274 if (S_ISDIR(mail_st.st_mode)) { /* Assume Maildir format */
275 sprintf(dir, "%.250s/new", folder);
276 i = scandir(dir, &namelist, 0, alphasort);
284 sprintf(dir, "%.250s/cur", folder);
285 i = scandir(dir, &namelist, 0, alphasort);
290 } else if (ctrl & PAM_EMPTY_TOO) {
299 if (mail_st.st_size > 0) {
300 if (mail_st.st_atime < mail_st.st_mtime) /* new */
301 type = (ctrl & PAM_STANDARD_MAIL) ? "new " : "new";
303 type = (ctrl & PAM_STANDARD_MAIL) ? "" : "old";
304 } else if (ctrl & PAM_EMPTY_TOO) {
313 memset(&mail_st, 0, sizeof(mail_st));
314 D(("user has %s mail in %s folder", type, folder));
318 static int report_mail(pam_handle_t *pamh, int ctrl
319 , const char *type, const char *folder)
323 if (!(ctrl & PAM_MAIL_SILENT) || ((ctrl & PAM_QUIET_MAIL) && strcmp(type, "new"))) {
326 if (ctrl & PAM_STANDARD_MAIL)
327 if (!strcmp(type, "no"))
328 remark = malloc(strlen(NO_MAIL_STANDARD_FORMAT)+1);
330 remark = malloc(strlen(YOUR_MAIL_STANDARD_FORMAT)+strlen(type)+1);
332 remark = malloc(strlen(YOUR_MAIL_VERBOSE_FORMAT)+strlen(type)+strlen(folder)+1);
333 if (remark == NULL) {
334 retval = PAM_BUF_ERR;
336 struct pam_message msg[1], *mesg[1];
337 struct pam_response *resp=NULL;
339 if (ctrl & PAM_STANDARD_MAIL)
340 if (!strcmp(type, "no"))
341 sprintf(remark, NO_MAIL_STANDARD_FORMAT);
343 sprintf(remark, YOUR_MAIL_STANDARD_FORMAT, type);
345 sprintf(remark, YOUR_MAIL_VERBOSE_FORMAT, type, folder);
348 msg[0].msg_style = PAM_TEXT_INFO;
351 retval = converse(pamh, ctrl, 1, mesg, &resp);
353 _pam_overwrite(remark);
356 _pam_drop_reply(resp, 1);
359 D(("keeping quiet"));
360 retval = PAM_SUCCESS;
363 D(("returning %s", pam_strerror(pamh, retval)));
367 static int _do_mail(pam_handle_t *, int, int, const char **, int);
369 /* --- authentication functions --- */
372 int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc,
378 /* Checking mail as part of authentication */
380 int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc,
383 if (!(flags & (PAM_ESTABLISH_CRED|PAM_DELETE_CRED)))
385 return _do_mail(pamh,flags,argc,argv,(flags & PAM_ESTABLISH_CRED));
388 /* --- session management functions --- */
391 int pam_sm_close_session(pam_handle_t *pamh,int flags,int argc
394 return _do_mail(pamh,flags,argc,argv,0);
397 /* Checking mail as part of the session management */
399 int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc,
402 return _do_mail(pamh,flags,argc,argv,1);
406 /* --- The Beaf (Tm) --- */
408 static int _do_mail(pam_handle_t *pamh, int flags, int argc,
409 const char **argv, int est)
411 int retval, ctrl, hashcount;
412 char *path_mail=NULL, *folder;
416 * this module (un)sets the MAIL environment variable, and checks if
417 * the user has any new mail.
420 ctrl = _pam_parse(flags, argc, argv, &path_mail, &hashcount);
422 /* Do we have anything to do? */
424 if (flags & PAM_SILENT)
429 retval = get_folder(pamh, ctrl, &path_mail, &folder, hashcount);
430 if (retval != PAM_SUCCESS) {
431 D(("failed to find folder"));
435 /* set the MAIL variable? */
437 if (!(ctrl & PAM_NO_ENV) && est) {
440 tmp = malloc(strlen(folder)+sizeof(MAIL_ENV_FORMAT));
442 sprintf(tmp, MAIL_ENV_FORMAT, folder);
443 D(("setting env: %s", tmp));
444 retval = pam_putenv(pamh, tmp);
447 if (retval != PAM_SUCCESS) {
448 _pam_overwrite(folder);
450 _log_err(LOG_CRIT, "unable to set " MAIL_ENV_NAME " variable");
454 _log_err(LOG_CRIT, "no memory for " MAIL_ENV_NAME " variable");
455 _pam_overwrite(folder);
460 D(("not setting " MAIL_ENV_NAME " variable"));
464 * OK. we've got the mail folder... what about its status?
467 if ((est && !(ctrl & PAM_NO_LOGIN))
468 || (!est && (ctrl & PAM_LOGOUT_TOO))) {
469 type = get_mail_status(ctrl, folder);
471 retval = report_mail(pamh, ctrl, type, folder);
476 /* Delete environment variable? */
478 (void) pam_putenv(pamh, MAIL_ENV_NAME);
480 _pam_overwrite(folder); /* clean up */
483 /* indicate success or failure */
490 /* static module data */
492 struct pam_module _pam_mail_modstruct = {
498 pam_sm_close_session,
504 /* end of module definition */