pam_timestamp.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873
  1. /******************************************************************************
  2. * A module for Linux-PAM that will cache authentication results, inspired by
  3. * (and implemented with an eye toward being mixable with) sudo.
  4. *
  5. * Copyright (c) 2002 Red Hat, Inc.
  6. * Written by Nalin Dahyabhai <nalin@redhat.com>
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, and the entire permission notice in its entirety,
  13. * including the disclaimer of warranties.
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in the
  16. * documentation and/or other materials provided with the distribution.
  17. * 3. The name of the author may not be used to endorse or promote
  18. * products derived from this software without specific prior
  19. * written permission.
  20. *
  21. * ALTERNATIVELY, this product may be distributed under the terms of
  22. * the GNU Public License, in which case the provisions of the GPL are
  23. * required INSTEAD OF the above restrictions. (This clause is
  24. * necessary due to a potential bad interaction between the GPL and
  25. * the restrictions contained in a BSD-style copyright.)
  26. *
  27. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
  28. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  29. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  30. * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
  31. * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  32. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  33. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  34. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  35. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  36. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  37. * OF THE POSSIBILITY OF SUCH DAMAGE.
  38. *
  39. */
  40. #include "config.h"
  41. #include <sys/stat.h>
  42. #include <sys/types.h>
  43. #include <errno.h>
  44. #include <fcntl.h>
  45. #include <limits.h>
  46. #include <pwd.h>
  47. #include <signal.h>
  48. #include <stdio.h>
  49. #include <stdlib.h>
  50. #include <string.h>
  51. #include <time.h>
  52. #include <sys/time.h>
  53. #include <unistd.h>
  54. #include <utmp.h>
  55. #include <syslog.h>
  56. #include <paths.h>
  57. #ifdef WITH_OPENSSL
  58. #include "hmac_openssl_wrapper.h"
  59. #else
  60. #include "hmacsha1.h"
  61. #endif /* WITH_OPENSSL */
  62. #include <security/pam_modules.h>
  63. #include <security/_pam_macros.h>
  64. #include <security/pam_ext.h>
  65. #include <security/pam_modutil.h>
  66. #include "pam_inline.h"
  67. /* The default timeout we use is 5 minutes, which matches the sudo default
  68. * for the timestamp_timeout parameter. */
  69. #define DEFAULT_TIMESTAMP_TIMEOUT (5 * 60)
  70. #define MODULE "pam_timestamp"
  71. #define TIMESTAMPDIR _PATH_VARRUN MODULE
  72. #define TIMESTAMPKEY TIMESTAMPDIR "/_pam_timestamp_key"
  73. /* Various buffers we use need to be at least as large as either PATH_MAX or
  74. * LINE_MAX, so choose the larger of the two. */
  75. #if (LINE_MAX > PATH_MAX)
  76. #define BUFLEN LINE_MAX
  77. #else
  78. #define BUFLEN PATH_MAX
  79. #endif
  80. #define ROOT_USER 0
  81. #define ROOT_GROUP 0
  82. /* Return PAM_SUCCESS if the given directory looks "safe". */
  83. static int
  84. check_dir_perms(pam_handle_t *pamh, const char *tdir)
  85. {
  86. char scratch[BUFLEN];
  87. struct stat st;
  88. int i;
  89. /* Check that the directory is "safe". */
  90. if ((tdir == NULL) || (strlen(tdir) == 0)) {
  91. return PAM_AUTH_ERR;
  92. }
  93. /* Iterate over the path, checking intermediate directories. */
  94. memset(scratch, 0, sizeof(scratch));
  95. for (i = 0; (tdir[i] != '\0') && (i < (int)sizeof(scratch)); i++) {
  96. scratch[i] = tdir[i];
  97. if ((scratch[i] == '/') || (tdir[i + 1] == '\0')) {
  98. /* We now have the name of a directory in the path, so
  99. * we need to check it. */
  100. if ((lstat(scratch, &st) == -1) && (errno != ENOENT)) {
  101. pam_syslog(pamh, LOG_ERR,
  102. "unable to read `%s': %m",
  103. scratch);
  104. return PAM_AUTH_ERR;
  105. }
  106. if (!S_ISDIR(st.st_mode)) {
  107. pam_syslog(pamh, LOG_ERR,
  108. "`%s' is not a directory",
  109. scratch);
  110. return PAM_AUTH_ERR;
  111. }
  112. if (S_ISLNK(st.st_mode)) {
  113. pam_syslog(pamh, LOG_ERR,
  114. "`%s' is a symbolic link",
  115. scratch);
  116. return PAM_AUTH_ERR;
  117. }
  118. if (st.st_uid != 0) {
  119. pam_syslog(pamh, LOG_ERR,
  120. "`%s' owner UID != 0",
  121. scratch);
  122. return PAM_AUTH_ERR;
  123. }
  124. if (st.st_gid != 0) {
  125. pam_syslog(pamh, LOG_ERR,
  126. "`%s' owner GID != 0",
  127. scratch);
  128. return PAM_AUTH_ERR;
  129. }
  130. if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
  131. pam_syslog(pamh, LOG_ERR,
  132. "`%s' permissions are lax",
  133. scratch);
  134. return PAM_AUTH_ERR;
  135. }
  136. }
  137. }
  138. return PAM_SUCCESS;
  139. }
  140. /* Validate a tty pathname as actually belonging to a tty, and return its base
  141. * name if it's valid. */
  142. static const char *
  143. check_tty(const char *tty)
  144. {
  145. /* Check that we're not being set up to take a fall. */
  146. if ((tty == NULL) || (strlen(tty) == 0)) {
  147. return NULL;
  148. }
  149. /* Pull out the meaningful part of the tty's name. */
  150. if (strchr(tty, '/') != NULL) {
  151. if (pam_str_skip_prefix(tty, "/dev/") == NULL) {
  152. /* Make sure the device node is actually in /dev/,
  153. * noted by Michal Zalewski. */
  154. return NULL;
  155. }
  156. tty = strrchr(tty, '/') + 1;
  157. }
  158. /* Make sure the tty wasn't actually a directory (no basename). */
  159. if (!strlen(tty) || !strcmp(tty, ".") || !strcmp(tty, "..")) {
  160. return NULL;
  161. }
  162. return tty;
  163. }
  164. /* Determine the right path name for a given user's timestamp. */
  165. static int
  166. format_timestamp_name(char *path, size_t len,
  167. const char *timestamp_dir,
  168. const char *tty,
  169. const char *ruser,
  170. const char *user)
  171. {
  172. if (strcmp(ruser, user) == 0) {
  173. return snprintf(path, len, "%s/%s/%s", timestamp_dir,
  174. ruser, tty);
  175. } else {
  176. return snprintf(path, len, "%s/%s/%s:%s", timestamp_dir,
  177. ruser, tty, user);
  178. }
  179. }
  180. /* Check if a given timestamp date, when compared to a current time, fits
  181. * within the given interval. */
  182. static int
  183. timestamp_good(time_t then, time_t now, time_t interval)
  184. {
  185. if (((now >= then) && ((now - then) < interval)) ||
  186. ((now < then) && ((then - now) < (2 * interval)))) {
  187. return PAM_SUCCESS;
  188. }
  189. return PAM_AUTH_ERR;
  190. }
  191. static int
  192. check_login_time(const char *ruser, time_t timestamp)
  193. {
  194. struct utmp utbuf, *ut;
  195. time_t oldest_login = 0;
  196. setutent();
  197. while(
  198. #ifdef HAVE_GETUTENT_R
  199. !getutent_r(&utbuf, &ut)
  200. #else
  201. (ut = getutent()) != NULL
  202. #endif
  203. ) {
  204. if (ut->ut_type != USER_PROCESS) {
  205. continue;
  206. }
  207. if (strncmp(ruser, ut->ut_user, sizeof(ut->ut_user)) != 0) {
  208. continue;
  209. }
  210. if (oldest_login == 0 || oldest_login > ut->ut_tv.tv_sec) {
  211. oldest_login = ut->ut_tv.tv_sec;
  212. }
  213. }
  214. endutent();
  215. if(oldest_login == 0 || timestamp < oldest_login) {
  216. return PAM_AUTH_ERR;
  217. }
  218. return PAM_SUCCESS;
  219. }
  220. #ifndef PAM_TIMESTAMP_MAIN
  221. static int
  222. get_ruser(pam_handle_t *pamh, char *ruserbuf, size_t ruserbuflen)
  223. {
  224. const void *ruser;
  225. struct passwd *pwd;
  226. if (ruserbuf == NULL || ruserbuflen < 1)
  227. return -2;
  228. /* Get the name of the source user. */
  229. if (pam_get_item(pamh, PAM_RUSER, &ruser) != PAM_SUCCESS) {
  230. ruser = NULL;
  231. }
  232. if ((ruser == NULL) || (strlen(ruser) == 0)) {
  233. /* Barring that, use the current RUID. */
  234. pwd = pam_modutil_getpwuid(pamh, getuid());
  235. if (pwd != NULL) {
  236. ruser = pwd->pw_name;
  237. }
  238. } else {
  239. /*
  240. * This ruser is used by format_timestamp_name as a component
  241. * of constructed timestamp pathname, so ".", "..", and '/'
  242. * are disallowed to avoid potential path traversal issues.
  243. */
  244. if (!strcmp(ruser, ".") ||
  245. !strcmp(ruser, "..") ||
  246. strchr(ruser, '/')) {
  247. ruser = NULL;
  248. }
  249. }
  250. if (ruser == NULL || strlen(ruser) >= ruserbuflen) {
  251. *ruserbuf = '\0';
  252. return -1;
  253. }
  254. strcpy(ruserbuf, ruser);
  255. return 0;
  256. }
  257. /* Get the path to the timestamp to use. */
  258. static int
  259. get_timestamp_name(pam_handle_t *pamh, int argc, const char **argv,
  260. char *path, size_t len)
  261. {
  262. const char *user, *tty;
  263. const void *void_tty;
  264. const char *tdir = TIMESTAMPDIR;
  265. char ruser[BUFLEN];
  266. int i, debug = 0;
  267. /* Parse arguments. */
  268. for (i = 0; i < argc; i++) {
  269. if (strcmp(argv[i], "debug") == 0) {
  270. debug = 1;
  271. }
  272. }
  273. for (i = 0; i < argc; i++) {
  274. const char *str;
  275. if ((str = pam_str_skip_prefix(argv[i], "timestampdir=")) != NULL) {
  276. tdir = str;
  277. if (debug) {
  278. pam_syslog(pamh, LOG_DEBUG,
  279. "storing timestamps in `%s'",
  280. tdir);
  281. }
  282. }
  283. }
  284. i = check_dir_perms(pamh, tdir);
  285. if (i != PAM_SUCCESS) {
  286. return i;
  287. }
  288. /* Get the name of the target user. */
  289. if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS || user[0] == '\0') {
  290. return PAM_AUTH_ERR;
  291. }
  292. if (debug) {
  293. pam_syslog(pamh, LOG_DEBUG, "becoming user `%s'", user);
  294. }
  295. /* Get the name of the source user. */
  296. if (get_ruser(pamh, ruser, sizeof(ruser)) || strlen(ruser) == 0) {
  297. return PAM_AUTH_ERR;
  298. }
  299. if (debug) {
  300. pam_syslog(pamh, LOG_DEBUG, "currently user `%s'", ruser);
  301. }
  302. /* Get the name of the terminal. */
  303. if (pam_get_item(pamh, PAM_TTY, &void_tty) != PAM_SUCCESS) {
  304. tty = NULL;
  305. } else {
  306. tty = void_tty;
  307. }
  308. if ((tty == NULL) || (strlen(tty) == 0)) {
  309. tty = ttyname(STDIN_FILENO);
  310. if ((tty == NULL) || (strlen(tty) == 0)) {
  311. tty = ttyname(STDOUT_FILENO);
  312. }
  313. if ((tty == NULL) || (strlen(tty) == 0)) {
  314. tty = ttyname(STDERR_FILENO);
  315. }
  316. if ((tty == NULL) || (strlen(tty) == 0)) {
  317. /* Match sudo's behavior for this case. */
  318. tty = "unknown";
  319. }
  320. }
  321. if (debug) {
  322. pam_syslog(pamh, LOG_DEBUG, "tty is `%s'", tty);
  323. }
  324. /* Snip off all but the last part of the tty name. */
  325. tty = check_tty(tty);
  326. if (tty == NULL) {
  327. return PAM_AUTH_ERR;
  328. }
  329. /* Generate the name of the file used to cache auth results. These
  330. * paths should jive with sudo's per-tty naming scheme. */
  331. if (format_timestamp_name(path, len, tdir, tty, ruser, user) >= (int)len) {
  332. return PAM_AUTH_ERR;
  333. }
  334. if (debug) {
  335. pam_syslog(pamh, LOG_DEBUG, "using timestamp file `%s'", path);
  336. }
  337. return PAM_SUCCESS;
  338. }
  339. /* Tell the user that access has been granted. */
  340. static void
  341. verbose_success(pam_handle_t *pamh, long diff)
  342. {
  343. pam_info(pamh, _("Access has been granted"
  344. " (last access was %ld seconds ago)."), diff);
  345. }
  346. int
  347. pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
  348. {
  349. struct stat st;
  350. time_t interval = DEFAULT_TIMESTAMP_TIMEOUT;
  351. int i, fd, debug = 0, verbose = 0;
  352. char path[BUFLEN], *p, *message, *message_end;
  353. long tmp;
  354. const void *void_service;
  355. const char *service;
  356. time_t now, then;
  357. /* Parse arguments. */
  358. for (i = 0; i < argc; i++) {
  359. if (strcmp(argv[i], "debug") == 0) {
  360. debug = 1;
  361. }
  362. }
  363. for (i = 0; i < argc; i++) {
  364. const char *str;
  365. if ((str = pam_str_skip_prefix(argv[i], "timestamp_timeout=")) != NULL) {
  366. tmp = strtol(str, &p, 0);
  367. if ((p != NULL) && (*p == '\0')) {
  368. interval = tmp;
  369. if (debug) {
  370. pam_syslog(pamh, LOG_DEBUG,
  371. "setting timeout to %ld"
  372. " seconds", (long)interval);
  373. }
  374. }
  375. } else
  376. if (strcmp(argv[i], "verbose") == 0) {
  377. verbose = 1;
  378. if (debug) {
  379. pam_syslog(pamh, LOG_DEBUG,
  380. "becoming more verbose");
  381. }
  382. }
  383. }
  384. if (flags & PAM_SILENT) {
  385. verbose = 0;
  386. }
  387. /* Get the name of the timestamp file. */
  388. if (get_timestamp_name(pamh, argc, argv,
  389. path, sizeof(path)) != PAM_SUCCESS) {
  390. return PAM_AUTH_ERR;
  391. }
  392. /* Get the name of the service. */
  393. if (pam_get_item(pamh, PAM_SERVICE, &void_service) != PAM_SUCCESS) {
  394. service = NULL;
  395. } else {
  396. service = void_service;
  397. }
  398. if ((service == NULL) || (strlen(service) == 0)) {
  399. service = "(unknown)";
  400. }
  401. /* Open the timestamp file. */
  402. fd = open(path, O_RDONLY | O_NOFOLLOW);
  403. if (fd == -1) {
  404. if (debug) {
  405. pam_syslog(pamh, LOG_DEBUG,
  406. "cannot open timestamp `%s': %m",
  407. path);
  408. }
  409. return PAM_AUTH_ERR;
  410. }
  411. if (fstat(fd, &st) == 0) {
  412. int count;
  413. void *mac;
  414. size_t maclen;
  415. char ruser[BUFLEN];
  416. /* Check that the file is owned by the superuser. */
  417. if ((st.st_uid != 0) || (st.st_gid != 0)) {
  418. pam_syslog(pamh, LOG_ERR, "timestamp file `%s' is "
  419. "not owned by root", path);
  420. close(fd);
  421. return PAM_AUTH_ERR;
  422. }
  423. /* Check that the file is a normal file. */
  424. if (!(S_ISREG(st.st_mode))) {
  425. pam_syslog(pamh, LOG_ERR, "timestamp file `%s' is "
  426. "not a regular file", path);
  427. close(fd);
  428. return PAM_AUTH_ERR;
  429. }
  430. #ifdef WITH_OPENSSL
  431. if (hmac_size(pamh, debug, &maclen)) {
  432. return PAM_AUTH_ERR;
  433. }
  434. #else
  435. maclen = hmac_sha1_size();
  436. #endif /* WITH_OPENSSL */
  437. /* Check that the file is the expected size. */
  438. if (st.st_size == 0) {
  439. /* Invalid, but may have been created by sudo. */
  440. close(fd);
  441. return PAM_AUTH_ERR;
  442. }
  443. if (st.st_size !=
  444. (off_t)(strlen(path) + 1 + sizeof(then) + maclen)) {
  445. pam_syslog(pamh, LOG_NOTICE, "timestamp file `%s' "
  446. "appears to be corrupted", path);
  447. close(fd);
  448. return PAM_AUTH_ERR;
  449. }
  450. /* Read the file contents. */
  451. message = malloc(st.st_size);
  452. count = 0;
  453. if (!message) {
  454. close(fd);
  455. return PAM_BUF_ERR;
  456. }
  457. while (count < st.st_size) {
  458. i = read(fd, message + count, st.st_size - count);
  459. if ((i == 0) || (i == -1)) {
  460. break;
  461. }
  462. count += i;
  463. }
  464. if (count < st.st_size) {
  465. pam_syslog(pamh, LOG_NOTICE, "error reading timestamp "
  466. "file `%s': %m", path);
  467. close(fd);
  468. free(message);
  469. return PAM_AUTH_ERR;
  470. }
  471. message_end = message + strlen(path) + 1 + sizeof(then);
  472. /* Regenerate the MAC. */
  473. #ifdef WITH_OPENSSL
  474. if (hmac_generate(pamh, debug, &mac, &maclen, TIMESTAMPKEY,
  475. ROOT_USER, ROOT_GROUP, message, message_end - message)) {
  476. close(fd);
  477. free(message);
  478. return PAM_AUTH_ERR;
  479. }
  480. #else
  481. hmac_sha1_generate_file(pamh, &mac, &maclen, TIMESTAMPKEY,
  482. ROOT_USER, ROOT_GROUP, message, message_end - message);
  483. #endif /* WITH_OPENSSL */
  484. if ((mac == NULL) ||
  485. (memcmp(path, message, strlen(path)) != 0) ||
  486. (memcmp(mac, message_end, maclen) != 0)) {
  487. pam_syslog(pamh, LOG_NOTICE, "timestamp file `%s' is "
  488. "corrupted", path);
  489. close(fd);
  490. free(mac);
  491. free(message);
  492. return PAM_AUTH_ERR;
  493. }
  494. free(mac);
  495. memmove(&then, message + strlen(path) + 1, sizeof(then));
  496. free(message);
  497. /* Check oldest login against timestamp */
  498. if (get_ruser(pamh, ruser, sizeof(ruser)))
  499. {
  500. close(fd);
  501. return PAM_AUTH_ERR;
  502. }
  503. if (check_login_time(ruser, then) != PAM_SUCCESS)
  504. {
  505. pam_syslog(pamh, LOG_NOTICE, "timestamp file `%s' is "
  506. "older than oldest login, disallowing "
  507. "access to %s for user %s",
  508. path, service, ruser);
  509. close(fd);
  510. return PAM_AUTH_ERR;
  511. }
  512. /* Compare the dates. */
  513. now = time(NULL);
  514. if (timestamp_good(then, now, interval) == PAM_SUCCESS) {
  515. close(fd);
  516. pam_syslog(pamh, LOG_NOTICE, "timestamp file `%s' is "
  517. "only %ld seconds old, allowing access to %s "
  518. "for user %s", path, (long) (now - st.st_mtime),
  519. service, ruser);
  520. if (verbose) {
  521. verbose_success(pamh, now - st.st_mtime);
  522. }
  523. return PAM_SUCCESS;
  524. } else {
  525. close(fd);
  526. pam_syslog(pamh, LOG_NOTICE, "timestamp file `%s' has "
  527. "unacceptable age (%ld seconds), disallowing "
  528. "access to %s for user %s",
  529. path, (long) (now - st.st_mtime),
  530. service, ruser);
  531. return PAM_AUTH_ERR;
  532. }
  533. }
  534. close(fd);
  535. /* Fail by default. */
  536. return PAM_AUTH_ERR;
  537. }
  538. int
  539. pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED, int argc UNUSED, const char **argv UNUSED)
  540. {
  541. return PAM_SUCCESS;
  542. }
  543. int
  544. pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED, int argc, const char **argv)
  545. {
  546. char path[BUFLEN], subdir[BUFLEN], *text, *p;
  547. void *mac;
  548. size_t maclen;
  549. time_t now;
  550. int fd, i, debug = 0;
  551. /* Parse arguments. */
  552. for (i = 0; i < argc; i++) {
  553. if (strcmp(argv[i], "debug") == 0) {
  554. debug = 1;
  555. }
  556. }
  557. /* Get the name of the timestamp file. */
  558. if (get_timestamp_name(pamh, argc, argv,
  559. path, sizeof(path)) != PAM_SUCCESS) {
  560. return PAM_SESSION_ERR;
  561. }
  562. /* Create the directory for the timestamp file if it doesn't already
  563. * exist. */
  564. for (i = 1; i < (int) sizeof(path) && path[i] != '\0'; i++) {
  565. if (path[i] == '/') {
  566. /* Attempt to create the directory. */
  567. memcpy(subdir, path, i);
  568. subdir[i] = '\0';
  569. if (mkdir(subdir, 0700) == 0) {
  570. /* Attempt to set the owner to the superuser. */
  571. if (lchown(subdir, 0, 0) != 0) {
  572. if (debug) {
  573. pam_syslog(pamh, LOG_DEBUG,
  574. "error setting permissions on `%s': %m",
  575. subdir);
  576. }
  577. return PAM_SESSION_ERR;
  578. }
  579. } else {
  580. if (errno != EEXIST) {
  581. if (debug) {
  582. pam_syslog(pamh, LOG_DEBUG,
  583. "error creating directory `%s': %m",
  584. subdir);
  585. }
  586. return PAM_SESSION_ERR;
  587. }
  588. }
  589. }
  590. }
  591. #ifdef WITH_OPENSSL
  592. if (hmac_size(pamh, debug, &maclen)) {
  593. return PAM_SESSION_ERR;
  594. }
  595. #else
  596. maclen = hmac_sha1_size();
  597. #endif /* WITH_OPENSSL */
  598. /* Generate the message. */
  599. text = malloc(strlen(path) + 1 + sizeof(now) + maclen);
  600. if (text == NULL) {
  601. pam_syslog(pamh, LOG_CRIT, "unable to allocate memory: %m");
  602. return PAM_SESSION_ERR;
  603. }
  604. p = text;
  605. strcpy(text, path);
  606. p += strlen(path) + 1;
  607. now = time(NULL);
  608. memmove(p, &now, sizeof(now));
  609. p += sizeof(now);
  610. /* Generate the MAC and append it to the plaintext. */
  611. #ifdef WITH_OPENSSL
  612. if (hmac_generate(pamh, debug, &mac, &maclen, TIMESTAMPKEY,
  613. ROOT_USER, ROOT_GROUP, text, p - text)) {
  614. free(text);
  615. return PAM_SESSION_ERR;
  616. }
  617. #else
  618. hmac_sha1_generate_file(pamh, &mac, &maclen, TIMESTAMPKEY,
  619. ROOT_USER, ROOT_GROUP, text, p - text);
  620. if (mac == NULL) {
  621. pam_syslog(pamh, LOG_ERR, "failure generating MAC: %m");
  622. free(text);
  623. return PAM_SESSION_ERR;
  624. }
  625. #endif /* WITH_OPENSSL */
  626. memmove(p, mac, maclen);
  627. p += maclen;
  628. free(mac);
  629. /* Open the file. */
  630. fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
  631. if (fd == -1) {
  632. pam_syslog(pamh, LOG_ERR, "unable to open `%s': %m", path);
  633. free(text);
  634. return PAM_SESSION_ERR;
  635. }
  636. /* Attempt to set the owner to the superuser. */
  637. if (fchown(fd, 0, 0) != 0) {
  638. if (debug) {
  639. pam_syslog(pamh, LOG_DEBUG,
  640. "error setting ownership of `%s': %m",
  641. path);
  642. }
  643. close(fd);
  644. free(text);
  645. return PAM_SESSION_ERR;
  646. }
  647. /* Write the timestamp to the file. */
  648. if (write(fd, text, p - text) != p - text) {
  649. pam_syslog(pamh, LOG_ERR, "unable to write to `%s': %m", path);
  650. close(fd);
  651. free(text);
  652. return PAM_SESSION_ERR;
  653. }
  654. /* Close the file and return successfully. */
  655. close(fd);
  656. free(text);
  657. pam_syslog(pamh, LOG_DEBUG, "updated timestamp file `%s'", path);
  658. return PAM_SUCCESS;
  659. }
  660. int
  661. pam_sm_close_session(pam_handle_t *pamh UNUSED, int flags UNUSED, int argc UNUSED, const char **argv UNUSED)
  662. {
  663. return PAM_SUCCESS;
  664. }
  665. #else /* PAM_TIMESTAMP_MAIN */
  666. #define USAGE "Usage: %s [[-k] | [-d]] [target user]\n"
  667. #define CHECK_INTERVAL 7
  668. int
  669. main(int argc, char **argv)
  670. {
  671. int i, retval = 0, dflag = 0, kflag = 0;
  672. const char *target_user = NULL, *user = NULL, *tty = NULL;
  673. struct passwd *pwd;
  674. struct timeval tv;
  675. fd_set write_fds;
  676. char path[BUFLEN];
  677. struct stat st;
  678. /* Check that there's nothing funny going on with stdio. */
  679. if ((fstat(STDIN_FILENO, &st) == -1) ||
  680. (fstat(STDOUT_FILENO, &st) == -1) ||
  681. (fstat(STDERR_FILENO, &st) == -1)) {
  682. /* Appropriate the "no controlling tty" error code. */
  683. return 3;
  684. }
  685. /* Parse arguments. */
  686. while ((i = getopt(argc, argv, "dk")) != -1) {
  687. switch (i) {
  688. case 'd':
  689. dflag++;
  690. break;
  691. case 'k':
  692. kflag++;
  693. break;
  694. default:
  695. fprintf(stderr, USAGE, argv[0]);
  696. return 1;
  697. break;
  698. }
  699. }
  700. /* Bail if both -k and -d are given together. */
  701. if ((kflag + dflag) > 1) {
  702. fprintf(stderr, USAGE, argv[0]);
  703. return 1;
  704. }
  705. /* Check that we're setuid. */
  706. if (geteuid() != 0) {
  707. fprintf(stderr, "%s must be setuid root\n",
  708. argv[0]);
  709. retval = 2;
  710. }
  711. /* Check that we have a controlling tty. */
  712. tty = ttyname(STDIN_FILENO);
  713. if ((tty == NULL) || (strlen(tty) == 0)) {
  714. tty = ttyname(STDOUT_FILENO);
  715. }
  716. if ((tty == NULL) || (strlen(tty) == 0)) {
  717. tty = ttyname(STDERR_FILENO);
  718. }
  719. if ((tty == NULL) || (strlen(tty) == 0)) {
  720. tty = "unknown";
  721. }
  722. /* Get the name of the invoking (requesting) user. */
  723. pwd = getpwuid(getuid());
  724. if (pwd == NULL) {
  725. retval = 4;
  726. }
  727. /* Get the name of the target user. */
  728. user = strdup(pwd->pw_name);
  729. if (user == NULL) {
  730. retval = 4;
  731. } else {
  732. target_user = (optind < argc) ? argv[optind] : user;
  733. if ((strchr(target_user, '.') != NULL) ||
  734. (strchr(target_user, '/') != NULL) ||
  735. (strchr(target_user, '%') != NULL)) {
  736. fprintf(stderr, "unknown user: %s\n",
  737. target_user);
  738. retval = 4;
  739. }
  740. }
  741. /* Sanity check the tty to make sure we should be checking
  742. * for timestamps which pertain to it. */
  743. if (retval == 0) {
  744. tty = check_tty(tty);
  745. if (tty == NULL) {
  746. fprintf(stderr, "invalid tty\n");
  747. retval = 6;
  748. }
  749. }
  750. do {
  751. /* Sanity check the timestamp directory itself. */
  752. if (retval == 0) {
  753. if (check_dir_perms(NULL, TIMESTAMPDIR) != PAM_SUCCESS) {
  754. retval = 5;
  755. }
  756. }
  757. if (retval == 0) {
  758. /* Generate the name of the timestamp file. */
  759. format_timestamp_name(path, sizeof(path), TIMESTAMPDIR,
  760. tty, user, target_user);
  761. }
  762. if (retval == 0) {
  763. if (kflag) {
  764. /* Remove the timestamp. */
  765. if (lstat(path, &st) != -1) {
  766. retval = unlink(path);
  767. }
  768. } else {
  769. /* Check the timestamp. */
  770. if (lstat(path, &st) != -1) {
  771. /* Check oldest login against timestamp */
  772. if (check_login_time(user, st.st_mtime) != PAM_SUCCESS) {
  773. retval = 7;
  774. } else if (timestamp_good(st.st_mtime, time(NULL),
  775. DEFAULT_TIMESTAMP_TIMEOUT) != PAM_SUCCESS) {
  776. retval = 7;
  777. }
  778. } else {
  779. retval = 7;
  780. }
  781. }
  782. }
  783. if (dflag > 0) {
  784. struct timeval now;
  785. /* Send the would-be-returned value to our parent. */
  786. signal(SIGPIPE, SIG_DFL);
  787. fprintf(stdout, "%d\n", retval);
  788. fflush(stdout);
  789. /* Wait. */
  790. gettimeofday(&now, NULL);
  791. tv.tv_sec = CHECK_INTERVAL;
  792. /* round the sleep time to get woken up on a whole second */
  793. tv.tv_usec = 1000000 - now.tv_usec;
  794. if (now.tv_usec < 500000)
  795. tv.tv_sec--;
  796. FD_ZERO(&write_fds);
  797. FD_SET(STDOUT_FILENO, &write_fds);
  798. select(STDOUT_FILENO + 1,
  799. NULL, NULL, &write_fds,
  800. &tv);
  801. retval = 0;
  802. }
  803. } while (dflag > 0);
  804. return retval;
  805. }
  806. #endif