Example #1
0
    def encrypt_signed_uid(self, key, filename):
        '''Encrypt the file we exported the signed UID to.'''
        (base, ext) = os.path.splitext(os.path.basename(filename))
        enc_file = '%s_ENCRYPTED%s' % (base, ext)
        enc_path = self._outfile_path(enc_file)
        if os.path.exists(enc_path):
            os.unlink(enc_path)
        cmd = [self.gpg] + self.gpg_base_opts + self.gpg_quiet_opts + \
          self.gpg_fd_opts + [
              '--no-default-keyring',
              '--keyring', self.tmp_keyring,
              '--always-trust',
              '--armor',
              '-r', key,
              '--output', enc_path,
              '-e', filename,
          ]
        logcmd(cmd)
        gpg = subprocess.Popen(cmd,
                               stdin=subprocess.PIPE,
                               stdout=subprocess.PIPE,
                               stderr=self.null,
                               close_fds=True)

        # Must send a blank line...
        gpg.stdin.write('\n')
        while True:
            debug('Waiting for response')
            line = gpg.stdout.readline().strip()
            debug('Got %s' % line)
            if PiusSigner.GPG_ENC_BEG in line:
                debug('Got GPG_ENC_BEG')
                continue
            elif PiusSigner.GPG_ENC_COMPLIANT_MODE in line:
                debug('Got ENCRYPTION_COMPLIANCE_MODE')
                continue
            elif PiusSigner.GPG_ENC_END in line:
                debug('Got GPG_ENC_END')
                break
            elif PiusSigner.GPG_ENC_INV in line:
                debug('Got GPG_ENC_INV')
                raise EncryptionKeyError
            elif (PiusSigner.GPG_KEY_EXP in line
                  or PiusSigner.GPG_SIG_EXP in line):
                # These just mean we passed a given key/sig that's expired, there
                # may be ones left that are good. We cannot report an error until
                # we get a ENC_INV.
                debug('Got GPG_KEY_EXP')
                continue
            elif PiusSigner.GPG_KEY_CONSIDERED in line:
                debug('Got KEY_CONSIDERED')
                continue
            elif PiusSigner.GPG_PROGRESS in line:
                debug('Got skippable stuff')
                continue
            else:
                raise EncryptionUnknownError(line)

        gpg.wait()
        return enc_file
Example #2
0
File: signer.py Project: zorun/pius
  def _is_gpg2(self):
    cmd = [self.gpg, '--version']
    logcmd(cmd)
    gpg = subprocess.Popen(
        cmd,
        stdin=self.null,
        stdout=subprocess.PIPE,
        stderr=self.null,
    )

    v = None
    for line in gpg.stdout:
      # On Linux this looks like:
      #   gpg (GnuPG) 2.1.8
      # On Mac this looks like:
      #   gpg (GnuPG/MacGPG2) 2.0.28
      m = re.match(r'^gpg \(GnuPG.*\) ([0-9\.]+)$', line)
      if m:
        v = m.group(1)

    if not v:
      print "ERROR: Could not determine gpg version\n"
      sys.exit(1)

    return v.startswith('2.')
Example #3
0
    def _is_gpg2(self):
        cmd = [self.gpg, '--version']
        logcmd(cmd)
        gpg = subprocess.Popen(
            cmd,
            stdin=self.null,
            stdout=subprocess.PIPE,
            stderr=self.null,
        )

        v = None
        for line in gpg.stdout:
            # On Linux this looks like:
            #   gpg (GnuPG) 2.1.8
            # On Mac this looks like:
            #   gpg (GnuPG/MacGPG2) 2.0.28
            m = re.match(r'^gpg \(GnuPG.*\) ([0-9\.]+)$', line)
            if m:
                v = m.group(1)

        if not v:
            print("ERROR: Could not determine gpg version\n")
            sys.exit(1)

        return v.startswith('2.')
