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,2008,2009 Fluendo, S.L. 
  6  # Copyright (C) 2010,2011 Flumotion Services, S.A. 
  7  # All rights reserved. 
  8  # 
  9  # This file may be distributed and/or modified under the terms of 
 10  # the GNU Lesser General Public License version 2.1 as published by 
 11  # the Free Software Foundation. 
 12  # This file is distributed without any warranty; without even the implied 
 13  # warranty of merchantability or fitness for a particular purpose. 
 14  # See "LICENSE.LGPL" in the source distribution for more information. 
 15  # 
 16  # Headers in this file shall remain intact. 
 17   
 18  """opening a telnet or ssh manhole 
 19  """ 
 20   
 21  import base64 
 22  import binascii 
 23  import os 
 24   
 25  from twisted import conch 
 26   
 27  from twisted.conch import error, manhole 
 28  from twisted.conch.insults import insults 
 29  from twisted.conch.ssh import keys 
 30  from twisted.cred import credentials, portal 
 31  from twisted.cred.checkers import ICredentialsChecker 
 32  from twisted.cred.error import UnauthorizedLogin 
 33  from twisted.internet import defer, reactor 
 34  from twisted.python import failure 
 35  from zope import interface 
 36   
 37  from flumotion.common import log 
 38   
 39  __version__ = "$Rev$" 
 40   
 41   
 42  # This class is from twisted.conch.checkers, copyright 2001-2007 Paul 
 43  # Swartz, Jp Calderone, and others. Original license: 
 44  # 
 45  # Permission is hereby granted, free of charge, to any person obtaining 
 46  # a copy of this software and associated documentation files (the 
 47  # "Software"), to deal in the Software without restriction, including 
 48  # without limitation the rights to use, copy, modify, merge, publish, 
 49  # distribute, sublicense, and/or sell copies of the Software, and to 
 50  # permit persons to whom the Software is furnished to do so, subject to 
 51  # the following conditions: 
 52   
 53  # The above copyright notice and this permission notice shall be 
 54  # included in all copies or substantial portions of the Software. 
 55   
 56  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
 57  # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
 58  # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 59  # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 
 60  # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
 61  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
 62  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
 63   
 64  # It has been modified to check a particular authorized_keys file 
 65  # instead of poking in users' ~/.ssh directories. 
 66   
 67   
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 if conch.version.major < 10: 95 pubKey = keys.getPublicKeyObject(data=credentials.blob) 96 if keys.verifySignature(pubKey, credentials.signature, 97 credentials.sigData): 98 return credentials.username 99 else: 100 pubKey = keys.Key.fromString(credentials.blob) 101 if pubKey.verify(credentials.signature, 102 credentials.sigData): 103 return credentials.username 104 105 except: # any error should be treated as a failed login 106 f = failure.Failure() 107 log.warning('manhole', 108 'error checking signature on creds %r: %r', 109 credentials, log.getFailureMessage(f)) 110 return f 111 return failure.Failure(UnauthorizedLogin())
112
113 - def checkKey(self, credentials):
114 filename = self.authorizedKeysFile 115 if not os.path.exists(filename): 116 return 0 117 lines = open(filename).xreadlines() 118 for l in lines: 119 l2 = l.split() 120 if len(l2) < 2: 121 continue 122 try: 123 if base64.decodestring(l2[1]) == credentials.blob: 124 return 1 125 except binascii.Error: 126 continue 127 return 0
128
129 - def _ebRequestAvatarId(self, f):
130 if not f.check(UnauthorizedLogin, error.ValidPublicKey): 131 log.warning('manhole', 'failed login: %r', 132 log.getFailureMessage(f)) 133 return failure.Failure(UnauthorizedLogin()) 134 return f
135 136
137 -def openSSHManhole(authorizedKeysFile, namespace, portNum=-1):
138 from twisted.conch import manhole_ssh 139 140 def makeProtocol(): 141 return insults.ServerProtocol(manhole.Manhole, namespace)
142 checker = SSHPublicKeyChecker(authorizedKeysFile) 143 sshRealm = manhole_ssh.TerminalRealm() 144 sshRealm.chainedProtocolFactory = makeProtocol 145 sshPortal = portal.Portal(sshRealm, [checker]) 146 sshFactory = manhole_ssh.ConchFactory(sshPortal) 147 port = reactor.listenTCP(portNum, sshFactory, interface='localhost') 148 return port 149 150
151 -def openAnonymousTelnetManhole(namespace, portNum=-1):
152 from twisted.conch import telnet 153 from twisted.internet import protocol 154 155 def makeProtocol(): 156 return telnet.TelnetTransport(telnet.TelnetBootstrapProtocol, 157 insults.ServerProtocol, 158 manhole.Manhole, namespace)
159 160 telnetFactory = protocol.ServerFactory() 161 telnetFactory.protocol = makeProtocol 162 port = reactor.listenTCP(portNum, telnetFactory, 163 interface='localhost') 164 return port 165