libselinux: Ignore the stem when looking up all matches in file context
diff mbox series

Message ID 20190417180955.136942-1-xunchang@google.com
State Accepted
Headers show
Series
  • libselinux: Ignore the stem when looking up all matches in file context
Related show

Commit Message

xunchang April 17, 2019, 6:09 p.m. UTC
This is a follow up fix to the restorecon change in
commit 6ab5fbaabc84f7093b37c1afae855292e918090f This change has been
tested in android for a while.

The stem is a list of top level directory (without regex metachar)
covered in the file context. And it constructs from finding the
second '/' in the regex_string; and aims to speed up the lookup by
skipping unnecessary regex matches. More contexts in
https://lore.kernel.org/selinux/200309231522.25749.russell@coker.com.au/

However, this caused some issue when we try to find all the partial
matches for a root directory. For example, the path "/data" doesn't
have a stem while the regex "/data/misc/(/.*)?" has "/data" as the
stem. As a result, all the regex for the subdirs of /data will not
considered as a match for "/data". And the restorecon will wrongly
skip on top level "/data" when there's a context change to one of
subdir.

This CL always includes the stem when compiling the regex in all
circumstances. Also, it ignores the stem id check in the "match all"
case, while the behavior for the single match stays unchanged. I will
collect more data to find out if stem id check is still necessary at
all with the new restorecon logic.

Test: run restorecon on "/data"; change the context of one subdir and
run again, and the context is restored on that subdir; search the caller
of regex_match

Signed-off-by: Tianjie Xu <xunchang@google.com>
---
 libselinux/src/label_file.c | 27 ++++++++++++---------------
 libselinux/src/label_file.h | 10 ++--------
 2 files changed, 14 insertions(+), 23 deletions(-)

Patch
diff mbox series

diff --git a/libselinux/src/label_file.c b/libselinux/src/label_file.c
index 90cbd666..37b037a0 100644
--- a/libselinux/src/label_file.c
+++ b/libselinux/src/label_file.c
@@ -39,18 +39,17 @@  static int get_stem_from_file_name(const char *const buf)
 
 /* find the stem of a file name, returns the index into stem_arr (or -1 if
  * there is no match - IE for a file in the root directory or a regex that is
- * too complex for us).  Makes buf point to the text AFTER the stem. */
-static int find_stem_from_file(struct saved_data *data, const char **buf)
+ * too complex for us). */
+static int find_stem_from_file(struct saved_data *data, const char *key)
 {
 	int i;
-	int stem_len = get_stem_from_file_name(*buf);
+	int stem_len = get_stem_from_file_name(key);
 
 	if (!stem_len)
 		return -1;
 	for (i = 0; i < data->num_stems; i++) {
 		if (stem_len == data->stem_arr[i].len
-		    && !strncmp(*buf, data->stem_arr[i].buf, stem_len)) {
-			*buf += stem_len;
+		    && !strncmp(key, data->stem_arr[i].buf, stem_len)) {
 			return i;
 		}
 	}
@@ -856,7 +855,6 @@  static const struct spec **lookup_all(struct selabel_handle *rec,
 	struct spec *spec_arr = data->spec_arr;
 	int i, rc, file_stem;
 	mode_t mode = (mode_t)type;
-	const char *buf;
 	char *clean_key = NULL;
 	const char *prev_slash, *next_slash;
 	unsigned int sofar = 0;
@@ -900,8 +898,7 @@  static const struct spec **lookup_all(struct selabel_handle *rec,
 	if (sub)
 		key = sub;
 
-	buf = key;
-	file_stem = find_stem_from_file(data, &buf);
+	file_stem = find_stem_from_file(data, key);
 	mode &= S_IFMT;
 
 	/*
@@ -914,15 +911,15 @@  static const struct spec **lookup_all(struct selabel_handle *rec,
 		 * stem as the file AND if the spec in question has no mode
 		 * specified or if the mode matches the file mode then we do
 		 * a regex check        */
-		if ((spec->stem_id == -1 || spec->stem_id == file_stem) &&
+		bool stem_matches = spec->stem_id == -1 || spec->stem_id == file_stem;
+		// Don't check the stem if we want to find partial matches.
+                // Otherwise the case "/abc/efg/(/.*)?" will be considered
+                //a miss for "/abc".
+		if ((partial || stem_matches) &&
 				(!mode || !spec->mode || mode == spec->mode)) {
-			if (compile_regex(data, spec, NULL) < 0)
+			if (compile_regex(spec, NULL) < 0)
 				goto finish;
-			if (spec->stem_id == -1)
-				rc = regex_match(spec->regex, key, partial);
-			else
-				rc = regex_match(spec->regex, buf, partial);
-
+			rc = regex_match(spec->regex, key, partial);
 			if (rc == REGEX_MATCH || (partial && rc == REGEX_MATCH_PARTIAL)) {
 				if (rc == REGEX_MATCH) {
 					spec->matches++;
diff --git a/libselinux/src/label_file.h b/libselinux/src/label_file.h
index 47859baf..6f4ee101 100644
--- a/libselinux/src/label_file.h
+++ b/libselinux/src/label_file.h
@@ -336,13 +336,11 @@  static inline int next_entry(void *buf, struct mmap_area *fp, size_t bytes)
 	return 0;
 }
 
-static inline int compile_regex(struct saved_data *data, struct spec *spec,
-					    const char **errbuf)
+static inline int compile_regex(struct spec *spec, const char **errbuf)
 {
 	char *reg_buf, *anchored_regex, *cp;
 	struct regex_error_data error_data;
 	static char regex_error_format_buffer[256];
-	struct stem *stem_arr = data->stem_arr;
 	size_t len;
 	int rc;
 	bool regex_compiled;
@@ -379,11 +377,7 @@  static inline int compile_regex(struct saved_data *data, struct spec *spec,
 		return 0;
 	}
 
-	/* Skip the fixed stem. */
 	reg_buf = spec->regex_str;
-	if (spec->stem_id >= 0)
-		reg_buf += stem_arr[spec->stem_id].len;
-
 	/* Anchor the regular expression. */
 	len = strlen(reg_buf);
 	cp = anchored_regex = malloc(len + 3);
@@ -501,7 +495,7 @@  static inline int process_line(struct selabel_handle *rec,
 	data->nspec++;
 
 	if (rec->validating
-			&& compile_regex(data, &spec_arr[nspec], &errbuf)) {
+			&& compile_regex(&spec_arr[nspec], &errbuf)) {
 		COMPAT_LOG(SELINUX_ERROR,
 			   "%s:  line %u has invalid regex %s:  %s\n",
 			   path, lineno, regex, errbuf);