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: 6964 $" 
 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 -class SSHPublicKeyChecker(log.Loggable):
69 try: 70 credentialInterfaces = credentials.ISSHPrivateKey, 71 except AttributeError: 72 log.warning('manhole', 'ssh manhole unavailable (old twisted)') 73 # you won't be able to log anything in 74 credentialInterfaces = () 75 76 interface.implements(ICredentialsChecker) 77
78 - def __init__(self, authorizedKeysFile):
79 self.authorizedKeysFile = authorizedKeysFile
80
81 - def requestAvatarId(self, credentials):
82 d = defer.maybeDeferred(self.checkKey, credentials) 83 d.addCallback(self._cbRequestAvatarId, credentials) 84 d.addErrback(self._ebRequestAvatarId) 85 return d
86
87 - def _cbRequestAvatarId(self, validKey, credentials):
88 if not validKey: 89 return failure.Failure(UnauthorizedLogin()) 90 if not credentials.signature: 91 return failure.Failure(error.ValidPublicKey()) 92 else: 93 try: 94 pubKey = keys.getPublicKeyObject(data = credentials.blob) 95 if keys.verifySignature(pubKey, credentials.signature, 96 credentials.sigData): 97 return credentials.username 98 except: # any error should be treated as a failed login 99 f = failure.Failure() 100 log.warning('error checking signature: %r', credentials) 101 return f 102 return failure.Failure(UnauthorizedLogin())
103
104 - def checkKey(self, credentials):
105 filename = self.authorizedKeysFile 106 if not os.path.exists(filename): 107 return 0 108 lines = open(filename).xreadlines() 109 for l in lines: 110 l2 = l.split() 111 if len(l2) < 2: 112 continue 113 try: 114 if base64.decodestring(l2[1]) == credentials.blob: 115 return 1 116 except binascii.Error: 117 continue 118 return 0
119
120 - def _ebRequestAvatarId(self, f):
121 if not f.check(UnauthorizedLogin, error.ValidPublicKey): 122 log.warning('failed login %r', f) 123 return failure.Failure(UnauthorizedLogin()) 124 return f
125
126 -def openSSHManhole(authorizedKeysFile, namespace, portNum=-1):
127 from twisted.conch import manhole_ssh 128 129 def makeProtocol(): 130 return insults.ServerProtocol(manhole.Manhole, namespace)
131 checker = SSHPublicKeyChecker(authorizedKeysFile) 132 sshRealm = manhole_ssh.TerminalRealm() 133 sshRealm.chainedProtocolFactory = makeProtocol 134 sshPortal = portal.Portal(sshRealm, [checker]) 135 sshFactory = manhole_ssh.ConchFactory(sshPortal) 136 port = reactor.listenTCP(portNum, sshFactory, interface='localhost') 137 return port 138
139 -def openAnonymousTelnetManhole(namespace, portNum=-1):
140 from twisted.conch import telnet 141 from twisted.internet import protocol 142 143 def makeProtocol(): 144 return telnet.TelnetTransport(telnet.TelnetBootstrapProtocol, 145 insults.ServerProtocol, 146 manhole.Manhole, namespace)
147 148 telnetFactory = protocol.ServerFactory() 149 telnetFactory.protocol = makeProtocol 150 port = reactor.listenTCP(portNum, telnetFactory, 151 interface='localhost') 152 return port 153