pam_mail.c 11 KB

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