| # portage_gpg.py -- core Portage functionality |
| # Copyright 2004 Gentoo Foundation |
| # Distributed under the terms of the GNU General Public License v2 |
| # $Id$ |
| |
| |
| import os |
| import copy |
| import types |
| import commands |
| import portage_exception |
| import portage_checksum |
| |
| GPG_BINARY = "/usr/bin/gpg" |
| GPG_OPTIONS = " --lock-never --no-random-seed-file --no-greeting --no-sig-cache " |
| GPG_VERIFY_FLAGS = " --verify " |
| GPG_KEYDIR = " --homedir '%s' " |
| GPG_KEYRING = " --keyring '%s' " |
| |
| UNTRUSTED = 0 |
| EXISTS = UNTRUSTED + 1 |
| MARGINAL = EXISTS + 1 |
| TRUSTED = MARGINAL + 1 |
| |
| def fileStats(filepath): |
| mya = [] |
| for x in os.stat(filepath): |
| mya.append(x) |
| mya.append(portage_checksum.perform_checksum(filepath)) |
| return mya |
| |
| |
| class FileChecker: |
| def __init__(self,keydir=None,keyring=None,requireSignedRing=False,minimumTrust=EXISTS): |
| self.minimumTrust = TRUSTED # Default we require trust. For rings. |
| self.keydir = None |
| self.keyring = None |
| self.keyringPath = None |
| self.keyringStats = None |
| self.keyringIsTrusted = False |
| |
| if (keydir != None): |
| # Verify that the keydir is valid. |
| if type(keydir) != types.StringType: |
| raise portage_exception.InvalidDataType, "keydir argument: %s" % keydir |
| if not os.path.isdir(keydir): |
| raise portage_exception.DirectoryNotFound, "keydir: %s" % keydir |
| self.keydir = copy.deepcopy(keydir) |
| |
| if (keyring != None): |
| # Verify that the keyring is a valid filename and exists. |
| if type(keyring) != types.StringType: |
| raise portage_exception.InvalidDataType, "keyring argument: %s" % keyring |
| if keyring.find("/") != -1: |
| raise portage_exception.InvalidData, "keyring: %s" % keyring |
| pathname = "" |
| if keydir: |
| pathname = keydir + "/" + keyring |
| if not os.path.isfile(pathname): |
| raise portage_exception.FileNotFound, "keyring missing: %s (dev.gentoo.org/~carpaski/gpg/)" % pathname |
| |
| keyringPath = keydir+"/"+keyring |
| |
| if not keyring or not keyringPath and requireSignedRing: |
| raise portage_exception.MissingParameter |
| |
| self.keyringStats = fileStats(keyringPath) |
| self.minimumTrust = TRUSTED |
| if not self.verify(keyringPath, keyringPath+".asc"): |
| self.keyringIsTrusted = False |
| if requireSignedRing: |
| raise portage_exception.InvalidSignature, "Required keyring verification: "+keyringPath |
| else: |
| self.keyringIsTrusted = True |
| |
| self.keyring = copy.deepcopy(keyring) |
| self.keyringPath = self.keydir+"/"+self.keyring |
| self.minimumTrust = minimumTrust |
| |
| def _verifyKeyring(self): |
| if self.keyringStats and self.keyringPath: |
| new_stats = fileStats(self.keyringPath) |
| if new_stats != self.keyringStats: |
| raise portage_exception.SecurityViolation, "GPG keyring changed!" |
| |
| def verify(self, filename, sigfile=None): |
| """Uses minimumTrust to determine if it is Valid/True or Invalid/False""" |
| self._verifyKeyring() |
| |
| if not os.path.isfile(filename): |
| raise portage_exception.FileNotFound, filename |
| |
| if sigfile and not os.path.isfile(sigfile): |
| raise portage_exception.FileNotFound, sigfile |
| |
| if self.keydir and not os.path.isdir(self.keydir): |
| raise portage_exception.DirectoryNotFound, filename |
| |
| if self.keyringPath: |
| if not os.path.isfile(self.keyringPath): |
| raise portage_exception.FileNotFound, self.keyringPath |
| |
| if not os.path.isfile(filename): |
| raise portage_exception.CommandNotFound, filename |
| |
| command = GPG_BINARY + GPG_VERIFY_FLAGS + GPG_OPTIONS |
| if self.keydir: |
| command += GPG_KEYDIR % (self.keydir) |
| if self.keyring: |
| command += GPG_KEYRING % (self.keyring) |
| |
| if sigfile: |
| command += " '"+sigfile+"'" |
| command += " '"+filename+"'" |
| |
| result,output = commands.getstatusoutput(command) |
| |
| signal = result & 0xff |
| result = (result >> 8) |
| |
| if signal: |
| raise SignalCaught, "Signal: %d" % (signal) |
| |
| trustLevel = UNTRUSTED |
| if result == 0: |
| trustLevel = TRUSTED |
| #if output.find("WARNING") != -1: |
| # trustLevel = MARGINAL |
| if output.find("BAD") != -1: |
| raise portage_exception.InvalidSignature, filename |
| elif result == 1: |
| trustLevel = EXISTS |
| if output.find("BAD") != -1: |
| raise portage_exception.InvalidSignature, filename |
| elif result == 2: |
| trustLevel = UNTRUSTED |
| if output.find("could not be verified") != -1: |
| raise portage_exception.MissingSignature, filename |
| if output.find("public key not found") != -1: |
| if self.keyringIsTrusted: # We trust the ring, but not the key specifically. |
| trustLevel = MARGINAL |
| else: |
| raise portage_exception.InvalidSignature, filename+" (Unknown Signature)" |
| else: |
| raise portage_exception.UnknownCondition, "GPG returned unknown result: %d" % (result) |
| |
| if trustLevel >= self.minimumTrust: |
| return True |
| return False |