trouble shutting down avc netlink socket

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

I'm having a problem where calling avc_destroy doesn't seem to close the
netlink socket, because a subsequent avc_init is unable to bind to the
socket, and gets an error "Address already in use".

The attached test program lets me reproduce the problem - the very
interesting thing is it seems to only happen about 50% of the time.  Is
there some race here in the kernel?

As far as I can tell the close() is being called so the socket should be
shut down.

#include <errno.h>
#include <pthread.h>
#include <syslog.h>
#include <selinux/selinux.h>
#include <selinux/avc.h>
#include <selinux/av_permissions.h>
#include <selinux/flask.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>

#define HAVE_SELINUX

#ifdef HAVE_SELINUX
/* Store the value telling us if SELinux is enabled in the kernel. */
static int selinux_enabled = 0;

/* Store an avc_entry_ref to speed AVC decisions. */
static struct avc_entry_ref aeref;

/* Store the SID of the bus itself to use as the default. */
static security_id_t bus_sid = SECSID_WILD;

/* Thread to listen for SELinux status changes via netlink. */
static pthread_t avc_notify_thread;

/* Prototypes for AVC callback functions.  */
static void log_callback (const char *fmt, ...);
static void *avc_create_thread (void (*run) (void));
static void avc_stop_thread (void *thread);
static void *avc_alloc_lock (void);
static void avc_get_lock (void *lock);
static void avc_release_lock (void *lock);
static void avc_free_lock (void *lock);

/* AVC callback structures for use in avc_init.  */
static const struct avc_memory_callback mem_cb =
{
  .func_malloc = malloc,
  .func_free = free
};
static const struct avc_log_callback log_cb =
{
  .func_log = log_callback,
  .func_audit = NULL
};
static const struct avc_thread_callback thread_cb =
{
  .func_create_thread = avc_create_thread,
  .func_stop_thread = avc_stop_thread
};
static const struct avc_lock_callback lock_cb =
{
  .func_alloc_lock = avc_alloc_lock,
  .func_get_lock = avc_get_lock,
  .func_release_lock = avc_release_lock,
  .func_free_lock = avc_free_lock
};
#endif /* HAVE_SELINUX */

/**
 * Log callback to log denial messages from the AVC.
 * This is used in avc_init.  Logs to both standard
 * error and syslogd.
 *
 * @param fmt the format string
 * @param variable argument list
 */
#ifdef HAVE_SELINUX
static void 
log_callback (const char *fmt, ...) 
{
  va_list ap;
  va_start(ap, fmt);
  vsyslog (LOG_INFO, fmt, ap);
  va_end(ap);
}

/**
 * On a policy reload we need to reparse the SELinux configuration file, since
 * this could have changed.  Send a SIGHUP to reload all configs.
 */
static int
policy_reload_callback (u_int32_t event, security_id_t ssid, 
                        security_id_t tsid, security_class_t tclass, 
                        access_vector_t perms, access_vector_t *out_retained)
{
  if (event == AVC_CALLBACK_RESET)
    return raise (SIGHUP);
  
  return 0;
}

/**
 * Create thread to notify the AVC of enforcing and policy reload
 * changes via netlink.
 *
 * @param run the thread run function
 * @return pointer to the thread
 */
static void *
avc_create_thread (void (*run) (void))
{
  int rc;

  rc = pthread_create (&avc_notify_thread, NULL, (void *(*) (void *)) run, NULL);
  if (rc != 0)
    {
      fprintf (stderr, "Failed to start AVC thread: %s\n", strerror (rc));
      exit (1);
    }
  return &avc_notify_thread;
}

/* Stop AVC netlink thread.  */
static void
avc_stop_thread (void *thread)
{
  pthread_cancel (*(pthread_t *) thread);
}

