• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #define _GNU_SOURCE
2 #include <sys/inotify.h>
3 #include <errno.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <unistd.h>
8 #include <ctype.h>
9 #include <sys/types.h>
10 #include <syslog.h>
11 #include "restore.h"
12 #include <glob.h>
13 #include <libgen.h>
14 #include <sys/stat.h>
15 #include <string.h>
16 #include <stdio.h>
17 #include <fcntl.h>
18 #include <selinux/selinux.h>
19 #include "restorecond.h"
20 #include "stringslist.h"
21 #include "utmpwatcher.h"
22 
23 #ifndef GLOB_TILDE
24 #define GLOB_TILDE 0
25 #endif
26 
27 #ifndef GLOB_BRACE
28 #define GLOB_BRACE 0
29 #endif
30 
31 /* size of the event structure, not counting name */
32 #define EVENT_SIZE  (sizeof (struct inotify_event))
33 /* reasonable guess as to size of 1024 events */
34 #define BUF_LEN        (1024 * (EVENT_SIZE + 16))
35 
36 struct watchList {
37 	struct watchList *next;
38 	int wd;
39 	char *dir;
40 	struct stringsList *files;
41 };
42 struct watchList *firstDir = NULL;
43 
watch_list_isempty(void)44 int watch_list_isempty(void) {
45 	return firstDir == NULL;
46 }
47 
watch_list_add(int fd,const char * path)48 void watch_list_add(int fd, const char *path)
49 {
50 	struct watchList *ptr = NULL;
51 	size_t i = 0;
52 	struct watchList *prev = NULL;
53 	glob_t globbuf;
54 	char *x = strdup(path);
55 	if (!x) exitApp("Out of Memory");
56 	char *file = basename(x);
57 	char *dir = dirname(x);
58 	ptr = firstDir;
59 	int len;
60 
61 	globbuf.gl_offs = 1;
62 	if (glob(path,
63 		 GLOB_TILDE | GLOB_PERIOD,
64 		 NULL,
65 		 &globbuf) >= 0) {
66 		for (i = 0; i < globbuf.gl_pathc; i++) {
67 			len = strlen(globbuf.gl_pathv[i]) - 2;
68 			if (len > 0 &&
69 			    strcmp(&globbuf.gl_pathv[i][len--], "/.") == 0)
70 				continue;
71 			if (len > 0 &&
72 			    strcmp(&globbuf.gl_pathv[i][len], "/..") == 0)
73 				continue;
74 			selinux_restorecon(globbuf.gl_pathv[i],
75 					   r_opts.restorecon_flags);
76 		}
77 		globfree(&globbuf);
78 	}
79 
80 	while (ptr != NULL) {
81 		if (strcmp(dir, ptr->dir) == 0) {
82 			strings_list_add(&ptr->files, file);
83 			goto end;
84 		}
85 		prev = ptr;
86 		ptr = ptr->next;
87 	}
88 	ptr = calloc(1, sizeof(struct watchList));
89 
90 	if (!ptr) exitApp("Out of Memory");
91 
92 	ptr->wd = inotify_add_watch(fd, dir, IN_CREATE | IN_MOVED_TO);
93 	if (ptr->wd == -1) {
94 		free(ptr);
95 		if (! run_as_user)
96 			syslog(LOG_ERR, "Unable to watch (%s) %s\n",
97 			       path, strerror(errno));
98 		goto end;
99 	}
100 
101 	ptr->dir = strdup(dir);
102 	if (!ptr->dir)
103 		exitApp("Out of Memory");
104 
105 	strings_list_add(&ptr->files, file);
106 	if (prev)
107 		prev->next = ptr;
108 	else
109 		firstDir = ptr;
110 
111 	if (debug_mode)
112 		printf("%d: Dir=%s, File=%s\n", ptr->wd, ptr->dir, file);
113 
114 end:
115 	free(x);
116 	return;
117 }
118 
119 /*
120    A file was in a direcroty has been created. This function checks to
121    see if it is one that we are watching.
122 */
123 
watch_list_find(int wd,const char * file)124 int watch_list_find(int wd, const char *file)
125 {
126 	struct watchList *ptr = NULL;
127 
128 	ptr = firstDir;
129 
130 	if (debug_mode)
131 		printf("%d: File=%s\n", wd, file);
132 	while (ptr != NULL) {
133 		if (ptr->wd == wd) {
134 			int exact=0;
135 			if (strings_list_find(ptr->files, file, &exact) == 0) {
136 				char *path = NULL;
137 				if (asprintf(&path, "%s/%s", ptr->dir, file) <
138 				    0)
139 					exitApp("Error allocating memory.");
140 
141 				selinux_restorecon(path,
142 						   r_opts.restorecon_flags);
143 				free(path);
144 				return 0;
145 			}
146 			if (debug_mode)
147 				strings_list_print(ptr->files);
148 
149 			/* Not found in this directory */
150 			return -1;
151 		}
152 		ptr = ptr->next;
153 	}
154 	/* Did not find a directory */
155 	return -1;
156 }
157 
watch_list_free(int fd)158 void watch_list_free(int fd)
159 {
160 	struct watchList *ptr = NULL;
161 	struct watchList *prev = NULL;
162 	ptr = firstDir;
163 
164 	while (ptr != NULL) {
165 		inotify_rm_watch(fd, ptr->wd);
166 		strings_list_free(ptr->files);
167 		free(ptr->dir);
168 		prev = ptr;
169 		ptr = ptr->next;
170 		free(prev);
171 	}
172 	firstDir = NULL;
173 }
174 
175 /*
176    Inotify watch loop
177 */
watch(int fd,const char * watch_file)178 int watch(int fd, const char *watch_file)
179 {
180 	char buf[BUF_LEN];
181 	int len, i = 0;
182 	if (firstDir == NULL) return 0;
183 
184 	len = read(fd, buf, BUF_LEN);
185 	if (len < 0) {
186 		if (terminate == 0) {
187 			syslog(LOG_ERR, "Read error (%s)", strerror(errno));
188 			return 0;
189 		}
190 		syslog(LOG_INFO, "terminated");
191 		return -1;
192 	} else if (!len)
193 		/* BUF_LEN too small? */
194 		return -1;
195 	while (i < len) {
196 		struct inotify_event *event;
197 		event = (struct inotify_event *)&buf[i];
198 		if (debug_mode)
199 			printf("wd=%d mask=%u cookie=%u len=%u\n",
200 			       event->wd, event->mask,
201 			       event->cookie, event->len);
202 		if (event->mask & ~IN_IGNORED) {
203 			if (event->wd == master_wd)
204 				read_config(fd, watch_file);
205 			else {
206 				switch (utmpwatcher_handle(fd, event->wd)) {
207 				case -1:	/* Message was not for utmpwatcher */
208 					if (event->len)
209 						watch_list_find(event->wd, event->name);
210 					break;
211 				case 1:	/* utmp has changed need to reload */
212 					read_config(fd, watch_file);
213 					break;
214 
215 				default:	/* No users logged in or out */
216 					break;
217 				}
218 			}
219 		}
220 
221 		i += EVENT_SIZE + event->len;
222 	}
223 	return 0;
224 }
225 
process_config(int fd,FILE * cfg)226 static void process_config(int fd, FILE * cfg)
227 {
228 	char *line_buf = NULL;
229 	size_t len = 0;
230 
231 	while (getline(&line_buf, &len, cfg) > 0) {
232 		char *buffer = line_buf;
233 		while (isspace(*buffer))
234 			buffer++;
235 		if (buffer[0] == '#')
236 			continue;
237 		int l = strlen(buffer) - 1;
238 		if (l <= 0)
239 			continue;
240 		buffer[l] = 0;
241 		if (buffer[0] == '~') {
242 			if (run_as_user) {
243 				char *ptr=NULL;
244 				if (asprintf(&ptr, "%s%s", homedir, &buffer[1]) < 0)
245 					exitApp("Error allocating memory.");
246 
247 				watch_list_add(fd, ptr);
248 				free(ptr);
249 			} else {
250 				utmpwatcher_add(fd, &buffer[1]);
251 			}
252 		} else {
253 			watch_list_add(fd, buffer);
254 		}
255 	}
256 	free(line_buf);
257 }
258 
259 /*
260    Read config file ignoring Comment lines
261    Files specified one per line.  Files with "~" will be expanded to the logged in users
262    homedirs.
263 */
264 
read_config(int fd,const char * watch_file_path)265 void read_config(int fd, const char *watch_file_path)
266 {
267 
268 	FILE *cfg = NULL;
269 	if (debug_mode)
270 		printf("Read Config\n");
271 
272 	watch_list_free(fd);
273 
274 	cfg = fopen(watch_file_path, "r");
275 	if (!cfg){
276 		perror(watch_file_path);
277 		exitApp("Error reading config file");
278 	}
279 	process_config(fd, cfg);
280 	fclose(cfg);
281 
282 	inotify_rm_watch(fd, master_wd);
283 	master_wd =
284 	    inotify_add_watch(fd, watch_file_path, IN_MOVED_FROM | IN_MODIFY);
285 	if (master_wd == -1)
286 		exitApp("Error watching config file.");
287 }
288