Example #4
0
  def encrypt_signed_uid(self, key, filename):
    '''Encrypt the file we exported the signed UID to.'''
    (base, ext) = os.path.splitext(os.path.basename(filename))
    enc_file = '%s_ENCRYPTED%s' % (base, ext)
    enc_path = self._outfile_path(enc_file)
    if os.path.exists(enc_path):
      os.unlink(enc_path)
    cmd = [self.gpg] + self.gpg_base_opts + self.gpg_quiet_opts + \
      self.gpg_fd_opts + [
          '--no-default-keyring',
          '--keyring', self.tmp_keyring,
          '--always-trust',
          '--armor',
          '-r', key,
          '--output', enc_path,
          '-e', filename,
      ]
    logcmd(cmd)
    gpg = subprocess.Popen(cmd, stdin=subprocess.PIPE,
                           stdout=subprocess.PIPE,
                           stderr=self.null,
                           close_fds=True)

    # Must send a blank line...
    gpg.stdin.write('\n')
    while True:
      debug('Waiting for response')
      line = gpg.stdout.readline().strip()
      debug('Got %s' % line)
      if PiusSigner.GPG_ENC_BEG in line:
        debug('Got GPG_ENC_BEG')
        continue
      elif PiusSigner.GPG_ENC_COMPLIANT_MODE in line:
        debug('Got ENCRYPTION_COMPLIANCE_MODE')
        continue
      elif PiusSigner.GPG_ENC_END in line:
        debug('Got GPG_ENC_END')
        break
      elif PiusSigner.GPG_ENC_INV in line:
        debug('Got GPG_ENC_INV')
        raise EncryptionKeyError
      elif (PiusSigner.GPG_KEY_EXP in line or
            PiusSigner.GPG_SIG_EXP in line):
        # These just mean we passed a given key/sig that's expired, there
        # may be ones left that are good. We cannot report an error until
        # we get a ENC_INV.
        debug('Got GPG_KEY_EXP')
        continue
      elif PiusSigner.GPG_KEY_CONSIDERED in line:
        debug('Got KEY_CONSIDERED')
        continue
      elif PiusSigner.GPG_PROGRESS in line:
        debug('Got skippable stuff')
        continue
      else:
        raise EncryptionUnknownError(line)

    gpg.wait()
    return enc_file
Example #5
0
    def verify_passphrase(self):
        '''Verify a passpharse gotten from get_passpharse().'''
        magic_string = 'test1234'
        filename = self._tmpfile_path('pius_tmp')
        filename_enc = self._tmpfile_path('pius_tmp.gpg')
        filename_dec = self._tmpfile_path('pius_tmp2')
        clean_files([filename, filename_enc, filename_dec])
        tfile = open(filename, 'w')
        tfile.write(magic_string)
        tfile.close()
        cmd = [self.gpg] + self.gpg_base_opts + self.gpg_quiet_opts + [
            '--no-armor',
            '--always-trust',
            '-r',
            self.signer,
            '-e',
            filename,
        ]
        logcmd(cmd)
        subprocess.call(cmd,
                        stdout=self.null,
                        stderr=self.null,
                        close_fds=True)
        cmd = [self.gpg] + self.gpg_base_opts + self.gpg_quiet_opts + \
          self.gpg_fd_opts + [
              '--output', filename_dec,
              '-d', filename_enc,
          ]
        logcmd(cmd)
        gpg = subprocess.Popen(cmd,
                               stdin=subprocess.PIPE,
                               stdout=subprocess.PIPE,
                               stderr=self.null,
                               close_fds=True)

        debug('Sending passphrase')
        gpg.stdin.write('%s\n' % self.passphrase)

        line = gpg.stdout.read()
        debug('wait()ing on gpg')
        retval = gpg.wait()
        if retval != 0:
            debug('gpg decrypt return code %s' % retval)
            clean_files([filename, filename_enc, filename_dec])
            return False

        if not os.path.exists(filename_dec):
            debug('Resulting file %s not found' % filename_dec)
            clean_files([filename, filename_enc, filename_dec])
            return False
        tfile = open(filename_dec, 'r')
        line = tfile.readline()
        tfile.close()
        clean_files([filename, filename_enc, filename_dec])
        if line == magic_string:
            return True
        debug('File does not contain magic string')
        return False
