Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(138)

Side by Side Diff: third_party/twisted_8_1/twisted/conch/ssh/userauth.py

Issue 12261012: Remove third_party/twisted_8_1 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 # Copyright (c) 2001-2004 Twisted Matrix Laboratories.
2 # See LICENSE for details.
3
4 #
5
6 """
7 Implementation of the ssh-userauth service.
8 Currently implemented authentication types are public-key and password.
9
10 Maintainer: U{Paul Swartz<mailto:z3p@twistedmatrix.com>}
11 """
12
13 import struct
14 from twisted.conch import error, interfaces
15 from twisted.cred import credentials
16 from twisted.internet import defer, reactor
17 from twisted.python import failure, log
18 from common import NS, getNS, MP
19 import keys, transport, service
20
21 class SSHUserAuthServer(service.SSHService):
22 name = 'ssh-userauth'
23 loginTimeout = 10 * 60 * 60 # 10 minutes before we disconnect them
24 attemptsBeforeDisconnect = 20 # number of attempts to allow before a disconn ect
25 passwordDelay = 1 # number of seconds to delay on a failed password
26 protocolMessages = None # set later
27 interfaceToMethod = {
28 credentials.ISSHPrivateKey : 'publickey',
29 credentials.IUsernamePassword : 'password',
30 credentials.IPluggableAuthenticationModules : 'keyboard-interactive',
31 }
32
33 def serviceStarted(self):
34 self.authenticatedWith = []
35 self.loginAttempts = 0
36 self.user = None
37 self.nextService = None
38 self.portal = self.transport.factory.portal
39
40 self.supportedAuthentications = []
41 for i in self.portal.listCredentialsInterfaces():
42 if i in self.interfaceToMethod:
43 self.supportedAuthentications.append(self.interfaceToMethod[i])
44
45 if not self.transport.isEncrypted('out'):
46 if 'password' in self.supportedAuthentications:
47 self.supportedAuthentications.remove('password')
48 if 'keyboard-interactive' in self.supportedAuthentications:
49 self.supportedAuthentications.remove('keyboard-interactive')
50 # don't let us transport password in plaintext
51 self.cancelLoginTimeout = reactor.callLater(self.loginTimeout,
52 self.timeoutAuthentication)
53
54 def serviceStopped(self):
55 if self.cancelLoginTimeout:
56 self.cancelLoginTimeout.cancel()
57 self.cancelLoginTimeout = None
58
59 def timeoutAuthentication(self):
60 self.cancelLoginTimeout = None
61 self.transport.sendDisconnect(
62 transport.DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE,
63 'you took too long')
64
65
66 def tryAuth(self, kind, user, data):
67 log.msg('%s trying auth %s' % (user, kind))
68 if kind not in self.supportedAuthentications:
69 return defer.fail(error.ConchError('unsupported authentication, fail ing'))
70 kind = kind.replace('-', '_')
71 f = getattr(self,'auth_%s'%kind, None)
72 if f:
73 ret = f(data)
74 if not ret:
75 return defer.fail(error.ConchError('%s return None instead of a Deferred' % kind))
76 else:
77 return ret
78 return defer.fail(error.ConchError('bad auth type: %s' % kind))
79
80 def ssh_USERAUTH_REQUEST(self, packet):
81 user, nextService, method, rest = getNS(packet, 3)
82 if user != self.user or nextService != self.nextService:
83 self.authenticatedWith = [] # clear auth state
84 self.user = user
85 self.nextService = nextService
86 self.method = method
87 d = self.tryAuth(method, user, rest)
88 if not d:
89 self._ebBadAuth(
90 failure.Failure(error.ConchError('auth returned none')))
91 return
92 d.addCallbacks(self._cbFinishedAuth)
93 d.addErrback(self._ebMaybeBadAuth)
94 d.addErrback(self._ebBadAuth)
95 return d
96
97 def _cbFinishedAuth(self, (interface, avatar, logout)):
98 self.transport.avatar = avatar
99 self.transport.logoutFunction = logout
100 service = self.transport.factory.getService(self.transport,
101 self.nextService)
102 if not service:
103 raise error.ConchError('could not get next service: %s'
104 % self.nextService)
105 log.msg('%s authenticated with %s' % (self.user, self.method))
106 if self.cancelLoginTimeout:
107 self.cancelLoginTimeout.cancel()
108 self.cancelLoginTimeout = None
109 self.transport.sendPacket(MSG_USERAUTH_SUCCESS, '')
110 self.transport.setService(service())
111
112 def _ebMaybeBadAuth(self, reason):
113 reason.trap(error.NotEnoughAuthentication)
114 self.transport.sendPacket(MSG_USERAUTH_FAILURE, NS(','.join(self.support edAuthentications))+'\xff')
115
116 def _ebBadAuth(self, reason):
117 if reason.type == error.IgnoreAuthentication:
118 return
119 if self.method != 'none':
120 log.msg('%s failed auth %s' % (self.user, self.method))
121 log.msg('reason:')
122 if reason.type == error.ConchError:
123 log.msg(str(reason))
124 else:
125 log.msg(reason.getTraceback())
126 self.loginAttempts += 1
127 if self.loginAttempts > self.attemptsBeforeDisconnect:
128 self.transport.sendDisconnect(transport.DISCONNECT_NO_MORE_AUTH_ METHODS_AVAILABLE,
129 'too many bad auths')
130 self.transport.sendPacket(MSG_USERAUTH_FAILURE, NS(','.join(self.support edAuthentications))+'\x00')
131
132 def auth_publickey(self, packet):
133 hasSig = ord(packet[0])
134 algName, blob, rest = getNS(packet[1:], 2)
135 pubKey = keys.getPublicKeyObject(data = blob)
136 signature = hasSig and getNS(rest)[0] or None
137 if hasSig:
138 b = NS(self.transport.sessionID) + chr(MSG_USERAUTH_REQUEST) + \
139 NS(self.user) + NS(self.nextService) + NS('publickey') + \
140 chr(hasSig) + NS(keys.objectType(pubKey)) + NS(blob)
141 c = credentials.SSHPrivateKey(self.user, algName, blob, b, signature )
142 return self.portal.login(c, None, interfaces.IConchUser)
143 else:
144 c = credentials.SSHPrivateKey(self.user, algName, blob, None, None)
145 return self.portal.login(c, None, interfaces.IConchUser).addErrback(
146 self._ebCheckKey,
147 packet[1:])
148
149 def _ebCheckKey(self, reason, packet):
150 reason.trap(error.ValidPublicKey)
151 # if we make it here, it means that the publickey is valid
152 self.transport.sendPacket(MSG_USERAUTH_PK_OK, packet)
153 return failure.Failure(error.IgnoreAuthentication())
154
155 def auth_password(self, packet):
156 password = getNS(packet[1:])[0]
157 c = credentials.UsernamePassword(self.user, password)
158 return self.portal.login(c, None, interfaces.IConchUser).addErrback(
159 self._ebPassword)
160
161 def _ebPassword(self, f):
162 d = defer.Deferred()
163 reactor.callLater(self.passwordDelay, lambda d,f:d.callback(f), d, f)
164 return d
165
166 def auth_keyboard_interactive(self, packet):
167 if hasattr(self, '_pamDeferred'):
168 self.transport.sendDisconnect(transport.DISCONNECT_PROTOCOL_ERROR, " only one keyboard interactive attempt at a time")
169 return failure.Failure(error.IgnoreAuthentication())
170 c = credentials.PluggableAuthenticationModules(self.user, self._pamConv)
171 return self.portal.login(c, None, interfaces.IConchUser)
172
173 def _pamConv(self, items):
174 resp = []
175 for message, kind in items:
176 if kind == 1: # password
177 resp.append((message, 0))
178 elif kind == 2: # text
179 resp.append((message, 1))
180 elif kind in (3, 4):
181 return defer.fail(error.ConchError('cannot handle PAM 3 or 4 mes sages'))
182 else:
183 return defer.fail(error.ConchError('bad PAM auth kind %i' % kind ))
184 packet = NS('')+NS('')+NS('')
185 packet += struct.pack('>L', len(resp))
186 for prompt, echo in resp:
187 packet += NS(prompt)
188 packet += chr(echo)
189 self.transport.sendPacket(MSG_USERAUTH_INFO_REQUEST, packet)
190 self._pamDeferred = defer.Deferred()
191 return self._pamDeferred
192
193 def ssh_USERAUTH_INFO_RESPONSE(self, packet):
194 d = self._pamDeferred
195 del self._pamDeferred
196 try:
197 resp = []
198 numResps = struct.unpack('>L', packet[:4])[0]
199 packet = packet[4:]
200 while packet:
201 response, packet = getNS(packet)
202 resp.append((response, 0))
203 assert len(resp) == numResps
204 except:
205 d.errback(failure.Failure())
206 else:
207 d.callback(resp)
208
209 class SSHUserAuthClient(service.SSHService):
210 name = 'ssh-userauth'
211 protocolMessages = None # set later
212
213 preferredOrder = ['publickey', 'password', 'keyboard-interactive']
214
215 def __init__(self, user, instance):
216 self.user = user
217 self.instance = instance
218
219 def serviceStarted(self):
220 self.authenticatedWith = []
221 self.triedPublicKeys = []
222 self.lastPublicKey = None
223 self.askForAuth('none', '')
224
225 def askForAuth(self, kind, extraData):
226 self.lastAuth = kind
227 self.transport.sendPacket(MSG_USERAUTH_REQUEST, NS(self.user) + \
228 NS(self.instance.name) + NS(kind) + extraData)
229 def tryAuth(self, kind):
230 kind = kind.replace('-', '_')
231 log.msg('trying to auth with %s' % kind)
232 f= getattr(self,'auth_%s'%kind, None)
233 if f:
234 return f()
235
236 def _ebAuth(self, ignored, *args):
237 self.tryAuth('none')
238
239 def ssh_USERAUTH_SUCCESS(self, packet):
240 self.transport.setService(self.instance)
241 #self.ssh_USERAUTH_SUCCESS = lambda *a: None # ignore these
242
243 def ssh_USERAUTH_FAILURE(self, packet):
244 canContinue, partial = getNS(packet)
245 canContinue = canContinue.split(',')
246 partial = ord(partial)
247 if partial:
248 self.authenticatedWith.append(self.lastAuth)
249 def _(x, y):
250 try:
251 i1 = self.preferredOrder.index(x)
252 except ValueError:
253 return 1
254 try:
255 i2 = self.preferredOrder.index(y)
256 except ValueError:
257 return -1
258 return cmp(i1, i2)
259 canContinue.sort(_)
260 log.msg('can continue with: %s' % canContinue)
261 for method in canContinue:
262 if method not in self.authenticatedWith and self.tryAuth(method):
263 return
264 self.transport.sendDisconnect(transport.DISCONNECT_NO_MORE_AUTH_METHODS_ AVAILABLE, 'no more authentication methods available')
265
266 def ssh_USERAUTH_PK_OK(self, packet):
267 if self.lastAuth == 'publickey':
268 # this is ok
269 publicKey = self.lastPublicKey
270 keyType = getNS(publicKey)[0]
271 b = NS(self.transport.sessionID) + chr(MSG_USERAUTH_REQUEST) + \
272 NS(self.user) + NS(self.instance.name) + NS('publickey') + '\xff' +\
273 NS(keyType) + NS(publicKey)
274 d = self.signData(publicKey, b)
275 if not d:
276 self.askForAuth('none', '')
277 # this will fail, we'll move on
278 return
279 d.addCallback(self._cbSignedData)
280 d.addErrback(self._ebAuth)
281 elif self.lastAuth == 'password':
282 prompt, language, rest = getNS(packet, 2)
283 self._oldPass = self._newPass = None
284 self.getPassword('Old Password: ').addCallbacks(self._setOldPass, se lf._ebAuth)
285 self.getPassword(prompt).addCallbacks(self._setNewPass, self._ebAuth )
286 elif self.lastAuth == 'keyboard-interactive':
287 name, instruction, lang, data = getNS(packet, 3)
288 numPrompts = struct.unpack('!L', data[:4])[0]
289 data = data[4:]
290 prompts = []
291 for i in range(numPrompts):
292 prompt, data = getNS(data)
293 echo = bool(ord(data[0]))
294 data = data[1:]
295 prompts.append((prompt, echo))
296 d = self.getGenericAnswers(name, instruction, prompts)
297 d.addCallback(self._cbGenericAnswers)
298 d.addErrback(self._ebAuth)
299
300 def _cbSignedData(self, signedData):
301 publicKey = self.lastPublicKey
302 keyType = getNS(publicKey)[0]
303 self.askForAuth('publickey', '\xff' + NS(keyType) + NS(publicKey) + \
304 NS(signedData))
305
306
307
308
309
310
311 def _setOldPass(self, op):
312 if self._newPass:
313 np = self._newPass
314 self._newPass = None
315 self.askForAuth('password', '\xff'+NS(op)+NS(np))
316 else:
317 self._oldPass = op
318
319 def _setNewPass(self, np):
320 if self._oldPass:
321 op = self._oldPass
322 self._oldPass = None
323 self.askForAuth('password', '\xff'+NS(op)+NS(np))
324 else:
325 self._newPass = np
326
327 def _cbGenericAnswers(self, responses):
328 data = struct.pack('!L', len(responses))
329 for r in responses:
330 data += NS(r.encode('UTF8'))
331 self.transport.sendPacket(MSG_USERAUTH_INFO_RESPONSE, data)
332
333 def auth_publickey(self):
334 publicKey = self.getPublicKey()
335 if publicKey:
336 self.lastPublicKey = publicKey
337 self.triedPublicKeys.append(publicKey)
338 keyType = getNS(publicKey)[0]
339 log.msg('using key of type %s' % keyType)
340 self.askForAuth('publickey', '\x00' + NS(keyType) + \
341 NS(publicKey))
342 return 1
343 else:
344 return 0
345
346 def auth_password(self):
347 d = self.getPassword()
348 if d:
349 d.addCallbacks(self._cbPassword, self._ebAuth)
350 return 1
351 else: # returned None, don't do password auth
352 return 0
353
354 def auth_keyboard_interactive(self):
355 log.msg('authing with keyboard-interactive')
356 self.askForAuth('keyboard-interactive', NS('') + NS(''))
357 return 1
358
359 def _cbPassword(self, password):
360 self.askForAuth('password', '\x00'+NS(password))
361
362 def signData(self, publicKey, signData):
363 """
364 Sign the given data with the given public key blob.
365 By default, this will call getPrivateKey to get the private key,
366 the sign the data using keys.signData.
367 However, this is factored out so that it can use alternate methods,
368 such as a key agent.
369 """
370 key = self.getPrivateKey()
371 if not key:
372 return
373 return key.addCallback(self._cbSignData, signData)
374
375 def _cbSignData(self, privateKey, signData):
376 return keys.signData(privateKey, signData)
377
378 def getPublicKey(self):
379 """
380 Return a public key for the user. If no more public keys are
381 available, return None.
382
383 @rtype: C{str}/C{None}
384 """
385 return None
386 #raise NotImplementedError
387
388
389 def getPrivateKey(self):
390 """
391 Return a L{Deferred} that will be called back with the private key
392 corresponding to the last public key from getPublicKey().
393 If the private key is not available, errback on the Deferred.
394
395 @rtype: L{Deferred}
396 """
397 return defer.fail(NotImplementedError())
398
399 def getPassword(self, prompt = None):
400 """
401 Return a L{Deferred} that will be called back with a password.
402 prompt is a string to display for the password, or None for a generic
403 'user@hostname's password: '.
404
405 @type prompt: C{str}/C{None}
406 @rtype: L{Deferred}
407 """
408 return defer.fail(NotImplementedError())
409
410 def getGenericAnswers(self, name, instruction, prompts):
411 """
412 Returns a L{Deferred} with the responses to the promopts.
413
414 @param name: The name of the authentication currently in progress.
415 @param instruction: Describes what the authentication wants.
416 @param prompts: A list of (prompt, echo) pairs, where prompt is a
417 string to display and echo is a boolean indicating whether the
418 user's response should be echoed as they type it.
419 """
420 return defer.fail(NotImplementedError())
421
422 MSG_USERAUTH_REQUEST = 50
423 MSG_USERAUTH_FAILURE = 51
424 MSG_USERAUTH_SUCCESS = 52
425 MSG_USERAUTH_BANNER = 53
426 MSG_USERAUTH_PASSWD_CHANGEREQ = 60
427 MSG_USERAUTH_INFO_REQUEST = 60
428 MSG_USERAUTH_INFO_RESPONSE = 61
429 MSG_USERAUTH_PK_OK = 60
430
431 messages = {}
432 import userauth
433 for v in dir(userauth):
434 if v[:4]=='MSG_':
435 messages[getattr(userauth,v)] = v # doesn't handle doubles
436
437 SSHUserAuthServer.protocolMessages = messages
438 SSHUserAuthClient.protocolMessages = messages
OLDNEW
« no previous file with comments | « third_party/twisted_8_1/twisted/conch/ssh/transport.py ('k') | third_party/twisted_8_1/twisted/conch/stdio.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698