[Twisted-Python] Question on pamauth.py
Phil Mayers
p.mayers at imperial.ac.uk
Wed May 3 02:34:13 MDT 2006
Duncan McGreggor wrote:
>
> You biggest problem is actually going to be getting PyPAM working. As
> far as I know, and as far as tummy.com knows (the original sponsors of
> PyPAM), there's been no release since 1999. I toyed with the idea of
> using it at one point, but the amount of work necessary in updating the
> python was too onerous. Perhaps you have a stronger stomach than I :-)
Agreed that PyPAM has bitrotted. FWIW, I circumvented this by using
Cyrus SASLs "saslauthd" unix socket protocol, with saslauthd configured
to talk to PAM.
/usr/sbin/saslauthd -m /var/run/saslauthd -a pam -c -n 0
def encode_short(s):
i = socket.htons(s)
return chr(i & 0xff) + chr((i >> 8) & 0xff)
def encode_str(s):
l = encode_short(len(s))
return l+s
def decode_short(s):
return socket.ntohs( (ord(s[1]) << 8) + ord(s[0]) )
class SaslAuthdProtocol(protocol.Protocol):
def connectionMade(self):
self.data = ''
# we're going to check this lot
username = encode_str(self.factory.username)
password = encode_str(self.factory.password)
service = encode_str(PAMSERVICENAME)
realm = encode_str(YOURREALM)
# ok
message = username + password + service + realm
self.transport.write(message)
def dataReceived(self, data):
# ok, we've an outstanding request - where are we?
# we're expecting 2 bytes of length, then length bytes of
# data which is "code<SP>reason"
self.data = self.data + data
dl = len(self.data)
if dl < 2:
# we don't have the length yet
return
l = decode_short(self.data[:2])
if dl < l + 2:
# we don't have the rest of the reply yet
return
if dl > l + 2:
# wtf?
self.transport.loseConnection()
# Ok, we can reply
resp = self.data[2:2+l]
if ' ' in resp:
resp, reason = resp.split(' ', 1)
else:
reason = ''
if resp=='OK':
self.factory.deferred.callback(reason)
else:
self.factory.deferred.errback(Exception(reason))
class SaslChecker:
# We are an ICredentialsChecker implementor
interface.implements(checkers.ICredentialsChecker)
# We can only check plaintext username/password combos
credentialInterfaces = (
credentials.IUsernamePassword,
)
# return the "avatar ID" - username
def ok(self, matched, username):
return username
def err(self, f, username):
raise error.UnauthorizedLogin(f.getErrorMessage())
def requestAvatarId(self, creds):
# Adapt the credentials to a username/password pair
up = credentials.IUsernamePassword(creds, default=None)
# It's going to be a deferred reply
d = defer.Deferred()
d.addCallbacks(
self.ok, self.err, (up.username,), {}, (up.username,), {}
)
# Send the reply off to saslauthd via unix socket
f = protocol.ClientFactory()
f.username = up.username
f.password = up.password
f.deferred = d
f.protocol = SaslAuthdProtocol
reactor.connectUNIX('/var/run/saslauthd/mux', f)
return d
Works like a charm.
More information about the Twisted-Python
mailing list