Example #6
0
File: signer.py Project: zorun/pius
  def verify_passphrase(self):
    '''Verify a passpharse gotten from get_passpharse().'''
    magic_string = 'test1234'
    filename = self._tmpfile_path('pius_tmp')
    filename_enc = self._tmpfile_path('pius_tmp.gpg')
    filename_dec = self._tmpfile_path('pius_tmp2')
    clean_files([filename, filename_enc, filename_dec])
    tfile = open(filename, 'w')
    tfile.write(magic_string)
    tfile.close()
    cmd = [self.gpg] + self.gpg_base_opts + self.gpg_quiet_opts + [
        '--no-armor',
        '--always-trust',
        '-r', self.signer,
        '-e', filename,
    ]
    logcmd(cmd)
    subprocess.call(cmd, stdout=self.null, stderr=self.null, close_fds=True)
    cmd = [self.gpg] + self.gpg_base_opts + self.gpg_quiet_opts + \
      self.gpg_fd_opts + [
          '--output', filename_dec,
          '-d', filename_enc,
      ]
    logcmd(cmd)
    gpg = subprocess.Popen(cmd, stdin=subprocess.PIPE,
                           stdout=subprocess.PIPE,
                           stderr=self.null,
                           close_fds=True)

    debug('Sending passphrase')
    gpg.stdin.write('%s\n' % self.passphrase)

    line = gpg.stdout.read()
    debug('wait()ing on gpg')
    retval = gpg.wait()
    if retval != 0:
      debug('gpg decrypt return code %s' % retval)
      clean_files([filename, filename_enc, filename_dec])
      return False

    if not os.path.exists(filename_dec):
      debug('Resulting file %s not found' % filename_dec)
      clean_files([filename, filename_enc, filename_dec])
      return False
    tfile = open(filename_dec, 'r')
    line = tfile.readline()
    tfile.close()
    clean_files([filename, filename_enc, filename_dec])
    if line == magic_string:
      return True
    debug('File does not contain magic string')
    return False
Example #7
0
File: signer.py Project: zorun/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
Example #8
0
File: signer.py Project: doyke/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
Example #9
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.'''
   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
     ))
Example #10
0
    def check_fingerprint(self, key):
        '''Prompt the user to see if they have verified this fingerprint.'''
        cmd = [self.gpg] + self.gpg_base_opts + self.gpg_quiet_opts + [
            '--no-default-keyring',
            '--keyring',
            self.keyring,
            '--fingerprint',
            key,
        ]
        logcmd(cmd)
        gpg = subprocess.Popen(cmd,
                               stdin=self.null,
                               stdout=subprocess.PIPE,
                               stderr=self.null,
                               close_fds=True)
        output = gpg.stdout.read()
        output = output.strip()
        retval = gpg.wait()
        if retval != 0:
            print('WARNING: Keyid %s not valid, skipping.' % key)
            return False

        print(output)

        while True:
            ans = input(
                "\nHave you verified this user/key, and if so, what level"
                " do you want to sign at?\n  0-3, Show again, Next, Help,"
                " or Quit? [0|1|2|3|s|n|h|q] (default: n) ")
            print()

            if ans == 'y':
                print(
                    '"Yes" is no longer a valid answer, please specify a level to'
                    ' sign at.')
            elif ans in ('n', 'N', ''):
                return False
            elif ans in ('s', 'S'):
                print(output)
            elif ans in ('0', '1', '2', '3'):
                return ans
            elif ans in ('?', 'h', 'H'):
                self._print_cert_levels()
            elif ans in ('q', 'Q'):
                print('Dying at user request')
                sys.exit(1)
Example #11
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.'''
     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))
Example #12
0
    def sign_uid_expect(self, key, index, level):
        '''Sign a UID, using the expect stuff. Interactive mode.'''
        cmd = [self.gpg] + self.gpg_base_opts + [
            '--no-default-keyring',
            '--keyring',
            self.tmp_keyring,
            '--default-cert-level',
            level,
            '--no-ask-cert-level',
            '--no-use-agent',
            '--edit-key',
            key,
        ] + self.policy_opts()
        logcmd(cmd)
        gpg = pexpect.spawn(' '.join((quote(arg) for arg in cmd)))
        gpg.setecho(False)
        gpg.expect('gpg> ')
        debug('Selecting UID %s' % index)
        gpg.sendline(str(index))
        gpg.expect('gpg> ')
        debug('Running sign subcommand')
        gpg.sendline('sign')
        line = gpg.readline()
        if 'already signed' in line:
            print('  UID already signed')
            return False
        # else it's a blank line...

        gpg.expect(re.compile('Really sign.*'))
        debug('Confirming signing')
        gpg.sendline('y')
        # Tell the user how to get out of this, and then drop them into the gpg
        # shell.
        print('\n\nPassing you to gpg for passphrase.')
        print('Hit ^] after succesfully typing in your passphrase')
        gpg.interact()
        # When we return, we have a Command> prompt that w can't
        # 'expect'... or at least if the user did it right
        print('')
        # Unselect this UID
        debug('unselecting uid')
        debug('Saving key')
        gpg.sendline('save')
        return True
