From 25960992072917e7def037374db7b173d7836477 Mon Sep 17 00:00:00 2001 From: Euan Kemp Date: Fri, 17 Feb 2017 15:49:32 -0800 Subject: [PATCH 1/2] check_root: add check for broken symlinks --- build_library/check_root | 66 +++++++++++++++++++++++++++++ build_library/test_image_content.sh | 5 +++ 2 files changed, 71 insertions(+) diff --git a/build_library/check_root b/build_library/check_root index a6af980ccb..643ca57d0b 100755 --- a/build_library/check_root +++ b/build_library/check_root @@ -84,6 +84,22 @@ IGNORE_SHEBANG = ( b"*/doc/*", ) +IGNORE_SYMLINK = ( + # symlinks to sdk chroot + b"/build/*", + b"/var/tmp/portage/*", + b"/etc/portage/*", + + # symlinks to /run + b"/usr/share/baselayout/motd", + b"/etc/issue", + b"/etc/motd", + + # Other + b"/etc/lsb-release" # set later in the build process +) + + def provided_sonames(): for cpv in VARDB.cpv_all(): raw = VARDB.aux_get(cpv, ["PROVIDES"])[0] @@ -201,6 +217,55 @@ def check_shebang(): ok = False return ok +class chrooted(): + """ + chrooted provides a context so that it can be used via with. + For example: + + with chrooted("/some/rootfs"): + do_operations_in_rootfs() + do_operations_not_in_rootfs() + """ + + def __init__(self, path): + self.path=path + + def __enter__(self): + self.restore_fd=os.open(b"/", os.O_RDONLY) + self.working_dir=os.getcwd() + + os.chroot(self.path) + + def __exit__(self, type, value, traceback): + os.fchdir(self.restore_fd) + os.chroot(b".") + os.chdir(self.working_dir) + os.close(self.restore_fd) + + +def check_symlink(): + if os.getuid() != 0: + error("symlink check must be run as root (chroot)") + return False + + ok = True + root = os.environ.get("ROOT", b"/") + + with chrooted(root): + for parent, dirs, files in os.walk(b"/"): + for path in [os.path.join(parent, p) for p in files + dirs ]: + if any(fnmatch.fnmatchcase(path, i) for i in IGNORE_SYMLINK): + continue + + if os.path.islink(path) and not os.path.exists(path): + ok = False + error("broken link: %s", path) + + return ok + + + + def error(fmt, *args): sys.stderr.write(output.red(fmt % args)) sys.stderr.write("\n") @@ -211,6 +276,7 @@ def main(): "libs": check_libs, "usr": check_usr, "shebang": check_shebang, + "symlink": check_symlink, } if not sys.stderr.isatty(): diff --git a/build_library/test_image_content.sh b/build_library/test_image_content.sh index 2136811941..2189e5ec17 100644 --- a/build_library/test_image_content.sh +++ b/build_library/test_image_content.sh @@ -56,6 +56,11 @@ test_image_content() { #returncode=1 fi + if ! sudo ROOT="$root" "$check_root" symlink; then + error "test_image_content: Failed symlink check" + returncode=1 + fi + if ! ROOT="$root" glsa_image; then returncode=1 fi From 3eea9d27014678f080f27847fa5370bf7cbffb1d Mon Sep 17 00:00:00 2001 From: Euan Kemp Date: Fri, 17 Feb 2017 16:00:33 -0800 Subject: [PATCH 2/2] check_root: fix lint warnings Per `flake8-3`'s recommendations --- build_library/check_root | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/build_library/check_root b/build_library/check_root index 643ca57d0b..2e5ed60c59 100755 --- a/build_library/check_root +++ b/build_library/check_root @@ -96,7 +96,7 @@ IGNORE_SYMLINK = ( b"/etc/motd", # Other - b"/etc/lsb-release" # set later in the build process + b"/etc/lsb-release" # set later in the build process ) @@ -110,11 +110,13 @@ def provided_sonames(): for atom in VARDB.settings.soname_provided: yield atom + def ignore_sonames(cpv): for key in dep.match_to_list(cpv, IGNORE_MISSING.iterkeys()): for atom in IGNORE_MISSING[key]: yield atom + def missing_sonames(): provided = frozenset(provided_sonames()) for cpv in VARDB.cpv_all(): @@ -125,6 +127,7 @@ def missing_sonames(): if missing: yield (cpv, missing) + def usr_conflicts(): for cpv in VARDB.cpv_all(): raw = VARDB.aux_get(cpv, ["CONTENTS"])[0] @@ -157,6 +160,7 @@ def usr_conflicts(): if conflicts: yield (cpv, conflicts) + def check_libs(): ok = True for cpv, sonames in missing_sonames(): @@ -166,6 +170,7 @@ def check_libs(): ok = False return ok + def check_usr(): ok = True for cpv, conflicts in usr_conflicts(): @@ -175,11 +180,13 @@ def check_usr(): ok = False return ok + def is_exe(path): # just check other, assuming root or group only commands are not scripts. - perms = stat.S_IROTH|stat.S_IXOTH + perms = stat.S_IROTH | stat.S_IXOTH mode = os.lstat(path).st_mode - return stat.S_ISREG(mode) and mode&perms == perms + return stat.S_ISREG(mode) and (mode & perms) == perms + def check_shebang(): ok = True @@ -187,7 +194,7 @@ def check_shebang(): root = os.environ.get("ROOT", b"/") for parent, _, files in os.walk(root): for path in [os.path.join(parent, f) for f in files]: - if any(fnmatch.fnmatchcase(path,i) for i in IGNORE_SHEBANG): + if any(fnmatch.fnmatchcase(path, i) for i in IGNORE_SHEBANG): continue if not is_exe(path): continue @@ -217,6 +224,7 @@ def check_shebang(): ok = False return ok + class chrooted(): """ chrooted provides a context so that it can be used via with. @@ -228,11 +236,11 @@ class chrooted(): """ def __init__(self, path): - self.path=path + self.path = path def __enter__(self): - self.restore_fd=os.open(b"/", os.O_RDONLY) - self.working_dir=os.getcwd() + self.restore_fd = os.open(b"/", os.O_RDONLY) + self.working_dir = os.getcwd() os.chroot(self.path) @@ -245,15 +253,15 @@ class chrooted(): def check_symlink(): if os.getuid() != 0: - error("symlink check must be run as root (chroot)") - return False + error("symlink check must be run as root (chroot)") + return False ok = True root = os.environ.get("ROOT", b"/") with chrooted(root): for parent, dirs, files in os.walk(b"/"): - for path in [os.path.join(parent, p) for p in files + dirs ]: + for path in [os.path.join(parent, p) for p in files + dirs]: if any(fnmatch.fnmatchcase(path, i) for i in IGNORE_SYMLINK): continue @@ -264,12 +272,11 @@ def check_symlink(): return ok - - def error(fmt, *args): sys.stderr.write(output.red(fmt % args)) sys.stderr.write("\n") + def main(): ok = True check_funcs = {