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