/modules/pam_tty_audit/pam_tty_audit.c

https://github.com/wichert/linux-pam · C · 374 lines · 303 code · 31 blank · 40 comment · 92 complexity · 1ab93ebc7ef32e08a48700f5baaeb337 MD5 · raw file

  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. #define PAM_SM_SESSION
  43. #include <security/pam_ext.h>
  44. #include <security/pam_modules.h>
  45. #include <security/pam_modutil.h>
  46. #define DATANAME "pam_tty_audit_last_state"
  47. /* Open an audit netlink socket */
  48. static int
  49. nl_open (void)
  50. {
  51. return socket (AF_NETLINK, SOCK_RAW, NETLINK_AUDIT);
  52. }
  53. static int
  54. nl_send (int fd, unsigned type, unsigned flags, const void *data, size_t size)
  55. {
  56. struct sockaddr_nl addr;
  57. struct msghdr msg;
  58. struct nlmsghdr nlm;
  59. struct iovec iov[2];
  60. ssize_t res;
  61. nlm.nlmsg_len = NLMSG_LENGTH (size);
  62. nlm.nlmsg_type = type;
  63. nlm.nlmsg_flags = NLM_F_REQUEST | flags;
  64. nlm.nlmsg_seq = 0;
  65. nlm.nlmsg_pid = 0;
  66. iov[0].iov_base = &nlm;
  67. iov[0].iov_len = sizeof (nlm);
  68. iov[1].iov_base = (void *)data;
  69. iov[1].iov_len = size;
  70. addr.nl_family = AF_NETLINK;
  71. addr.nl_pid = 0;
  72. addr.nl_groups = 0;
  73. msg.msg_name = &addr;
  74. msg.msg_namelen = sizeof (addr);
  75. msg.msg_iov = iov;
  76. msg.msg_iovlen = 2;
  77. msg.msg_control = NULL;
  78. msg.msg_controllen = 0;
  79. msg.msg_flags = 0;
  80. res = sendmsg (fd, &msg, 0);
  81. if (res == -1)
  82. return -1;
  83. if ((size_t)res != nlm.nlmsg_len)
  84. {
  85. errno = EIO;
  86. return -1;
  87. }
  88. return 0;
  89. }
  90. static int
  91. nl_recv (int fd, unsigned type, void *buf, size_t size)
  92. {
  93. struct sockaddr_nl addr;
  94. struct msghdr msg;
  95. struct nlmsghdr nlm;
  96. struct iovec iov[2];
  97. ssize_t res, resdiff;
  98. again:
  99. iov[0].iov_base = &nlm;
  100. iov[0].iov_len = sizeof (nlm);
  101. msg.msg_name = &addr;
  102. msg.msg_namelen = sizeof (addr);
  103. msg.msg_iov = iov;
  104. msg.msg_iovlen = 1;
  105. msg.msg_control = NULL;
  106. msg.msg_controllen = 0;
  107. if (type != NLMSG_ERROR)
  108. {
  109. res = recvmsg (fd, &msg, MSG_PEEK);
  110. if (res == -1)
  111. return -1;
  112. if (res != NLMSG_LENGTH (0))
  113. {
  114. errno = EIO;
  115. return -1;
  116. }
  117. if (nlm.nlmsg_type == NLMSG_ERROR)
  118. {
  119. struct nlmsgerr err;
  120. iov[1].iov_base = &err;
  121. iov[1].iov_len = sizeof (err);
  122. msg.msg_iovlen = 2;
  123. res = recvmsg (fd, &msg, 0);
  124. if (res == -1)
  125. return -1;
  126. if ((size_t)res != NLMSG_LENGTH (sizeof (err))
  127. || nlm.nlmsg_type != NLMSG_ERROR)
  128. {
  129. errno = EIO;
  130. return -1;
  131. }
  132. if (err.error == 0)
  133. goto again;
  134. errno = -err.error;
  135. return -1;
  136. }
  137. }
  138. if (size != 0)
  139. {
  140. iov[1].iov_base = buf;
  141. iov[1].iov_len = size;
  142. msg.msg_iovlen = 2;
  143. }
  144. res = recvmsg (fd, &msg, 0);
  145. if (res == -1)
  146. return -1;
  147. resdiff = NLMSG_LENGTH(size) - (size_t)res;
  148. if (resdiff < 0
  149. || nlm.nlmsg_type != type)
  150. {
  151. errno = EIO;
  152. return -1;
  153. }
  154. else if (resdiff > 0)
  155. {
  156. memset((char *)buf + size - resdiff, 0, resdiff);
  157. }
  158. return 0;
  159. }
  160. static int
  161. nl_recv_ack (int fd)
  162. {
  163. struct nlmsgerr err;
  164. if (nl_recv (fd, NLMSG_ERROR, &err, sizeof (err)) != 0)
  165. return -1;
  166. if (err.error != 0)
  167. {
  168. errno = -err.error;
  169. return -1;
  170. }
  171. return 0;
  172. }
  173. static void
  174. cleanup_old_status (pam_handle_t *pamh, void *data, int error_status)
  175. {
  176. (void)pamh;
  177. (void)error_status;
  178. free (data);
  179. }
  180. int
  181. pam_sm_open_session (pam_handle_t *pamh, int flags, int argc, const char **argv)
  182. {
  183. enum command { CMD_NONE, CMD_ENABLE, CMD_DISABLE };
  184. enum command command;
  185. struct audit_tty_status *old_status, new_status;
  186. const char *user;
  187. int i, fd, open_only;
  188. #ifdef HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD
  189. int log_passwd;
  190. #endif /* HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD */
  191. (void)flags;
  192. if (pam_get_user (pamh, &user, NULL) != PAM_SUCCESS)
  193. {
  194. pam_syslog (pamh, LOG_ERR, "error determining target user's name");
  195. return PAM_SESSION_ERR;
  196. }
  197. command = CMD_NONE;
  198. open_only = 0;
  199. #ifdef HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD
  200. log_passwd = 0;
  201. #endif /* HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD */
  202. for (i = 0; i < argc; i++)
  203. {
  204. if (strncmp (argv[i], "enable=", 7) == 0
  205. || strncmp (argv[i], "disable=", 8) == 0)
  206. {
  207. enum command this_command;
  208. char *copy, *tok_data, *tok;
  209. this_command = *argv[i] == 'e' ? CMD_ENABLE : CMD_DISABLE;
  210. copy = strdup (strchr (argv[i], '=') + 1);
  211. if (copy == NULL)
  212. return PAM_SESSION_ERR;
  213. for (tok = strtok_r (copy, ",", &tok_data); tok != NULL;
  214. tok = strtok_r (NULL, ",", &tok_data))
  215. {
  216. if (fnmatch (tok, user, 0) == 0)
  217. {
  218. command = this_command;
  219. break;
  220. }
  221. }
  222. free (copy);
  223. }
  224. else if (strcmp (argv[i], "open_only") == 0)
  225. open_only = 1;
  226. else if (strcmp (argv[i], "log_passwd") == 0)
  227. #ifdef HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD
  228. log_passwd = 1;
  229. #else /* HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD */
  230. pam_syslog (pamh, LOG_WARNING,
  231. "The log_passwd option was not available at compile time.");
  232. #warning "pam_tty_audit: The log_passwd option is not available. Please upgrade your headers/kernel."
  233. #endif /* HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD */
  234. else
  235. {
  236. pam_syslog (pamh, LOG_ERR, "unknown option `%s'", argv[i]);
  237. }
  238. }
  239. if (command == CMD_NONE)
  240. return PAM_SUCCESS;
  241. old_status = malloc (sizeof (*old_status));
  242. if (old_status == NULL)
  243. return PAM_SESSION_ERR;
  244. fd = nl_open ();
  245. if (fd == -1
  246. || nl_send (fd, AUDIT_TTY_GET, 0, NULL, 0) != 0
  247. || nl_recv (fd, AUDIT_TTY_GET, old_status, sizeof (*old_status)) != 0)
  248. {
  249. pam_syslog (pamh, LOG_ERR, "error reading current audit status: %m");
  250. if (fd != -1)
  251. close (fd);
  252. free (old_status);
  253. return PAM_SESSION_ERR;
  254. }
  255. memcpy(&new_status, old_status, sizeof(new_status));
  256. new_status.enabled = (command == CMD_ENABLE ? 1 : 0);
  257. #ifdef HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD
  258. new_status.log_passwd = log_passwd;
  259. #endif /* HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD */
  260. if (old_status->enabled == new_status.enabled
  261. #ifdef HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD
  262. && old_status->log_passwd == new_status.log_passwd
  263. #endif /* HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD */
  264. )
  265. {
  266. open_only = 1; /* to clean up old_status */
  267. goto ok_fd;
  268. }
  269. if (open_only == 0
  270. && pam_set_data (pamh, DATANAME, old_status, cleanup_old_status)
  271. != PAM_SUCCESS)
  272. {
  273. pam_syslog (pamh, LOG_ERR, "error saving old audit status");
  274. close (fd);
  275. free (old_status);
  276. return PAM_SESSION_ERR;
  277. }
  278. if (nl_send (fd, AUDIT_TTY_SET, NLM_F_ACK, &new_status,
  279. sizeof (new_status)) != 0
  280. || nl_recv_ack (fd) != 0)
  281. {
  282. pam_syslog (pamh, LOG_ERR, "error setting current audit status: %m");
  283. close (fd);
  284. if (open_only != 0)
  285. free (old_status);
  286. return PAM_SESSION_ERR;
  287. }
  288. /* Fall through */
  289. ok_fd:
  290. close (fd);
  291. pam_syslog (pamh, LOG_DEBUG, "changed status from %d to %d",
  292. old_status->enabled, new_status.enabled);
  293. if (open_only != 0)
  294. free (old_status);
  295. return PAM_SUCCESS;
  296. }
  297. int
  298. pam_sm_close_session (pam_handle_t *pamh, int flags, int argc,
  299. const char **argv)
  300. {
  301. const void *status_;
  302. (void)flags;
  303. (void)argc;
  304. (void)argv;
  305. if (pam_get_data (pamh, DATANAME, &status_) == PAM_SUCCESS)
  306. {
  307. const struct audit_tty_status *status;
  308. int fd;
  309. status = status_;
  310. fd = nl_open ();
  311. if (fd == -1
  312. || nl_send (fd, AUDIT_TTY_SET, NLM_F_ACK, status,
  313. sizeof (*status)) != 0
  314. || nl_recv_ack (fd) != 0)
  315. {
  316. pam_syslog (pamh, LOG_ERR, "error restoring audit status: %m");
  317. if (fd != -1)
  318. close (fd);
  319. return PAM_SESSION_ERR;
  320. }
  321. close (fd);
  322. pam_syslog (pamh, LOG_DEBUG, "restored status to %d", status->enabled);
  323. }
  324. return PAM_SUCCESS;
  325. }
  326. /* static module data */
  327. #ifdef PAM_STATIC
  328. struct pam_module _pam_tty_audit_modstruct = {
  329. "pam_tty_audit",
  330. NULL,
  331. NULL,
  332. NULL,
  333. pam_sm_open_session,
  334. pam_sm_close_session,
  335. NULL
  336. };
  337. #endif