summaryrefslogtreecommitdiff
blob: deaf36ad4a7f0ccabba940275e520bbde1956ff3 (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
# portage_gpg.py -- core Portage functionality
# Copyright 2004 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Header: /var/cvsroot/gentoo-src/portage/pym/portage_gpg.py,v 1.6.2.1 2005/01/16 02:35:33 carpaski Exp $
cvs_id_string="$Id: portage_gpg.py,v 1.6.2.1 2005/01/16 02:35:33 carpaski Exp $"[5:-2]

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