Chris PeBenito 473ea7
/*
Chris PeBenito 473ea7
 * Callbacks for user-supplied memory allocation, supplemental
Chris PeBenito 473ea7
 * auditing, and locking routines.
Chris PeBenito 473ea7
 *
Chris PeBenito 473ea7
 * Author : Eamon Walsh <ewalsh@epoch.ncsc.mil>
Chris PeBenito 473ea7
 *
Chris PeBenito 473ea7
 * Netlink code derived in part from sample code by
Chris PeBenito 473ea7
 * James Morris <jmorris@redhat.com>.
Chris PeBenito 473ea7
 */
Chris PeBenito 473ea7
Chris PeBenito 473ea7
#include <errno.h>
Chris PeBenito 473ea7
#include <stdio.h>
Chris PeBenito 473ea7
#include <stdlib.h>
Chris PeBenito 473ea7
#include <unistd.h>
Chris PeBenito 473ea7
#include <fcntl.h>
Chris PeBenito 473ea7
#include <string.h>
Chris PeBenito 473ea7
#include <sys/types.h>
Chris PeBenito 473ea7
#include <sys/socket.h>
Chris PeBenito 473ea7
#include <asm/types.h>
Chris PeBenito 473ea7
#include <linux/netlink.h>
Chris PeBenito 473ea7
#include "selinux_netlink.h"
Chris PeBenito 473ea7
#include "avc_internal.h"
Chris PeBenito 473ea7
Chris PeBenito 473ea7
#ifndef NETLINK_SELINUX
Chris PeBenito 473ea7
#define NETLINK_SELINUX 7
Chris PeBenito 473ea7
#endif
Chris PeBenito 473ea7
Chris PeBenito 473ea7
/* callback pointers */
Chris PeBenito 473ea7
void *(*avc_func_malloc)(size_t) = NULL;
Chris PeBenito 473ea7
void (*avc_func_free)(void*) = NULL;
Chris PeBenito 473ea7
Chris PeBenito 473ea7
void (*avc_func_log)(const char*, ...) = NULL;
Chris PeBenito 473ea7
void (*avc_func_audit)(void*,security_class_t,char*,size_t) = NULL;
Chris PeBenito 473ea7
Chris PeBenito 473ea7
int avc_using_threads = 0;
Chris PeBenito 473ea7
void *(*avc_func_create_thread)(void (*)(void)) = NULL;
Chris PeBenito 473ea7
void (*avc_func_stop_thread)(void*) = NULL;
Chris PeBenito 473ea7
Chris PeBenito 473ea7
void *(*avc_func_alloc_lock)(void) = NULL;
Chris PeBenito 473ea7
void (*avc_func_get_lock)(void*) = NULL;
Chris PeBenito 473ea7
void (*avc_func_release_lock)(void*) = NULL;
Chris PeBenito 473ea7
void (*avc_func_free_lock)(void*) = NULL;
Chris PeBenito 473ea7
Chris PeBenito 473ea7
/* message prefix string and avc enforcing mode */
Chris PeBenito 473ea7
char avc_prefix[AVC_PREFIX_SIZE] = "uavc";
Chris PeBenito 473ea7
int avc_enforcing = 1;
Chris PeBenito 473ea7
int avc_netlink_trouble = 0;
Chris PeBenito 473ea7
Chris PeBenito 473ea7
/* netlink socket code */
Chris PeBenito 473ea7
static int fd;
Chris PeBenito 473ea7
Chris PeBenito 473ea7
int avc_netlink_open(int blocking) {
Chris PeBenito 473ea7
  int len, rc = 0;
Chris PeBenito 473ea7
  struct sockaddr_nl addr;
Chris PeBenito 473ea7
	
Chris PeBenito 473ea7
  fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_SELINUX);
Chris PeBenito 473ea7
  if (fd < 0) {
Chris PeBenito 473ea7
    rc = fd;
Chris PeBenito 473ea7
    goto out;
Chris PeBenito 473ea7
  }
Chris PeBenito 473ea7
Chris PeBenito 473ea7
  if (!blocking && fcntl(fd, F_SETFL, O_NONBLOCK)) {
Chris PeBenito 473ea7
    close(fd);
Chris PeBenito 473ea7
    rc = -1;
Chris PeBenito 473ea7
    goto out;
Chris PeBenito 473ea7
  }
