/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
- /* Copyright © 2007, 2008 Red Hat, Inc. All rights reserved.
- Red Hat author: Miloslav Trmač <mitr@redhat.com>
- Redistribution and use in source and binary forms of Linux-PAM, with
- or without modification, are permitted provided that the following
- conditions are met:
- 1. Redistributions of source code must retain any existing copyright
- notice, and this entire permission notice in its entirety,
- including the disclaimer of warranties.
- 2. Redistributions in binary form must reproduce all prior and current
- copyright notices, this list of conditions, and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
- 3. The name of any author may not be used to endorse or promote
- products derived from this software without their specific prior
- written permission.
- ALTERNATIVELY, this product may be distributed under the terms of the
- GNU General Public License, in which case the provisions of the GNU
- GPL are required INSTEAD OF the above restrictions. (This clause is
- necessary due to a potential conflict between the GNU GPL and the
- restrictions contained in a BSD-style copyright.)
- THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
- DAMAGE. */
- #include "config.h"
- #include <errno.h>
- #include <fnmatch.h>
- #include <stdlib.h>
- #include <string.h>
- #include <syslog.h>
- #include <sys/socket.h>
- #include <unistd.h>
- #include <libaudit.h>
- #include <linux/netlink.h>
- #define PAM_SM_SESSION
- #include <security/pam_ext.h>
- #include <security/pam_modules.h>
- #include <security/pam_modutil.h>
- #define DATANAME "pam_tty_audit_last_state"
- /* Open an audit netlink socket */
- static int
- nl_open (void)
- {
- return socket (AF_NETLINK, SOCK_RAW, NETLINK_AUDIT);
- }
- static int
- nl_send (int fd, unsigned type, unsigned flags, const void *data, size_t size)
- {
- struct sockaddr_nl addr;
- struct msghdr msg;
- struct nlmsghdr nlm;
- struct iovec iov[2];
- ssize_t res;
- nlm.nlmsg_len = NLMSG_LENGTH (size);
- nlm.nlmsg_type = type;
- nlm.nlmsg_flags = NLM_F_REQUEST | flags;
- nlm.nlmsg_seq = 0;
- nlm.nlmsg_pid = 0;
- iov[0].iov_base = &nlm;
- iov[0].iov_len = sizeof (nlm);
- iov[1].iov_base = (void *)data;
- iov[1].iov_len = size;
- addr.nl_family = AF_NETLINK;
- addr.nl_pid = 0;
- addr.nl_groups = 0;
- msg.msg_name = &addr;
- msg.msg_namelen = sizeof (addr);
- msg.msg_iov = iov;
- msg.msg_iovlen = 2;
- msg.msg_control = NULL;
- msg.msg_controllen = 0;
- msg.msg_flags = 0;
- res = sendmsg (fd, &msg, 0);
- if (res == -1)
- return -1;
- if ((size_t)res != nlm.nlmsg_len)
- {
- errno = EIO;
- return -1;
- }
- return 0;
- }
- static int
- nl_recv (int fd, unsigned type, void *buf, size_t size)
- {
- struct sockaddr_nl addr;
- struct msghdr msg;
- struct nlmsghdr nlm;
- struct iovec iov[2];
- ssize_t res, resdiff;
- again:
- iov[0].iov_base = &nlm;
- iov[0].iov_len = sizeof (nlm);
- msg.msg_name = &addr;
- msg.msg_namelen = sizeof (addr);
- msg.msg_iov = iov;
- msg.msg_iovlen = 1;
- msg.msg_control = NULL;
- msg.msg_controllen = 0;
- if (type != NLMSG_ERROR)
- {
- res = recvmsg (fd, &msg, MSG_PEEK);
- if (res == -1)
- return -1;
- if (res != NLMSG_LENGTH (0))
- {
- errno = EIO;
- return -1;
- }
- if (nlm.nlmsg_type == NLMSG_ERROR)
- {
- struct nlmsgerr err;
- iov[1].iov_base = &err;
- iov[1].iov_len = sizeof (err);
- msg.msg_iovlen = 2;
- res = recvmsg (fd, &msg, 0);
- if (res == -1)
- return -1;
- if ((size_t)res != NLMSG_LENGTH (sizeof (err))
- || nlm.nlmsg_type != NLMSG_ERROR)
- {
- errno = EIO;
- return -1;
- }
- if (err.error == 0)
- goto again;
- errno = -err.error;
- return -1;
- }
- }
- if (size != 0)
- {
- iov[1].iov_base = buf;
- iov[1].iov_len = size;
- msg.msg_iovlen = 2;
- }
- res = recvmsg (fd, &msg, 0);
- if (res == -1)
- return -1;
- resdiff = NLMSG_LENGTH(size) - (size_t)res;
- if (resdiff < 0
- || nlm.nlmsg_type != type)
- {
- errno = EIO;
- return -1;
- }
- else if (resdiff > 0)
- {
- memset((char *)buf + size - resdiff, 0, resdiff);
- }
- return 0;
- }
- static int
- nl_recv_ack (int fd)
- {
- struct nlmsgerr err;
- if (nl_recv (fd, NLMSG_ERROR, &err, sizeof (err)) != 0)
- return -1;
- if (err.error != 0)
- {
- errno = -err.error;
- return -1;
- }
- return 0;
- }
- static void
- cleanup_old_status (pam_handle_t *pamh, void *data, int error_status)
- {
- (void)pamh;
- (void)error_status;
- free (data);
- }
- int
- pam_sm_open_session (pam_handle_t *pamh, int flags, int argc, const char **argv)
- {
- enum command { CMD_NONE, CMD_ENABLE, CMD_DISABLE };
- enum command command;
- struct audit_tty_status *old_status, new_status;
- const char *user;
- int i, fd, open_only;
- #ifdef HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD
- int log_passwd;
- #endif /* HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD */
- (void)flags;
- if (pam_get_user (pamh, &user, NULL) != PAM_SUCCESS)
- {
- pam_syslog (pamh, LOG_ERR, "error determining target user's name");
- return PAM_SESSION_ERR;
- }
- command = CMD_NONE;
- open_only = 0;
- #ifdef HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD
- log_passwd = 0;
- #endif /* HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD */
- for (i = 0; i < argc; i++)
- {
- if (strncmp (argv[i], "enable=", 7) == 0
- || strncmp (argv[i], "disable=", 8) == 0)
- {
- enum command this_command;
- char *copy, *tok_data, *tok;
- this_command = *argv[i] == 'e' ? CMD_ENABLE : CMD_DISABLE;
- copy = strdup (strchr (argv[i], '=') + 1);
- if (copy == NULL)
- return PAM_SESSION_ERR;
- for (tok = strtok_r (copy, ",", &tok_data); tok != NULL;
- tok = strtok_r (NULL, ",", &tok_data))
- {
- if (fnmatch (tok, user, 0) == 0)
- {
- command = this_command;
- break;
- }
- }
- free (copy);
- }
- else if (strcmp (argv[i], "open_only") == 0)
- open_only = 1;
- else if (strcmp (argv[i], "log_passwd") == 0)
- #ifdef HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD
- log_passwd = 1;
- #else /* HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD */
- pam_syslog (pamh, LOG_WARNING,
- "The log_passwd option was not available at compile time.");
- #warning "pam_tty_audit: The log_passwd option is not available. Please upgrade your headers/kernel."
- #endif /* HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD */
- else
- {
- pam_syslog (pamh, LOG_ERR, "unknown option `%s'", argv[i]);
- }
- }
- if (command == CMD_NONE)
- return PAM_SUCCESS;
- old_status = malloc (sizeof (*old_status));
- if (old_status == NULL)
- return PAM_SESSION_ERR;
- fd = nl_open ();
- if (fd == -1
- || nl_send (fd, AUDIT_TTY_GET, 0, NULL, 0) != 0
- || nl_recv (fd, AUDIT_TTY_GET, old_status, sizeof (*old_status)) != 0)
- {
- pam_syslog (pamh, LOG_ERR, "error reading current audit status: %m");
- if (fd != -1)
- close (fd);
- free (old_status);
- return PAM_SESSION_ERR;
- }
- memcpy(&new_status, old_status, sizeof(new_status));
- new_status.enabled = (command == CMD_ENABLE ? 1 : 0);
- #ifdef HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD
- new_status.log_passwd = log_passwd;
- #endif /* HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD */
- if (old_status->enabled == new_status.enabled
- #ifdef HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD
- && old_status->log_passwd == new_status.log_passwd
- #endif /* HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD */
- )
- {
- open_only = 1; /* to clean up old_status */
- goto ok_fd;
- }
- if (open_only == 0
- && pam_set_data (pamh, DATANAME, old_status, cleanup_old_status)
- != PAM_SUCCESS)
- {
- pam_syslog (pamh, LOG_ERR, "error saving old audit status");
- close (fd);
- free (old_status);
- return PAM_SESSION_ERR;
- }
- if (nl_send (fd, AUDIT_TTY_SET, NLM_F_ACK, &new_status,
- sizeof (new_status)) != 0
- || nl_recv_ack (fd) != 0)
- {
- pam_syslog (pamh, LOG_ERR, "error setting current audit status: %m");
- close (fd);
- if (open_only != 0)
- free (old_status);
- return PAM_SESSION_ERR;
- }
- /* Fall through */
- ok_fd:
- close (fd);
- pam_syslog (pamh, LOG_DEBUG, "changed status from %d to %d",
- old_status->enabled, new_status.enabled);
- if (open_only != 0)
- free (old_status);
- return PAM_SUCCESS;
- }
- int
- pam_sm_close_session (pam_handle_t *pamh, int flags, int argc,
- const char **argv)
- {
- const void *status_;
- (void)flags;
- (void)argc;
- (void)argv;
- if (pam_get_data (pamh, DATANAME, &status_) == PAM_SUCCESS)
- {
- const struct audit_tty_status *status;
- int fd;
- status = status_;
- fd = nl_open ();
- if (fd == -1
- || nl_send (fd, AUDIT_TTY_SET, NLM_F_ACK, status,
- sizeof (*status)) != 0
- || nl_recv_ack (fd) != 0)
- {
- pam_syslog (pamh, LOG_ERR, "error restoring audit status: %m");
- if (fd != -1)
- close (fd);
- return PAM_SESSION_ERR;
- }
- close (fd);
- pam_syslog (pamh, LOG_DEBUG, "restored status to %d", status->enabled);
- }
- return PAM_SUCCESS;
- }
- /* static module data */
- #ifdef PAM_STATIC
- struct pam_module _pam_tty_audit_modstruct = {
- "pam_tty_audit",
- NULL,
- NULL,
- NULL,
- pam_sm_open_session,
- pam_sm_close_session,
- NULL
- };
- #endif