def analyze(self, pkginfo, tar): invalid_elffiles = [] questionable_elffiles = [] for entry in tar: # is it a regular file ? if not entry.isfile(): continue # is it outside standard binary dirs ? in_std_dirs = any(entry.name.startswith(d) for d in valid_dirs) in_que_dirs = any(entry.name.startswith(d) for d in questionable_dirs) if in_std_dirs: continue # is it an ELF file ? f = tar.extractfile(entry) if is_elf(f): if in_que_dirs: questionable_elffiles.append(entry.name) else: invalid_elffiles.append(entry.name) que_elfdirs = [d for d in questionable_dirs if any(f.startswith(d) for f in questionable_elffiles)] self.errors = [("elffile-not-in-allowed-dirs %s", i) for i in invalid_elffiles] self.errors.extend(("elffile-in-questionable-dirs %s", i) for i in que_elfdirs) self.infos = [("elffile-not-in-allowed-dirs %s", i) for i in questionable_elffiles]
def scanlibs(fileobj, filename, custom_libs): """ Find shared libraries in a file-like binary object If it depends on a library, store that library's path. returns: a dictionary { library => set(ELF files using that library) } """ if not is_elf(fileobj): return {} elffile = ELFFile(fileobj) sharedlibs = defaultdict(set) for section in elffile.iter_sections(): if not isinstance(section, DynamicSection): continue for tag in section.iter_tags(): # DT_NEEDED means shared library if tag.entry.d_tag != 'DT_NEEDED': continue bitsize = elffile.elfclass architecture = {32:'i686', 64:'x86-64'}[bitsize] libname = tag.needed if libname in custom_libs: sharedlibs[custom_libs[libname][1:]].add(filename) continue try: libpath = os.path.abspath( libcache[architecture][libname])[1:] sharedlibs[libpath].add(filename) except KeyError: # We didn't know about the library, so add it for fail later sharedlibs[libname].add(filename) return sharedlibs
def analyze(self, pkginfo, tar): print("inside") gettime() for entry in tar: if not entry.isfile(): continue # is it an ELF file ? fileobj = tar.extractfile(entry) if not is_elf(fileobj): continue for path in get_rpaths(fileobj): path_ok = path in allowed for allowed_toplevel in allowed_toplevels: if path.startswith(allowed_toplevel): path_ok = True if not path_ok: self.errors.append( ("insecure-rpath %s %s", (path, entry.name))) break if path in warn and entry.name not in insecure_rpaths: self.warnings.append( ("insecure-rpath %s %s", (path, entry.name)))
def analyze(self, pkginfo, tar): liblist = {} dependlist = {} filllibcache() os.environ['LC_ALL'] = 'C' pkg_so_files = ['/' + n for n in tar.getnames() if '.so' in n] print("has # files", len(tar.getnames())) for entry in tar: print(gettime(), "tar entry - ", entry.name) if not entry.isfile(): continue f = tar.extractfile(entry) # find anything that could be rpath related rpath_files = {} if is_elf(f): rpaths = list(get_rpaths(f)) f.seek(0) print(gettime(), "rpaths - ", rpaths) for n in pkg_so_files: if any(n.startswith(rp) for rp in rpaths): rpath_files[os.path.basename(n)] = n liblist.update(scanlibs(f, entry.name, rpath_files)) f.close() print(gettime(), "DONE tar entries") # Ldd all the files and find all the link and script dependencies dependlist, orphans = finddepends(liblist) # Handle "no package associated" errors self.warnings.extend([("library-no-package-associated %s", i) for i in orphans]) print(gettime(), "link level deps") # Print link-level deps for pkg, libraries in dependlist.items(): if isinstance(libraries, set): files = list(libraries) needing = set().union(*[liblist[lib] for lib in libraries]) reasons = pkginfo.detected_deps.setdefault(pkg, []) reasons.append(("libraries-needed %s %s", (str(files), str(list(needing))))) self.infos.append( ("link-level-dependence %s in %s", (pkg, str(files)))) print(gettime(), "link level deps 2") # Check for packages in testing for i in dependlist.keys(): p = Namcap.package.load_testing_package(i) q = Namcap.package.load_from_db(i) if p is not None and q is not None and p["version"] == q["version"]: self.warnings.append(("dependency-is-testing-release %s", i))
def analyze(self, pkginfo, tar): nopie_binaries = [] for entry in tar: if not entry.isfile(): continue fp = tar.extractfile(entry) if not is_elf(fp): continue elffile = ELFFile(fp) if elffile.header['e_type'] != 'ET_DYN' or not self.has_dt_debug( elffile): nopie_binaries.append(entry.name) if nopie_binaries: self.warnings = [("elffile-nopie %s", i) for i in nopie_binaries]
def analyze(self, pkginfo, tar): missing_relro = [] for entry in tar: if not entry.isfile(): continue fp = tar.extractfile(entry) if not is_elf(fp): continue elffile = ELFFile(fp) if any(seg['p_type'] == 'PT_GNU_RELRO' for seg in elffile.iter_segments()): if self.has_bind_now(elffile): continue missing_relro.append(entry.name) if missing_relro: self.warnings = [("elffile-without-relro %s", i) for i in missing_relro]
def analyze(self, pkginfo, tar): files_with_textrel = [] for entry in tar: if not entry.isfile(): continue fp = tar.extractfile(entry) if not is_elf(fp): continue elffile = ELFFile(fp) for section in elffile.iter_sections(): if not isinstance(section, DynamicSection): continue for tag in section.iter_tags('DT_TEXTREL'): files_with_textrel.append(entry.name) if files_with_textrel: self.warnings = [("elffile-with-textrel %s", i) for i in files_with_textrel]
def analyze(self, pkginfo, tar): exec_stacks = [] for entry in tar: if not entry.isfile(): continue fp = tar.extractfile(entry) if not is_elf(fp): continue elffile = ELFFile(fp) for segment in elffile.iter_segments(): if segment['p_type'] != 'PT_GNU_STACK': continue mode = segment['p_flags'] if mode & 1: exec_stacks.append(entry.name) if exec_stacks: self.warnings = [("elffile-with-execstack %s", i) for i in exec_stacks]
def analyze(self, pkginfo, tar): supress_name = ['^mingw-'] if any(re.search(s, pkginfo['name']) for s in supress_name): return found_elffiles = [] for entry in tar.getmembers(): if not entry.isfile(): continue f = tar.extractfile(entry) # Ar files (static libs) are also architecture specific (FS#24854) if is_elf(f) or is_static(f): found_elffiles.append(entry.name) f.close() if pkginfo["arch"] and pkginfo["arch"][0] == 'any': self.errors = [("elffile-in-any-package %s", i) for i in found_elffiles] else: if len(found_elffiles) == 0: self.warnings.append(("no-elffiles-not-any-package", ()))
def analyze(self, pkginfo, tar): unstripped_binaries = [] for entry in tar: if not entry.isfile(): continue fp = tar.extractfile(entry) if not is_elf(fp): continue elffile = ELFFile(fp) for section in elffile.iter_sections(): if not isinstance(section, SymbolTableSection): continue if section['sh_entsize'] == 0: continue if section.name == '.symtab': unstripped_binaries.append(entry.name) if unstripped_binaries: self.warnings = [("elffile-unstripped %s", i) for i in unstripped_binaries]
def analyze(self, pkginfo, tar): for entry in tar: if not entry.isfile(): continue fileobj = tar.extractfile(entry) if not is_elf(fileobj): continue for path in get_runpaths(fileobj): path_ok = path in allowed if any(path.startswith(tl) for tl in allowed_toplevels): path_ok = True if not path_ok: self.errors.append( ("insecure-runpath %s %s", (path, entry.name))) break if path in warn and entry.name not in insecure_rpaths: self.warnings.append( ("insecure-runpath %s %s", (path, entry.name)))
def analyze(self, pkginfo, tar): for entry in tar: if not entry.isfile(): continue # is it an ELF file ? f = tar.extractfile(entry) if not is_elf(f): f.close() continue elf = f.read() f.close() # write it to a temporary file f = tempfile.NamedTemporaryFile(delete = False) f.write(elf) f.close() os.chmod(f.name, 0o755) for lib in get_unused_sodepends(f.name): self.warnings.append(("unused-sodepend %s %s", (lib, entry.name))) os.unlink(f.name)