OLD | NEW |
| (Empty) |
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.ta
r.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, IPluggab
leAuthenticationModules | |
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 | |
OLD | NEW |