1
import os, base64, binascii
10
# get this from http://www.twistedmatrix.com/users/z3p/files/pyshadow-0.2.tar.gz
20
from twisted.conch import error
21
from twisted.conch.ssh import keys
22
from twisted.cred.checkers import ICredentialsChecker
23
from twisted.cred.credentials import IUsernamePassword, ISSHPrivateKey, IPluggableAuthenticationModules
24
from twisted.cred.error import UnauthorizedLogin, UnhandledCredentials
25
from twisted.internet import defer
26
from twisted.python import failure, reflect, log
27
from zope import interface
29
def verifyCryptedPassword(crypted, pw):
30
if crypted[0] == '$': # md5_crypt encrypted
31
salt = '$1$' + crypted.split('$')[2]
34
return crypt.crypt(pw, salt) == crypted
36
class UNIXPasswordDatabase:
37
credentialInterfaces = IUsernamePassword,
38
interface.implements(ICredentialsChecker)
40
def requestAvatarId(self, credentials):
43
cryptedPass = pwd.getpwnam(credentials.username)[1]
45
return defer.fail(UnauthorizedLogin())
47
if cryptedPass not in ['*', 'x'] and \
48
verifyCryptedPassword(cryptedPass, credentials.password):
49
return defer.succeed(credentials.username)
56
shadowPass = shadow.getspnam(credentials.username)[1]
60
return defer.fail(UnauthorizedLogin())
63
if verifyCryptedPassword(shadowPass, credentials.password):
64
return defer.succeed(credentials.username)
65
return defer.fail(UnauthorizedLogin())
67
return defer.fail(UnauthorizedLogin())
70
class SSHPublicKeyDatabase:
71
credentialInterfaces = ISSHPrivateKey,
72
interface.implements(ICredentialsChecker)
74
def requestAvatarId(self, credentials):
75
d = defer.maybeDeferred(self.checkKey, credentials)
76
d.addCallback(self._cbRequestAvatarId, credentials)
77
d.addErrback(self._ebRequestAvatarId)
80
def _cbRequestAvatarId(self, validKey, credentials):
82
return failure.Failure(UnauthorizedLogin())
83
if not credentials.signature:
84
return failure.Failure(error.ValidPublicKey())
87
pubKey = keys.getPublicKeyObject(data = credentials.blob)
88
if keys.verifySignature(pubKey, credentials.signature,
90
return credentials.username
91
except: # any error should be treated as a failed login
95
return failure.Failure(UnauthorizedLogin())
97
def checkKey(self, credentials):
98
sshDir = os.path.expanduser('~%s/.ssh/' % credentials.username)
99
if sshDir.startswith('~'): # didn't expand
101
uid, gid = os.geteuid(), os.getegid()
102
ouid, ogid = pwd.getpwnam(credentials.username)[2:4]
107
for name in ['authorized_keys2', 'authorized_keys']:
108
if not os.path.exists(sshDir+name):
110
lines = open(sshDir+name).xreadlines()
120
if base64.decodestring(l2[1]) == credentials.blob:
122
except binascii.Error:
126
def _ebRequestAvatarId(self, f):
127
if not f.check(UnauthorizedLogin, error.ValidPublicKey):
129
return failure.Failure(UnauthorizedLogin())
133
class SSHProtocolChecker:
134
interface.implements(ICredentialsChecker)
138
successfulCredentials = {}
140
def get_credentialInterfaces(self):
141
return self.checkers.keys()
143
credentialInterfaces = property(get_credentialInterfaces)
145
def registerChecker(self, checker, *credentialInterfaces):
146
if not credentialInterfaces:
147
credentialInterfaces = checker.credentialInterfaces
148
for credentialInterface in credentialInterfaces:
149
self.checkers[credentialInterface] = checker
151
def requestAvatarId(self, credentials):
152
ifac = interface.providedBy(credentials)
154
c = self.checkers.get(i)
156
return c.requestAvatarId(credentials).addCallback(
157
self._cbGoodAuthentication, credentials)
158
return defer.fail(UnhandledCredentials("No checker for %s" % \
159
', '.join(map(reflect.qal, ifac))))
161
def _cbGoodAuthentication(self, avatarId, credentials):
162
if avatarId not in self.successfulCredentials:
163
self.successfulCredentials[avatarId] = []
164
self.successfulCredentials[avatarId].append(credentials)
165
if self.areDone(avatarId):
166
del self.successfulCredentials[avatarId]
169
raise error.NotEnoughAuthentication()
171
def areDone(self, avatarId):
172
"""Override to determine if the authentication is finished for a given