| [528] | 1 | from twisted.application import internet, service | 
|---|
 | 2 | from twisted.internet import protocol, reactor, defer | 
|---|
 | 3 | from twisted.protocols import basic | 
|---|
| [627] | 4 | import ldap, ldap.filter | 
|---|
 | 5 | import os, sys, pwd, glob | 
|---|
| [528] | 6 |  | 
|---|
 | 7 | class WhoisProtocol(basic.LineReceiver): | 
|---|
 | 8 |     def lineReceived(self, hostname): | 
|---|
| [771] | 9 |         (key, hostname) = hostname.split('=',2) | 
|---|
| [772] | 10 |         if key != self.factory.key: | 
|---|
| [771] | 11 |             self.transport.write("Unauthorized to use whois"+"\r\n") | 
|---|
 | 12 |             self.transport.loseConnection() | 
|---|
 | 13 |         else: | 
|---|
 | 14 |             self.factory.getWhois(hostname | 
|---|
 | 15 |             ).addErrback(lambda _: "Internal error in server" | 
|---|
 | 16 |             ).addCallback(lambda m: | 
|---|
 | 17 |                           (self.transport.write(m+"\r\n"), | 
|---|
 | 18 |                            self.transport.loseConnection())) | 
|---|
| [528] | 19 | class WhoisFactory(protocol.ServerFactory): | 
|---|
 | 20 |     protocol = WhoisProtocol | 
|---|
| [772] | 21 |     def __init__(self, vhostDir, ldap_URL, ldap_base, keyFile): | 
|---|
| [528] | 22 |         self.vhostDir = vhostDir | 
|---|
| [627] | 23 |         self.ldap_URL = ldap_URL | 
|---|
 | 24 |         self.ldap = ldap.initialize(self.ldap_URL) | 
|---|
 | 25 |         self.ldap_base = ldap_base | 
|---|
| [528] | 26 |         self.vhosts = {} | 
|---|
| [762] | 27 |         if vhostDir: | 
|---|
 | 28 |             self.rescanVhosts() | 
|---|
| [772] | 29 |         self.key = file(keyFile).read() | 
|---|
| [528] | 30 |     def rescanVhosts(self): | 
|---|
 | 31 |         newVhosts = {} | 
|---|
 | 32 |         for f in glob.iglob(os.path.join(self.vhostDir, "*.conf")): | 
|---|
 | 33 |             locker = os.path.splitext(os.path.basename(f))[0] | 
|---|
 | 34 |             newVhosts.update(self.parseApacheConf(file(f))) | 
|---|
 | 35 |         self.vhosts = newVhosts | 
|---|
 | 36 |         self.vhostTime = os.stat(self.vhostDir).st_mtime | 
|---|
 | 37 |     def parseApacheConf(self, f): | 
|---|
 | 38 |         vhosts = {} | 
|---|
 | 39 |         hostnames = [] | 
|---|
 | 40 |         locker = None | 
|---|
 | 41 |         docroot = None | 
|---|
 | 42 |         for l in f: | 
|---|
 | 43 |             parts = l.split() | 
|---|
 | 44 |             if not parts: continue | 
|---|
 | 45 |             command = parts.pop(0) | 
|---|
 | 46 |             if command in ("ServerName", "ServerAlias"): | 
|---|
 | 47 |                 hostnames.extend(parts) | 
|---|
 | 48 |             elif command in ("SuExecUserGroup",): | 
|---|
 | 49 |                 locker = parts[0] | 
|---|
 | 50 |             elif command in ("DocumentRoot",): | 
|---|
 | 51 |                 docroot = parts[0] | 
|---|
 | 52 |             elif command == "</VirtualHost>": | 
|---|
| [627] | 53 |                 d = {'locker': locker, 'apacheDocumentRoot': docroot, 'apacheServerName': hostnames[0]} | 
|---|
| [528] | 54 |                 for h in hostnames: vhosts[h] = d | 
|---|
 | 55 |                 hostnames = [] | 
|---|
 | 56 |                 locker = None | 
|---|
 | 57 |                 docroot = None | 
|---|
 | 58 |         return vhosts | 
|---|
 | 59 |     def canonicalize(self, vhost): | 
|---|
 | 60 |         vhost = vhost.lower().rstrip(".") | 
|---|
 | 61 |         return vhost | 
|---|
 | 62 | #        if vhost.endswith(".mit.edu"): | 
|---|
 | 63 | #            return vhost | 
|---|
 | 64 | #        else: | 
|---|
 | 65 | #            return vhost + ".mit.edu" | 
|---|
| [627] | 66 |     def searchLDAP(self, vhost): | 
|---|
 | 67 |         results = self.ldap.search_s(self.ldap_base, ldap.SCOPE_SUBTREE, | 
|---|
 | 68 |             ldap.filter.filter_format( | 
|---|
 | 69 |                 '(|(apacheServername=%s)(apacheServerAlias=%s))', (vhost,)*2)) | 
|---|
 | 70 |         if len(results) >= 1: | 
|---|
 | 71 |             result = results[0] | 
|---|
 | 72 |             attrs = result[1] | 
|---|
 | 73 |             for attr in ('apacheServerName','apacheDocumentRoot', 'apacheSuexecUid', 'apacheSuexecGid'): | 
|---|
 | 74 |                 attrs[attr] = attrs[attr][0] | 
|---|
 | 75 |             user = pwd.getpwuid(int(attrs['apacheSuexecUid'])) | 
|---|
 | 76 |             if user: | 
|---|
| [771] | 77 |                 attrs['locker'] = user.pw_name | 
|---|
| [627] | 78 |             else: | 
|---|
 | 79 |                 attrs['locker'] = None | 
|---|
 | 80 |             return attrs | 
|---|
 | 81 |         else: | 
|---|
 | 82 |             return None | 
|---|
| [528] | 83 |     def getWhois(self, vhost): | 
|---|
 | 84 |         vhost = self.canonicalize(vhost) | 
|---|
 | 85 |         info = self.vhosts.get(vhost) | 
|---|
| [627] | 86 |         if not info: | 
|---|
 | 87 |             info = self.searchLDAP(vhost) | 
|---|
| [528] | 88 |         if info: | 
|---|
 | 89 |             ret = "Hostname: %s\nAlias: %s\nLocker: %s\nDocument Root: %s" % \ | 
|---|
| [627] | 90 |                 (info['apacheServerName'], vhost, info['locker'], info['apacheDocumentRoot']) | 
|---|
| [528] | 91 |         else: | 
|---|
 | 92 |             ret = "No such hostname" | 
|---|
 | 93 |         return defer.succeed(ret) | 
|---|
 | 94 |  | 
|---|
 | 95 | application = service.Application('whois', uid=99, gid=99) | 
|---|
| [762] | 96 | factory = WhoisFactory(None, | 
|---|
| [772] | 97 |     "ldap://localhost", "ou=VirtualHosts,dc=scripts,dc=mit,dc=edu", "/etc/whoisd-password") | 
|---|
| [528] | 98 | internet.TCPServer(43, factory).setServiceParent( | 
|---|
 | 99 |     service.IServiceCollection(application)) | 
|---|