blob: 1ddb99d3ee4e4dfef19a1c4f75eb1c4555d0284f [file] [log] [blame]
# 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
from portage.exception import CommandNotFound, \
DirectoryNotFound, FileNotFound, \
InvalidData, InvalidDataType, InvalidSignature, MissingParameter, \
MissingSignature, PortageException, SecurityViolation
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(object):
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 InvalidDataType(
"keydir argument: %s" % keydir)
if not os.path.isdir(keydir):
raise 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 InvalidDataType("keyring argument: %s" % keyring)
if keyring.find("/") != -1:
raise InvalidData("keyring: %s" % keyring)
pathname = ""
if keydir:
pathname = keydir + "/" + keyring
if not os.path.isfile(pathname):
raise FileNotFound(
"keyring missing: %s (dev.gentoo.org/~carpaski/gpg/)" % \
pathname)
keyringPath = keydir+"/"+keyring
if not keyring or not keyringPath and requireSignedRing:
raise MissingParameter((keyring, keyringPath))
self.keyringStats = fileStats(keyringPath)
self.minimumTrust = TRUSTED
if not self.verify(keyringPath, keyringPath+".asc"):
self.keyringIsTrusted = False
if requireSignedRing:
raise 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 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 FileNotFound, filename
if sigfile and not os.path.isfile(sigfile):
raise FileNotFound, sigfile
if self.keydir and not os.path.isdir(self.keydir):
raise DirectoryNotFound, filename
if self.keyringPath:
if not os.path.isfile(self.keyringPath):
raise FileNotFound, self.keyringPath
if not os.path.isfile(filename):
raise 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 PortageException("Signal: %d" % (signal))
trustLevel = UNTRUSTED
if result == 0:
trustLevel = TRUSTED
#if portage.output.find("WARNING") != -1:
# trustLevel = MARGINAL
if portage.output.find("BAD") != -1:
raise InvalidSignature(filename)
elif result == 1:
trustLevel = EXISTS
if portage.output.find("BAD") != -1:
raise InvalidSignature(filename)
elif result == 2:
trustLevel = UNTRUSTED
if portage.output.find("could not be verified") != -1:
raise MissingSignature(filename)
if portage.output.find("public key not found") != -1:
if self.keyringIsTrusted: # We trust the ring, but not the key specifically.
trustLevel = MARGINAL
else:
raise InvalidSignature(filename+"(Unknown Signature)")
else:
raise PortageException("GPG returned unknown result: %d" % (result))
if trustLevel >= self.minimumTrust:
return True
return False