Chris PeBenito 473ea7
	
Chris PeBenito 473ea7
  len = sizeof(addr);
Chris PeBenito 473ea7
	
Chris PeBenito 473ea7
  memset(&addr, 0, len);
Chris PeBenito 473ea7
  addr.nl_family = AF_NETLINK;
Chris PeBenito 473ea7
  addr.nl_groups = SELNL_GRP_AVC;
Chris PeBenito 473ea7
	
Chris PeBenito 473ea7
  if (bind(fd, (struct sockaddr *)&addr, len) < 0) {
Chris PeBenito 473ea7
    close(fd);
Chris PeBenito 473ea7
    rc = -1;
Chris PeBenito 473ea7
    goto out;
Chris PeBenito 473ea7
  }
Chris PeBenito 473ea7
 out:
Chris PeBenito 473ea7
  return rc;
Chris PeBenito 473ea7
}
Chris PeBenito 473ea7
Chris PeBenito 473ea7
void avc_netlink_close(void) {
Chris PeBenito 473ea7
  close(fd);
Chris PeBenito 473ea7
}
Chris PeBenito 473ea7
Chris PeBenito 473ea7
int avc_netlink_check_nb(void) {
Chris PeBenito 473ea7
  int rc;
Chris PeBenito 473ea7
  struct sockaddr_nl nladdr;
Chris PeBenito 473ea7
  socklen_t nladdrlen = sizeof nladdr;
Chris PeBenito 473ea7
  char buf[1024];
Chris PeBenito 473ea7
  struct nlmsghdr *nlh;
Chris PeBenito 473ea7
Chris PeBenito 473ea7
  while (1) {
Chris PeBenito 473ea7
    rc = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr*)&nladdr, &nladdrlen);
Chris PeBenito 473ea7
    if (rc < 0) {
Chris PeBenito 473ea7
      if (errno == EINTR)
Chris PeBenito 473ea7
	continue;
Chris PeBenito 473ea7
      if (errno != EAGAIN) {
Chris PeBenito 473ea7
	avc_log("%s:  socket error during read: %d\n", avc_prefix, errno);
Chris PeBenito 473ea7
      } else {
Chris PeBenito 473ea7
	errno = 0;
Chris PeBenito 473ea7
	rc = 0;
Chris PeBenito 473ea7
      }
Chris PeBenito 473ea7
      goto out;
Chris PeBenito 473ea7
    }
Chris PeBenito 473ea7
Chris PeBenito 473ea7
    if (nladdrlen != sizeof nladdr) {
Chris PeBenito 473ea7
      avc_log("%s:  warning: netlink address truncated, len %d?\n", 
Chris PeBenito 473ea7
	      avc_prefix, nladdrlen);
Chris PeBenito 473ea7
      rc = -1;
Chris PeBenito 473ea7
      goto out;
Chris PeBenito 473ea7
    }
Chris PeBenito 473ea7
Chris PeBenito 473ea7
    if (nladdr.nl_pid) {
Chris PeBenito 473ea7
      avc_log("%s:  warning: received spoofed netlink packet from: %d\n", 
Chris PeBenito 473ea7
	      avc_prefix, nladdr.nl_pid);
Chris PeBenito 473ea7
      continue;
Chris PeBenito 473ea7
    }
Chris PeBenito 473ea7
Chris PeBenito 473ea7
    if (rc == 0) {
Chris PeBenito 473ea7
      avc_log("%s:  warning: received EOF on socket\n", avc_prefix);
Chris PeBenito 473ea7
      goto out;
Chris PeBenito 473ea7
    }
Chris PeBenito 473ea7
		
Chris PeBenito 473ea7
    nlh = (struct nlmsghdr *)buf;
Chris PeBenito 473ea7
		
Chris PeBenito 473ea7
    if (nlh->nlmsg_flags & MSG_TRUNC || nlh->nlmsg_len > (unsigned)rc) {
Chris PeBenito 473ea7
      avc_log("%s:  warning: incomplete netlink message\n", avc_prefix);
Chris PeBenito 473ea7
      goto out;
Chris PeBenito 473ea7
    }
Chris PeBenito 473ea7
Chris PeBenito 473ea7
    rc = 0;
