]> git.decadent.org.uk Git - dak.git/commitdiff
Merge branch 'dak-unpriv' into merge
authorJoerg Jaspert <joerg@debian.org>
Sun, 16 Sep 2012 15:56:54 +0000 (17:56 +0200)
committerJoerg Jaspert <joerg@debian.org>
Sun, 16 Sep 2012 15:56:54 +0000 (17:56 +0200)
* dak-unpriv:
  update function docu
  make use of dak-unpriv

Signed-off-by: Joerg Jaspert <joerg@debian.org>
1  2 
daklib/archive.py
daklib/checks.py

diff --combined daklib/archive.py
index 5c98eeca826cfec49355fc7a43e5fdf40454c94b,13cec358f58693578c14f89066533131503808dc..dcdcc8e121ec46b8f50b599d89c7472b14a6d216
@@@ -595,11 -595,6 +595,11 @@@ class ArchiveUpload(object)
          @type: bool
          """
  
 +        self._checked = False
 +        """checks passes. set by C{check}
 +        @type: bool
 +        """
 +
          self._new_queue = self.session.query(PolicyQueue).filter_by(queue_name='new').one()
          self._new = self._new_queue.suite
  
          cnf = Config()
          session = self.transaction.session
  
-         self.directory = tempfile.mkdtemp(dir=cnf.get('Dir::TempPath'))
+         (None, self.directory) = utils.temp_dirname(parent=cnf.get('Dir::TempPath'),
+                                                     mode=0o2750, cnf.unprivgroup)
          with FilesystemTransaction() as fs:
              src = os.path.join(self.original_directory, self.original_changes.filename)
              dst = os.path.join(self.directory, self.original_changes.filename)
-             fs.copy(src, dst)
+             fs.copy(src, dst, mode=0o640)
  
              self.changes = upload.Changes(self.directory, self.original_changes.filename, self.keyrings)
  
                  dst = os.path.join(self.directory, f.filename)
                  if not os.path.exists(src):
                      continue
-                 fs.copy(src, dst)
+                 fs.copy(src, dst, mode=0o640)
  
              source = self.changes.source
              if source is not None:
          assert self.changes.valid_signature
  
          try:
 +            # Validate signatures and hashes before we do any real work:
              for chk in (
                      checks.SignatureCheck,
                      checks.ChangesCheck,
 -                    checks.TransitionCheck,
 -                    checks.UploadBlockCheck,
                      checks.HashesCheck,
                      checks.SourceCheck,
                      checks.BinaryCheck,
                      checks.BinaryTimestampCheck,
 -                    checks.ACLCheck,
                      checks.SingleDistributionCheck,
 -                    checks.NoSourceOnlyCheck,
 -                    checks.LintianCheck,
                      ):
                  chk().check(self)
  
              final_suites = self._final_suites()
              if len(final_suites) == 0:
 -                self.reject_reasons.append('Ended with no suite to install to.')
 +                self.reject_reasons.append('No target suite found. Please check your target distribution and that you uploaded to the right archive.')
                  return False
  
 +            self.final_suites = final_suites
 +
              for chk in (
 +                    checks.TransitionCheck,
 +                    checks.ACLCheck,
 +                    checks.NoSourceOnlyCheck,
 +                    checks.LintianCheck,
 +                    ):
 +                chk().check(self)
 +
 +            for chk in (
 +                    checks.ACLCheck,
                      checks.SourceFormatCheck,
                      checks.SuiteArchitectureCheck,
                      checks.VersionCheck,
              if len(self.reject_reasons) != 0:
                  return False
  
 -            self.final_suites = final_suites
 +            self._checked = True
              return True
          except checks.Reject as e:
              self.reject_reasons.append(unicode(e))
          assert len(self.reject_reasons) == 0
          assert self.changes.valid_signature
          assert self.final_suites is not None
 +        assert self._checked
  
          byhand = self.changes.byhand_files
          if len(byhand) == 0:
          assert len(self.reject_reasons) == 0
          assert self.changes.valid_signature
          assert self.final_suites is not None
 +        assert self._checked
          assert not self.new
  
          db_changes = self._install_changes()
          binaries = self.changes.binaries
          byhand = self.changes.byhand_files
  
 -        new_queue = self.transaction.session.query(PolicyQueue).filter_by(queue_name='new').one()
 -        if len(byhand) > 0:
 -            new_queue = self.transaction.session.query(PolicyQueue).filter_by(queue_name='byhand').one()
 -        new_suite = new_queue.suite
 -
          # we need a suite to guess components
          suites = list(self.final_suites)
          assert len(suites) == 1, "NEW uploads must be to a single suite"
          suite = suites[0]
  
 +        # decide which NEW queue to use
 +        if suite.new_queue is None:
 +            new_queue = self.transaction.session.query(PolicyQueue).filter_by(queue_name='new').one()
 +        else:
 +            new_queue = suite.new_queue
 +        if len(byhand) > 0:
 +            # There is only one global BYHAND queue
 +            new_queue = self.transaction.session.query(PolicyQueue).filter_by(queue_name='byhand').one()
 +        new_suite = new_queue.suite
 +
 +
          def binary_component_func(binary):
              return self._binary_component(suite, binary, only_overrides=False)
  
diff --combined daklib/checks.py
index 6a95049f4d93377795cf83d0c9b6c272024c9cc9,de180941eba05bdd9b7006f1f07eac7f77cd9361..25cbb8f0ad6a493e90ba5b89366b0e54addebff7
@@@ -352,80 -352,28 +352,80 @@@ class SingleDistributionCheck(Check)
  
  class ACLCheck(Check):
      """Check the uploader is allowed to upload the packages in .changes"""
 -    def _check_dm(self, upload):
 +
 +    def _does_hijack(self, session, upload, suite):
 +        # Try to catch hijacks.
 +        # This doesn't work correctly. Uploads to experimental can still
 +        # "hijack" binaries from unstable. Also one can hijack packages
 +        # via buildds (but people who try this should not be DMs).
 +        for binary_name in upload.changes.binary_names:
 +            binaries = session.query(DBBinary).join(DBBinary.source) \
 +                .filter(DBBinary.suites.contains(suite)) \
 +                .filter(DBBinary.package == binary_name)
 +            for binary in binaries:
 +                if binary.source.source != upload.changes.changes['Source']:
 +                    return True, binary, binary.source.source
 +        return False, None, None
 +
 +    def _check_acl(self, session, upload, acl):
 +        source_name = upload.changes.source_name
 +
 +        if acl.match_fingerprint and upload.fingerprint not in acl.fingerprints:
 +            return None, None
 +        if acl.match_keyring is not None and upload.fingerprint.keyring != acl.match_keyring:
 +            return None, None
 +
 +        if not acl.allow_new:
 +            if upload.new:
 +                return False, "NEW uploads are not allowed"
 +            for f in upload.changes.files.itervalues():
 +                if f.section == 'byhand' or f.section.startswith("raw-"):
 +                    return False, "BYHAND uploads are not allowed"
 +        if not acl.allow_source and upload.changes.source is not None:
 +            return False, "sourceful uploads are not allowed"
 +        binaries = upload.changes.binaries
 +        if len(binaries) != 0:
 +            if not acl.allow_binary:
 +                return False, "binary uploads are not allowed"
 +            if upload.changes.source is None and not acl.allow_binary_only:
 +                return False, "binary-only uploads are not allowed"
 +            if not acl.allow_binary_all:
 +                uploaded_arches = set(upload.changes.architectures)
 +                uploaded_arches.discard('source')
 +                allowed_arches = set(a.arch_string for a in acl.architectures)
 +                forbidden_arches = uploaded_arches - allowed_arches
 +                if len(forbidden_arches) != 0:
 +                    return False, "uploads for architecture(s) {0} are not allowed".format(", ".join(forbidden_arches))
 +        if not acl.allow_hijack:
 +            for suite in upload.final_suites:
 +                does_hijack, hijacked_binary, hijacked_from = self._does_hijack(session, upload, suite)
 +                if does_hijack:
 +                    return False, "hijacks are not allowed (binary={0}, other-source={1})".format(hijacked_binary, hijacked_from)
 +
 +        acl_per_source = session.query(ACLPerSource).filter_by(acl=acl, fingerprint=upload.fingerprint, source=source_name).first()
 +        if acl.allow_per_source:
 +            # XXX: Drop DMUA part here and switch to new implementation.
 +            # XXX: Send warning mail once users can set the new DMUA flag
 +            dmua_status, dmua_reason = self._check_dmua(upload)
 +            if not dmua_status:
 +                return False, dmua_reason
 +            #if acl_per_source is None:
 +            #    return False, "not allowed to upload source package '{0}'".format(source_name)
 +        if acl.deny_per_source and acl_per_source is not None:
 +            return False, acl_per_source.reason or "forbidden to upload source package '{0}'".format(source_name)
 +
 +        return True, None
 +
 +    def _check_dmua(self, upload):
          # This code is not very nice, but hopefully works until we can replace
          # DM-Upload-Allowed, cf. https://lists.debian.org/debian-project/2012/06/msg00029.html
          session = upload.session
  
 -        if 'source' not in upload.changes.architectures:
 -            raise Reject('DM uploads must include source')
 -        for f in upload.changes.files.itervalues():
 -            if f.section == 'byhand' or f.section[:4] == "raw-":
 -                raise Reject("Uploading byhand packages is not allowed for DMs.")
 -
 -        # Reject NEW packages
 -        distributions = upload.changes.distributions
 -        assert len(distributions) == 1
 -        suite = session.query(Suite).filter_by(suite_name=distributions[0]).one()
 -        overridesuite = suite
 -        if suite.overridesuite is not None:
 -            overridesuite = session.query(Suite).filter_by(suite_name=suite.overridesuite).one()
 -        if upload._check_new(overridesuite):
 -            raise Reject('Uploading NEW packages is not allowed for DMs.')
 -
          # Check DM-Upload-Allowed
 +        suites = upload.final_suites
 +        assert len(suites) == 1
 +        suite = list(suites)[0]
 +
          last_suites = ['unstable', 'experimental']
          if suite.suite_name.endswith('-backports'):
              last_suites = [suite.suite_name]
              .join(DBSource.suites).filter(Suite.suite_name.in_(last_suites)) \
              .order_by(DBSource.version.desc()).limit(1).first()
          if last is None:
 -            raise Reject('No existing source found in {0}'.format(' or '.join(last_suites)))
 +            return False, 'No existing source found in {0}'.format(' or '.join(last_suites))
          if not last.dm_upload_allowed:
 -            raise Reject('DM-Upload-Allowed is not set in {0}={1}'.format(last.source, last.version))
 +            return False, 'DM-Upload-Allowed is not set in {0}={1}'.format(last.source, last.version)
  
          # check current Changed-by is in last Maintainer or Uploaders
          uploader_names = [ u.name for u in last.uploaders ]
          changed_by_field = upload.changes.changes.get('Changed-By', upload.changes.changes['Maintainer'])
          if changed_by_field not in uploader_names:
 -            raise Reject('{0} is not an uploader for {1}={2}'.format(changed_by_field, last.source, last.version))
 +            return False, '{0} is not an uploader for {1}={2}'.format(changed_by_field, last.source, last.version)
  
          # check Changed-by is the DM
          changed_by = fix_maintainer(changed_by_field)
          uid = upload.fingerprint.uid
          if uid is None:
 -            raise Reject('Unknown uid for fingerprint {0}'.format(upload.fingerprint.fingerprint))
 +            return False, 'Unknown uid for fingerprint {0}'.format(upload.fingerprint.fingerprint)
          if uid.uid != changed_by[3] and uid.name != changed_by[2]:
 -            raise Reject('DMs are not allowed to sponsor uploads (expected {0} <{1}> as maintainer, but got {2})'.format(uid.name, uid.uid, changed_by_field))
 +            return False, 'DMs are not allowed to sponsor uploads (expected {0} <{1}> as maintainer, but got {2})'.format(uid.name, uid.uid, changed_by_field)
  
 -        # Try to catch hijacks.
 -        # This doesn't work correctly. Uploads to experimental can still
 -        # "hijack" binaries from unstable. Also one can hijack packages
 -        # via buildds (but people who try this should not be DMs).
 -        for binary_name in upload.changes.binary_names:
 -            binaries = session.query(DBBinary).join(DBBinary.source) \
 -                .join(DBBinary.suites).filter(Suite.suite_name.in_(upload.changes.distributions)) \
 -                .filter(DBBinary.package == binary_name)
 -            for binary in binaries:
 -                if binary.source.source != upload.changes.changes['Source']:
 -                    raise Reject('DMs must not hijack binaries (binary={0}, other-source={1})'.format(binary_name, binary.source.source))
 -
 -        return True
 +        return True, None
  
      def check(self, upload):
 +        session = upload.session
          fingerprint = upload.fingerprint
 -        source_acl = fingerprint.source_acl
 -        if source_acl is None:
 -            if 'source' in upload.changes.architectures:
 -                raise Reject('Fingerprint {0} must not upload source'.format(fingerprint.fingerprint))
 -        elif source_acl.access_level == 'dm':
 -            self._check_dm(upload)
 -        elif source_acl.access_level != 'full':
 -            raise Reject('Unknown source_acl access level {0} for fingerprint {1}'.format(source_acl.access_level, fingerprint.fingerprint))
 -
 -        bin_architectures = set(upload.changes.architectures)
 -        bin_architectures.discard('source')
 -        binary_acl = fingerprint.binary_acl
 -        if binary_acl is None:
 -            if len(bin_architectures) > 0:
 -                raise Reject('Fingerprint {0} must not upload binary packages'.format(fingerprint.fingerprint))
 -        elif binary_acl.access_level == 'map':
 -            query = upload.session.query(BinaryACLMap).filter_by(fingerprint=fingerprint)
 -            allowed_architectures = [ m.architecture.arch_string for m in query ]
 -
 -            for arch in upload.changes.architectures:
 -                if arch not in allowed_architectures:
 -                    raise Reject('Fingerprint {0} must not  upload binaries for architecture {1}'.format(fingerprint.fingerprint, arch))
 -        elif binary_acl.access_level != 'full':
 -            raise Reject('Unknown binary_acl access level {0} for fingerprint {1}'.format(binary_acl.access_level, fingerprint.fingerprint))
 +        keyring = fingerprint.keyring
  
 -        return True
 +        if keyring is None:
 +            raise Reject('No keyring for fingerprint {0}'.format(fingerprint.fingerprint))
 +        if not keyring.active:
 +            raise Reject('Keyring {0} is not active'.format(keyring.name))
  
 -class UploadBlockCheck(Check):
 -    """check for upload blocks"""
 -    def check(self, upload):
 -        session = upload.session
 -        control = upload.changes.changes
 +        acl = fingerprint.acl or keyring.acl
 +        if acl is None:
 +            raise Reject('No ACL for fingerprint {0}'.format(fingerprint.fingerprint))
 +        result, reason = self._check_acl(session, upload, acl)
 +        if not result:
 +            raise Reject(reason)
  
 -        source = re_field_source.match(control['Source']).group('package')
 -        version = control['Version']
 -        blocks = session.query(UploadBlock).filter_by(source=source) \
 -                        .filter((UploadBlock.version == version) | (UploadBlock.version == None))
 +        for acl in session.query(ACL).filter_by(is_global=True):
 +            result, reason = self._check_acl(session, upload, acl)
 +            if result == False:
 +                raise Reject(reason)
  
 -        for block in blocks:
 -            if block.fingerprint == upload.fingerprint:
 -                raise Reject('Manual upload block in place for package {0} and fingerprint {1}:\n{2}'.format(source, upload.fingerprint.fingerprint, block.reason))
 -            if block.uid == upload.fingerprint.uid:
 -                raise Reject('Manual upload block in place for package {0} and uid {1}:\n{2}'.format(source, block.uid.uid, block.reason))
 +        return True
  
 +    def per_suite_check(self, upload, suite):
 +        acls = suite.acls
 +        if len(acls) != 0:
 +            accept = False
 +            for acl in acls:
 +                result, reason = self._check_acl(upload.session, upload, acl)
 +                if result == False:
 +                    raise Reject(reason)
 +                accept = accept or result
 +            if not accept:
 +                raise Reject('Not accepted by any per-suite acl (suite={0})'.format(suite.suite_name))
          return True
  
  class TransitionCheck(Check):
@@@ -595,7 -566,7 +595,7 @@@ class LintianCheck(Check)
          except yaml.YAMLError as msg:
              raise Exception('Could not read lintian tags file {0}, YAML error: {1}'.format(tagfile, msg))
  
-         fd, temp_filename = utils.temp_filename()
+         fd, temp_filename = utils.temp_filename(mode=0o644)
          temptagfile = os.fdopen(fd, 'w')
          for tags in lintiantags.itervalues():
              for tag in tags:
  
          changespath = os.path.join(upload.directory, changes.filename)
          try:
-             # FIXME: no shell
-             cmd = "lintian --show-overrides --tags-from-file {0} {1}".format(temp_filename, changespath)
+             if cnf.unpribgroup:
+                 cmd = "sudo -H -u {0} -- /usr/bin/lintian --show-overrides --tags-from-file {1} {2}".format(cnf.unprivgroup, temp_filename, changespath)
+             else:
+                 cmd = "/usr/bin/lintian --show-overrides --tags-from-file {0} {1}".format(temp_filename, changespath)
              result, output = commands.getstatusoutput(cmd)
          finally:
              os.unlink(temp_filename)