1 /*
2 * restorecond
3 *
4 * Copyright (C) 2006-2009 Red Hat
5 * Copyright (C) 2020 Nicolas Iooss
6 * see file 'COPYING' for use and warranty information
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of
11 * the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 .*
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 * 02111-1307 USA
22 *
23 * Authors:
24 * Dan Walsh <[email protected]>
25 * Nicolas Iooss <[email protected]>
26 */
27
28 #define _GNU_SOURCE
29 #include <sys/inotify.h>
30 #include <errno.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <signal.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <ctype.h>
37 #include <sys/file.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <syslog.h>
41 #include <limits.h>
42 #include <fcntl.h>
43
44 #include <selinux/selinux.h>
45
46 #include "restorecond.h"
47 #include "stringslist.h"
48 #include <glib.h>
49 #include <glib-unix.h>
50
51 static int local_lock_fd = -1;
52
53 #ifdef HAVE_DBUS
54 #include <gio/gio.h>
55
56 static const char *DBUS_NAME = "org.selinux.Restorecond";
57
on_name_acquired(GDBusConnection * connection G_GNUC_UNUSED,const gchar * name,gpointer user_data G_GNUC_UNUSED)58 static void on_name_acquired(GDBusConnection *connection G_GNUC_UNUSED,
59 const gchar *name,
60 gpointer user_data G_GNUC_UNUSED)
61 {
62 if (debug_mode)
63 g_print("D-Bus name acquired: %s\n", name);
64 }
65
on_name_lost(GDBusConnection * connection G_GNUC_UNUSED,const gchar * name,gpointer user_data)66 static void on_name_lost(GDBusConnection *connection G_GNUC_UNUSED,
67 const gchar *name,
68 gpointer user_data)
69 {
70 /* Exit when the D-Bus connection closes */
71 GMainLoop *loop = user_data;
72
73 if (debug_mode)
74 g_print("D-Bus name lost (%s), exiting\n", name);
75 g_main_loop_quit(loop);
76 }
77
78 /**
79 * Try starting a D-Bus server on the session bus.
80 * Returns -1 if the connection failed, so that a local server can be launched
81 */
dbus_server(GMainLoop * loop)82 static int dbus_server(GMainLoop *loop)
83 {
84 GDBusConnection *bus;
85 guint client_id;
86
87 bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
88 if (!bus)
89 return -1;
90
91 client_id = g_bus_own_name_on_connection(
92 bus,
93 DBUS_NAME,
94 G_BUS_NAME_OWNER_FLAGS_NONE,
95 on_name_acquired,
96 on_name_lost,
97 loop,
98 NULL);
99 g_object_unref(bus);
100 if (client_id == 0)
101 return -1;
102
103 return 0;
104 }
105
106 #endif
107
108 /* size of the event structure, not counting name */
109 #define EVENT_SIZE (sizeof (struct inotify_event))
110 /* reasonable guess as to size of 1024 events */
111 #define BUF_LEN (1024 * (EVENT_SIZE + 16))
112
113 static gboolean
io_channel_callback(GIOChannel * source,GIOCondition condition,gpointer data)114 io_channel_callback
115 (GIOChannel *source,
116 GIOCondition condition,
117 gpointer data __attribute__((__unused__)))
118 {
119
120 char buffer[BUF_LEN+1];
121 gsize bytes_read;
122 unsigned int i = 0;
123
124 if (condition & G_IO_IN) {
125 /* Data is available. */
126 g_io_channel_read_chars
127 (source, buffer,
128 sizeof (buffer),
129 &bytes_read, NULL);
130
131 if (! bytes_read) {
132 /* Session/Terminal Ended */
133 exit(0);
134 }
135
136 while (i < bytes_read) {
137 struct inotify_event *event;
138 event = (struct inotify_event *)&buffer[i];
139 if (debug_mode)
140 printf("wd=%d mask=%u cookie=%u len=%u\n",
141 event->wd, event->mask,
142 event->cookie, event->len);
143 if (event->len)
144 watch_list_find(event->wd, event->name);
145
146 i += EVENT_SIZE + event->len;
147 }
148 }
149
150 /* An error happened while reading
151 the file. */
152
153 if (condition & G_IO_NVAL)
154 return FALSE;
155
156 /* We have reached the end of the
157 file. */
158
159 if (condition & G_IO_HUP) {
160 g_io_channel_shutdown (source, 0, NULL);
161 exit(0);
162 return FALSE;
163 }
164
165 /* Returning TRUE will make sure
166 the callback remains associated
167 to the channel. */
168
169 return TRUE;
170 }
171
start(void)172 int start(void) {
173 #ifdef HAVE_DBUS
174 GDBusConnection *bus;
175 GError *err = NULL;
176 GVariant *result;
177
178 /* Get a connection to the session bus */
179 bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &err);
180 if (!bus) {
181 if (debug_mode)
182 g_warning("Failed to connect to the D-BUS daemon: %s", err->message);
183 g_error_free(err);
184 return 1;
185 }
186
187 /* Start restorecond D-Bus service by pinging its bus name
188 *
189 * https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-peer
190 */
191 result = g_dbus_connection_call_sync(bus,
192 DBUS_NAME, /* bus name */
193 "/", /* object path */
194 "org.freedesktop.DBus.Peer", /* interface */
195 "Ping", /* method */
196 NULL, /* parameters */
197 NULL, /* reply_type */
198 G_DBUS_CALL_FLAGS_NONE,
199 -1, /* timeout_msec */
200 NULL,
201 &err);
202 if (!result) {
203 g_object_unref(bus);
204 if (debug_mode)
205 g_warning("Failed to start %s: %s", DBUS_NAME, err->message);
206 g_error_free(err);
207 return 1;
208 }
209 g_object_unref(bus);
210 #endif /* HAVE_DBUS */
211 return 0;
212 }
213
local_server(void)214 static int local_server(void) {
215 // ! dbus, run as local service
216 char *ptr=NULL;
217 if (asprintf(&ptr, "%s/.restorecond", homedir) < 0) {
218 if (debug_mode)
219 perror("asprintf");
220 return -1;
221 }
222 local_lock_fd = open(ptr, O_CREAT | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, S_IRUSR | S_IWUSR);
223 if (debug_mode)
224 g_warning ("Lock file: %s", ptr);
225
226 free(ptr);
227 if (local_lock_fd < 0) {
228 if (debug_mode)
229 perror("open");
230 return -1;
231 }
232 if (flock(local_lock_fd, LOCK_EX | LOCK_NB) < 0) {
233 if (debug_mode)
234 perror("flock");
235 close(local_lock_fd);
236 local_lock_fd = -1;
237 return -1;
238 }
239 /* watch for stdin/terminal going away */
240 GIOChannel *in = g_io_channel_unix_new(0);
241 g_io_add_watch_full( in,
242 G_PRIORITY_HIGH,
243 G_IO_IN|G_IO_ERR|G_IO_HUP,
244 io_channel_callback, NULL, NULL);
245
246 return 0;
247 }
248
end_local_server(void)249 static void end_local_server(void) {
250 if (local_lock_fd >= 0)
251 close(local_lock_fd);
252 local_lock_fd = -1;
253 }
254
sigterm_handler(gpointer user_data)255 static int sigterm_handler(gpointer user_data)
256 {
257 GMainLoop *loop = user_data;
258
259 if (debug_mode)
260 g_print("Received SIGTERM, exiting\n");
261 g_main_loop_quit(loop);
262 return FALSE;
263 }
264
265
server(int master_fd,const char * watch_file)266 int server(int master_fd, const char *watch_file) {
267 GMainLoop *loop;
268
269 loop = g_main_loop_new (NULL, FALSE);
270
271 #ifdef HAVE_DBUS
272 if (dbus_server(loop) != 0)
273 #endif /* HAVE_DBUS */
274 if (local_server())
275 goto end;
276
277 read_config(master_fd, watch_file);
278
279 if (watch_list_isempty())
280 goto end;
281
282 set_matchpathcon_flags(MATCHPATHCON_NOTRANS);
283
284 GIOChannel *c = g_io_channel_unix_new(master_fd);
285
286 g_io_add_watch_full(c,
287 G_PRIORITY_HIGH,
288 G_IO_IN|G_IO_ERR|G_IO_HUP,
289 io_channel_callback, NULL, NULL);
290
291 /* Handle SIGTERM */
292 g_unix_signal_add_full(G_PRIORITY_DEFAULT,
293 SIGTERM,
294 sigterm_handler,
295 loop,
296 NULL);
297
298 g_main_loop_run (loop);
299
300 end:
301 end_local_server();
302 g_main_loop_unref (loop);
303 return 0;
304 }
305
306