Chris PeBenito 473ea7
    switch (nlh->nlmsg_type) {
Chris PeBenito 473ea7
    case NLMSG_ERROR: {
Chris PeBenito 473ea7
      struct nlmsgerr *err = NLMSG_DATA(nlh);
Chris PeBenito 473ea7
			
Chris PeBenito 473ea7
      /* Netlink ack */
Chris PeBenito 473ea7
      if (err->error == 0)
Chris PeBenito 473ea7
	break;
Chris PeBenito 473ea7
Chris PeBenito 473ea7
      errno = -err->error;
Chris PeBenito 473ea7
      avc_log("%s:  netlink error: %d\n", avc_prefix, errno);
Chris PeBenito 473ea7
      rc = -1;
Chris PeBenito 473ea7
      goto out;
Chris PeBenito 473ea7
    }
Chris PeBenito 473ea7
			
Chris PeBenito 473ea7
    case SELNL_MSG_SETENFORCE: {
Chris PeBenito 473ea7
      struct selnl_msg_setenforce *msg = NLMSG_DATA(nlh);
Chris PeBenito 473ea7
      avc_log("%s:  received setenforce notice (enforcing=%d)\n",
Chris PeBenito 473ea7
	      avc_prefix, msg->val);
Chris PeBenito 473ea7
      avc_enforcing = msg->val;
Chris PeBenito 473ea7
      break;
Chris PeBenito 473ea7
    }
Chris PeBenito 473ea7
	
Chris PeBenito 473ea7
    case SELNL_MSG_POLICYLOAD: {
Chris PeBenito 473ea7
      struct selnl_msg_policyload *msg = NLMSG_DATA(nlh);
Chris PeBenito 473ea7
      avc_log("%s:  received policyload notice (seqno=%d)\n",
Chris PeBenito 473ea7
	      avc_prefix, msg->seqno);
Chris PeBenito 473ea7
      rc = avc_ss_reset(msg->seqno);
Chris PeBenito 473ea7
      if (rc < 0) {
Chris PeBenito 473ea7
	  avc_log("%s:  cache reset returned %d (errno %d)\n",
Chris PeBenito 473ea7
		  avc_prefix, rc, errno);
Chris PeBenito 473ea7
	  goto out;
Chris PeBenito 473ea7
      }
Chris PeBenito 473ea7
      break;
Chris PeBenito 473ea7
    }
Chris PeBenito 473ea7
		
Chris PeBenito 473ea7
    default:
Chris PeBenito 473ea7
      avc_log("%s:  warning: unknown netlink message %d\n",
Chris PeBenito 473ea7
	      avc_prefix, nlh->nlmsg_type);
Chris PeBenito 473ea7
    }
Chris PeBenito 473ea7
  }
Chris PeBenito 473ea7
 out:
Chris PeBenito 473ea7
  return rc;
Chris PeBenito 473ea7
}
Chris PeBenito 473ea7
Chris PeBenito 473ea7
/* run routine for the netlink listening thread */
Chris PeBenito 473ea7
void avc_netlink_loop(void) {
Chris PeBenito 473ea7
  int ret;
Chris PeBenito 473ea7
  struct sockaddr_nl nladdr;
Chris PeBenito 473ea7
  socklen_t nladdrlen = sizeof nladdr;
Chris PeBenito 473ea7
  char buf[1024];
Chris PeBenito 473ea7
  struct nlmsghdr *nlh;
Chris PeBenito 473ea7
	
Chris PeBenito 473ea7
  while (1) {
Chris PeBenito 473ea7
    ret = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr*)&nladdr, &nladdrlen);
Chris PeBenito 473ea7
    if (ret < 0) {
Chris PeBenito 473ea7
      if (errno == EINTR)
Chris PeBenito 473ea7
	continue;
Chris PeBenito 473ea7
      avc_log("%s:  netlink thread: recvfrom: error %d\n", avc_prefix, errno);
Chris PeBenito 473ea7
      goto out;
Chris PeBenito 473ea7
    }
Chris PeBenito 473ea7
Chris PeBenito 473ea7
    if (nladdrlen != sizeof nladdr) {
Chris PeBenito 473ea7
      avc_log("%s:  warning: netlink address truncated, len %d?\n", 
Chris PeBenito 473ea7
	      avc_prefix, nladdrlen);
Chris PeBenito 473ea7
      ret = -1;
Chris PeBenito 473ea7
      goto out;
Chris PeBenito 473ea7
    }
