/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
5971.1.36 by Jonathan Riddell
update copyright
1
# Copyright (C) 2005, 2011 Canonical Ltd
1442.1.57 by Robert Collins
check that we get the right command line from the default gpg strategy.
2
#   Authors: Robert Collins <robert.collins@canonical.com>
3
#
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1442.1.57 by Robert Collins
check that we get the right command line from the default gpg strategy.
17
18
"""GPG signing and checking logic."""
19
1996.3.1 by John Arbash Meinel
Demandloading builtins.py drops our load time from 350ms to 291ms
20
import os
21
import sys
5971.1.4 by Jonathan Riddell
tidy up repository and gpg.py
22
from StringIO import StringIO
1996.3.1 by John Arbash Meinel
Demandloading builtins.py drops our load time from 350ms to 291ms
23
24
from bzrlib.lazy_import import lazy_import
25
lazy_import(globals(), """
1442.1.58 by Robert Collins
gpg signing of content
26
import errno
1442.1.57 by Robert Collins
check that we get the right command line from the default gpg strategy.
27
import subprocess
28
1912.3.2 by John Arbash Meinel
Adding some logging, because on my machine TTY is not exported by default.
29
from bzrlib import (
30
    errors,
31
    trace,
1551.8.11 by Aaron Bentley
Clear terminal before signing
32
    ui,
5971.1.11 by Jonathan Riddell
add set_acceptable_keys() so user can specify which gpg keys can be used for verification
33
    i18n,
1912.3.2 by John Arbash Meinel
Adding some logging, because on my machine TTY is not exported by default.
34
    )
1996.3.1 by John Arbash Meinel
Demandloading builtins.py drops our load time from 350ms to 291ms
35
""")
1442.1.57 by Robert Collins
check that we get the right command line from the default gpg strategy.
36
5971.1.4 by Jonathan Riddell
tidy up repository and gpg.py
37
#verification results
5971.1.1 by Jonathan Riddell
add a verify command
38
SIGNATURE_VALID = 0
39
SIGNATURE_KEY_MISSING = 1
40
SIGNATURE_NOT_VALID = 2
41
SIGNATURE_NOT_SIGNED = 3
42
43
1442.1.62 by Robert Collins
Allow creation of testaments from uncommitted data, and use that to get signatures before committing revisions.
44
class DisabledGPGStrategy(object):
45
    """A GPG Strategy that makes everything fail."""
46
47
    def __init__(self, ignored):
48
        """Real strategies take a configuration."""
49
50
    def sign(self, content):
51
        raise errors.SigningFailed('Signing is disabled.')
52
5971.1.31 by Jonathan Riddell
and update tests
53
    def verify(self, content, testament):
5971.1.33 by Jonathan Riddell
rename errors.VerifyFailed to errors.SignatureVerificationFailed
54
        raise errors.SignatureVerificationFailed('Signature verification is \
55
disabled.')
5971.1.6 by Jonathan Riddell
fix methods for dummy gpg strategies
56
5971.1.14 by Jonathan Riddell
add test for set_acceptable_keys, accept non-trusted keys if specified as acceptable, import dummy key in tests so it works outside my machine
57
    def set_acceptable_keys(self, key_patterns):
58
        pass
59
1442.1.62 by Robert Collins
Allow creation of testaments from uncommitted data, and use that to get signatures before committing revisions.
60
1442.1.59 by Robert Collins
Add re-sign command to generate a digital signature on a single revision.
61
class LoopbackGPGStrategy(object):
1442.1.62 by Robert Collins
Allow creation of testaments from uncommitted data, and use that to get signatures before committing revisions.
62
    """A GPG Strategy that acts like 'cat' - data is just passed through."""
1442.1.59 by Robert Collins
Add re-sign command to generate a digital signature on a single revision.
63
64
    def __init__(self, ignored):
65
        """Real strategies take a configuration."""
66
67
    def sign(self, content):
1551.12.15 by Aaron Bentley
add header/trailer to fake clearsigned texts
68
        return ("-----BEGIN PSEUDO-SIGNED CONTENT-----\n" + content +
1551.12.52 by Aaron Bentley
speling fix
69
                "-----END PSEUDO-SIGNED CONTENT-----\n")
1442.1.59 by Robert Collins
Add re-sign command to generate a digital signature on a single revision.
70
5971.1.31 by Jonathan Riddell
and update tests
71
    def verify(self, content, testament):
5971.1.22 by Jonathan Riddell
fix tests
72
        return SIGNATURE_VALID, None
5971.1.5 by Jonathan Riddell
catch errors from gpgme, implement verify in dummy gpg strategies
73
5971.1.14 by Jonathan Riddell
add test for set_acceptable_keys, accept non-trusted keys if specified as acceptable, import dummy key in tests so it works outside my machine
74
    def set_acceptable_keys(self, key_patterns):
75
        patterns = key_patterns.split(",")
76
        self.acceptable_keys = []
77
        for pattern in patterns:
78
            if pattern == "unknown":
79
                pass
80
            else:
81
                self.acceptable_keys.append(pattern)
82
1442.1.59 by Robert Collins
Add re-sign command to generate a digital signature on a single revision.
83
1912.3.1 by John Arbash Meinel
updating gpg.py to set GPG_TTY in the environment.
84
def _set_gpg_tty():
85
    tty = os.environ.get('TTY')
86
    if tty is not None:
87
        os.environ['GPG_TTY'] = tty
1912.3.2 by John Arbash Meinel
Adding some logging, because on my machine TTY is not exported by default.
88
        trace.mutter('setting GPG_TTY=%s', tty)
89
    else:
90
        # This is not quite worthy of a warning, because some people
91
        # don't need GPG_TTY to be set. But it is worthy of a big mark
92
        # in ~/.bzr.log, so that people can debug it if it happens to them
93
        trace.mutter('** Env var TTY empty, cannot set GPG_TTY.'
94
                     '  Is TTY exported?')
1912.3.1 by John Arbash Meinel
updating gpg.py to set GPG_TTY in the environment.
95
96
1442.1.57 by Robert Collins
check that we get the right command line from the default gpg strategy.
97
class GPGStrategy(object):
98
    """GPG Signing and checking facilities."""
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
99
5971.1.11 by Jonathan Riddell
add set_acceptable_keys() so user can specify which gpg keys can be used for verification
100
    acceptable_keys = None
101
1442.1.57 by Robert Collins
check that we get the right command line from the default gpg strategy.
102
    def _command_line(self):
1185.78.4 by John Arbash Meinel
Reverting gpg changes, should not be mainline, see gpg_uses_tempfile plugin.
103
        return [self._config.gpg_signing_command(), '--clearsign']
1442.1.57 by Robert Collins
check that we get the right command line from the default gpg strategy.
104
105
    def __init__(self, config):
106
        self._config = config
1442.1.58 by Robert Collins
gpg signing of content
107
108
    def sign(self, content):
2273.1.1 by John Arbash Meinel
``GPGStrategy.sign()`` will now raise ``BzrBadParameterUnicode`` if
109
        if isinstance(content, unicode):
110
            raise errors.BzrBadParameterUnicode('content')
1551.8.11 by Aaron Bentley
Clear terminal before signing
111
        ui.ui_factory.clear_term()
1963.1.8 by John Arbash Meinel
Don't use preexec_fn on win32
112
113
        preexec_fn = _set_gpg_tty
114
        if sys.platform == 'win32':
115
            # Win32 doesn't support preexec_fn, but wouldn't support TTY anyway.
116
            preexec_fn = None
1442.1.58 by Robert Collins
gpg signing of content
117
        try:
1185.78.4 by John Arbash Meinel
Reverting gpg changes, should not be mainline, see gpg_uses_tempfile plugin.
118
            process = subprocess.Popen(self._command_line(),
119
                                       stdin=subprocess.PIPE,
1912.3.1 by John Arbash Meinel
updating gpg.py to set GPG_TTY in the environment.
120
                                       stdout=subprocess.PIPE,
1963.1.8 by John Arbash Meinel
Don't use preexec_fn on win32
121
                                       preexec_fn=preexec_fn)
1442.1.58 by Robert Collins
gpg signing of content
122
            try:
1185.78.4 by John Arbash Meinel
Reverting gpg changes, should not be mainline, see gpg_uses_tempfile plugin.
123
                result = process.communicate(content)[0]
1442.1.58 by Robert Collins
gpg signing of content
124
                if process.returncode is None:
125
                    process.wait()
126
                if process.returncode != 0:
1185.78.4 by John Arbash Meinel
Reverting gpg changes, should not be mainline, see gpg_uses_tempfile plugin.
127
                    raise errors.SigningFailed(self._command_line())
1442.1.58 by Robert Collins
gpg signing of content
128
                return result
1442.1.59 by Robert Collins
Add re-sign command to generate a digital signature on a single revision.
129
            except OSError, e:
1442.1.58 by Robert Collins
gpg signing of content
130
                if e.errno == errno.EPIPE:
1185.78.4 by John Arbash Meinel
Reverting gpg changes, should not be mainline, see gpg_uses_tempfile plugin.
131
                    raise errors.SigningFailed(self._command_line())
1442.1.59 by Robert Collins
Add re-sign command to generate a digital signature on a single revision.
132
                else:
133
                    raise
1442.1.58 by Robert Collins
gpg signing of content
134
        except ValueError:
135
            # bad subprocess parameters, should never happen.
136
            raise
137
        except OSError, e:
138
            if e.errno == errno.ENOENT:
139
                # gpg is not installed
1185.78.4 by John Arbash Meinel
Reverting gpg changes, should not be mainline, see gpg_uses_tempfile plugin.
140
                raise errors.SigningFailed(self._command_line())
1442.1.58 by Robert Collins
gpg signing of content
141
            else:
142
                raise
5971.1.1 by Jonathan Riddell
add a verify command
143
5971.1.30 by Jonathan Riddell
check the testament actually matches the commit when validating
144
    def verify(self, content, testament):
5971.1.7 by Jonathan Riddell
add method docs
145
        """Check content has a valid signature.
146
        
147
        :param content: the commit signature
5971.1.30 by Jonathan Riddell
check the testament actually matches the commit when validating
148
        :param testament: the valid testament string for the commit
5971.1.7 by Jonathan Riddell
add method docs
149
        
5971.1.18 by Jonathan Riddell
add email to verbose output
150
        :return: SIGNATURE_VALID or a failed SIGNATURE_ value, key uid if valid
5971.1.7 by Jonathan Riddell
add method docs
151
        """
5971.1.4 by Jonathan Riddell
tidy up repository and gpg.py
152
        try:
153
            import gpgme
5971.1.41 by Jonathan Riddell
fix calling GpgmeNotInstalled
154
        except ImportError, error:
155
            raise errors.GpgmeNotInstalled(error)
5971.1.4 by Jonathan Riddell
tidy up repository and gpg.py
156
5971.1.1 by Jonathan Riddell
add a verify command
157
        context = gpgme.Context()
158
        signature = StringIO(content)
5971.1.4 by Jonathan Riddell
tidy up repository and gpg.py
159
        plain_output = StringIO()
5971.1.1 by Jonathan Riddell
add a verify command
160
        
5971.1.5 by Jonathan Riddell
catch errors from gpgme, implement verify in dummy gpg strategies
161
        try:
162
            result = context.verify(signature, None, plain_output)
163
        except gpgme.GpgmeError,error:
5971.1.33 by Jonathan Riddell
rename errors.VerifyFailed to errors.SignatureVerificationFailed
164
            raise errors.SignatureVerificationFailed(error[2])
5971.1.1 by Jonathan Riddell
add a verify command
165
5971.1.9 by Jonathan Riddell
add some tests
166
        if len(result) == 0:
5971.1.17 by Jonathan Riddell
add verbose option
167
            return SIGNATURE_NOT_VALID, None
5971.1.13 by Jonathan Riddell
return missing if not in acceptable keys
168
        fingerprint = result[0].fpr
169
        if self.acceptable_keys is not None:
170
            if not fingerprint in self.acceptable_keys:
5971.1.27 by Jonathan Riddell
verbose info for unknown keys
171
                return SIGNATURE_KEY_MISSING, fingerprint[-8:]
5971.1.30 by Jonathan Riddell
check the testament actually matches the commit when validating
172
        if testament != plain_output.getvalue():
173
            return SIGNATURE_NOT_VALID, None
5971.1.1 by Jonathan Riddell
add a verify command
174
        if result[0].summary & gpgme.SIGSUM_VALID:
5971.1.17 by Jonathan Riddell
add verbose option
175
            key = context.get_key(fingerprint)
176
            name = key.uids[0].name
5971.1.18 by Jonathan Riddell
add email to verbose output
177
            email = key.uids[0].email
178
            return SIGNATURE_VALID, name + " <" + email + ">"
5971.1.1 by Jonathan Riddell
add a verify command
179
        if result[0].summary & gpgme.SIGSUM_RED:
5971.1.17 by Jonathan Riddell
add verbose option
180
            return SIGNATURE_NOT_VALID, None
5971.1.1 by Jonathan Riddell
add a verify command
181
        if result[0].summary & gpgme.SIGSUM_KEY_MISSING:
5971.1.27 by Jonathan Riddell
verbose info for unknown keys
182
            return SIGNATURE_KEY_MISSING, fingerprint[-8:]
5971.1.14 by Jonathan Riddell
add test for set_acceptable_keys, accept non-trusted keys if specified as acceptable, import dummy key in tests so it works outside my machine
183
        #summary isn't set if sig is valid but key is untrusted
184
        if result[0].summary == 0 and self.acceptable_keys is not None:
185
            if fingerprint in self.acceptable_keys:
5971.1.17 by Jonathan Riddell
add verbose option
186
                return SIGNATURE_VALID, None
5971.1.14 by Jonathan Riddell
add test for set_acceptable_keys, accept non-trusted keys if specified as acceptable, import dummy key in tests so it works outside my machine
187
        else:
5971.1.17 by Jonathan Riddell
add verbose option
188
            return SIGNATURE_KEY_MISSING, None
5971.1.42 by Jonathan Riddell
fix string formatting
189
        raise errors.SignatureVerificationFailed("Unknown GnuPG key "\
190
                                                 "verification result")
5971.1.11 by Jonathan Riddell
add set_acceptable_keys() so user can specify which gpg keys can be used for verification
191
192
    def set_acceptable_keys(self, key_patterns):
193
        try:
194
            import gpgme
5971.1.41 by Jonathan Riddell
fix calling GpgmeNotInstalled
195
        except ImportError, error:
196
            raise errors.GpgmeNotInstalled(error)
5971.1.11 by Jonathan Riddell
add set_acceptable_keys() so user can specify which gpg keys can be used for verification
197
        patterns = key_patterns.split(",")
198
199
        self.acceptable_keys = []
200
        context = gpgme.Context()
201
        for pattern in patterns:
202
            result = context.keylist(pattern)
203
            found_key = False
204
            for key in result:
205
                found_key = True
206
                self.acceptable_keys.append(key.subkeys[0].fpr)
207
                trace.mutter("Added acceptable key: " + key.subkeys[0].fpr)
208
            if not found_key:
209
                trace.note(i18n.gettext(
210
                           "No GnuPG key results for pattern: {}"
211
                            ).format(pattern))