| Index: third_party/twisted_8_1/twisted/conch/ssh/userauth.py
|
| diff --git a/third_party/twisted_8_1/twisted/conch/ssh/userauth.py b/third_party/twisted_8_1/twisted/conch/ssh/userauth.py
|
| deleted file mode 100644
|
| index 4e40201a627df6da29858c98b0f3de620b25c31f..0000000000000000000000000000000000000000
|
| --- a/third_party/twisted_8_1/twisted/conch/ssh/userauth.py
|
| +++ /dev/null
|
| @@ -1,438 +0,0 @@
|
| -# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
|
| -# See LICENSE for details.
|
| -
|
| -#
|
| -
|
| -"""
|
| -Implementation of the ssh-userauth service.
|
| -Currently implemented authentication types are public-key and password.
|
| -
|
| -Maintainer: U{Paul Swartz<mailto:z3p@twistedmatrix.com>}
|
| -"""
|
| -
|
| -import struct
|
| -from twisted.conch import error, interfaces
|
| -from twisted.cred import credentials
|
| -from twisted.internet import defer, reactor
|
| -from twisted.python import failure, log
|
| -from common import NS, getNS, MP
|
| -import keys, transport, service
|
| -
|
| -class SSHUserAuthServer(service.SSHService):
|
| - name = 'ssh-userauth'
|
| - loginTimeout = 10 * 60 * 60 # 10 minutes before we disconnect them
|
| - attemptsBeforeDisconnect = 20 # number of attempts to allow before a disconnect
|
| - passwordDelay = 1 # number of seconds to delay on a failed password
|
| - protocolMessages = None # set later
|
| - interfaceToMethod = {
|
| - credentials.ISSHPrivateKey : 'publickey',
|
| - credentials.IUsernamePassword : 'password',
|
| - credentials.IPluggableAuthenticationModules : 'keyboard-interactive',
|
| - }
|
| -
|
| - def serviceStarted(self):
|
| - self.authenticatedWith = []
|
| - self.loginAttempts = 0
|
| - self.user = None
|
| - self.nextService = None
|
| - self.portal = self.transport.factory.portal
|
| -
|
| - self.supportedAuthentications = []
|
| - for i in self.portal.listCredentialsInterfaces():
|
| - if i in self.interfaceToMethod:
|
| - self.supportedAuthentications.append(self.interfaceToMethod[i])
|
| -
|
| - if not self.transport.isEncrypted('out'):
|
| - if 'password' in self.supportedAuthentications:
|
| - self.supportedAuthentications.remove('password')
|
| - if 'keyboard-interactive' in self.supportedAuthentications:
|
| - self.supportedAuthentications.remove('keyboard-interactive')
|
| - # don't let us transport password in plaintext
|
| - self.cancelLoginTimeout = reactor.callLater(self.loginTimeout,
|
| - self.timeoutAuthentication)
|
| -
|
| - def serviceStopped(self):
|
| - if self.cancelLoginTimeout:
|
| - self.cancelLoginTimeout.cancel()
|
| - self.cancelLoginTimeout = None
|
| -
|
| - def timeoutAuthentication(self):
|
| - self.cancelLoginTimeout = None
|
| - self.transport.sendDisconnect(
|
| - transport.DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE,
|
| - 'you took too long')
|
| -
|
| -
|
| - def tryAuth(self, kind, user, data):
|
| - log.msg('%s trying auth %s' % (user, kind))
|
| - if kind not in self.supportedAuthentications:
|
| - return defer.fail(error.ConchError('unsupported authentication, failing'))
|
| - kind = kind.replace('-', '_')
|
| - f = getattr(self,'auth_%s'%kind, None)
|
| - if f:
|
| - ret = f(data)
|
| - if not ret:
|
| - return defer.fail(error.ConchError('%s return None instead of a Deferred' % kind))
|
| - else:
|
| - return ret
|
| - return defer.fail(error.ConchError('bad auth type: %s' % kind))
|
| -
|
| - def ssh_USERAUTH_REQUEST(self, packet):
|
| - user, nextService, method, rest = getNS(packet, 3)
|
| - if user != self.user or nextService != self.nextService:
|
| - self.authenticatedWith = [] # clear auth state
|
| - self.user = user
|
| - self.nextService = nextService
|
| - self.method = method
|
| - d = self.tryAuth(method, user, rest)
|
| - if not d:
|
| - self._ebBadAuth(
|
| - failure.Failure(error.ConchError('auth returned none')))
|
| - return
|
| - d.addCallbacks(self._cbFinishedAuth)
|
| - d.addErrback(self._ebMaybeBadAuth)
|
| - d.addErrback(self._ebBadAuth)
|
| - return d
|
| -
|
| - def _cbFinishedAuth(self, (interface, avatar, logout)):
|
| - self.transport.avatar = avatar
|
| - self.transport.logoutFunction = logout
|
| - service = self.transport.factory.getService(self.transport,
|
| - self.nextService)
|
| - if not service:
|
| - raise error.ConchError('could not get next service: %s'
|
| - % self.nextService)
|
| - log.msg('%s authenticated with %s' % (self.user, self.method))
|
| - if self.cancelLoginTimeout:
|
| - self.cancelLoginTimeout.cancel()
|
| - self.cancelLoginTimeout = None
|
| - self.transport.sendPacket(MSG_USERAUTH_SUCCESS, '')
|
| - self.transport.setService(service())
|
| -
|
| - def _ebMaybeBadAuth(self, reason):
|
| - reason.trap(error.NotEnoughAuthentication)
|
| - self.transport.sendPacket(MSG_USERAUTH_FAILURE, NS(','.join(self.supportedAuthentications))+'\xff')
|
| -
|
| - def _ebBadAuth(self, reason):
|
| - if reason.type == error.IgnoreAuthentication:
|
| - return
|
| - if self.method != 'none':
|
| - log.msg('%s failed auth %s' % (self.user, self.method))
|
| - log.msg('reason:')
|
| - if reason.type == error.ConchError:
|
| - log.msg(str(reason))
|
| - else:
|
| - log.msg(reason.getTraceback())
|
| - self.loginAttempts += 1
|
| - if self.loginAttempts > self.attemptsBeforeDisconnect:
|
| - self.transport.sendDisconnect(transport.DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE,
|
| - 'too many bad auths')
|
| - self.transport.sendPacket(MSG_USERAUTH_FAILURE, NS(','.join(self.supportedAuthentications))+'\x00')
|
| -
|
| - def auth_publickey(self, packet):
|
| - hasSig = ord(packet[0])
|
| - algName, blob, rest = getNS(packet[1:], 2)
|
| - pubKey = keys.getPublicKeyObject(data = blob)
|
| - signature = hasSig and getNS(rest)[0] or None
|
| - if hasSig:
|
| - b = NS(self.transport.sessionID) + chr(MSG_USERAUTH_REQUEST) + \
|
| - NS(self.user) + NS(self.nextService) + NS('publickey') + \
|
| - chr(hasSig) + NS(keys.objectType(pubKey)) + NS(blob)
|
| - c = credentials.SSHPrivateKey(self.user, algName, blob, b, signature)
|
| - return self.portal.login(c, None, interfaces.IConchUser)
|
| - else:
|
| - c = credentials.SSHPrivateKey(self.user, algName, blob, None, None)
|
| - return self.portal.login(c, None, interfaces.IConchUser).addErrback(
|
| - self._ebCheckKey,
|
| - packet[1:])
|
| -
|
| - def _ebCheckKey(self, reason, packet):
|
| - reason.trap(error.ValidPublicKey)
|
| - # if we make it here, it means that the publickey is valid
|
| - self.transport.sendPacket(MSG_USERAUTH_PK_OK, packet)
|
| - return failure.Failure(error.IgnoreAuthentication())
|
| -
|
| - def auth_password(self, packet):
|
| - password = getNS(packet[1:])[0]
|
| - c = credentials.UsernamePassword(self.user, password)
|
| - return self.portal.login(c, None, interfaces.IConchUser).addErrback(
|
| - self._ebPassword)
|
| -
|
| - def _ebPassword(self, f):
|
| - d = defer.Deferred()
|
| - reactor.callLater(self.passwordDelay, lambda d,f:d.callback(f), d, f)
|
| - return d
|
| -
|
| - def auth_keyboard_interactive(self, packet):
|
| - if hasattr(self, '_pamDeferred'):
|
| - self.transport.sendDisconnect(transport.DISCONNECT_PROTOCOL_ERROR, "only one keyboard interactive attempt at a time")
|
| - return failure.Failure(error.IgnoreAuthentication())
|
| - c = credentials.PluggableAuthenticationModules(self.user, self._pamConv)
|
| - return self.portal.login(c, None, interfaces.IConchUser)
|
| -
|
| - def _pamConv(self, items):
|
| - resp = []
|
| - for message, kind in items:
|
| - if kind == 1: # password
|
| - resp.append((message, 0))
|
| - elif kind == 2: # text
|
| - resp.append((message, 1))
|
| - elif kind in (3, 4):
|
| - return defer.fail(error.ConchError('cannot handle PAM 3 or 4 messages'))
|
| - else:
|
| - return defer.fail(error.ConchError('bad PAM auth kind %i' % kind))
|
| - packet = NS('')+NS('')+NS('')
|
| - packet += struct.pack('>L', len(resp))
|
| - for prompt, echo in resp:
|
| - packet += NS(prompt)
|
| - packet += chr(echo)
|
| - self.transport.sendPacket(MSG_USERAUTH_INFO_REQUEST, packet)
|
| - self._pamDeferred = defer.Deferred()
|
| - return self._pamDeferred
|
| -
|
| - def ssh_USERAUTH_INFO_RESPONSE(self, packet):
|
| - d = self._pamDeferred
|
| - del self._pamDeferred
|
| - try:
|
| - resp = []
|
| - numResps = struct.unpack('>L', packet[:4])[0]
|
| - packet = packet[4:]
|
| - while packet:
|
| - response, packet = getNS(packet)
|
| - resp.append((response, 0))
|
| - assert len(resp) == numResps
|
| - except:
|
| - d.errback(failure.Failure())
|
| - else:
|
| - d.callback(resp)
|
| -
|
| -class SSHUserAuthClient(service.SSHService):
|
| - name = 'ssh-userauth'
|
| - protocolMessages = None # set later
|
| -
|
| - preferredOrder = ['publickey', 'password', 'keyboard-interactive']
|
| -
|
| - def __init__(self, user, instance):
|
| - self.user = user
|
| - self.instance = instance
|
| -
|
| - def serviceStarted(self):
|
| - self.authenticatedWith = []
|
| - self.triedPublicKeys = []
|
| - self.lastPublicKey = None
|
| - self.askForAuth('none', '')
|
| -
|
| - def askForAuth(self, kind, extraData):
|
| - self.lastAuth = kind
|
| - self.transport.sendPacket(MSG_USERAUTH_REQUEST, NS(self.user) + \
|
| - NS(self.instance.name) + NS(kind) + extraData)
|
| - def tryAuth(self, kind):
|
| - kind = kind.replace('-', '_')
|
| - log.msg('trying to auth with %s' % kind)
|
| - f= getattr(self,'auth_%s'%kind, None)
|
| - if f:
|
| - return f()
|
| -
|
| - def _ebAuth(self, ignored, *args):
|
| - self.tryAuth('none')
|
| -
|
| - def ssh_USERAUTH_SUCCESS(self, packet):
|
| - self.transport.setService(self.instance)
|
| - #self.ssh_USERAUTH_SUCCESS = lambda *a: None # ignore these
|
| -
|
| - def ssh_USERAUTH_FAILURE(self, packet):
|
| - canContinue, partial = getNS(packet)
|
| - canContinue = canContinue.split(',')
|
| - partial = ord(partial)
|
| - if partial:
|
| - self.authenticatedWith.append(self.lastAuth)
|
| - def _(x, y):
|
| - try:
|
| - i1 = self.preferredOrder.index(x)
|
| - except ValueError:
|
| - return 1
|
| - try:
|
| - i2 = self.preferredOrder.index(y)
|
| - except ValueError:
|
| - return -1
|
| - return cmp(i1, i2)
|
| - canContinue.sort(_)
|
| - log.msg('can continue with: %s' % canContinue)
|
| - for method in canContinue:
|
| - if method not in self.authenticatedWith and self.tryAuth(method):
|
| - return
|
| - self.transport.sendDisconnect(transport.DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE, 'no more authentication methods available')
|
| -
|
| - def ssh_USERAUTH_PK_OK(self, packet):
|
| - if self.lastAuth == 'publickey':
|
| - # this is ok
|
| - publicKey = self.lastPublicKey
|
| - keyType = getNS(publicKey)[0]
|
| - b = NS(self.transport.sessionID) + chr(MSG_USERAUTH_REQUEST) + \
|
| - NS(self.user) + NS(self.instance.name) + NS('publickey') + '\xff' +\
|
| - NS(keyType) + NS(publicKey)
|
| - d = self.signData(publicKey, b)
|
| - if not d:
|
| - self.askForAuth('none', '')
|
| - # this will fail, we'll move on
|
| - return
|
| - d.addCallback(self._cbSignedData)
|
| - d.addErrback(self._ebAuth)
|
| - elif self.lastAuth == 'password':
|
| - prompt, language, rest = getNS(packet, 2)
|
| - self._oldPass = self._newPass = None
|
| - self.getPassword('Old Password: ').addCallbacks(self._setOldPass, self._ebAuth)
|
| - self.getPassword(prompt).addCallbacks(self._setNewPass, self._ebAuth)
|
| - elif self.lastAuth == 'keyboard-interactive':
|
| - name, instruction, lang, data = getNS(packet, 3)
|
| - numPrompts = struct.unpack('!L', data[:4])[0]
|
| - data = data[4:]
|
| - prompts = []
|
| - for i in range(numPrompts):
|
| - prompt, data = getNS(data)
|
| - echo = bool(ord(data[0]))
|
| - data = data[1:]
|
| - prompts.append((prompt, echo))
|
| - d = self.getGenericAnswers(name, instruction, prompts)
|
| - d.addCallback(self._cbGenericAnswers)
|
| - d.addErrback(self._ebAuth)
|
| -
|
| - def _cbSignedData(self, signedData):
|
| - publicKey = self.lastPublicKey
|
| - keyType = getNS(publicKey)[0]
|
| - self.askForAuth('publickey', '\xff' + NS(keyType) + NS(publicKey) + \
|
| - NS(signedData))
|
| -
|
| -
|
| -
|
| -
|
| -
|
| -
|
| - def _setOldPass(self, op):
|
| - if self._newPass:
|
| - np = self._newPass
|
| - self._newPass = None
|
| - self.askForAuth('password', '\xff'+NS(op)+NS(np))
|
| - else:
|
| - self._oldPass = op
|
| -
|
| - def _setNewPass(self, np):
|
| - if self._oldPass:
|
| - op = self._oldPass
|
| - self._oldPass = None
|
| - self.askForAuth('password', '\xff'+NS(op)+NS(np))
|
| - else:
|
| - self._newPass = np
|
| -
|
| - def _cbGenericAnswers(self, responses):
|
| - data = struct.pack('!L', len(responses))
|
| - for r in responses:
|
| - data += NS(r.encode('UTF8'))
|
| - self.transport.sendPacket(MSG_USERAUTH_INFO_RESPONSE, data)
|
| -
|
| - def auth_publickey(self):
|
| - publicKey = self.getPublicKey()
|
| - if publicKey:
|
| - self.lastPublicKey = publicKey
|
| - self.triedPublicKeys.append(publicKey)
|
| - keyType = getNS(publicKey)[0]
|
| - log.msg('using key of type %s' % keyType)
|
| - self.askForAuth('publickey', '\x00' + NS(keyType) + \
|
| - NS(publicKey))
|
| - return 1
|
| - else:
|
| - return 0
|
| -
|
| - def auth_password(self):
|
| - d = self.getPassword()
|
| - if d:
|
| - d.addCallbacks(self._cbPassword, self._ebAuth)
|
| - return 1
|
| - else: # returned None, don't do password auth
|
| - return 0
|
| -
|
| - def auth_keyboard_interactive(self):
|
| - log.msg('authing with keyboard-interactive')
|
| - self.askForAuth('keyboard-interactive', NS('') + NS(''))
|
| - return 1
|
| -
|
| - def _cbPassword(self, password):
|
| - self.askForAuth('password', '\x00'+NS(password))
|
| -
|
| - def signData(self, publicKey, signData):
|
| - """
|
| - Sign the given data with the given public key blob.
|
| - By default, this will call getPrivateKey to get the private key,
|
| - the sign the data using keys.signData.
|
| - However, this is factored out so that it can use alternate methods,
|
| - such as a key agent.
|
| - """
|
| - key = self.getPrivateKey()
|
| - if not key:
|
| - return
|
| - return key.addCallback(self._cbSignData, signData)
|
| -
|
| - def _cbSignData(self, privateKey, signData):
|
| - return keys.signData(privateKey, signData)
|
| -
|
| - def getPublicKey(self):
|
| - """
|
| - Return a public key for the user. If no more public keys are
|
| - available, return None.
|
| -
|
| - @rtype: C{str}/C{None}
|
| - """
|
| - return None
|
| - #raise NotImplementedError
|
| -
|
| -
|
| - def getPrivateKey(self):
|
| - """
|
| - Return a L{Deferred} that will be called back with the private key
|
| - corresponding to the last public key from getPublicKey().
|
| - If the private key is not available, errback on the Deferred.
|
| -
|
| - @rtype: L{Deferred}
|
| - """
|
| - return defer.fail(NotImplementedError())
|
| -
|
| - def getPassword(self, prompt = None):
|
| - """
|
| - Return a L{Deferred} that will be called back with a password.
|
| - prompt is a string to display for the password, or None for a generic
|
| - 'user@hostname's password: '.
|
| -
|
| - @type prompt: C{str}/C{None}
|
| - @rtype: L{Deferred}
|
| - """
|
| - return defer.fail(NotImplementedError())
|
| -
|
| - def getGenericAnswers(self, name, instruction, prompts):
|
| - """
|
| - Returns a L{Deferred} with the responses to the promopts.
|
| -
|
| - @param name: The name of the authentication currently in progress.
|
| - @param instruction: Describes what the authentication wants.
|
| - @param prompts: A list of (prompt, echo) pairs, where prompt is a
|
| - string to display and echo is a boolean indicating whether the
|
| - user's response should be echoed as they type it.
|
| - """
|
| - return defer.fail(NotImplementedError())
|
| -
|
| -MSG_USERAUTH_REQUEST = 50
|
| -MSG_USERAUTH_FAILURE = 51
|
| -MSG_USERAUTH_SUCCESS = 52
|
| -MSG_USERAUTH_BANNER = 53
|
| -MSG_USERAUTH_PASSWD_CHANGEREQ = 60
|
| -MSG_USERAUTH_INFO_REQUEST = 60
|
| -MSG_USERAUTH_INFO_RESPONSE = 61
|
| -MSG_USERAUTH_PK_OK = 60
|
| -
|
| -messages = {}
|
| -import userauth
|
| -for v in dir(userauth):
|
| - if v[:4]=='MSG_':
|
| - messages[getattr(userauth,v)] = v # doesn't handle doubles
|
| -
|
| -SSHUserAuthServer.protocolMessages = messages
|
| -SSHUserAuthClient.protocolMessages = messages
|
|
|