before and after this method.
"""
+ # When new_contents is supplied, the security check has already been
+ # done for this slot, so it shouldn't be repeated until the next
+ # replacement or unmerge operation.
+ if new_contents is None:
+ slot = self.vartree.dbapi.aux_get(self.mycpv, ["SLOT"])[0]
+ slot_matches = self.vartree.dbapi.match(
+ "%s:%s" % (dep_getkey(self.mycpv), slot))
+ retval = self._security_check(slot_matches)
+ if retval:
+ return retval
+
contents = self.getcontents()
# Now, don't assume that the name of the ebuild is the same as the
# name of the dir; the package may have been moved.
writemsg_stdout("--- !md5 %s %s\n" % ("obj", obj))
continue
try:
- if statobj.st_mode & (stat.S_ISUID | stat.S_ISGID):
- # Always blind chmod 0 before unlinking to avoid race conditions.
- os.chmod(obj, 0000)
- if statobj.st_nlink > 1:
- writemsg("setXid: "+str(statobj.st_nlink-1)+ \
- " hardlinks to '%s'\n" % obj)
+ # Remove permissions to ensure that any hardlinks to
+ # suid/sgid files are rendered harmless.
+ os.chmod(obj, 0)
os.unlink(obj)
except (OSError,IOError),e:
pass
return False
+ def _security_check(self, slot_matches):
+ if not slot_matches:
+ return 0
+ file_paths = set()
+ for cpv in slot_matches:
+ file_paths.update(dblink(self.cat, catsplit(cpv)[1],
+ self.vartree.root, self.settings,
+ vartree=self.vartree).getcontents())
+ inode_map = {}
+ for path in file_paths:
+ try:
+ s = os.lstat(path)
+ except OSError, e:
+ if e.errno != errno.ENOENT:
+ raise
+ del e
+ continue
+ if stat.S_ISREG(s.st_mode) and \
+ s.st_nlink > 1 and \
+ s.st_mode & (stat.S_ISUID | stat.S_ISGID):
+ k = (s.st_dev, s.st_ino)
+ inode_map.setdefault(k, []).append((path, s))
+ suspicious_hardlinks = []
+ for path_list in inode_map.itervalues():
+ path, s = path_list[0]
+ if len(path_list) == s.st_nlink:
+ # All hardlinks seem to be owned by this package.
+ continue
+ suspicious_hardlinks.append(path_list)
+ if not suspicious_hardlinks:
+ return 0
+ from output import colorize
+ prefix = colorize("SECURITY_WARN", "*") + " WARNING: "
+ writemsg(prefix + "suid/sgid file(s) " + \
+ "with suspicious hardlink(s):\n", noiselevel=-1)
+ for path_list in suspicious_hardlinks:
+ for path, s in path_list:
+ writemsg(prefix + " '%s'\n" % path, noiselevel=-1)
+ writemsg(prefix + "See the Gentoo Security Handbook " + \
+ "guide for advice on how to proceed.\n", noiselevel=-1)
+ return 1
+
def treewalk(self, srcroot, destroot, inforoot, myebuild, cleanup=0,
mydbapi=None, prev_mtimes=None):
"""
slot_matches = self.vartree.dbapi.match(
"%s:%s" % (self.mysplit[0], self.settings["SLOT"]))
+ retval = self._security_check(slot_matches)
+ if retval:
+ return retval
+
if slot_matches:
# Used by self.isprotected().
max_cpv = None