306 lines
6.7 KiB
C
306 lines
6.7 KiB
C
/*
|
|
* restorecond
|
|
*
|
|
* Copyright (C) 2006-2009 Red Hat
|
|
* Copyright (C) 2020 Nicolas Iooss
|
|
* see file 'COPYING' for use and warranty information
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of
|
|
* the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
.*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
* 02111-1307 USA
|
|
*
|
|
* Authors:
|
|
* Dan Walsh <dwalsh@redhat.com>
|
|
* Nicolas Iooss <nicolas.iooss@m4x.org>
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
#include <sys/inotify.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <signal.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <ctype.h>
|
|
#include <sys/file.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <syslog.h>
|
|
#include <limits.h>
|
|
#include <fcntl.h>
|
|
|
|
#include <selinux/selinux.h>
|
|
|
|
#include "restorecond.h"
|
|
#include "stringslist.h"
|
|
#include <glib.h>
|
|
#include <glib-unix.h>
|
|
|
|
static int local_lock_fd = -1;
|
|
|
|
#ifdef HAVE_DBUS
|
|
#include <gio/gio.h>
|
|
|
|
static const char *DBUS_NAME = "org.selinux.Restorecond";
|
|
|
|
static void on_name_acquired(GDBusConnection *connection G_GNUC_UNUSED,
|
|
const gchar *name,
|
|
gpointer user_data G_GNUC_UNUSED)
|
|
{
|
|
if (debug_mode)
|
|
g_print("D-Bus name acquired: %s\n", name);
|
|
}
|
|
|
|
static void on_name_lost(GDBusConnection *connection G_GNUC_UNUSED,
|
|
const gchar *name,
|
|
gpointer user_data)
|
|
{
|
|
/* Exit when the D-Bus connection closes */
|
|
GMainLoop *loop = user_data;
|
|
|
|
if (debug_mode)
|
|
g_print("D-Bus name lost (%s), exiting\n", name);
|
|
g_main_loop_quit(loop);
|
|
}
|
|
|
|
/**
|
|
* Try starting a D-Bus server on the session bus.
|
|
* Returns -1 if the connection failed, so that a local server can be launched
|
|
*/
|
|
static int dbus_server(GMainLoop *loop)
|
|
{
|
|
GDBusConnection *bus;
|
|
guint client_id;
|
|
|
|
bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
|
|
if (!bus)
|
|
return -1;
|
|
|
|
client_id = g_bus_own_name_on_connection(
|
|
bus,
|
|
DBUS_NAME,
|
|
G_BUS_NAME_OWNER_FLAGS_NONE,
|
|
on_name_acquired,
|
|
on_name_lost,
|
|
loop,
|
|
NULL);
|
|
g_object_unref(bus);
|
|
if (client_id == 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
/* size of the event structure, not counting name */
|
|
#define EVENT_SIZE (sizeof (struct inotify_event))
|
|
/* reasonable guess as to size of 1024 events */
|
|
#define BUF_LEN (1024 * (EVENT_SIZE + 16))
|
|
|
|
static gboolean
|
|
io_channel_callback
|
|
(GIOChannel *source,
|
|
GIOCondition condition,
|
|
gpointer data __attribute__((__unused__)))
|
|
{
|
|
|
|
char buffer[BUF_LEN+1];
|
|
gsize bytes_read;
|
|
unsigned int i = 0;
|
|
|
|
if (condition & G_IO_IN) {
|
|
/* Data is available. */
|
|
g_io_channel_read_chars
|
|
(source, buffer,
|
|
sizeof (buffer),
|
|
&bytes_read, NULL);
|
|
|
|
if (! bytes_read) {
|
|
/* Session/Terminal Ended */
|
|
exit(0);
|
|
}
|
|
|
|
while (i < bytes_read) {
|
|
struct inotify_event *event;
|
|
event = (struct inotify_event *)&buffer[i];
|
|
if (debug_mode)
|
|
printf("wd=%d mask=%u cookie=%u len=%u\n",
|
|
event->wd, event->mask,
|
|
event->cookie, event->len);
|
|
if (event->len)
|
|
watch_list_find(event->wd, event->name);
|
|
|
|
i += EVENT_SIZE + event->len;
|
|
}
|
|
}
|
|
|
|
/* An error happened while reading
|
|
the file. */
|
|
|
|
if (condition & G_IO_NVAL)
|
|
return FALSE;
|
|
|
|
/* We have reached the end of the
|
|
file. */
|
|
|
|
if (condition & G_IO_HUP) {
|
|
g_io_channel_shutdown (source, 0, NULL);
|
|
exit(0);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Returning TRUE will make sure
|
|
the callback remains associated
|
|
to the channel. */
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int start() {
|
|
#ifdef HAVE_DBUS
|
|
GDBusConnection *bus;
|
|
GError *err = NULL;
|
|
GVariant *result;
|
|
|
|
/* Get a connection to the session bus */
|
|
bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &err);
|
|
if (!bus) {
|
|
if (debug_mode)
|
|
g_warning("Failed to connect to the D-BUS daemon: %s", err->message);
|
|
g_error_free(err);
|
|
return 1;
|
|
}
|
|
|
|
/* Start restorecond D-Bus service by pinging its bus name
|
|
*
|
|
* https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-peer
|
|
*/
|
|
result = g_dbus_connection_call_sync(bus,
|
|
DBUS_NAME, /* bus name */
|
|
"/", /* object path */
|
|
"org.freedesktop.DBus.Peer", /* interface */
|
|
"Ping", /* method */
|
|
NULL, /* parameters */
|
|
NULL, /* reply_type */
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1, /* timeout_msec */
|
|
NULL,
|
|
&err);
|
|
if (!result) {
|
|
g_object_unref(bus);
|
|
if (debug_mode)
|
|
g_warning("Failed to start %s: %s", DBUS_NAME, err->message);
|
|
g_error_free(err);
|
|
return 1;
|
|
}
|
|
g_object_unref(bus);
|
|
#endif /* HAVE_DBUS */
|
|
return 0;
|
|
}
|
|
|
|
static int local_server(void) {
|
|
// ! dbus, run as local service
|
|
char *ptr=NULL;
|
|
if (asprintf(&ptr, "%s/.restorecond", homedir) < 0) {
|
|
if (debug_mode)
|
|
perror("asprintf");
|
|
return -1;
|
|
}
|
|
local_lock_fd = open(ptr, O_CREAT | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, S_IRUSR | S_IWUSR);
|
|
if (debug_mode)
|
|
g_warning ("Lock file: %s", ptr);
|
|
|
|
free(ptr);
|
|
if (local_lock_fd < 0) {
|
|
if (debug_mode)
|
|
perror("open");
|
|
return -1;
|
|
}
|
|
if (flock(local_lock_fd, LOCK_EX | LOCK_NB) < 0) {
|
|
if (debug_mode)
|
|
perror("flock");
|
|
close(local_lock_fd);
|
|
local_lock_fd = -1;
|
|
return -1;
|
|
}
|
|
/* watch for stdin/terminal going away */
|
|
GIOChannel *in = g_io_channel_unix_new(0);
|
|
g_io_add_watch_full( in,
|
|
G_PRIORITY_HIGH,
|
|
G_IO_IN|G_IO_ERR|G_IO_HUP,
|
|
io_channel_callback, NULL, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void end_local_server(void) {
|
|
if (local_lock_fd >= 0)
|
|
close(local_lock_fd);
|
|
local_lock_fd = -1;
|
|
}
|
|
|
|
static int sigterm_handler(gpointer user_data)
|
|
{
|
|
GMainLoop *loop = user_data;
|
|
|
|
if (debug_mode)
|
|
g_print("Received SIGTERM, exiting\n");
|
|
g_main_loop_quit(loop);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
int server(int master_fd, const char *watch_file) {
|
|
GMainLoop *loop;
|
|
|
|
loop = g_main_loop_new (NULL, FALSE);
|
|
|
|
#ifdef HAVE_DBUS
|
|
if (dbus_server(loop) != 0)
|
|
#endif /* HAVE_DBUS */
|
|
if (local_server())
|
|
goto end;
|
|
|
|
read_config(master_fd, watch_file);
|
|
|
|
if (watch_list_isempty())
|
|
goto end;
|
|
|
|
set_matchpathcon_flags(MATCHPATHCON_NOTRANS);
|
|
|
|
GIOChannel *c = g_io_channel_unix_new(master_fd);
|
|
|
|
g_io_add_watch_full(c,
|
|
G_PRIORITY_HIGH,
|
|
G_IO_IN|G_IO_ERR|G_IO_HUP,
|
|
io_channel_callback, NULL, NULL);
|
|
|
|
/* Handle SIGTERM */
|
|
g_unix_signal_add_full(G_PRIORITY_DEFAULT,
|
|
SIGTERM,
|
|
sigterm_handler,
|
|
loop,
|
|
NULL);
|
|
|
|
g_main_loop_run (loop);
|
|
|
|
end:
|
|
end_local_server();
|
|
g_main_loop_unref (loop);
|
|
return 0;
|
|
}
|
|
|