Home | History | Annotate | Download | only in restorecond
      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 
     44 int watch_list_isempty(void) {
     45 	return firstDir == NULL;
     46 }
     47 
     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 
    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 
    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 */
    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 
    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 
    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