Example #13
0
File: signer.py Project: zorun/pius
  def check_fingerprint(self, key):
    '''Prompt the user to see if they have verified this fingerprint.'''
    cmd = [self.gpg] + self.gpg_base_opts + self.gpg_quiet_opts + [
        '--no-default-keyring',
        '--keyring', self.keyring,
        '--fingerprint', key,
    ]
    logcmd(cmd)
    gpg = subprocess.Popen(cmd,
                           stdin=self.null,
                           stdout=subprocess.PIPE,
                           stderr=self.null,
                           close_fds=True)
    output = gpg.stdout.read()
    output = output.strip()
    retval = gpg.wait()
    if retval != 0:
      print 'WARNING: Keyid %s not valid, skipping.' % key
      return False

    print output

    while True:
      ans = raw_input("\nHave you verified this user/key, and if so, what level"
                      " do you want to sign at?\n  0-3, Show again, Next, Help,"
                      " or Quit? [0|1|2|3|s|n|h|q] (default: n) ")
      print

      if ans == 'y':
        print ('"Yes" is no longer a valid answer, please specify a level to'
               ' sign at.')
      elif ans in ('n', 'N', ''):
        return False
      elif ans in ('s', 'S'):
        print output
      elif ans in ('0', '1', '2', '3'):
        return ans
      elif ans in ('?', 'h', 'H'):
        self._print_cert_levels()
      elif ans in ('q', 'Q'):
        print 'Dying at user request'
        sys.exit(1)
Example #14
0
File: signer.py Project: zorun/pius
  def sign_uid_expect(self, key, index, level):
    '''Sign a UID, using the expect stuff. Interactive mode.'''
    cmd = [self.gpg] + self.gpg_base_opts + [
        '--no-default-keyring',
        '--keyring', self.tmp_keyring,
        '--default-cert-level', level,
        '--no-ask-cert-level',
        '--no-use-agent',
        '--edit-key', key,
    ] + self.policy_opts()
    logcmd(cmd)
    gpg = pexpect.spawn(' '.join((quote(arg) for arg in cmd)))
    gpg.setecho(False)
    gpg.expect('gpg> ')
    debug('Selecting UID %s' % index)
    gpg.sendline(str(index))
    gpg.expect('gpg> ')
    debug('Running sign subcommand')
    gpg.sendline('sign')
    line = gpg.readline()
    if 'already signed' in line:
      print '  UID already signed'
      return False
    # else it's a blank line...

    gpg.expect(re.compile('Really sign.*'))
    debug('Confirming signing')
    gpg.sendline('y')
    # Tell the user how to get out of this, and then drop them into the gpg
    # shell.
    print '\n\nPassing you to gpg for passphrase.'
    print 'Hit ^] after succesfully typing in your passphrase'
    gpg.interact()
    # When we return, we have a Command> prompt that w can't
    # 'expect'... or at least if the user did it right
    print ''
    # Unselect this UID
    debug('unselecting uid')
    debug('Saving key')
    gpg.sendline('save')
    return True
Example #15
0
File: signer.py Project: zorun/pius
  def get_all_keyids(self):
    '''Given a keyring, get all the KeyIDs from it.'''
    debug('extracting all keyids from keyring')
    cmd = [self.gpg] + self.gpg_base_opts + [
        '--no-default-keyring',
        '--keyring', self.keyring,
        '--no-options',
        '--with-colons',
        '--keyid-format', 'long',
        '--fingerprint',
        '--fixed-list-mode',
    ]
    logcmd(cmd)
    gpg = subprocess.Popen(
        cmd,
        stdin=self.null,
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT
    )
    # We use 'pub' instead of 'fpr' to support old crufty keys too...
    pub_re = re.compile('^pub:')
    uid_re = re.compile('^uid:')
    key_tuples = []
    name = keyid = None
    for line in gpg.stdout:
      if pub_re.match(line):
        lineparts = line.split(':')
        keyid = lineparts[4]
      elif keyid and uid_re.match(line):
        lineparts = line.split(':')
        name = lineparts[9]
        debug('Got id %s for %s' % (keyid, name))
        key_tuples.append((name, keyid))
        name = keyid = None

    # sort the list
    if self.sort_keyring:
      keyids = [i[1] for i in sorted(key_tuples)]
    else:
      keyids = [i[1] for i in key_tuples]
    return keyids
