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