tomoyo: Loosen pathname/domainname validation.
authorTetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Wed, 9 Sep 2020 11:05:58 +0000 (20:05 +0900)
committerTetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Mon, 12 Oct 2020 10:53:34 +0000 (19:53 +0900)
Since commit e2dc9bf3f5275ca3 ("umd: Transform fork_usermode_blob into
fork_usermode_driver") started calling execve() on a program written in
a local mount which is not connected to mount tree,
tomoyo_realpath_from_path() started returning a pathname in
"$fsname:/$pathname" format which violates TOMOYO's domainname rule that
it must start with "<$namespace>" followed by zero or more repetitions of
pathnames which start with '/'.

Since $fsname must not contain '.' since commit 79c0b2df79eb56fc ("add
filesystem subtype support"), tomoyo_correct_path() can recognize a token
which appears '/' before '.' appears (e.g. proc:/self/exe ) as a pathname
while rejecting a token which appears '.' before '/' appears (e.g.
exec.realpath="/bin/bash" ) as a condition parameter.

Therefore, accept domainnames which contain pathnames which do not start
with '/' but contain '/' before '.' (e.g. <kernel> tmpfs:/bpfilter_umh ).

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
security/tomoyo/util.c

index eba0b33..a40abb0 100644 (file)
@@ -143,6 +143,8 @@ char *tomoyo_read_token(struct tomoyo_acl_param *param)
        return pos;
 }
 
+static bool tomoyo_correct_path2(const char *filename, const size_t len);
+
 /**
  * tomoyo_get_domainname - Read a domainname from a line.
  *
@@ -157,10 +159,10 @@ const struct tomoyo_path_info *tomoyo_get_domainname
        char *pos = start;
 
        while (*pos) {
-               if (*pos++ != ' ' || *pos++ == '/')
+               if (*pos++ != ' ' ||
+                   tomoyo_correct_path2(pos, strchrnul(pos, ' ') - pos))
                        continue;
-               pos -= 2;
-               *pos++ = '\0';
+               *(pos - 1) = '\0';
                break;
        }
        param->data = pos;
@@ -514,6 +516,22 @@ bool tomoyo_correct_word(const char *string)
 }
 
 /**
+ * tomoyo_correct_path2 - Check whether the given pathname follows the naming rules.
+ *
+ * @filename: The pathname to check.
+ * @len:      Length of @filename.
+ *
+ * Returns true if @filename follows the naming rules, false otherwise.
+ */
+static bool tomoyo_correct_path2(const char *filename, const size_t len)
+{
+       const char *cp1 = memchr(filename, '/', len);
+       const char *cp2 = memchr(filename, '.', len);
+
+       return cp1 && (!cp2 || (cp1 < cp2)) && tomoyo_correct_word2(filename, len);
+}
+
+/**
  * tomoyo_correct_path - Validate a pathname.
  *
  * @filename: The pathname to check.
@@ -523,7 +541,7 @@ bool tomoyo_correct_word(const char *string)
  */
 bool tomoyo_correct_path(const char *filename)
 {
-       return *filename == '/' && tomoyo_correct_word(filename);
+       return tomoyo_correct_path2(filename, strlen(filename));
 }
 
 /**
@@ -545,8 +563,7 @@ bool tomoyo_correct_domain(const unsigned char *domainname)
 
                if (!cp)
                        break;
-               if (*domainname != '/' ||
-                   !tomoyo_correct_word2(domainname, cp - domainname))
+               if (!tomoyo_correct_path2(domainname, cp - domainname))
                        return false;
                domainname = cp + 1;
        }