pam_motd.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. /*
  2. * pam_motd module
  3. *
  4. * Modified for pam_motd by Ben Collins <bcollins@debian.org>
  5. * Written by Michael K. Johnson <johnsonm@redhat.com> 1996/10/24
  6. */
  7. #include "config.h"
  8. #include <stdio.h>
  9. #include <string.h>
  10. #include <stdlib.h>
  11. #include <unistd.h>
  12. #include <fcntl.h>
  13. #include <dirent.h>
  14. #include <sys/types.h>
  15. #include <sys/stat.h>
  16. #include <pwd.h>
  17. #include <syslog.h>
  18. #include <errno.h>
  19. #include <security/_pam_macros.h>
  20. #include <security/pam_ext.h>
  21. #include <security/pam_modules.h>
  22. #include <security/pam_modutil.h>
  23. #include "pam_inline.h"
  24. #define DEFAULT_MOTD "/etc/motd:/run/motd:/usr/lib/motd"
  25. #define DEFAULT_MOTD_D "/etc/motd.d:/run/motd.d:/usr/lib/motd.d"
  26. /* --- session management functions (only) --- */
  27. int
  28. pam_sm_close_session (pam_handle_t *pamh UNUSED, int flags UNUSED,
  29. int argc UNUSED, const char **argv UNUSED)
  30. {
  31. return PAM_IGNORE;
  32. }
  33. static const char default_motd[] = DEFAULT_MOTD;
  34. static const char default_motd_dir[] = DEFAULT_MOTD_D;
  35. static void try_to_display_fd(pam_handle_t *pamh, int fd)
  36. {
  37. struct stat st;
  38. char *mtmp = NULL;
  39. /* fill in message buffer with contents of motd */
  40. if ((fstat(fd, &st) < 0) || !st.st_size || st.st_size > 0x10000)
  41. return;
  42. if (!(mtmp = malloc(st.st_size+1)))
  43. return;
  44. if (pam_modutil_read(fd, mtmp, st.st_size) == st.st_size) {
  45. if (mtmp[st.st_size-1] == '\n')
  46. mtmp[st.st_size-1] = '\0';
  47. else
  48. mtmp[st.st_size] = '\0';
  49. pam_info (pamh, "%s", mtmp);
  50. }
  51. _pam_drop(mtmp);
  52. }
  53. /*
  54. * Split a DELIM-separated string ARG into an array.
  55. * Outputs a newly allocated array of strings OUT_ARG_SPLIT
  56. * and the number of strings OUT_NUM_STRS.
  57. * Returns 0 in case of error, 1 in case of success.
  58. */
  59. static int pam_split_string(const pam_handle_t *pamh, char *arg, char delim,
  60. char ***out_arg_split, unsigned int *out_num_strs)
  61. {
  62. char *arg_extracted = NULL;
  63. const char *arg_ptr = arg;
  64. char **arg_split = NULL;
  65. char delim_str[2];
  66. unsigned int i = 0;
  67. unsigned int num_strs = 0;
  68. int retval = 0;
  69. delim_str[0] = delim;
  70. delim_str[1] = '\0';
  71. if (arg == NULL) {
  72. goto out;
  73. }
  74. while (arg_ptr != NULL) {
  75. num_strs++;
  76. arg_ptr = strchr(arg_ptr + sizeof(const char), delim);
  77. }
  78. arg_split = calloc(num_strs, sizeof(*arg_split));
  79. if (arg_split == NULL) {
  80. pam_syslog(pamh, LOG_CRIT, "failed to allocate string array");
  81. goto out;
  82. }
  83. arg_extracted = strtok_r(arg, delim_str, &arg);
  84. while (arg_extracted != NULL && i < num_strs) {
  85. arg_split[i++] = arg_extracted;
  86. arg_extracted = strtok_r(NULL, delim_str, &arg);
  87. }
  88. retval = 1;
  89. out:
  90. *out_num_strs = num_strs;
  91. *out_arg_split = arg_split;
  92. return retval;
  93. }
  94. /* Join A_STR and B_STR, inserting a "/" between them if one is not already trailing
  95. * in A_STR or beginning B_STR. A pointer to a newly allocated string holding the
  96. * joined string is returned in STRP_OUT.
  97. * Returns -1 in case of error, or the number of bytes in the joined string in
  98. * case of success. */
  99. static int join_dir_strings(char **strp_out, const char *a_str, const char *b_str)
  100. {
  101. int has_sep = 0;
  102. int retval = -1;
  103. char *join_strp = NULL;
  104. if (strp_out == NULL || a_str == NULL || b_str == NULL) {
  105. goto out;
  106. }
  107. if (strlen(a_str) == 0) {
  108. goto out;
  109. }
  110. has_sep = (a_str[strlen(a_str) - 1] == '/') || (b_str[0] == '/');
  111. retval = asprintf(&join_strp, "%s%s%s", a_str,
  112. (has_sep == 1) ? "" : "/", b_str);
  113. if (retval < 0) {
  114. goto out;
  115. }
  116. *strp_out = join_strp;
  117. out:
  118. return retval;
  119. }
  120. static int compare_strings(const void *a, const void *b)
  121. {
  122. const char *a_str = *(const char * const *)a;
  123. const char *b_str = *(const char * const *)b;
  124. if (a_str == NULL && b_str == NULL) {
  125. return 0;
  126. }
  127. else if (a_str == NULL) {
  128. return -1;
  129. }
  130. else if (b_str == NULL) {
  131. return 1;
  132. }
  133. else {
  134. return strcmp(a_str, b_str);
  135. }
  136. }
  137. static int filter_dirents(const struct dirent *d)
  138. {
  139. return (d->d_type == DT_REG || d->d_type == DT_LNK);
  140. }
  141. static void try_to_display_directories_with_overrides(pam_handle_t *pamh,
  142. char **motd_dir_path_split, unsigned int num_motd_dirs, int report_missing)
  143. {
  144. struct dirent ***dirscans = NULL;
  145. unsigned int *dirscans_sizes = NULL;
  146. unsigned int dirscans_size_total = 0;
  147. char **dirnames_all = NULL;
  148. unsigned int i;
  149. int i_dirnames = 0;
  150. if (pamh == NULL || motd_dir_path_split == NULL) {
  151. goto out;
  152. }
  153. if (num_motd_dirs < 1) {
  154. goto out;
  155. }
  156. if ((dirscans = calloc(num_motd_dirs, sizeof(*dirscans))) == NULL) {
  157. pam_syslog(pamh, LOG_CRIT, "failed to allocate dirent arrays");
  158. goto out;
  159. }
  160. if ((dirscans_sizes = calloc(num_motd_dirs, sizeof(*dirscans_sizes))) == NULL) {
  161. pam_syslog(pamh, LOG_CRIT, "failed to allocate dirent array sizes");
  162. goto out;
  163. }
  164. for (i = 0; i < num_motd_dirs; i++) {
  165. int rv;
  166. rv = scandir(motd_dir_path_split[i], &(dirscans[i]),
  167. filter_dirents, alphasort);
  168. if (rv < 0) {
  169. if (errno != ENOENT || report_missing) {
  170. pam_syslog(pamh, LOG_ERR, "error scanning directory %s: %m",
  171. motd_dir_path_split[i]);
  172. }
  173. } else {
  174. dirscans_sizes[i] = rv;
  175. }
  176. dirscans_size_total += dirscans_sizes[i];
  177. }
  178. if (dirscans_size_total == 0)
  179. goto out;
  180. /* Allocate space for all file names found in the directories, including duplicates. */
  181. if ((dirnames_all = calloc(dirscans_size_total, sizeof(*dirnames_all))) == NULL) {
  182. pam_syslog(pamh, LOG_CRIT, "failed to allocate dirname array");
  183. goto out;
  184. }
  185. for (i = 0; i < num_motd_dirs; i++) {
  186. unsigned int j;
  187. for (j = 0; j < dirscans_sizes[i]; j++) {
  188. dirnames_all[i_dirnames] = dirscans[i][j]->d_name;
  189. i_dirnames++;
  190. }
  191. }
  192. qsort(dirnames_all, dirscans_size_total,
  193. sizeof(const char *), compare_strings);
  194. for (i = 0; i < dirscans_size_total; i++) {
  195. unsigned int j;
  196. if (dirnames_all[i] == NULL) {
  197. continue;
  198. }
  199. /* Skip duplicate file names. */
  200. if (i > 0 && strcmp(dirnames_all[i], dirnames_all[i - 1]) == 0) {
  201. continue;
  202. }
  203. for (j = 0; j < num_motd_dirs; j++) {
  204. char *abs_path = NULL;
  205. int fd;
  206. if (join_dir_strings(&abs_path, motd_dir_path_split[j],
  207. dirnames_all[i]) < 0 || abs_path == NULL) {
  208. continue;
  209. }
  210. fd = open(abs_path, O_RDONLY, 0);
  211. _pam_drop(abs_path);
  212. if (fd >= 0) {
  213. try_to_display_fd(pamh, fd);
  214. close(fd);
  215. /* We displayed a file, skip to the next file name. */
  216. break;
  217. }
  218. }
  219. }
  220. out:
  221. _pam_drop(dirnames_all);
  222. if (dirscans_sizes != NULL) {
  223. for (i = 0; i < num_motd_dirs; i++) {
  224. unsigned int j;
  225. for (j = 0; j < dirscans_sizes[i]; j++)
  226. _pam_drop(dirscans[i][j]);
  227. _pam_drop(dirscans[i]);
  228. }
  229. _pam_drop(dirscans_sizes);
  230. }
  231. _pam_drop(dirscans);
  232. }
  233. static int drop_privileges(pam_handle_t *pamh, struct pam_modutil_privs *privs)
  234. {
  235. struct passwd *pw;
  236. const char *username;
  237. int retval;
  238. retval = pam_get_user(pamh, &username, NULL);
  239. if (retval == PAM_SUCCESS) {
  240. pw = pam_modutil_getpwnam (pamh, username);
  241. } else {
  242. return PAM_SESSION_ERR;
  243. }
  244. if (pw == NULL || pam_modutil_drop_priv(pamh, privs, pw)) {
  245. return PAM_SESSION_ERR;
  246. }
  247. return PAM_SUCCESS;
  248. }
  249. static int try_to_display(pam_handle_t *pamh, char **motd_path_split,
  250. unsigned int num_motd_paths,
  251. char **motd_dir_path_split,
  252. unsigned int num_motd_dir_paths, int report_missing)
  253. {
  254. PAM_MODUTIL_DEF_PRIVS(privs);
  255. if (drop_privileges(pamh, &privs) != PAM_SUCCESS) {
  256. pam_syslog(pamh, LOG_ERR, "Unable to drop privileges");
  257. return PAM_SESSION_ERR;
  258. }
  259. if (motd_path_split != NULL) {
  260. unsigned int i;
  261. for (i = 0; i < num_motd_paths; i++) {
  262. int fd = open(motd_path_split[i], O_RDONLY, 0);
  263. if (fd >= 0) {
  264. try_to_display_fd(pamh, fd);
  265. close(fd);
  266. /* We found and displayed a file,
  267. * move onto next filename.
  268. */
  269. break;
  270. }
  271. }
  272. }
  273. if (motd_dir_path_split != NULL) {
  274. try_to_display_directories_with_overrides(pamh,
  275. motd_dir_path_split,
  276. num_motd_dir_paths,
  277. report_missing);
  278. }
  279. if (pam_modutil_regain_priv(pamh, &privs)) {
  280. pam_syslog(pamh, LOG_ERR, "Unable to regain privileges");
  281. return PAM_SESSION_ERR;
  282. }
  283. return PAM_SUCCESS;
  284. }
  285. int pam_sm_open_session(pam_handle_t *pamh, int flags,
  286. int argc, const char **argv)
  287. {
  288. int retval = PAM_IGNORE;
  289. const char *motd_path = NULL;
  290. char *motd_path_copy = NULL;
  291. unsigned int num_motd_paths = 0;
  292. char **motd_path_split = NULL;
  293. const char *motd_dir_path = NULL;
  294. char *motd_dir_path_copy = NULL;
  295. unsigned int num_motd_dir_paths = 0;
  296. char **motd_dir_path_split = NULL;
  297. int report_missing;
  298. if (flags & PAM_SILENT) {
  299. return retval;
  300. }
  301. for (; argc-- > 0; ++argv) {
  302. const char *str;
  303. if ((str = pam_str_skip_prefix(*argv, "motd=")) != NULL) {
  304. motd_path = str;
  305. if (*motd_path != '\0') {
  306. D(("set motd path: %s", motd_path));
  307. } else {
  308. motd_path = NULL;
  309. pam_syslog(pamh, LOG_ERR,
  310. "motd= specification missing argument - ignored");
  311. }
  312. }
  313. else if ((str = pam_str_skip_prefix(*argv, "motd_dir=")) != NULL) {
  314. motd_dir_path = str;
  315. if (*motd_dir_path != '\0') {
  316. D(("set motd.d path: %s", motd_dir_path));
  317. } else {
  318. motd_dir_path = NULL;
  319. pam_syslog(pamh, LOG_ERR,
  320. "motd_dir= specification missing argument - ignored");
  321. }
  322. }
  323. else
  324. pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
  325. }
  326. if (motd_path == NULL && motd_dir_path == NULL) {
  327. motd_path = default_motd;
  328. motd_dir_path = default_motd_dir;
  329. report_missing = 0;
  330. } else {
  331. report_missing = 1;
  332. }
  333. if (motd_path != NULL) {
  334. motd_path_copy = strdup(motd_path);
  335. }
  336. if (motd_path_copy != NULL) {
  337. if (pam_split_string(pamh, motd_path_copy, ':',
  338. &motd_path_split, &num_motd_paths) == 0) {
  339. goto out;
  340. }
  341. }
  342. if (motd_dir_path != NULL) {
  343. motd_dir_path_copy = strdup(motd_dir_path);
  344. }
  345. if (motd_dir_path_copy != NULL) {
  346. if (pam_split_string(pamh, motd_dir_path_copy, ':',
  347. &motd_dir_path_split, &num_motd_dir_paths) == 0) {
  348. goto out;
  349. }
  350. }
  351. retval = try_to_display(pamh, motd_path_split, num_motd_paths,
  352. motd_dir_path_split, num_motd_dir_paths,
  353. report_missing);
  354. out:
  355. _pam_drop(motd_path_copy);
  356. _pam_drop(motd_path_split);
  357. _pam_drop(motd_dir_path_copy);
  358. _pam_drop(motd_dir_path_split);
  359. if (retval == PAM_SUCCESS) {
  360. retval = pam_putenv(pamh, "MOTD_SHOWN=pam");
  361. return retval == PAM_SUCCESS ? PAM_IGNORE : retval;
  362. } else {
  363. return retval;
  364. }
  365. }
  366. /* end of module definition */