~landscape/zope3/ztk-1.1.3

« back to all changes in this revision

Viewing changes to src/twisted/conch/checkers.py

  • Committer: Andreas Hasenack
  • Date: 2009-07-20 17:49:16 UTC
  • Revision ID: andreas@canonical.com-20090720174916-g2tn6qmietz2hn0u
Revert twisted removal, it breaks several dozen tests [trivial]

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import os, base64, binascii
 
2
try:
 
3
    import pwd
 
4
except ImportError:
 
5
    pwd = None
 
6
else:
 
7
    import crypt
 
8
 
 
9
try:
 
10
    # get this from http://www.twistedmatrix.com/users/z3p/files/pyshadow-0.2.tar.gz
 
11
    import shadow
 
12
except:
 
13
    shadow = None
 
14
 
 
15
try:
 
16
    import pamauth
 
17
except ImportError:
 
18
    pamauth = None
 
19
 
 
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
 
28
 
 
29
def verifyCryptedPassword(crypted, pw):
 
30
    if crypted[0] == '$': # md5_crypt encrypted
 
31
        salt = '$1$' + crypted.split('$')[2]
 
32
    else:
 
33
        salt = crypted[:2]
 
34
    return crypt.crypt(pw, salt) == crypted
 
35
 
 
36
class UNIXPasswordDatabase:
 
37
    credentialInterfaces = IUsernamePassword,
 
38
    interface.implements(ICredentialsChecker)
 
39
 
 
40
    def requestAvatarId(self, credentials):
 
41
        if pwd:
 
42
            try:
 
43
                cryptedPass = pwd.getpwnam(credentials.username)[1]
 
44
            except KeyError:
 
45
                return defer.fail(UnauthorizedLogin())
 
46
            else:
 
47
                if cryptedPass not in ['*', 'x'] and \
 
48
                    verifyCryptedPassword(cryptedPass, credentials.password):
 
49
                    return defer.succeed(credentials.username)
 
50
        if shadow:
 
51
            gid = os.getegid()
 
52
            uid = os.geteuid()
 
53
            os.setegid(0)
 
54
            os.seteuid(0)
 
55
            try:
 
56
                shadowPass = shadow.getspnam(credentials.username)[1]
 
57
            except KeyError:
 
58
                os.setegid(gid)
 
59
                os.seteuid(uid)
 
60
                return defer.fail(UnauthorizedLogin())
 
61
            os.setegid(gid)
 
62
            os.seteuid(uid)
 
63
            if verifyCryptedPassword(shadowPass, credentials.password):
 
64
                return defer.succeed(credentials.username)
 
65
            return defer.fail(UnauthorizedLogin())
 
66
        
 
67
        return defer.fail(UnauthorizedLogin())
 
68
 
 
69
 
 
70
class SSHPublicKeyDatabase:
 
71
    credentialInterfaces = ISSHPrivateKey,
 
72
    interface.implements(ICredentialsChecker)
 
73
 
 
74
    def requestAvatarId(self, credentials):
 
75
        d = defer.maybeDeferred(self.checkKey, credentials)
 
76
        d.addCallback(self._cbRequestAvatarId, credentials)
 
77
        d.addErrback(self._ebRequestAvatarId)
 
78
        return d
 
79
 
 
80
    def _cbRequestAvatarId(self, validKey, credentials):
 
81
        if not validKey:
 
82
            return failure.Failure(UnauthorizedLogin())
 
83
        if not credentials.signature:
 
84
            return failure.Failure(error.ValidPublicKey())
 
85
        else:
 
86
            try:
 
87
                pubKey = keys.getPublicKeyObject(data = credentials.blob)
 
88
                if keys.verifySignature(pubKey, credentials.signature,
 
89
                                        credentials.sigData):
 
90
                    return credentials.username
 
91
            except: # any error should be treated as a failed login
 
92
                f = failure.Failure()
 
93
                log.err()
 
94
                return f
 
95
        return failure.Failure(UnauthorizedLogin())
 
96
 
 
97
    def checkKey(self, credentials):
 
98
        sshDir = os.path.expanduser('~%s/.ssh/' % credentials.username)
 
99
        if sshDir.startswith('~'): # didn't expand
 
100
            return 0
 
101
        uid, gid = os.geteuid(), os.getegid()
 
102
        ouid, ogid = pwd.getpwnam(credentials.username)[2:4]
 
103
        os.setegid(0)
 
104
        os.seteuid(0)
 
105
        os.setegid(ogid)
 
106
        os.seteuid(ouid)
 
107
        for name in ['authorized_keys2', 'authorized_keys']:
 
108
            if not os.path.exists(sshDir+name):
 
109
                continue
 
110
            lines = open(sshDir+name).xreadlines()
 
111
            os.setegid(0)
 
112
            os.seteuid(0)
 
113
            os.setegid(gid)
 
114
            os.seteuid(uid)
 
115
            for l in lines:
 
116
                l2 = l.split()
 
117
                if len(l2) < 2:
 
118
                    continue
 
119
                try:
 
120
                    if base64.decodestring(l2[1]) == credentials.blob:
 
121
                        return 1
 
122
                except binascii.Error:
 
123
                    continue
 
124
        return 0
 
125
 
 
126
    def _ebRequestAvatarId(self, f):
 
127
        if not f.check(UnauthorizedLogin, error.ValidPublicKey):
 
128
            log.msg(f)
 
129
            return failure.Failure(UnauthorizedLogin())
 
130
        return f
 
131
 
 
132
 
 
133
class SSHProtocolChecker:
 
134
    interface.implements(ICredentialsChecker)
 
135
 
 
136
    checkers = {}
 
137
 
 
138
    successfulCredentials = {}
 
139
 
 
140
    def get_credentialInterfaces(self):
 
141
        return self.checkers.keys()
 
142
 
 
143
    credentialInterfaces = property(get_credentialInterfaces)
 
144
 
 
145
    def registerChecker(self, checker, *credentialInterfaces):
 
146
        if not credentialInterfaces:
 
147
            credentialInterfaces = checker.credentialInterfaces
 
148
        for credentialInterface in credentialInterfaces:
 
149
            self.checkers[credentialInterface] = checker
 
150
 
 
151
    def requestAvatarId(self, credentials):
 
152
        ifac = interface.providedBy(credentials)
 
153
        for i in ifac:
 
154
            c = self.checkers.get(i)
 
155
            if c is not None:
 
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))))
 
160
    
 
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]
 
167
            return avatarId
 
168
        else:
 
169
            raise error.NotEnoughAuthentication()
 
170
 
 
171
    def areDone(self, avatarId):
 
172
        """Override to determine if the authentication is finished for a given
 
173
        avatarId.
 
174
        """
 
175
        return 1
 
176