Example #16
0
    def get_all_keyids(self):
        '''Given a keyring, get all the KeyIDs from it.'''
        debug('extracting all keyids from keyring')
        cmd = [self.gpg] + self.gpg_base_opts + [
            '--no-default-keyring',
            '--keyring',
            self.keyring,
            '--no-options',
            '--with-colons',
            '--keyid-format',
            'long',
            '--fingerprint',
            '--fixed-list-mode',
        ]
        logcmd(cmd)
        gpg = subprocess.Popen(cmd,
                               stdin=self.null,
                               stdout=subprocess.PIPE,
                               stderr=subprocess.STDOUT)
        # We use 'pub' instead of 'fpr' to support old crufty keys too...
        pub_re = re.compile('^pub:')
        uid_re = re.compile('^uid:')
        key_tuples = []
        name = keyid = None
        for line in gpg.stdout:
            if pub_re.match(line):
                lineparts = line.split(':')
                keyid = lineparts[4]
            elif keyid and uid_re.match(line):
                lineparts = line.split(':')
                name = lineparts[9]
                debug('Got id %s for %s' % (keyid, name))
                key_tuples.append((name, keyid))
                name = keyid = None

        # sort the list
        if self.sort_keyring:
            keyids = [i[1] for i in sorted(key_tuples)]
        else:
            keyids = [i[1] for i in key_tuples]
        return keyids
Example #17
0
    def get_uids(self, key):
        '''Get all UIDs on a given key.'''
        cmd = [self.gpg] + self.gpg_base_opts +  self.gpg_quiet_opts + \
          self.gpg_fd_opts + [
              '--no-default-keyring',
              '--keyring', self.keyring,
              '--no-options',
              '--with-colons',
              '--edit-key', key,
          ]
        logcmd(cmd)
        gpg = subprocess.Popen(cmd,
                               stdin=subprocess.PIPE,
                               stdout=subprocess.PIPE,
                               stderr=self.null,
                               close_fds=True)

        gpg.stdin.write('\n')
        uids = []
        unique_files = []
        while True:
            line = gpg.stdout.readline().strip()

            # skip the things we don't care about...
            if not line:
                debug('breaking, EOF')
                break
            if line == PiusSigner.GPG_PROMPT:
                debug('got to command prompt')
                break

            # Parse the line...
            debug('Got a line %s' % line)
            fields = line.split(':')

            if fields[0] != 'uid':
                continue

            status = fields[1]
            uid = fields[9]
            index = int(fields[13].split(',')[0])

            debug('Got UID %s with status %s' % (uid, status))

            # If we can we capture an email address is saved for
            # emailing off signed keys (not yet implemented), and
            # also for the ID for that UID.
            #
            # If we can't, then we grab what we can and make it the
            # id and blank out the email.
            #
            # For the normal case (have email), we'll be storing each email twice
            # but that's OK since it means that email is *always* a valid email or
            # None and id is *always* a valid identifier
            match = re.search('.* <(.*)>', uid)
            if match:
                email = match.group(1)
                debug('got email %s' % email)
                filename = re.sub('@', '_at_', email)
                filename = '%s__%s' % (key, filename)
                uid = email
            else:
                # but if it doesn't have an email, do the right thing
                email = None
                debug('no email')
                uid = re.sub(' ', '_', uid)
                uid = re.sub('\'', '', uid)
                filename = '%s__%s' % (key, uid)
            # Append the UID we're signing with in case people sign with 2
            # keys in succession:
            filename = '%s__%s' % (filename, self.signer)

            if filename in unique_files:
                debug('Filename is a duplicate')
                count = 2
                while True:
                    test = '%s_%s' % (filename, count)
                    debug('Trying %s' % test)
                    if test not in unique_files:
                        debug('%s worked!' % test)
                        filename = test
                        break
                    else:
                        count += 1
            else:
                debug('%s isn\'t in %s' % (filename, repr(unique_files)))

            # NOTE: Make sure to append the file BEFORE adding the extension
            #       since that's what we test against above!
            unique_files.append(filename)
            filename += '.asc'
            uids.append({
                'email': email,
                'file': self._outfile_path(filename),
                'status': status,
                'id': uid,
                'index': index
            })

        debug('quitting')
        # sometimes it wants a save here. I don't know why. We can quit and check
        # for a save prompt, and then hit no, but we have to make sure it's still
        # running or we'll hang. It's just easier to issue a 'save' instead of a
        # quit
        gpg.stdin.write('save\n')
        debug('waiting')
        gpg.wait()

        return uids
