summaryrefslogtreecommitdiffstats
path: root/pym/portage/gpg.py
blob: 1ddb99d3ee4e4dfef19a1c4f75eb1c4555d0284f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# 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