Chris PeBenito 473ea7
Chris PeBenito 473ea7
    if (nladdr.nl_pid) {
Chris PeBenito 473ea7
      avc_log("%s:  warning: received spoofed netlink packet from: %d\n", 
Chris PeBenito 473ea7
	      avc_prefix, nladdr.nl_pid);
Chris PeBenito 473ea7
      continue;
Chris PeBenito 473ea7
    }
Chris PeBenito 473ea7
		
Chris PeBenito 473ea7
    if (ret == 0) {
Chris PeBenito 473ea7
      avc_log("%s:  netlink thread: received EOF on socket\n", avc_prefix);
Chris PeBenito 473ea7
      goto out;
Chris PeBenito 473ea7
    }
Chris PeBenito 473ea7
		
Chris PeBenito 473ea7
    nlh = (struct nlmsghdr *)buf;
Chris PeBenito 473ea7
		
Chris PeBenito 473ea7
    if (nlh->nlmsg_flags & MSG_TRUNC || nlh->nlmsg_len > (unsigned) ret) {
Chris PeBenito 473ea7
      avc_log("%s:  netlink thread: incomplete netlink message\n", avc_prefix);
Chris PeBenito 473ea7
      goto out;
Chris PeBenito 473ea7
    }
Chris PeBenito 473ea7
		
Chris PeBenito 473ea7
    switch (nlh->nlmsg_type) {
Chris PeBenito 473ea7
    case NLMSG_ERROR: {
Chris PeBenito 473ea7
      struct nlmsgerr *err = NLMSG_DATA(nlh);
Chris PeBenito 473ea7
			
Chris PeBenito 473ea7
      /* Netlink ack */
Chris PeBenito 473ea7
      if (err->error == 0)
Chris PeBenito 473ea7
	break;
Chris PeBenito 473ea7
Chris PeBenito 473ea7
      avc_log("%s:  netlink thread: msg: error %d\n", avc_prefix, -err->error);
Chris PeBenito 473ea7
      goto out;
Chris PeBenito 473ea7
    }
Chris PeBenito 473ea7
			
Chris PeBenito 473ea7
    case SELNL_MSG_SETENFORCE: {
Chris PeBenito 473ea7
      struct selnl_msg_setenforce *msg = NLMSG_DATA(nlh);
Chris PeBenito 473ea7
      avc_log("%s:  received setenforce notice (enforcing=%d)\n",
Chris PeBenito 473ea7
	      avc_prefix, msg->val);
Chris PeBenito 473ea7
      avc_enforcing = msg->val;
Chris PeBenito 473ea7
      break;
Chris PeBenito 473ea7
    }
Chris PeBenito 473ea7
	
Chris PeBenito 473ea7
    case SELNL_MSG_POLICYLOAD: {
Chris PeBenito 473ea7
      struct selnl_msg_policyload *msg = NLMSG_DATA(nlh);
Chris PeBenito 473ea7
      avc_log("%s:  received policyload notice (seqno=%d)\n",
Chris PeBenito 473ea7
	      avc_prefix, msg->seqno);
Chris PeBenito 473ea7
      ret = avc_ss_reset(msg->seqno);
Chris PeBenito 473ea7
      if (ret < 0) {
Chris PeBenito 473ea7
	  avc_log("%s:  netlink thread: cache reset returned %d (errno %d)\n",
Chris PeBenito 473ea7
		  avc_prefix, ret, errno);
Chris PeBenito 473ea7
	  goto out;
Chris PeBenito 473ea7
      }
Chris PeBenito 473ea7
      break;
Chris PeBenito 473ea7
    }
Chris PeBenito 473ea7
Chris PeBenito 473ea7
    default:
Chris PeBenito 473ea7
      avc_log("%s:  netlink thread: warning: unknown msg type %d\n",
Chris PeBenito 473ea7
	      avc_prefix, nlh->nlmsg_type);
Chris PeBenito 473ea7
    }
Chris PeBenito 473ea7
  }
Chris PeBenito 473ea7
 out:
Chris PeBenito 473ea7
  close(fd);
Chris PeBenito 473ea7
  avc_netlink_trouble = 1;
Chris PeBenito 473ea7
  avc_log("%s:  netlink thread: errors encountered, terminating\n",
Chris PeBenito 473ea7
	  avc_prefix);
Chris PeBenito 473ea7
}