From 4ad18969546c16bd78206799de642af6eb2293ea Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 29 Jun 2011 00:11:17 -0400 Subject: [PATCH] libselinux: resolv symlinks and dot directories before matching paths matchpathcon cannot handle ./ or ../ in pathnames and doesn't do well with symlinks. This patch uses the glibc function realpath() to try to determine a real path with resolved symlinks and dot directories. For example before this pach we would see: $ matchpathcon /tmp/../eric /tmp/../eric <> $ matchpathcon /eric /eric system_u:object_r:default_t:s0 Whereas after the path we get the same results. The one quirk with the patch is that we need special code to make sure that realpath() does not follow a symlink if it is the final component. aka if we have a symlink from /eric to /tmp/eric we do not want to resolv to /tmp/eric. We want to just resolv to the actual symlink /eric. Signed-off-by: Eric Paris Acked-by: Dan Walsh --- libselinux/utils/matchpathcon.c | 105 +++++++++++++++++++++++++++----- 1 file changed, 90 insertions(+), 15 deletions(-) diff --git a/libselinux/utils/matchpathcon.c b/libselinux/utils/matchpathcon.c index 4453a885..3ecd52f8 100644 --- a/libselinux/utils/matchpathcon.c +++ b/libselinux/utils/matchpathcon.c @@ -4,10 +4,14 @@ #include #include #include +#include #include #include #include #include +#include +#include + void usage(const char *progname) { @@ -39,6 +43,63 @@ int printmatchpathcon(char *path, int header, int mode) return 0; } +/* + * We do not want to resolve a symlink to a real path if it is the final + * component of the name. Thus we split the pathname on the last "/" and + * determine a real path component of the first portion. We then have to + * copy the last part back on to get the final real path. Wheww. + */ +static int symlink_realpath(char *name, char *resolved_path) +{ + char *last_component; + char *tmp_path, *p; + size_t len = 0; + int rc = 0; + + tmp_path = strdup(name); + if (!tmp_path) { + fprintf(stderr, "symlink_realpath(%s) strdup() failed: %s\n", + name, strerror(errno)); + rc = -1; + goto out; + } + + last_component = strrchr(tmp_path, '/'); + + if (last_component == tmp_path) { + last_component++; + p = strcpy(resolved_path, "/"); + } else if (last_component) { + *last_component = '\0'; + last_component++; + p = realpath(tmp_path, resolved_path); + } else { + last_component = tmp_path; + p = realpath("./", resolved_path); + } + + if (!p) { + fprintf(stderr, "symlink_realpath(%s) realpath() failed: %s\n", + name, strerror(errno)); + rc = -1; + goto out; + } + + len = strlen(p); + if (len + strlen(last_component) + 1 > PATH_MAX) { + fprintf(stderr, "symlink_realpath(%s) failed: Filename too long \n", + name); + rc = -1; + goto out; + } + + resolved_path += len; + strcpy(resolved_path, last_component); +out: + free(tmp_path); + return rc; +} + int main(int argc, char **argv) { int i, init = 0; @@ -103,48 +164,62 @@ int main(int argc, char **argv) } } for (i = optind; i < argc; i++) { - int mode = 0; + int rc, mode = 0; struct stat buf; - int len = strlen(argv[i]); - if (len > 1 && argv[i][len - 1 ] == '/') { - argv[i][len - 1 ] = '\0'; - } + char *p, *path = argv[i]; + char stackpath[PATH_MAX + 1]; + int len = strlen(path); + if (len > 1 && path[len - 1 ] == '/') + path[len - 1 ] = '\0'; - if (lstat(argv[i], &buf) == 0) + if (lstat(path, &buf) == 0) mode = buf.st_mode; + if (S_ISLNK(mode)) { + rc = symlink_realpath(path, stackpath); + if (!rc) + path = stackpath; + } else { + p = realpath(path, stackpath); + if (p) + path = p; + } + if (verify) { + rc = selinux_file_context_verify(path, mode); + if (quiet) { - if (selinux_file_context_verify(argv[i], mode)) + if (rc) continue; else exit(1); } - if (selinux_file_context_verify(argv[i], mode)) { - printf("%s verified.\n", argv[i]); + + if (rc) { + printf("%s verified.\n", path); } else { security_context_t con; int rc; error = 1; if (notrans) - rc = lgetfilecon_raw(argv[i], &con); + rc = lgetfilecon_raw(path, &con); else - rc = lgetfilecon(argv[i], &con); + rc = lgetfilecon(path, &con); if (rc >= 0) { printf("%s has context %s, should be ", - argv[i], con); - printmatchpathcon(argv[i], 0, mode); + path, con); + printmatchpathcon(path, 0, mode); freecon(con); } else { printf ("actual context unknown: %s, should be ", strerror(errno)); - printmatchpathcon(argv[i], 0, mode); + printmatchpathcon(path, 0, mode); } } } else { - error |= printmatchpathcon(argv[i], header, mode); + error |= printmatchpathcon(path, header, mode); } } matchpathcon_fini();