示例#1
0
文件: signer.py 项目: qayshp/pius
 def gpg_wait_for_string(self, fd, string):
     '''Look for a specific string on the status-fd.'''
     line = ''
     while line not in (string, ):
         debug('Waiting for line %s' % string)
         raw_line = fd.readline()
         if not raw_line:
             raise GpgUnknownError('gpg output unexpectedly ended')
         line = raw_line.strip()
         debug('got line %s' % line)
示例#2
0
 def gpg_wait_for_string(self, fd, string):
     """Look for a specific string on the status-fd."""
     line = ""
     while line not in (string, ):
         PiusUtil.debug("Waiting for line %s" % string)
         raw_line = fd.readline()
         if not raw_line:
             raise GpgUnknownError("gpg output unexpectedly ended")
         line = raw_line.strip()
         PiusUtil.debug("got line %s" % line)
示例#3
0
文件: signer.py 项目: qayshp/pius
 def _run_and_check_status(self, cmd):
     '''Helper function for running a gpg call that requires no input
 but that we want to make sure succeeded.'''
     logcmd(cmd)
     gpg = subprocess.Popen(cmd,
                            stdin=subprocess.PIPE,
                            stdout=self.null,
                            stderr=self.null,
                            close_fds=True)
     retval = gpg.wait()
     if retval != 0:
         # We don't catch this, but that's fine, if this errors, a stack
         # trace is what we want
         raise GpgUnknownError("'%s' exited with %d" %
                               (' '.join(cmd), retval))
示例#4
0
 def _run_and_check_status(self, cmd, shell=False):
     """Helper function for running a gpg call that requires no input
 but that we want to make sure succeeded."""
     PiusUtil.logcmd(cmd)
     gpg = subprocess.Popen(
         cmd,
         stdin=subprocess.PIPE,
         stdout=self.null,
         stderr=self.null,
         shell=shell,
         close_fds=(not shell),
     )
     retval = gpg.wait()
     if retval != 0:
         # We don't catch this, but that's fine, if this errors, a stack
         # trace is what we want
         raise GpgUnknownError(
             "'%s' exited with %d" %
             (" ".join(cmd) if isinstance(cmd, list) else cmd, retval))
