1 /*
2  * restorecond
3  *
4  * Copyright (C) 2006-2009 Red Hat
5  * see file 'COPYING' for use and warranty information
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 of
10  * the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16 .*
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20  * 02111-1307  USA
21  *
22  * Authors:
23  *   Dan Walsh <dwalsh@redhat.com>
24  *
25 */
26 
27 #define _GNU_SOURCE
28 #include <sys/inotify.h>
29 #include <errno.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <signal.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <ctype.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <syslog.h>
39 #include <limits.h>
40 #include <fcntl.h>
41 
42 #include "restorecond.h"
43 #include "stringslist.h"
44 #include <glib.h>
45 #ifdef HAVE_DBUS
46 #include <dbus/dbus.h>
47 #include <dbus/dbus-glib.h>
48 #include <dbus/dbus-glib-lowlevel.h>
49 
50 static DBusHandlerResult signal_filter (DBusConnection *connection, DBusMessage *message, void *user_data);
51 
52 static const char *PATH="/org/selinux/Restorecond";
53 //static const char *BUSNAME="org.selinux.Restorecond";
54 static const char *INTERFACE="org.selinux.RestorecondIface";
55 static const char *RULE="type='signal',interface='org.selinux.RestorecondIface'";
56 
57 static int local_lock_fd = -1;
58 
59 static DBusHandlerResult
signal_filter(DBusConnection * connection,DBusMessage * message,void * user_data)60 signal_filter (DBusConnection *connection  __attribute__ ((__unused__)), DBusMessage *message, void *user_data)
61 {
62   /* User data is the event loop we are running in */
63   GMainLoop *loop = user_data;
64 
65   /* A signal from the bus saying we are about to be disconnected */
66   if (dbus_message_is_signal
67         (message, INTERFACE, "Stop")) {
68 
69       /* Tell the main loop to quit */
70       g_main_loop_quit (loop);
71       /* We have handled this message, don't pass it on */
72       return DBUS_HANDLER_RESULT_HANDLED;
73   }
74   /* A Ping signal on the com.burtonini.dbus.Signal interface */
75   else if (dbus_message_is_signal (message, INTERFACE, "Start")) {
76     DBusError error;
77     dbus_error_init (&error);
78     g_print("Start received\n");
79     return DBUS_HANDLER_RESULT_HANDLED;
80   }
81   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
82 }
83 
dbus_server(GMainLoop * loop)84 static int dbus_server(GMainLoop *loop) {
85     DBusConnection *bus;
86     DBusError error;
87     dbus_error_init (&error);
88     bus = dbus_bus_get (DBUS_BUS_SESSION, &error);
89     if (bus) {
90 	dbus_connection_setup_with_g_main (bus, NULL);
91 
92 	/* listening to messages from all objects as no path is specified */
93 	dbus_bus_add_match (bus, RULE, &error); // see signals from the given interfacey
94 	dbus_connection_add_filter (bus, signal_filter, loop, NULL);
95 	return 0;
96     }
97     return -1;
98 }
99 
100 #endif
101 #include <selinux/selinux.h>
102 #include <sys/file.h>
103 
104 /* size of the event structure, not counting name */
105 #define EVENT_SIZE  (sizeof (struct inotify_event))
106 /* reasonable guess as to size of 1024 events */
107 #define BUF_LEN        (1024 * (EVENT_SIZE + 16))
108 
109 static gboolean
io_channel_callback(GIOChannel * source,GIOCondition condition,gpointer data)110 io_channel_callback
111  (GIOChannel *source,
112   GIOCondition condition,
113   gpointer data __attribute__((__unused__)))
114 {
115 
116   char buffer[BUF_LEN+1];
117   gsize bytes_read;
118   unsigned int i = 0;
119 
120   if (condition & G_IO_IN) {
121     /* Data is available. */
122     g_io_channel_read_chars
123       (source, buffer,
124        sizeof (buffer),
125        &bytes_read, NULL);
126 
127     if (! bytes_read) {
128 	    /* Session/Terminal Ended */
129 	    exit(0);
130     }
131 
132     while (i < bytes_read) {
133 	    struct inotify_event *event;
134 	    event = (struct inotify_event *)&buffer[i];
135 	    if (debug_mode)
136 		    printf("wd=%d mask=%u cookie=%u len=%u\n",
137 			   event->wd, event->mask,
138 			   event->cookie, event->len);
139 	    if (event->len)
140 		    watch_list_find(event->wd, event->name);
141 
142 	    i += EVENT_SIZE + event->len;
143     }
144   }
145 
146   /* An error happened while reading
147      the file. */
148 
149   if (condition & G_IO_NVAL)
150     return FALSE;
151 
152   /* We have reached the end of the
153      file. */
154 
155   if (condition & G_IO_HUP) {
156     g_io_channel_shutdown (source, 0, NULL);
157     exit(0);
158     return FALSE;
159   }
160 
161   /* Returning TRUE will make sure
162      the callback remains associated
163      to the channel. */
164 
165   return TRUE;
166 }
167 
start()168 int start() {
169 #ifdef HAVE_DBUS
170 	DBusConnection *bus;
171 	DBusError error;
172 	DBusMessage *message;
173 
174 	/* Get a connection to the session bus */
175 	dbus_error_init (&error);
176 	bus = dbus_bus_get (DBUS_BUS_SESSION, &error);
177 	if (!bus) {
178 		if (debug_mode)
179 			g_warning ("Failed to connect to the D-BUS daemon: %s", error.message);
180 		dbus_error_free (&error);
181 		return 1;
182 	}
183 
184 
185 	/* Create a new signal "Start" on the interface,
186 	 * from the object  */
187 	message = dbus_message_new_signal (PATH,
188 					   INTERFACE, "Start");
189 	/* Send the signal */
190 	dbus_connection_send (bus, message, NULL);
191 	/* Free the signal now we have finished with it */
192 	dbus_message_unref (message);
193 #endif /* HAVE_DBUS */
194 	return 0;
195 }
196 
local_server(void)197 static int local_server(void) {
198 	// ! dbus, run as local service
199 	char *ptr=NULL;
200 	if (asprintf(&ptr, "%s/.restorecond", homedir) < 0) {
201 		if (debug_mode)
202 			perror("asprintf");
203 		return -1;
204 	}
205 	local_lock_fd = open(ptr, O_CREAT | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, S_IRUSR | S_IWUSR);
206 	if (debug_mode)
207 		g_warning ("Lock file: %s", ptr);
208 
209 	free(ptr);
210 	if (local_lock_fd < 0) {
211 		if (debug_mode)
212 			perror("open");
213 		return -1;
214 	}
215 	if (flock(local_lock_fd, LOCK_EX | LOCK_NB) < 0) {
216 		close(local_lock_fd);
217 		if (debug_mode)
218 			perror("flock");
219 		return -1;
220 	}
221 	/* watch for stdin/terminal going away */
222 	GIOChannel *in = g_io_channel_unix_new(0);
223 	g_io_add_watch_full( in,
224 			     G_PRIORITY_HIGH,
225 			     G_IO_IN|G_IO_ERR|G_IO_HUP,
226 			     io_channel_callback, NULL, NULL);
227 
228 	return 0;
229 }
230 
end_local_server(void)231 static void end_local_server(void) {
232 	if (local_lock_fd >= 0)
233 		close(local_lock_fd);
234 	local_lock_fd = -1;
235 }
236 
server(int master_fd,const char * watch_file)237 int server(int master_fd, const char *watch_file) {
238     GMainLoop *loop;
239 
240     loop = g_main_loop_new (NULL, FALSE);
241 
242 #ifdef HAVE_DBUS
243     if (dbus_server(loop) != 0)
244 #endif /* HAVE_DBUS */
245 	    if (local_server())
246 		    goto end;
247 
248     read_config(master_fd, watch_file);
249 
250     if (watch_list_isempty()) goto end;
251 
252     set_matchpathcon_flags(MATCHPATHCON_NOTRANS);
253 
254     GIOChannel *c = g_io_channel_unix_new(master_fd);
255 
256     g_io_add_watch_full( c,
257 			 G_PRIORITY_HIGH,
258 			 G_IO_IN|G_IO_ERR|G_IO_HUP,
259 			 io_channel_callback, NULL, NULL);
260 
261     g_main_loop_run (loop);
262 
263 end:
264     end_local_server();
265     g_main_loop_unref (loop);
266     return 0;
267 }
268 
269