pam_tty_audit.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. /* Copyright © 2007, 2008 Red Hat, Inc. All rights reserved.
  2. Red Hat author: Miloslav Trmač <mitr@redhat.com>
  3. Redistribution and use in source and binary forms of Linux-PAM, with
  4. or without modification, are permitted provided that the following
  5. conditions are met:
  6. 1. Redistributions of source code must retain any existing copyright
  7. notice, and this entire permission notice in its entirety,
  8. including the disclaimer of warranties.
  9. 2. Redistributions in binary form must reproduce all prior and current
  10. copyright notices, this list of conditions, and the following
  11. disclaimer in the documentation and/or other materials provided
  12. with the distribution.
  13. 3. The name of any author may not be used to endorse or promote
  14. products derived from this software without their specific prior
  15. written permission.
  16. ALTERNATIVELY, this product may be distributed under the terms of the
  17. GNU General Public License, in which case the provisions of the GNU
  18. GPL are required INSTEAD OF the above restrictions. (This clause is
  19. necessary due to a potential conflict between the GNU GPL and the
  20. restrictions contained in a BSD-style copyright.)
  21. THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
  22. WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  23. MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  24. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
  25. INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  26. BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  27. OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  28. ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
  29. TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
  30. USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  31. DAMAGE. */
  32. #include "config.h"
  33. #include <errno.h>
  34. #include <fnmatch.h>
  35. #include <stdlib.h>
  36. #include <string.h>
  37. #include <syslog.h>
  38. #include <sys/socket.h>
  39. #include <unistd.h>
  40. #include <libaudit.h>
  41. #include <linux/netlink.h>
  42. #include <security/pam_ext.h>
  43. #include <security/pam_modules.h>
  44. #include <security/pam_modutil.h>
  45. #include "pam_cc_compat.h"
  46. #include "pam_inline.h"
  47. #define DATANAME "pam_tty_audit_last_state"
  48. /* Open an audit netlink socket */
  49. static int
  50. nl_open (void)
  51. {
  52. return socket (AF_NETLINK, SOCK_RAW, NETLINK_AUDIT);
  53. }
  54. static int
  55. nl_send (int fd, unsigned type, unsigned flags, const void *data, size_t size)
  56. {
  57. struct sockaddr_nl addr;
  58. struct msghdr msg;
  59. struct nlmsghdr nlm;
  60. struct iovec iov[2];
  61. ssize_t res;
  62. nlm.nlmsg_len = NLMSG_LENGTH (size);
  63. nlm.nlmsg_type = type;
  64. nlm.nlmsg_flags = NLM_F_REQUEST | flags;
  65. nlm.nlmsg_seq = 0;
  66. nlm.nlmsg_pid = 0;
  67. iov[0].iov_base = &nlm;
  68. iov[0].iov_len = sizeof (nlm);
  69. DIAG_PUSH_IGNORE_CAST_QUAL;
  70. iov[1].iov_base = (void *)data;
  71. DIAG_POP_IGNORE_CAST_QUAL;
  72. iov[1].iov_len = size;
  73. addr.nl_family = AF_NETLINK;
  74. addr.nl_pid = 0;
  75. addr.nl_groups = 0;
  76. msg.msg_name = &addr;
  77. msg.msg_namelen = sizeof (addr);
  78. msg.msg_iov = iov;
  79. msg.msg_iovlen = 2;
  80. msg.msg_control = NULL;
  81. msg.msg_controllen = 0;
  82. msg.msg_flags = 0;
  83. res = sendmsg (fd, &msg, 0);
  84. if (res == -1)
  85. return -1;
  86. if ((size_t)res != nlm.nlmsg_len)
  87. {
  88. errno = EIO;
  89. return -1;
  90. }
  91. return 0;
  92. }
  93. static int
  94. nl_recv (int fd, unsigned type, void *buf, size_t size)
  95. {
  96. struct sockaddr_nl addr;
  97. struct msghdr msg;
  98. struct nlmsghdr nlm;
  99. struct iovec iov[2];
  100. ssize_t res, resdiff;
  101. again:
  102. iov[0].iov_base = &nlm;
  103. iov[0].iov_len = sizeof (nlm);
  104. msg.msg_name = &addr;
  105. msg.msg_namelen = sizeof (addr);
  106. msg.msg_iov = iov;
  107. msg.msg_iovlen = 1;
  108. msg.msg_control = NULL;
  109. msg.msg_controllen = 0;
  110. msg.msg_flags = 0;
  111. if (type != NLMSG_ERROR)
  112. {
  113. res = recvmsg (fd, &msg, MSG_PEEK);
  114. if (res == -1)
  115. return -1;
  116. if (res != NLMSG_LENGTH (0))
  117. {
  118. errno = EIO;
  119. return -1;
  120. }
  121. if (nlm.nlmsg_type == NLMSG_ERROR)
  122. {
  123. struct nlmsgerr err;
  124. iov[1].iov_base = &err;
  125. iov[1].iov_len = sizeof (err);
  126. msg.msg_iovlen = 2;
  127. res = recvmsg (fd, &msg, 0);
  128. if (res == -1)
  129. return -1;
  130. if ((size_t)res != NLMSG_LENGTH (sizeof (err))
  131. || nlm.nlmsg_type != NLMSG_ERROR)
  132. {
  133. errno = EIO;
  134. return -1;
  135. }
  136. if (err.error == 0)
  137. goto again;
  138. errno = -err.error;
  139. return -1;
  140. }
  141. }
  142. if (size != 0)
  143. {
  144. iov[1].iov_base = buf;
  145. iov[1].iov_len = size;
  146. msg.msg_iovlen = 2;
  147. }
  148. res = recvmsg (fd, &msg, 0);
  149. if (res == -1)
  150. return -1;
  151. resdiff = NLMSG_LENGTH(size) - (size_t)res;
  152. if (resdiff < 0
  153. || nlm.nlmsg_type != type)
  154. {
  155. errno = EIO;
  156. return -1;
  157. }
  158. else if (resdiff > 0)
  159. {
  160. memset((char *)buf + size - resdiff, 0, resdiff);
  161. }
  162. return 0;
  163. }
  164. static int
  165. nl_recv_ack (int fd)
  166. {
  167. struct nlmsgerr err;
  168. if (nl_recv (fd, NLMSG_ERROR, &err, sizeof (err)) != 0)
  169. return -1;
  170. if (err.error != 0)
  171. {
  172. errno = -err.error;
  173. return -1;
  174. }
  175. return 0;
  176. }
  177. static void
  178. cleanup_old_status (pam_handle_t *pamh, void *data, int error_status)
  179. {
  180. (void)pamh;
  181. (void)error_status;
  182. free (data);
  183. }
  184. enum uid_range { UID_RANGE_NONE, UID_RANGE_MM, UID_RANGE_MIN,
  185. UID_RANGE_ONE, UID_RANGE_ERR };
  186. static enum uid_range
  187. parse_uid_range(pam_handle_t *pamh, const char *s,
  188. uid_t *min_uid, uid_t *max_uid)
  189. {
  190. const char *range = s;
  191. const char *pmax;
  192. char *endptr;
  193. enum uid_range rv = UID_RANGE_MM;
  194. if ((pmax=strchr(range, ':')) == NULL)
  195. return UID_RANGE_NONE;
  196. ++pmax;
  197. if (range[0] == ':')
  198. rv = UID_RANGE_ONE;
  199. else {
  200. errno = 0;
  201. *min_uid = strtoul (range, &endptr, 10);
  202. if (errno != 0 || (range == endptr) || *endptr != ':') {
  203. pam_syslog(pamh, LOG_DEBUG,
  204. "wrong min_uid value in '%s'", s);
  205. return UID_RANGE_ERR;
  206. }
  207. }
  208. if (*pmax == '\0') {
  209. if (rv == UID_RANGE_ONE)
  210. return UID_RANGE_ERR;
  211. return UID_RANGE_MIN;
  212. }
  213. errno = 0;
  214. *max_uid = strtoul (pmax, &endptr, 10);
  215. if (errno != 0 || (pmax == endptr) || *endptr != '\0') {
  216. pam_syslog(pamh, LOG_DEBUG,
  217. "wrong max_uid value in '%s'", s);
  218. return UID_RANGE_ERR;
  219. }
  220. if (rv == UID_RANGE_ONE)
  221. *min_uid = *max_uid;
  222. return rv;
  223. }
  224. int
  225. pam_sm_open_session (pam_handle_t *pamh, int flags, int argc, const char **argv)
  226. {
  227. enum command { CMD_NONE, CMD_ENABLE, CMD_DISABLE };
  228. enum command command;
  229. struct audit_tty_status *old_status, new_status;
  230. const char *user;
  231. int i, fd, open_only;
  232. struct passwd *pwd;
  233. #ifdef HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD
  234. int log_passwd;
  235. #endif /* HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD */
  236. (void)flags;
  237. if (pam_get_user (pamh, &user, NULL) != PAM_SUCCESS)
  238. {
  239. pam_syslog(pamh, LOG_NOTICE, "cannot determine user name");
  240. return PAM_SESSION_ERR;
  241. }
  242. pwd = pam_modutil_getpwnam(pamh, user);
  243. if (pwd == NULL)
  244. {
  245. pam_syslog(pamh, LOG_NOTICE,
  246. "open_session unknown user '%s'", user);
  247. return PAM_SESSION_ERR;
  248. }
  249. command = CMD_NONE;
  250. open_only = 0;
  251. #ifdef HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD
  252. log_passwd = 0;
  253. #endif /* HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD */
  254. for (i = 0; i < argc; i++)
  255. {
  256. const char *str;
  257. if ((str = pam_str_skip_prefix(argv[i], "enable=")) != NULL
  258. || (str = pam_str_skip_prefix(argv[i], "disable=")) != NULL)
  259. {
  260. enum command this_command;
  261. char *copy, *tok_data, *tok;
  262. this_command = *argv[i] == 'e' ? CMD_ENABLE : CMD_DISABLE;
  263. copy = strdup (str);
  264. if (copy == NULL)
  265. return PAM_SESSION_ERR;
  266. for (tok = strtok_r (copy, ",", &tok_data);
  267. tok != NULL && command != this_command;
  268. tok = strtok_r (NULL, ",", &tok_data))
  269. {
  270. uid_t min_uid = 0, max_uid = 0;
  271. switch (parse_uid_range(pamh, tok, &min_uid, &max_uid))
  272. {
  273. case UID_RANGE_NONE:
  274. if (fnmatch (tok, user, 0) == 0)
  275. command = this_command;
  276. break;
  277. case UID_RANGE_MM:
  278. if (pwd->pw_uid >= min_uid && pwd->pw_uid <= max_uid)
  279. command = this_command;
  280. break;
  281. case UID_RANGE_MIN:
  282. if (pwd->pw_uid >= min_uid)
  283. command = this_command;
  284. break;
  285. case UID_RANGE_ONE:
  286. if (pwd->pw_uid == max_uid)
  287. command = this_command;
  288. break;
  289. case UID_RANGE_ERR:
  290. break;
  291. }
  292. }
  293. free (copy);
  294. }
  295. else if (strcmp (argv[i], "open_only") == 0)
  296. open_only = 1;
  297. else if (strcmp (argv[i], "log_passwd") == 0)
  298. #ifdef HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD
  299. log_passwd = 1;
  300. #else /* HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD */
  301. pam_syslog (pamh, LOG_WARNING,
  302. "The log_passwd option was not available at compile time.");
  303. #warning "pam_tty_audit: The log_passwd option is not available. Please upgrade your headers/kernel."
  304. #endif /* HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD */
  305. else
  306. {
  307. pam_syslog (pamh, LOG_ERR, "unknown option `%s'", argv[i]);
  308. }
  309. }
  310. if (command == CMD_NONE)
  311. return PAM_SUCCESS;
  312. old_status = malloc (sizeof (*old_status));
  313. if (old_status == NULL)
  314. return PAM_SESSION_ERR;
  315. fd = nl_open ();
  316. if (fd == -1
  317. && errno == EPROTONOSUPPORT)
  318. {
  319. pam_syslog (pamh, LOG_WARNING, "unable to open audit socket, audit not "
  320. "supported; tty_audit skipped");
  321. free (old_status);
  322. return PAM_IGNORE;
  323. }
  324. else if (fd == -1
  325. || nl_send (fd, AUDIT_TTY_GET, 0, NULL, 0) != 0
  326. || nl_recv (fd, AUDIT_TTY_GET, old_status, sizeof (*old_status)) != 0)
  327. {
  328. pam_syslog (pamh, LOG_ERR, "error reading current audit status: %m");
  329. if (fd != -1)
  330. close (fd);
  331. free (old_status);
  332. return PAM_SESSION_ERR;
  333. }
  334. memcpy(&new_status, old_status, sizeof(new_status));
  335. new_status.enabled = (command == CMD_ENABLE ? 1 : 0);
  336. #ifdef HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD
  337. new_status.log_passwd = log_passwd;
  338. #endif /* HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD */
  339. if (old_status->enabled == new_status.enabled
  340. #ifdef HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD
  341. && old_status->log_passwd == new_status.log_passwd
  342. #endif /* HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD */
  343. )
  344. {
  345. open_only = 1; /* to clean up old_status */
  346. goto ok_fd;
  347. }
  348. if (open_only == 0
  349. && pam_set_data (pamh, DATANAME, old_status, cleanup_old_status)
  350. != PAM_SUCCESS)
  351. {
  352. pam_syslog (pamh, LOG_ERR, "error saving old audit status");
  353. close (fd);
  354. free (old_status);
  355. return PAM_SESSION_ERR;
  356. }
  357. if (nl_send (fd, AUDIT_TTY_SET, NLM_F_ACK, &new_status,
  358. sizeof (new_status)) != 0
  359. || nl_recv_ack (fd) != 0)
  360. {
  361. pam_syslog (pamh, LOG_ERR, "error setting current audit status: %m");
  362. close (fd);
  363. if (open_only != 0)
  364. free (old_status);
  365. return PAM_SESSION_ERR;
  366. }
  367. /* Fall through */
  368. ok_fd:
  369. close (fd);
  370. pam_syslog (pamh, LOG_DEBUG, "changed status from %d to %d",
  371. old_status->enabled, new_status.enabled);
  372. if (open_only != 0)
  373. free (old_status);
  374. return PAM_SUCCESS;
  375. }
  376. int
  377. pam_sm_close_session (pam_handle_t *pamh, int flags, int argc,
  378. const char **argv)
  379. {
  380. const void *status_;
  381. (void)flags;
  382. (void)argc;
  383. (void)argv;
  384. if (pam_get_data (pamh, DATANAME, &status_) == PAM_SUCCESS)
  385. {
  386. const struct audit_tty_status *status;
  387. int fd;
  388. status = status_;
  389. fd = nl_open ();
  390. if (fd == -1
  391. || nl_send (fd, AUDIT_TTY_SET, NLM_F_ACK, status,
  392. sizeof (*status)) != 0
  393. || nl_recv_ack (fd) != 0)
  394. {
  395. pam_syslog (pamh, LOG_ERR, "error restoring audit status: %m");
  396. if (fd != -1)
  397. close (fd);
  398. return PAM_SESSION_ERR;
  399. }
  400. close (fd);
  401. pam_syslog (pamh, LOG_DEBUG, "restored status to %d", status->enabled);
  402. }
  403. return PAM_SUCCESS;
  404. }