1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
|
# vim:fileencoding=utf8:et:ts=4:sts=4:sw=4:ft=python
from django.conf import settings
import paramiko
from io import BytesIO
import asyncore
import inspect
import socket
import threading
LISTEN_BACKLOG = 20
def ssh_handler(f):
if not hasattr(settings, 'SSH_HANDLERS'):
settings.SSH_HANDLERS = {}
settings.SSH_HANDLERS[f.__name__] = f
return f
class SSHServer(paramiko.ServerInterface):
def __init__(self):
paramiko.ServerInterface.__init__(self)
self._message = None
def get_allowed_auths(self, username):
return 'publickey'
def check_auth_publickey(self, username, key):
# for some reason, this is called twice...
if self._message:
return paramiko.AUTH_SUCCESSFUL
spl = username.split('+')
cmd = spl[0]
args = spl[1:]
try:
h = settings.SSH_HANDLERS[cmd]
# this is an easy way of checking if we have correct args
inspect.getcallargs(h, *args, key=key)
except (KeyError, TypeError) as e:
pass
else:
ret = h(*args, key=key)
if ret is not None:
self._message = ret
return paramiko.AUTH_SUCCESSFUL
return paramiko.AUTH_FAILED
def check_channel_request(self, kind, chanid):
if kind == 'session':
return paramiko.OPEN_SUCCEEDED
return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
def check_channel_subsystem_request(self, channel, name):
return False
def check_channel_exec_request(self, channel, command):
channel.send('%s\r\n' % self._message)
channel.shutdown(2)
channel.close()
self._message = None
return True
def check_channel_shell_request(self, channel):
channel.send('%s\r\n' % self._message)
channel.shutdown(2)
channel.close()
self._message = None
return True
def check_channel_pty_request(self, channel, term, width, height,
pixelwidth, pixelheight, modes):
return True
class SSHDispatcher(asyncore.dispatcher):
def __init__(self, server_key):
asyncore.dispatcher.__init__(self)
self._server_key = server_key
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.set_reuse_addr()
self.bind(settings.SSH_BIND)
self.listen(LISTEN_BACKLOG)
def handle_accepted(self, conn, addr):
t = paramiko.Transport(conn)
t.add_server_key(self._server_key)
# we need a dummy Event to make it non-blocking
# but we don't really need to play with it
t.start_server(event=threading.Event(), server=SSHServer())
# python<3.2 compat
def handle_accept(self):
ret = self.accept()
if ret is not None:
self.handle_accepted(*ret)
def ssh_main():
server_key = paramiko.RSAKey(file_obj=BytesIO(settings.SSH_SERVER_KEY))
disp = SSHDispatcher(server_key)
asyncore.loop()
raise SystemError('SSH server loop exited')
|