Package flumotion :: Package common :: Module manhole
[hide private]

Source Code for Module flumotion.common.manhole

  1  # -*- Mode: Python -*- 
  2  # vi:si:et:sw=4:sts=4:ts=4 
  3  # 
  4  # Flumotion - a streaming media server 
  5  # Copyright (C) 2004,2005,2006,2007 Fluendo, S.L. (www.fluendo.com). 
  6  # All rights reserved. 
  7   
  8  # This file may be distributed and/or modified under the terms of 
  9  # the GNU General Public License version 2 as published by 
 10  # the Free Software Foundation. 
 11  # This file is distributed without any warranty; without even the implied 
 12  # warranty of merchantability or fitness for a particular purpose. 
 13  # See "LICENSE.GPL" in the source distribution for more information. 
 14   
 15  # Licensees having purchased or holding a valid Flumotion Advanced 
 16  # Streaming Server license may use this file in accordance with the 
 17  # Flumotion Advanced Streaming Server Commercial License Agreement. 
 18  # See "LICENSE.Flumotion" in the source distribution for more information. 
 19   
 20  # Headers in this file shall remain intact. 
 21   
 22  """opening a telnet or ssh manhole 
 23  """ 
 24   
 25  import base64 
 26  import binascii 
 27  import os 
 28   
 29  from twisted.conch import error, manhole 
 30  from twisted.conch.insults import insults 
 31  from twisted.conch.ssh import keys 
 32  from twisted.cred import credentials, portal 
 33  from twisted.cred.checkers import ICredentialsChecker 
 34  from twisted.cred.error import UnauthorizedLogin 
 35  from twisted.internet import defer, reactor 
 36  from twisted.python import failure 
 37  from zope import interface 
 38   
 39  from flumotion.common import log 
 40   
 41  __version__ = "$Rev$" 
 42   
 43   
 44  # This class is from twisted.conch.checkers, copyright 2001-2007 Paul 
 45  # Swartz, Jp Calderone, and others. Original license: 
 46  # 
 47  # Permission is hereby granted, free of charge, to any person obtaining 
 48  # a copy of this software and associated documentation files (the 
 49  # "Software"), to deal in the Software without restriction, including 
 50  # without limitation the rights to use, copy, modify, merge, publish, 
 51  # distribute, sublicense, and/or sell copies of the Software, and to 
 52  # permit persons to whom the Software is furnished to do so, subject to 
 53  # the following conditions: 
 54   
 55  # The above copyright notice and this permission notice shall be 
 56  # included in all copies or substantial portions of the Software. 
 57   
 58  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
 59  # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
 60  # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 61  # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 
 62  # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
 63  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
 64  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
 65   
 66  # It has been modified to check a particular authorized_keys file 
 67  # instead of poking in users' ~/.ssh directories. 
 68   
 69   
70 -class SSHPublicKeyChecker(log.Loggable):
71 try: 72 credentialInterfaces = credentials.ISSHPrivateKey, 73 except AttributeError: 74 log.warning('manhole', 'ssh manhole unavailable (old twisted)') 75 # you won't be able to log anything in 76 credentialInterfaces = () 77 78 interface.implements(ICredentialsChecker) 79
80 - def __init__(self, authorizedKeysFile):
81 self.authorizedKeysFile = authorizedKeysFile
82
83 - def requestAvatarId(self, credentials):
84 d = defer.maybeDeferred(self.checkKey, credentials) 85 d.addCallback(self._cbRequestAvatarId, credentials) 86 d.addErrback(self._ebRequestAvatarId) 87 return d
88
89 - def _cbRequestAvatarId(self, validKey, credentials):
90 if not validKey: 91 return failure.Failure(UnauthorizedLogin()) 92 if not credentials.signature: 93 return failure.Failure(error.ValidPublicKey()) 94 else: 95 try: 96 pubKey = keys.getPublicKeyObject(data = credentials.blob) 97 if keys.verifySignature(pubKey, credentials.signature, 98 credentials.sigData): 99 return credentials.username 100 except: # any error should be treated as a failed login 101 f = failure.Failure() 102 log.warning('error checking signature: %r', credentials) 103 return f 104 return failure.Failure(UnauthorizedLogin())
105
106 - def checkKey(self, credentials):
107 filename = self.authorizedKeysFile 108 if not os.path.exists(filename): 109 return 0 110 lines = open(filename).xreadlines() 111 for l in lines: 112 l2 = l.split() 113 if len(l2) < 2: 114 continue 115 try: 116 if base64.decodestring(l2[1]) == credentials.blob: 117 return 1 118 except binascii.Error: 119 continue 120 return 0
121
122 - def _ebRequestAvatarId(self, f):
123 if not f.check(UnauthorizedLogin, error.ValidPublicKey): 124 log.warning('failed login %r', f) 125 return failure.Failure(UnauthorizedLogin()) 126 return f
127 128
129 -def openSSHManhole(authorizedKeysFile, namespace, portNum=-1):
130 from twisted.conch import manhole_ssh 131 132 def makeProtocol(): 133 return insults.ServerProtocol(manhole.Manhole, namespace)
134 checker = SSHPublicKeyChecker(authorizedKeysFile) 135 sshRealm = manhole_ssh.TerminalRealm() 136 sshRealm.chainedProtocolFactory = makeProtocol 137 sshPortal = portal.Portal(sshRealm, [checker]) 138 sshFactory = manhole_ssh.ConchFactory(sshPortal) 139 port = reactor.listenTCP(portNum, sshFactory, interface='localhost') 140 return port 141 142
143 -def openAnonymousTelnetManhole(namespace, portNum=-1):
144 from twisted.conch import telnet 145 from twisted.internet import protocol 146 147 def makeProtocol(): 148 return telnet.TelnetTransport(telnet.TelnetBootstrapProtocol, 149 insults.ServerProtocol, 150 manhole.Manhole, namespace)
151 152 telnetFactory = protocol.ServerFactory() 153 telnetFactory.protocol = makeProtocol 154 port = reactor.listenTCP(portNum, telnetFactory, 155 interface='localhost') 156 return port 157