测试了一下,在centos也是有效的。
首先给出程序的源代码。。使用twisted的plugin功能实现一个echo和远程执行命令的服务端:
#!/usr/bin/env python #coding:utf8 # Author : tuxpy # Email : q8886888@qq.com # Last modified : 2014-08-31 10:37:58 # Filename : tw_cred.py # Description : from zope.interface import implements,Interface,implementer from twisted.cred import checkers,credentials,portal from twisted.internet import protocol,reactor from twisted.protocols import basic class IProtocolAvatar(Interface): def logout(): """ clean up per-login resources ollocated to this avator. """ class Avatar(object): implements(IProtocolAvatar) def logout(self): self.transport.loseConnection() class EchoAvatar(Avatar): pass class DoAvatara(Avatar): def do_comm(self,cmd,*args): from twisted.internet import utils output=utils.getProcessOutput(cmd,args=args, errortoo=True) output.addCallback(self.writeResponse) def writeResponse(self,result): self.transport.write(result) class Echo(basic.LineReceiver): protal=None avatar=None logout=None delimiter='\n' def cocnnectionLost(self,reason): if self.logout: self.logout() self.avater=None seslf.logout=None def lineReceived(self,line): if not self.avatar: username,password=line.strip().split(" ")+[] self.tryLogin(username,password) else: if line.lower().strip()=='quit': self.avatar.logout() try: line=line.strip() cmd_parser=line.split(' ',1) if len(cmd_parser)>1: cmd=cmd_parser[0] args=cmd_parser[1:] else: cmd,args=cmd_parser[0],tuple() self.avatar.do_comm(cmd.strip(),*args) except AttributeError,e: self.sendLine(line) def tryLogin(self,username,password): self.portal.login(credentials.UsernamePassword(username, password), None,IProtocolAvatar).addCallbacks(self._cbLogin, self._ebLogin) def _cbLogin(self,(interface,avatar,logout)): self.avatar=avatar self.logout=logout self.avatar.transport=self.transport self.sendLine("Login successful,please procees.") def _ebLogin(self,failure): self.sendLine("Login denied,goodbye.") # print failure self.transport.loseConnection() class EchoFactory(protocol.Factory): def __init__(self,portal): self.portal=portal def buildProtocol(self,addr): protocol=Echo() protocol.portal=self.portal return protocol class Realm(object): implements(portal.IRealm) def requestAvatar(self,avatarId,mind,*interfaces): if avatarId=='root': avatar=DoAvatara() else: avatar=EchoAvatar() return None,avatar,avatar.logout raise NotImplementedError("This realm only supports the IProtocolAvatar interface.")
插件文件:gedit twisted/plugins/echo.py
#!/usr/bin/env python #coding:utf8 # Author : tuxpy # Email : q8886888@qq.com # Last modified : 2014-08-31 11:20:44 # Filename : twisted/plugins/echo.py # Description : from twisted.application import internet,service from twisted.plugin import IPlugin from twisted.cred import credentials,portal,strcred import sys import os sys.path.append(os.getcwd()) from tw_cred import EchoFactory,Realm from twisted.python import usage from zope.interface import implements class Options(usage.Options,strcred.AuthOptionMixin): supportedInterfaces=(credentials.IUsernamePassword,) optParameters=[ ["port",'p',1234,"The port number to listen on."] ] class EchoServiceMaker(object): implements(service.IServiceMaker,IPlugin) tapname="echo" description="A TCP-base echo server." options=Options def makeService(self,options): p=portal.Portal(Realm(),options["credCheckers"]) return internet.TCPServer(int(options['port']),EchoFactory(p)) serviceMaker=EchoServiceMaker()
启动:sudo twistd -n echo --auth unix -p 1235(为什么要用sudo,后面看cred_unix.py源代码中就会明白的)
但是这样启动好的话,按 书上说,我可以使用用户名和密码来验证,但是我怎么也验证不通过。因为我们的Options继承了strcred.AuthOptionMixin,同时我们又指定了credentials.IUsernamePassword接口,所以我们的插件会实现cred_*的那些checkers。于是查看twisted源代码。
twisted/plugins/cred_unix.py
# -*- test-case-name: twisted.test.test_strcred -*- # # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """ Cred plugin for UNIX user accounts. """ from zope.interface import implements from twisted import plugin from twisted.cred.strcred import ICheckerFactory from twisted.cred.checkers import ICredentialsChecker from twisted.cred.credentials import IUsernamePassword from twisted.cred.error import UnauthorizedLogin from twisted.internet import defer def verifyCryptedPassword(crypted, pw): if crypted[0] == '$': # md5_crypt encrypted salt = '$1$' + crypted.split('$')[2] else: salt = crypted[:2] try: import crypt except ImportError: crypt = None if crypt is None: raise NotImplementedError("cred_unix not supported on this platform") return crypt.crypt(pw, salt) == crypted class UNIXChecker(object): """ A credentials checker for a UNIX server. This will check that an authenticating username/password is a valid user on the system. Does not work on Windows. Right now this supports Python's pwd and spwd modules, if they are installed. It does not support PAM. """ implements(ICredentialsChecker) credentialInterfaces = (IUsernamePassword,) def checkPwd(self, pwd, username, password): try: cryptedPass = pwd.getpwnam(username)[1] except KeyError: return defer.fail(UnauthorizedLogin()) else: if cryptedPass in ('*', 'x'): # Allow checkSpwd to take over return None elif verifyCryptedPassword(cryptedPass, password): return defer.succeed(username) def checkSpwd(self, spwd, username, password): try: cryptedPass = spwd.getspnam(username)[1] except KeyError: return defer.fail(UnauthorizedLogin()) else: if verifyCryptedPassword(cryptedPass, password): return defer.succeed(username) def requestAvatarId(self, credentials): username, password = credentials.username, credentials.password try: import pwd except ImportError: pwd = None if pwd is not None: checked = self.checkPwd(pwd, username, password) if checked is not None: return checked try: import spwd except ImportError: spwd = None if spwd is not None: checked = self.checkSpwd(spwd, username, password) if checked is not None: return checked # TODO: check_pam? # TODO: check_shadow? return defer.fail(UnauthorizedLogin()) unixCheckerFactoryHelp = """ This checker will attempt to use every resource available to authenticate against the list of users on the local UNIX system. (This does not support Windows servers for very obvious reasons.) Right now, this includes support for: * Python's pwd module (which checks /etc/passwd) * Python's spwd module (which checks /etc/shadow) Future versions may include support for PAM authentication. """ class UNIXCheckerFactory(object): """ A factory for L{UNIXChecker}. """ implements(ICheckerFactory, plugin.IPlugin) authType = 'unix' authHelp = unixCheckerFactoryHelp argStringFormat = 'No argstring required.' credentialInterfaces = UNIXChecker.credentialInterfaces def generateChecker(self, argstring): """ This checker factory ignores the argument string. Everything needed to generate a user database is pulled out of the local UNIX environment. """ return UNIXChecker() theUnixCheckerFactory = UNIXCheckerFactory()
在verifyCryptedPassword函数中,也许是因为加密方式的不同,这样的验证会出现问题。只需要把verifyCryptedPassword函数改成这样,就能使用本地的username password验证了。。
def verifyCryptedPassword(crypted, pw): import platform if crypted[0] == '$': # md5_crypt encrypted if platform.system().lower() == "linux": salt = crypted else: salt = '$1$' + crypted.split('$')[2] else: salt = crypted[:2] try: import crypt except ImportError: crypt = None if crypt is None: raise NotImplementedError("cred_unix not supported on this platform") return crypt.crypt(pw, salt) == crypted
刚才在执行twistd时,为什么要加sudo?
通过源代码也可以看出,当pwd模块无法取得“密码”时,会使用spwd模块,而spwd模块是需要操作/etc/shadow文件,这个文件一般情况下,普通用户是没有读取权限的。spwd 会出现找不到用户的异常。