Example #18
0
File: signer.py Project: zorun/pius
  def get_uids(self, key):
    '''Get all UIDs on a given key.'''
    cmd = [self.gpg] + self.gpg_base_opts +  self.gpg_quiet_opts + \
      self.gpg_fd_opts + [
          '--no-default-keyring',
          '--keyring', self.keyring,
          '--no-options',
          '--with-colons',
          '--edit-key', key,
      ]
    logcmd(cmd)
    gpg = subprocess.Popen(cmd, stdin=subprocess.PIPE,
                           stdout=subprocess.PIPE,
                           stderr=self.null,
                           close_fds=True)

    gpg.stdin.write('\n')
    uids = []
    unique_files = []
    while True:
      line = gpg.stdout.readline().strip()

      # skip the things we don't care about...
      if not line:
        debug('breaking, EOF')
        break
      if line == PiusSigner.GPG_PROMPT:
        debug('got to command prompt')
        break

      # Parse the line...
      debug('Got a line %s' % line)
      fields = line.split(':')

      if fields[0] != 'uid':
        continue

      status = fields[1]
      uid = fields[9]
      index = int(fields[13].split(',')[0])

      debug('Got UID %s with status %s' % (uid, status))

      # If we can we capture an email address is saved for
      # emailing off signed keys (not yet implemented), and
      # also for the ID for that UID.
      #
      # If we can't, then we grab what we can and make it the
      # id and blank out the email.
      #
      # For the normal case (have email), we'll be storing each email twice
      # but that's OK since it means that email is *always* a valid email or
      # None and id is *always* a valid identifier
      match = re.search('.* <(.*)>', uid)
      if match:
        email = match.group(1)
        debug('got email %s' % email)
        filename = re.sub('@', '_at_', email)
        filename = '%s__%s' % (key, filename)
        uid = email
      else:
        # but if it doesn't have an email, do the right thing
        email = None
        debug('no email')
        uid = re.sub(' ', '_', uid)
        uid = re.sub('\'', '', uid)
        filename = '%s__%s' % (key, uid)
      # Append the UID we're signing with in case people sign with 2
      # keys in succession:
      filename = '%s__%s' % (filename, self.signer)

      if filename in unique_files:
        debug('Filename is a duplicate')
        count = 2
        while True:
          test = '%s_%s' % (filename, count)
          debug('Trying %s' % test)
          if test not in unique_files:
            debug('%s worked!' % test)
            filename = test
            break
          else:
            count += 1
      else:
        debug('%s isn\'t in %s' % (filename, repr(unique_files)))

      # NOTE: Make sure to append the file BEFORE adding the extension
      #       since that's what we test against above!
      unique_files.append(filename)
      filename += '.asc'
      uids.append({'email': email, 'file': self._outfile_path(filename),
                   'status': status, 'id': uid, 'index': index})

    debug('quitting')
    # sometimes it wants a save here. I don't know why. We can quit and check
    # for a save prompt, and then hit no, but we have to make sure it's still
    # running or we'll hang. It's just easier to issue a 'save' instead of a
    # quit
    gpg.stdin.write('save\n')
    debug('waiting')
    gpg.wait()

    return uids
Example #19
0
    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
