def succeedAuth(self): """ Sends a Userauth Success packet to the client, then switches the transport to the client's desired service. """ # Authentication has succeeded fully. # Obtain the service desired by the end user service = self.transport.factory.getService(self.transport, self.state.desired_service) self.transport.sendPacket(userauth.MSG_USERAUTH_SUCCESS, b'') self.transport.setService(service())
def _cbFinishedAuth(self, result): """ The callback when user has successfully been authenticated. For a description of the arguments, see L{twisted.cred.portal.Portal.login}. We start the service requested by the user. """ (interface, avatar, logout) = result 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)) self.transport.sendPacket(MSG_USERAUTH_SUCCESS, b'') self.transport.setService(service())
class SSHUserAuthServer(service.SSHService): """ A service implementing the server side of the 'ssh-userauth' service. It is used to authenticate the user on the other side as being able to access this server. @ivar name: the name of this service: 'ssh-userauth' @type name: C{str} @ivar authenticatedWith: a list of authentication methods that have already been used. @type authenticatedWith: C{list} @ivar loginTimeout: the number of seconds we wait before disconnecting the user for taking too long to authenticate @type loginTimeout: C{int} @ivar attemptsBeforeDisconnect: the number of failed login attempts we allow before disconnecting. @type attemptsBeforeDisconnect: C{int} @ivar loginAttempts: the number of login attempts that have been made @type loginAttempts: C{int} @ivar passwordDelay: the number of seconds to delay when the user gives an incorrect password @type passwordDelay: C{int} @ivar interfaceToMethod: a C{dict} mapping credential interfaces to authentication methods. The server checks to see which of the cred interfaces have checkers and tells the client that those methods are valid for authentication. @type interfaceToMethod: C{dict} @ivar supportedAuthentications: A list of the supported authentication methods. @type supportedAuthentications: C{list} of C{str} @ivar user: the last username the client tried to authenticate with @type user: C{str} @ivar method: the current authentication method @type method: C{str} @ivar nextService: the service the user wants started after authentication has been completed. @type nextService: C{str} @ivar portal: the L{twisted.cred.portal.Portal} we are using for authentication @type portal: L{twisted.cred.portal.Portal} @ivar clock: an object with a callLater method. Stubbed out for testing. """ name = 'ssh-userauth' loginTimeout = 10 * 60 * 60 # 10 minutes before we disconnect them attemptsBeforeDisconnect = 20 # 20 login attempts before a disconnect passwordDelay = 1 # number of seconds to delay on a failed password clock = reactor interfaceToMethod = { credentials.ISSHPrivateKey : 'publickey', credentials.IUsernamePassword : '******', credentials.IPluggableAuthenticationModules : 'keyboard-interactive', } def serviceStarted(self): """ Called when the userauth service is started. Set up instance variables, check if we should allow password/keyboard-interactive authentication (only allow if the outgoing connection is encrypted) and set up a login timeout. """ self.authenticatedWith = [] self.loginAttempts = 0 self.user = None self.nextService = None self._pamDeferred = 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('in'): # don't let us transport password in plaintext if 'password' in self.supportedAuthentications: self.supportedAuthentications.remove('password') if 'keyboard-interactive' in self.supportedAuthentications: self.supportedAuthentications.remove('keyboard-interactive') self._cancelLoginTimeout = self.clock.callLater( self.loginTimeout, self.timeoutAuthentication) def serviceStopped(self): """ Called when the userauth service is stopped. Cancel the login timeout if it's still going. """ if self._cancelLoginTimeout: self._cancelLoginTimeout.cancel() self._cancelLoginTimeout = None def timeoutAuthentication(self): """ Called when the user has timed out on authentication. Disconnect with a DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE message. """ self._cancelLoginTimeout = None self.transport.sendDisconnect( transport.DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE, 'you took too long') def tryAuth(self, kind, user, data): """ Try to authenticate the user with the given method. Dispatches to a auth_* method. @param kind: the authentication method to try. @type kind: C{str} @param user: the username the client is authenticating with. @type user: C{str} @param data: authentication specific data sent by the client. @type data: C{str} @return: A Deferred called back if the method succeeded, or erred back if it failed. @rtype: C{defer.Deferred} """ 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): """ The client has requested authentication. Payload:: string user string next service string method <authentication specific data> @type packet: C{str} """ 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.addCallback(self._cbFinishedAuth) d.addErrback(self._ebMaybeBadAuth) d.addErrback(self._ebBadAuth) return d def _cbFinishedAuth(self, (interface, avatar, logout)): """ The callback when user has successfully been authenticated. For a description of the arguments, see L{twisted.cred.portal.Portal.login}. We start the service requested by the user. """ 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)) self.transport.sendPacket(MSG_USERAUTH_SUCCESS, '') self.transport.setService(service())