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 "../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 
     37 int watch_list_isempty(void) {
     38 	return firstDir == NULL;
     39 }
     40 
     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 
    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 
    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 */
    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 
    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 
    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