Example #20
0
File: signer.py Project: zorun/pius
  def encrypt_and_sign_file(self, infile, outfile, keyid):
    '''Encrypt and sign a file.

    Used for PGP/Mime email generation.'''
    agent = []
    if self.mode == MODE_AGENT:
      agent = ['--use-agent']
    cmd = [self.gpg] + self.gpg_base_opts +  self.gpg_quiet_opts + \
      self.gpg_fd_opts + agent + [
          '--no-default-keyring',
          '--keyring', self.tmp_keyring,
          '--no-options',
          '--always-trust',
          '-u', self.force_signer,
          '-aes',
          '-r', keyid,
          '-r', self.signer,
          '--output', outfile,
          infile,
      ]
    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
      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)

    while True:
      debug('Waiting for response')
      line = gpg.stdout.readline().strip()
      debug('Got %s' % line)
      if PiusSigner.GPG_ENC_BEG in line:
        debug('Got GPG_ENC_BEG')
        continue
      elif PiusSigner.GPG_ENC_END in line:
        debug('Got GPG_ENC_END')
        break
      elif PiusSigner.GPG_ENC_INV in line:
        debug('Got GPG_ENC_INV')
        raise EncryptionKeyError
      elif (PiusSigner.GPG_KEY_EXP in line or
            PiusSigner.GPG_SIG_EXP in line):
        # These just mean we passed a given key/sig that's expired, there
        # may be ones left that are good. We cannot report an error until
        # we get a ENC_INV.
        debug('Got GPG_KEY/SIG_EXP')
        continue
      elif (PiusSigner.GPG_USERID in line or
            PiusSigner.GPG_NEED_PASS in line or
            PiusSigner.GPG_GOOD_PASS in line or
            PiusSigner.GPG_SIG_BEG in line or
            PiusSigner.GPG_SIG_CREATED in line or
            PiusSigner.GPG_PROGRESS in line or
            PiusSigner.GPG_PINENTRY_LAUNCHED in line):
        debug('Got skippable stuff')
        continue
      else:
        raise EncryptionUnknownError(line)

    retval = gpg.wait()
    if retval != 0:
      raise EncryptionUnknownError("Return code was %s" % retval)
Example #21
0
    def encrypt_and_sign_file(self, infile, outfile, keyid):
        '''Encrypt and sign a file.

    Used for PGP/Mime email generation.'''
        agent = []
        if self.mode == MODE_AGENT:
            agent = ['--use-agent']
        cmd = [self.gpg] + self.gpg_base_opts +  self.gpg_quiet_opts + \
          self.gpg_fd_opts + agent + [
              '--no-default-keyring',
              '--keyring', self.tmp_keyring,
              '--no-options',
              '--always-trust',
              '-u', self.force_signer,
              '-aes',
              '-r', keyid,
              '-r', self.signer,
              '--output', outfile,
              infile,
          ]
        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
            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)

        while True:
            debug('Waiting for response')
            line = gpg.stdout.readline().strip()
            debug('Got %s' % line)
            if PiusSigner.GPG_ENC_BEG in line:
                debug('Got GPG_ENC_BEG')
                continue
            elif PiusSigner.GPG_ENC_END in line:
                debug('Got GPG_ENC_END')
                break
            elif PiusSigner.GPG_ENC_INV in line:
                debug('Got GPG_ENC_INV')
                raise EncryptionKeyError
            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):
                # These just mean we passed a given key/sig that's expired, there
                # may be ones left that are good. We cannot report an error until
                # we get a ENC_INV.
                debug('Got GPG_KEY/SIG_EXP')
                continue
            elif (PiusSigner.GPG_USERID in line
                  or PiusSigner.GPG_NEED_PASS in line
                  or PiusSigner.GPG_GOOD_PASS in line
                  or PiusSigner.GPG_SIG_BEG in line
                  or PiusSigner.GPG_SIG_CREATED in line
                  or PiusSigner.GPG_PROGRESS in line
                  or PiusSigner.GPG_PINENTRY_LAUNCHED in line
                  or PiusSigner.GPG_WARN_VERSION):
                debug('Got skippable stuff')
                continue
            else:
                raise EncryptionUnknownError(line)

        retval = gpg.wait()
        if retval != 0:
            raise EncryptionUnknownError("Return code was %s" % retval)
Example #22
0
File: signer.py Project: zorun/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 + [
          '--default-cert-level', level,
          '--no-ask-cert-level',
          '--edit-key', key,
      ] + self.policy_opts()
    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_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