示例#5
0
文件: signer.py 项目: qayshp/pius
    def sign_uid(self, key, index, level):
        '''Sign a single UID of a key.

    This can use either cached passpharse or gpg-agent.'''
        agent = []
        if self.mode == MODE_AGENT:
            agent = ['--use-agent']
        keyring = ['--no-default-keyring', '--keyring', self.tmp_keyring]
        # Note that if passphrase-fd is different from command-fd, nothing works.
        cmd = [self.gpg] + self.gpg_base_opts + self.gpg_quiet_opts + \
          self.gpg_fd_opts + keyring + [
              '-u', self.force_signer,
          ] + agent + self.policy_opts() + [
              '--default-cert-level', level,
              '--no-ask-cert-level',
              '--edit-key', key,
          ]  # NB: keep the `--edit-key <key>` at the very end of this list!
        logcmd(cmd)
        gpg = subprocess.Popen(cmd,
                               stdin=subprocess.PIPE,
                               stdout=subprocess.PIPE,
                               stderr=self.null,
                               close_fds=True)

        if self.mode == MODE_AGENT:
            # For some reason when using agent an initial enter is needed
            if not self.gpg2:
                gpg.stdin.write('\n')
        else:
            # For some unidentified reason you must send the passphrase
            # first, not when it asks for it.
            debug('Sending passphrase')
            gpg.stdin.write('%s\n' % self.passphrase)

        debug('Waiting for prompt')
        self.gpg_wait_for_string(gpg.stdout, PiusSigner.GPG_PROMPT)
        debug('Selecting UID %d' % index)
        gpg.stdin.write('%s\n' % str(index))
        debug('Waiting for ack')
        self.gpg_wait_for_string(gpg.stdout, PiusSigner.GPG_ACK)

        debug('Running sign subcommand')
        self.gpg_wait_for_string(gpg.stdout, PiusSigner.GPG_PROMPT)
        debug('Sending sign command')
        gpg.stdin.write('sign\n')
        self.gpg_wait_for_string(gpg.stdout, PiusSigner.GPG_ACK)

        while True:
            debug('Waiting for response')
            line = gpg.stdout.readline()
            debug('Got %s' % line)
            if PiusSigner.GPG_ALREADY_SIGNED in line:
                print('  UID already signed')
                gpg.stdin.write('quit\n')
                return False
            elif PiusSigner.GPG_KEY_CONSIDERED in line:
                debug('Got KEY_CONSIDERED')
                continue
            elif (PiusSigner.GPG_KEY_EXP in line
                  or PiusSigner.GPG_SIG_EXP in line):
                # The user has an expired signing or encryption key, keep going
                debug('Got GPG_KEY/SIG_EXP')
                continue
            elif PiusSigner.GPG_PROMPT in line:
                # Unfortunately PGP doesn't give us anything parsable in this case. It
                # just gives us another prompt. We give the most likely problem. Best we
                # can do.
                print(
                    '  ERROR: GnuPG won\'t let us sign, this probably means it'
                    ' can\'t find a secret key, which most likely means that the'
                    ' keyring you are using doesn\'t have _your_ _public_ key on'
                    ' it.')
                gpg.stdin.write('quit\n')
                raise NoSelfKeyError
            elif PiusSigner.GPG_CONFIRM in line:
                # This is what we want
                break
            else:
                print('  ERROR: GnuPG reported an unknown error')
                gpg.stdin.write('quit\n')
                # Don't raise an exception, it's not probably just this UID...
                return False

        debug('Confirming signing')
        gpg.stdin.write('Y\n')
        self.gpg_wait_for_string(gpg.stdout, PiusSigner.GPG_ACK)

        #
        # gpg-agent doesn't always work as well as we like. Of the problems:
        #  * It can't always pop up an X window reliably (pinentry problems)
        #  * It doesn't seem able to figure out the best pinetry program
        #    to use in many situations
        #  * Sometimes it silently fails in odd ways
        #
        # So this chunk of code will follow gpg through as many tries as gpg-agent
        # is willing to give and then inform the user of an error and raise an
        # exception.
        #
        # Since we're here, we also handle the highly unlikely case where the
        # verified cached passphrase doesn't work.
        #
        while True:
            line = gpg.stdout.readline()
            debug('Got %s' % line)
            # gpg1 + gpgagent1 reported BAD_PASSPHRASE for both the agent the wrong
            # passphrase, and for canceling the prompt.
            #
            # gpg2.0 + gpgagent2.0 seems to do MISSING_PASSPHRASE and BAD_PASSPHRASE
            # for the respective answers
            #
            # gpg2.1 + gpgagent2.1 seems to just do ERROR
            if 'ERROR' in line:
                print('  ERROR: Agent reported an error.')
                raise AgentError
            if 'MISSING_PASSPHRASE' in line:
                print('  ERROR: Agent didn\'t provide passphrase to PGP.')
                raise AgentError
            if 'BAD_PASSPHRASE' in line:
                if self.mode == MODE_AGENT:
                    line = gpg.stdout.readline()
                    debug('Got %s' % line)
                    if 'USERID_HINT' in line:
                        continue
                    print(
                        '  ERROR: Agent reported the passphrase was incorrect.'
                    )
                    raise AgentError
                else:
                    print('  ERROR: GPG didn\'t accept the passphrase.')
                    raise PassphraseError
            if 'GOOD_PASSPHRASE' in line:
                break
            if PiusSigner.GPG_PROMPT in line:
                if self.gpg2:
                    break
                print('  ERROR: GPG didn\'t sign.')
                raise GpgUnknownError(line)

        debug('Saving key')
        if not self.gpg2:
            self.gpg_wait_for_string(gpg.stdout, PiusSigner.GPG_PROMPT)
        gpg.stdin.write('save\n')

        gpg.wait()
        return True