def __init__(self, src, dst): self._src = src self._dst = dst self._header = FileHeader(src) self._is_binary = None self._content = None self._has_zero = None
class InstallFile(object): def __init__(self, src, dst): self._src = src self._dst = dst self._header = FileHeader(src) self._is_binary = None self._content = None self._has_zero = None def is_binary(self): if self._is_binary is None: self._is_binary = self._header.is_binary() return self._is_binary def has_zero(self): if self._has_zero is None: self._has_zero = b'\0' in self.content() return self._has_zero def _make_user_rw(self, filename): st = os.stat(filename) os.chmod(filename, st.st_mode | stat.S_IREAD | stat.S_IWRITE) def copy(self): shutil.copy2(self._src, self._dst) self._make_user_rw(self._dst) def content(self): if self._content is None: with open(self._src, 'rb') as f: self._content = f.read() return self._content def find(self, marker): content = self.content() start = 0 while True: start = content.find(marker, start) if start == -1: return yield start start += len(marker) # cannot have overlapping markers def find_terminators(self, marker): """ Find positions of marker in the file Returns: dict: the keys are the terminators (byte after the marker, see ``ALL_TERMINATORS``) and the corresponding value is a tuple of start indices. Lists the start indices of the marker with the given terminator. """ result = dict() binary_terminator = False text_terminator = False for start in self.find(marker): term = self._find_terminator(marker, start, ALL_TERMINATORS) result[term] = result.get(term, ()) + (start, ) if term is None: continue binary_terminator = binary_terminator or ( term in BINARY_PATH_TERMINATORS) text_terminator = text_terminator or (term in TEXT_PATH_TERMINATORS) if self.is_binary() and text_terminator and not binary_terminator: log.warn('Suspicious text terminators {0} in {1}'.format( list(result.keys()), self._src)) if not self.is_binary() and not self.has_zero() and binary_terminator: log.warn('Suspicious binary terminators {0} in {1}'.format( list(result.keys()), self._src)) if self.has_zero() and text_terminator and not binary_terminator: # The rpath can be /foo:/marker:/usr/lib, for example in gp log.warn('Contains zero-terminated strings but marker is not: {0}'. format(self._src)) # Pretend that we found zero terminators, see :meth:`find_patch` result[b'\0'] = tuple() return result def _find_terminator(self, marker, start, terminators): content = self.content() end = start + len(marker) assert content[start:end] == marker pos = end while pos < len(content): ch = content[pos:pos + 1] # print(pos, ch) if ch not in PATH_CHARS: if ch not in terminators: log.error('At {0}'.format(content[start:pos + 1])) log.error('path terminator {0} not allowed in {1}'.format( ch, self._src)) raise SystemExit('invalid string terminator') # print('terminator') return ch pos += 1 return None def find_zero_terminated(self, marker): """ Returns pairs (start, end) of zero-terminated strings to search& replace """ content = self.content() start = 0 while True: start = content.find(marker, start) if start == -1: return end = start + len(marker) while end < len(content) and content[end:end + 1] != b'\0': end += 1 yield (start, end) start = end + 1 def find_patch(self, marker): occurences = self.find_terminators(marker) if not occurences: return if b'\0' in occurences.keys(): return [ BinaryPatch(start, end) for start, end in self.find_zero_terminated(marker) ] else: return SearchReplacePatch()
class InstallFile(object): def __init__(self, src, dst): self._src = src self._dst = dst self._header = FileHeader(src) self._is_binary = None self._content = None self._has_zero = None def is_binary(self): if self._is_binary is None: self._is_binary = self._header.is_binary() return self._is_binary def has_zero(self): if self._has_zero is None: self._has_zero = b'\0' in self.content() return self._has_zero def _make_user_rw(self, filename): st = os.stat(filename) os.chmod(filename, st.st_mode | stat.S_IREAD | stat.S_IWRITE ) def copy(self): shutil.copy2(self._src, self._dst) self._make_user_rw(self._dst) def content(self): if self._content is None: with open(self._src, 'rb') as f: self._content = f.read() return self._content def find(self, marker): content = self.content() start = 0 while True: start = content.find(marker, start) if start == -1: raise StopIteration yield start start += len(marker) # cannot have overlapping markers def find_terminators(self, marker): result = dict() binary_terminator = False text_terminator = False for start in self.find(marker): term = self._find_terminator(marker, start, ALL_TERMINATORS) result[term] = result.get(term, ()) + (start,) if term is None: continue binary_terminator = binary_terminator or (term in BINARY_PATH_TERMINATORS) text_terminator = text_terminator or (term in TEXT_PATH_TERMINATORS) if self.is_binary() and text_terminator and not binary_terminator: log.warn('Suspicious text terminators {0} in {1}'.format( list(result.keys()), self._src)) if not self.is_binary() and not self.has_zero() and binary_terminator: log.warn('Suspicious binary terminators {0} in {1}'.format( list(result.keys()), self._src)) if self.has_zero() and text_terminator and not binary_terminator: # The rpath can be /foo:/marker:/usr/lib, for example in gp log.warn('Contains zero-terminated strings but marker is not: {0}' .format(self._src)) return dict() return result def _find_terminator(self, marker, start, terminators): content = self.content() end = start + len(marker) assert content[start:end] == marker pos = end while pos < len(content): ch = content[pos:pos+1] # print(pos, ch) if ch not in PATH_CHARS: if ch not in terminators: log.error('At {0}'.format(content[start:pos+1])) log.error('path terminator {0} not allowed in {1}'.format(ch, self._src)) raise SystemExit('invalid string terminator') # print('terminator') return ch pos += 1 return None def find_zero_terminated(self, marker): """ Returns pairs (start, end) of zero-terminated strings to search& replace """ content = self.content() start = 0 while True: start = content.find(marker, start) if start == -1: raise StopIteration end = start + len(marker) while end < len(content) and content[end:end+1] != b'\0': end += 1 yield (start, end) start = end+1 def find_patch(self, marker): occurences = self.find_terminators(marker) if not occurences: return if b'\0' in occurences.keys(): return [BinaryPatch(start, end) for start, end in self.find_zero_terminated(marker)] else: return SearchReplacePatch()