xref: /aosp_15_r20/external/selinux/restorecond/user.c (revision 2d543d20722ada2425b5bdab9d0d1d29470e7bba)
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