测试了一下,在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 会出现找不到用户的异常。