/* Allocate a new AVC lock.  */
static void *
avc_alloc_lock (void)
{
  pthread_mutex_t *avc_mutex;

  avc_mutex = malloc (sizeof (pthread_mutex_t));
  if (avc_mutex == NULL)
    {
      fprintf (stderr, "Could not create mutex: %s\n", strerror (errno));
      exit (1);
    }
  pthread_mutex_init (avc_mutex, NULL);

  return avc_mutex;
}

/* Acquire an AVC lock.  */
static void
avc_get_lock (void *lock)
{
  pthread_mutex_lock (lock);
}

/* Release an AVC lock.  */
static void
avc_release_lock (void *lock)
{
  pthread_mutex_unlock (lock);
}

/* Free an AVC lock.  */
static void
avc_free_lock (void *lock)
{
  pthread_mutex_destroy (lock);
  free (lock);
}
#endif /* HAVE_SELINUX */

/**
 * Initialize the user space access vector cache (AVC) for D-BUS and set up
 * logging callbacks.
 */
int
bus_selinux_init (void)
{
#ifdef HAVE_SELINUX
  int r;
  char *bus_context;

  
  /* Determine if we are running an SELinux kernel. */
  r = is_selinux_enabled ();
  if (r < 0)
    {
      fprintf (stderr, "Could not tell if SELinux is enabled: %s\n",
                  strerror (errno));
      return 0;
    }

  selinux_enabled = r != 0;

  if (!selinux_enabled)
    {
      fprintf (stderr, "SELinux not enabled in this kernel.\n");
      return 1;
    }

  fprintf (stderr, "SELinux is enabled in this kernel.\n");

  avc_entry_ref_init (&aeref);
  if (avc_init ("avc", &mem_cb, &log_cb, &thread_cb, &lock_cb) < 0)
    {
      fprintf (stderr, "Failed to start Access Vector Cache (AVC).\n");
      return 0;
    }
  else
    {
      openlog ("dbus", LOG_PERROR, LOG_USER);
      fprintf (stderr, "Access Vector Cache (AVC) started.\n");
    }

  if (avc_add_callback (policy_reload_callback, AVC_CALLBACK_RESET,
                       NULL, NULL, 0, 0) < 0)
    {
      fprintf (stderr, "Failed to add policy reload callback: %s\n",
                  strerror (errno));
      avc_destroy ();
      return 0;
    }

  bus_context = NULL;
  bus_sid = SECSID_WILD;

  if (getcon (&bus_context) < 0)
    {
      fprintf (stderr, "Error getting context of bus: %s\n",
                     strerror (errno));
      return 0;
    }
      
  if (avc_context_to_sid (bus_context, &bus_sid) < 0)
    {
      fprintf (stderr, "Error getting SID from bus context: %s\n",
                     strerror (errno));
      freecon (bus_context);
      return 0;
    }

  freecon (bus_context);
  
  return 1;
#else
  return 1;
#endif /* HAVE_SELINUX */
}

void
bus_selinux_shutdown (void)
{
#ifdef HAVE_SELINUX
  if (!selinux_enabled)
    return;

  sidput (bus_sid);
  bus_sid = SECSID_WILD;
  
#ifdef DBUS_ENABLE_VERBOSE_MODE
  bus_avc_print_stats ();
#endif /* DBUS_ENABLE_VERBOSE_MODE */

  avc_destroy ();
#endif /* HAVE_SELINUX */
}

int
main(int argc, char **argv)
{
  if (!bus_selinux_init ()) {
    fprintf (stderr, "couldn't init selinux\n");
    exit (1);
  }
  bus_selinux_shutdown ();
  if (!bus_selinux_init ()) {
    fprintf (stderr, "couldn't init selinux\n");
    exit (1);
  }
  bus_selinux_shutdown ();
  return 0;
}

Attachment: signature.asc
Description: This is a digitally signed message part


[Index of Archives]     [Fedora Users]     [Fedora Desktop]     [Big List of Linux Books]     [Yosemite News]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux