~ntt-pf-lab/nova/monkey_patch_notification

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/conch/ssh/agent.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (c) 2001-2008 Twisted Matrix Laboratories.
 
2
# See LICENSE for details.
 
3
 
 
4
"""
 
5
Implements the SSH v2 key agent protocol.  This protocol is documented in the
 
6
SSH source code, in the file
 
7
U{PROTOCOL.agent<http://www.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.agent>}.
 
8
 
 
9
Maintainer: Paul Swartz
 
10
"""
 
11
 
 
12
import struct
 
13
 
 
14
from twisted.conch.ssh.common import NS, getNS, getMP
 
15
from twisted.conch.error import ConchError, MissingKeyStoreError
 
16
from twisted.conch.ssh import keys
 
17
from twisted.internet import defer, protocol
 
18
 
 
19
 
 
20
 
 
21
class SSHAgentClient(protocol.Protocol):
 
22
    """
 
23
    The client side of the SSH agent protocol.  This is equivalent to
 
24
    ssh-add(1) and can be used with either ssh-agent(1) or the SSHAgentServer
 
25
    protocol, also in this package.
 
26
    """
 
27
 
 
28
    def __init__(self):
 
29
        self.buf = ''
 
30
        self.deferreds = []
 
31
 
 
32
 
 
33
    def dataReceived(self, data):
 
34
        self.buf += data
 
35
        while 1:
 
36
            if len(self.buf) <= 4:
 
37
                return
 
38
            packLen = struct.unpack('!L', self.buf[:4])[0]
 
39
            if len(self.buf) < 4 + packLen:
 
40
                return
 
41
            packet, self.buf = self.buf[4:4 + packLen], self.buf[4 + packLen:]
 
42
            reqType = ord(packet[0])
 
43
            d = self.deferreds.pop(0)
 
44
            if reqType == AGENT_FAILURE:
 
45
                d.errback(ConchError('agent failure'))
 
46
            elif reqType == AGENT_SUCCESS:
 
47
                d.callback('')
 
48
            else:
 
49
                d.callback(packet)
 
50
 
 
51
 
 
52
    def sendRequest(self, reqType, data):
 
53
        pack = struct.pack('!LB',len(data) + 1, reqType) + data
 
54
        self.transport.write(pack)
 
55
        d = defer.Deferred()
 
56
        self.deferreds.append(d)
 
57
        return d
 
58
 
 
59
 
 
60
    def requestIdentities(self):
 
61
        """
 
62
        @return: A L{Deferred} which will fire with a list of all keys found in
 
63
            the SSH agent. The list of keys is comprised of (public key blob,
 
64
            comment) tuples.
 
65
        """
 
66
        d = self.sendRequest(AGENTC_REQUEST_IDENTITIES, '')
 
67
        d.addCallback(self._cbRequestIdentities)
 
68
        return d
 
69
 
 
70
 
 
71
    def _cbRequestIdentities(self, data):
 
72
        """
 
73
        Unpack a collection of identities into a list of tuples comprised of
 
74
        public key blobs and comments.
 
75
        """
 
76
        if ord(data[0]) != AGENT_IDENTITIES_ANSWER:
 
77
            raise ConchError('unexpected response: %i' % ord(data[0]))
 
78
        numKeys = struct.unpack('!L', data[1:5])[0]
 
79
        keys = []
 
80
        data = data[5:]
 
81
        for i in range(numKeys):
 
82
            blob, data = getNS(data)
 
83
            comment, data = getNS(data)
 
84
            keys.append((blob, comment))
 
85
        return keys
 
86
 
 
87
 
 
88
    def addIdentity(self, blob, comment = ''):
 
89
        """
 
90
        Add a private key blob to the agent's collection of keys.
 
91
        """
 
92
        req = blob
 
93
        req += NS(comment)
 
94
        return self.sendRequest(AGENTC_ADD_IDENTITY, req)
 
95
 
 
96
 
 
97
    def signData(self, blob, data):
 
98
        """
 
99
        Request that the agent sign the given C{data} with the private key
 
100
        which corresponds to the public key given by C{blob}.  The private
 
101
        key should have been added to the agent already.
 
102
 
 
103
        @type blob: C{str}
 
104
        @type data: C{str}
 
105
        @return: A L{Deferred} which fires with a signature for given data
 
106
            created with the given key.
 
107
        """
 
108
        req = NS(blob)
 
109
        req += NS(data)
 
110
        req += '\000\000\000\000' # flags
 
111
        return self.sendRequest(AGENTC_SIGN_REQUEST, req).addCallback(self._cbSignData)
 
112
 
 
113
 
 
114
    def _cbSignData(self, data):
 
115
        if ord(data[0]) != AGENT_SIGN_RESPONSE:
 
116
            raise ConchError('unexpected data: %i' % ord(data[0]))
 
117
        signature = getNS(data[1:])[0]
 
118
        return signature
 
119
 
 
120
 
 
121
    def removeIdentity(self, blob):
 
122
        """
 
123
        Remove the private key corresponding to the public key in blob from the
 
124
        running agent.
 
125
        """
 
126
        req = NS(blob)
 
127
        return self.sendRequest(AGENTC_REMOVE_IDENTITY, req)
 
128
 
 
129
 
 
130
    def removeAllIdentities(self):
 
131
        """
 
132
        Remove all keys from the running agent.
 
133
        """
 
134
        return self.sendRequest(AGENTC_REMOVE_ALL_IDENTITIES, '')
 
135
 
 
136
 
 
137
 
 
138
class SSHAgentServer(protocol.Protocol):
 
139
    """
 
140
    The server side of the SSH agent protocol.  This is equivalent to
 
141
    ssh-agent(1) and can be used with either ssh-add(1) or the SSHAgentClient
 
142
    protocol, also in this package.
 
143
    """
 
144
 
 
145
    def __init__(self):
 
146
        self.buf = ''
 
147
 
 
148
 
 
149
    def dataReceived(self, data):
 
150
        self.buf += data
 
151
        while 1:
 
152
            if len(self.buf) <= 4:
 
153
                return
 
154
            packLen = struct.unpack('!L', self.buf[:4])[0]
 
155
            if len(self.buf) < 4 + packLen:
 
156
                return
 
157
            packet, self.buf = self.buf[4:4 + packLen], self.buf[4 + packLen:]
 
158
            reqType = ord(packet[0])
 
159
            reqName = messages.get(reqType, None)
 
160
            if not reqName:
 
161
                self.sendResponse(AGENT_FAILURE, '')
 
162
            else:
 
163
                f = getattr(self, 'agentc_%s' % reqName)
 
164
                if getattr(self.factory, 'keys', None) is None:
 
165
                    self.sendResponse(AGENT_FAILURE, '')
 
166
                    raise MissingKeyStoreError()
 
167
                f(packet[1:])
 
168
 
 
169
 
 
170
    def sendResponse(self, reqType, data):
 
171
        pack = struct.pack('!LB', len(data) + 1, reqType) + data
 
172
        self.transport.write(pack)
 
173
 
 
174
 
 
175
    def agentc_REQUEST_IDENTITIES(self, data):
 
176
        """
 
177
        Return all of the identities that have been added to the server
 
178
        """
 
179
        assert data == ''
 
180
        numKeys = len(self.factory.keys)
 
181
        resp = []
 
182
 
 
183
        resp.append(struct.pack('!L', numKeys))
 
184
        for key, comment in self.factory.keys.itervalues():
 
185
            resp.append(NS(key.blob())) # yes, wrapped in an NS
 
186
            resp.append(NS(comment))
 
187
        self.sendResponse(AGENT_IDENTITIES_ANSWER, ''.join(resp))
 
188
 
 
189
 
 
190
    def agentc_SIGN_REQUEST(self, data):
 
191
        """
 
192
        Data is a structure with a reference to an already added key object and
 
193
        some data that the clients wants signed with that key.  If the key
 
194
        object wasn't loaded, return AGENT_FAILURE, else return the signature.
 
195
        """
 
196
        blob, data = getNS(data)
 
197
        if blob not in self.factory.keys:
 
198
            return self.sendResponse(AGENT_FAILURE, '')
 
199
        signData, data = getNS(data)
 
200
        assert data == '\000\000\000\000'
 
201
        self.sendResponse(AGENT_SIGN_RESPONSE, NS(self.factory.keys[blob][0].sign(signData)))
 
202
 
 
203
 
 
204
    def agentc_ADD_IDENTITY(self, data):
 
205
        """
 
206
        Adds a private key to the agent's collection of identities.  On
 
207
        subsequent interactions, the private key can be accessed using only the
 
208
        corresponding public key.
 
209
        """
 
210
 
 
211
        # need to pre-read the key data so we can get past it to the comment string
 
212
        keyType, rest = getNS(data)
 
213
        if keyType == 'ssh-rsa':
 
214
            nmp = 6
 
215
        elif keyType == 'ssh-dss':
 
216
            nmp = 5
 
217
        else:
 
218
            raise keys.BadKeyError('unknown blob type: %s' % keyType)
 
219
 
 
220
        rest = getMP(rest, nmp)[-1] # ignore the key data for now, we just want the comment
 
221
        comment, rest = getNS(rest) # the comment, tacked onto the end of the key blob
 
222
 
 
223
        k = keys.Key.fromString(data, type='private_blob') # not wrapped in NS here
 
224
        self.factory.keys[k.blob()] = (k, comment)
 
225
        self.sendResponse(AGENT_SUCCESS, '')
 
226
 
 
227
 
 
228
    def agentc_REMOVE_IDENTITY(self, data):
 
229
        """
 
230
        Remove a specific key from the agent's collection of identities.
 
231
        """
 
232
        blob, _ = getNS(data)
 
233
        k = keys.Key.fromString(blob, type='blob')
 
234
        del self.factory.keys[k.blob()]
 
235
        self.sendResponse(AGENT_SUCCESS, '')
 
236
 
 
237
 
 
238
    def agentc_REMOVE_ALL_IDENTITIES(self, data):
 
239
        """
 
240
        Remove all keys from the agent's collection of identities.
 
241
        """
 
242
        assert data == ''
 
243
        self.factory.keys = {}
 
244
        self.sendResponse(AGENT_SUCCESS, '')
 
245
 
 
246
    # v1 messages that we ignore because we don't keep v1 keys
 
247
    # open-ssh sends both v1 and v2 commands, so we have to
 
248
    # do no-ops for v1 commands or we'll get "bad request" errors
 
249
 
 
250
    def agentc_REQUEST_RSA_IDENTITIES(self, data):
 
251
        """
 
252
        v1 message for listing RSA1 keys; superseded by
 
253
        agentc_REQUEST_IDENTITIES, which handles different key types.
 
254
        """
 
255
        self.sendResponse(AGENT_RSA_IDENTITIES_ANSWER, struct.pack('!L', 0))
 
256
 
 
257
 
 
258
    def agentc_REMOVE_RSA_IDENTITY(self, data):
 
259
        """
 
260
        v1 message for removing RSA1 keys; superseded by
 
261
        agentc_REMOVE_IDENTITY, which handles different key types.
 
262
        """
 
263
        self.sendResponse(AGENT_SUCCESS, '')
 
264
 
 
265
 
 
266
    def agentc_REMOVE_ALL_RSA_IDENTITIES(self, data):
 
267
        """
 
268
        v1 message for removing all RSA1 keys; superseded by
 
269
        agentc_REMOVE_ALL_IDENTITIES, which handles different key types.
 
270
        """
 
271
        self.sendResponse(AGENT_SUCCESS, '')
 
272
 
 
273
 
 
274
AGENTC_REQUEST_RSA_IDENTITIES   = 1
 
275
AGENT_RSA_IDENTITIES_ANSWER     = 2
 
276
AGENT_FAILURE                   = 5
 
277
AGENT_SUCCESS                   = 6
 
278
 
 
279
AGENTC_REMOVE_RSA_IDENTITY         = 8
 
280
AGENTC_REMOVE_ALL_RSA_IDENTITIES   = 9
 
281
 
 
282
AGENTC_REQUEST_IDENTITIES       = 11
 
283
AGENT_IDENTITIES_ANSWER         = 12
 
284
AGENTC_SIGN_REQUEST             = 13
 
285
AGENT_SIGN_RESPONSE             = 14
 
286
AGENTC_ADD_IDENTITY             = 17
 
287
AGENTC_REMOVE_IDENTITY          = 18
 
288
AGENTC_REMOVE_ALL_IDENTITIES    = 19
 
289
 
 
290
messages = {}
 
291
for name, value in locals().copy().items():
 
292
    if name[:7] == 'AGENTC_':
 
293
        messages[value] = name[7:] # doesn't handle doubles
 
294