Skip to content

Commit

Permalink
[project @ Arch-1:[email protected]%secsh--dev--1.0--patch-7]
Browse files Browse the repository at this point in the history
cleaned up server code, renamed some files & classes
renamed demo-server.py and demo-host-key to demo_server.py and
demo_host_key, just to be consistent.

renamed SSHException -> SecshException.

generalized the mechanism where Channel decides whether to allow
different channel requests: 4 of the main ones (pty, window-change,
shell, and subsystem) go through easily override-able methods now.
you could probably make an actual ssh shell server.

gave ChannelFile a repr().

turned off ultra debugging in the demos.  demo_server creates a
subclass of Channel to allow pty/shell and sets an event when the
shell request is made, so that it knows when it can start sending
the fake bbs.

renamed to charmander and updated some of the distutils files.
  • Loading branch information
Robey Pointer committed Nov 10, 2003
1 parent 0e1ef2c commit aad7b85
Show file tree
Hide file tree
Showing 12 changed files with 127 additions and 78 deletions.
3 changes: 3 additions & 0 deletions MANIFEST
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
README
auth_transport.py
ber.py
channel.py
dsskey.py
Expand All @@ -11,3 +12,5 @@ setup.py
transport.py
util.py
demo.py
demo_server.py
demo_host_key
3 changes: 3 additions & 0 deletions NOTES
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ from Channel:
exec_command
invoke_subsystem
resize_pty
[server:]
check_pty_request
check_shell_request

from ChannelFile:
next
Expand Down
8 changes: 4 additions & 4 deletions auth_transport.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from transport import MSG_SERVICE_REQUEST, MSG_SERVICE_ACCEPT, MSG_USERAUTH_REQUEST, MSG_USERAUTH_FAILURE, \
MSG_USERAUTH_SUCCESS, MSG_USERAUTH_BANNER
from message import Message
from secsh import SSHException
from secsh import SecshException
from logging import DEBUG, INFO, WARNING, ERROR, CRITICAL

DISCONNECT_SERVICE_NOT_AVAILABLE, DISCONNECT_AUTH_CANCELLED_BY_USER, \
Expand Down Expand Up @@ -34,7 +34,7 @@ def request_auth(self):
def auth_key(self, username, key, event):
if (not self.active) or (not self.initial_kex_done):
# we should never try to send the password unless we're on a secure link
raise SSHException('No existing session')
raise SecshException('No existing session')
try:
self.lock.acquire()
self.auth_event = event
Expand All @@ -49,7 +49,7 @@ def auth_password(self, username, password, event):
'authenticate using a password; event is triggered on success or fail'
if (not self.active) or (not self.initial_kex_done):
# we should never try to send the password unless we're on a secure link
raise SSHException('No existing session')
raise SecshException('No existing session')
try:
self.lock.acquire()
self.auth_event = event
Expand Down Expand Up @@ -108,7 +108,7 @@ def parse_service_accept(self, m):
m.add_string(str(self.private_key))
m.add_string(self.private_key.sign_ssh_session(self.randpool, self.H, self.username))
else:
raise SSHException('Unknown auth method "%s"' % self.auth_method)
raise SecshException('Unknown auth method "%s"' % self.auth_method)
self.send_message(m)
else:
self.log(DEBUG, 'Service request "%s" accepted (?)' % service)
Expand Down
57 changes: 44 additions & 13 deletions channel.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from message import Message
from secsh import SSHException
from secsh import SecshException
from transport import MSG_CHANNEL_REQUEST, MSG_CHANNEL_CLOSE, MSG_CHANNEL_WINDOW_ADJUST, MSG_CHANNEL_DATA, \
MSG_CHANNEL_EOF, MSG_CHANNEL_SUCCESS, MSG_CHANNEL_FAILURE

Expand Down Expand Up @@ -100,6 +100,22 @@ def window_adjust(self, m):
finally:
self.lock.release()

def check_pty_request(self, term, width, height, pixelwidth, pixelheight, modes):
"override me! return True if a pty of the given dimensions (for shell access, usually) can be provided"
return False

def check_shell_request(self):
"override me! return True if shell access will be provided"
return False

def check_subsystem_request(self, name):
"override me! return True if the given subsystem can be provided"
return False

def check_window_change_request(self, width, height, pixelwidth, pixelheight):
"override me! return True if the pty was resized"
return False

def handle_request(self, m):
key = m.get_string()
want_reply = m.get_boolean()
Expand All @@ -110,10 +126,25 @@ def handle_request(self, m):
elif key == 'xon-xoff':
# ignore
ok = True
elif (key == 'pty-req') or (key == 'shell'):
if self.transport.server_mode:
# humor them
ok = True
elif key == 'pty-req':
term = m.get_string()
width = m.get_int()
height = m.get_int()
pixelwidth = m.get_int()
pixelheight = m.get_int()
modes = m.get_string()
ok = self.check_pty_request(term, width, height, pixelwidth, pixelheight, modes)
elif key == 'shell':
ok = self.check_shell_request()
elif key == 'subsystem':
name = m.get_string()
ok = self.check_subsystem_request(name)
elif key == 'window-change':
width = m.get_int()
height = m.get_int()
pixelwidth = m.get_int()
pixelheight = m.get_int()
ok = self.check_window_change_request(width, height, pixelwidth, pixelheight)
else:
self.log(DEBUG, 'Unhandled channel request "%s"' % key)
ok = False
Expand Down Expand Up @@ -155,7 +186,7 @@ def handle_close(self, m):

def get_pty(self, term='vt100', width=80, height=24):
if self.closed or self.eof_received or self.eof_sent or not self.active:
raise SSHException('Channel is not open')
raise SecshException('Channel is not open')
m = Message()
m.add_byte(chr(MSG_CHANNEL_REQUEST))
m.add_int(self.remote_chanid)
Expand All @@ -171,7 +202,7 @@ def get_pty(self, term='vt100', width=80, height=24):

def invoke_shell(self):
if self.closed or self.eof_received or self.eof_sent or not self.active:
raise SSHException('Channel is not open')
raise SecshException('Channel is not open')
m = Message()
m.add_byte(chr(MSG_CHANNEL_REQUEST))
m.add_int(self.remote_chanid)
Expand All @@ -181,7 +212,7 @@ def invoke_shell(self):

def exec_command(self, command):
if self.closed or self.eof_received or self.eof_sent or not self.active:
raise SSHException('Channel is not open')
raise SecshException('Channel is not open')
m = Message()
m.add_byte(chr(MSG_CHANNEL_REQUEST))
m.add_int(self.remote_chanid)
Expand All @@ -192,7 +223,7 @@ def exec_command(self, command):

def invoke_subsystem(self, subsystem):
if self.closed or self.eof_received or self.eof_sent or not self.active:
raise SSHException('Channel is not open')
raise SecshException('Channel is not open')
m = Message()
m.add_byte(chr(MSG_CHANNEL_REQUEST))
m.add_int(self.remote_chanid)
Expand All @@ -203,7 +234,7 @@ def invoke_subsystem(self, subsystem):

def resize_pty(self, width=80, height=24):
if self.closed or self.eof_received or self.eof_sent or not self.active:
raise SSHException('Channel is not open')
raise SecshException('Channel is not open')
m = Message()
m.add_byte(chr(MSG_CHANNEL_REQUEST))
m.add_int(self.remote_chanid)
Expand Down Expand Up @@ -500,9 +531,6 @@ class ChannelFile(object):
XXX Todo: the channel and its file-wrappers should be able to be closed or
garbage-collected independently, for compatibility with real sockets and
their file-wrappers. Currently, closing does nothing but flush the buffer.
XXX Todo: translation of the various forms of newline is not implemented,
let alone the universal newline. Line buffering (for writing) is
implemented, though, which makes little sense without text mode support.
"""

def __init__(self, channel, mode = "r", buf_size = -1):
Expand All @@ -528,6 +556,9 @@ def __init__(self, channel, mode = "r", buf_size = -1):
self.newlines = None
self.softspace = False

def __repr__(self):
return '<secsh.ChannelFile from ' + repr(self.channel) + '>'

def __iter__(self):
return self

Expand Down
2 changes: 1 addition & 1 deletion demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def load_host_keys():
try:
event = threading.Event()
t = secsh.Transport(sock)
t.ultra_debug = 1
t.ultra_debug = 0
t.start_client(event)
# print repr(t)
event.wait(10)
Expand Down
File renamed without changes.
30 changes: 24 additions & 6 deletions demo-server.py → demo_server.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,46 @@
#!/usr/bin/python

import sys, os, socket, threading, logging, traceback, time
import sys, os, socket, threading, logging, traceback
import secsh

# setup logging
l = logging.getLogger("secsh")
l.setLevel(logging.DEBUG)
if len(l.handlers) == 0:
f = open('demo-server.log', 'w')
f = open('demo_server.log', 'w')
lh = logging.StreamHandler(f)
lh.setFormatter(logging.Formatter('%(levelname)-.3s [%(asctime)s] %(name)s: %(message)s', '%Y%m%d:%H%M%S'))
l.addHandler(lh)

host_key = secsh.RSAKey()
host_key.read_private_key_file('demo-host-key')
host_key.read_private_key_file('demo_host_key')


class ServerTransport(secsh.Transport):
def check_channel_request(self, kind, chanid):
if kind == 'session':
return secsh.Channel(chanid)
return ServerChannel(chanid)
return self.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED

def check_auth_password(self, username, password):
if (username == 'robey') and (password == 'foo'):
return self.AUTH_SUCCESSFUL
return self.AUTH_FAILED

class ServerChannel(secsh.Channel):
"Channel descendant that pretends to understand pty and shell requests"

def __init__(self, chanid):
secsh.Channel.__init__(self, chanid)
self.event = threading.Event()

def check_pty_request(self, term, width, height, pixelwidth, pixelheight, modes):
return True

def check_shell_request(self):
self.event.set()
return True


# now connect
try:
Expand All @@ -50,7 +64,7 @@ def check_auth_password(self, username, password):
event = threading.Event()
t = ServerTransport(client)
t.add_server_key(host_key)
t.ultra_debug = 1
t.ultra_debug = 0
t.start_server(event)
# print repr(t)
event.wait(10)
Expand All @@ -60,7 +74,11 @@ def check_auth_password(self, username, password):
# print repr(t)

chan = t.accept()
time.sleep(2)
chan.event.wait(10)
if not chan.event.isSet():
print '*** Client never asked for a shell.'
sys.exit(1)

chan.send('\r\n\r\nWelcome to my dorky little BBS!\r\n\r\n')
chan.send('We are on fire all the time! Hooray! Candy corn for everyone!\r\n')
chan.send('Happy birthday to Robot Dave!\r\n\r\n')
Expand Down
24 changes: 7 additions & 17 deletions kex_gex.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
# LOT more on the server side).

from message import Message
from util import inflate_long, deflate_long, generate_prime
from secsh import SSHException
from util import inflate_long, deflate_long, generate_prime, bit_length
from secsh import SecshException
from transport import MSG_NEWKEYS
from Crypto.Hash import SHA
from Crypto.Util import number
Expand Down Expand Up @@ -49,17 +49,7 @@ def parse_next(self, ptype, m):
return self.parse_kexdh_gex_init(m)
elif ptype == MSG_KEXDH_GEX_REPLY:
return self.parse_kexdh_gex_reply(m)
raise SSHException('KexGex asked to handle packet type %d' % ptype)

def bit_length(n):
norm = deflate_long(n, 0)
hbyte = ord(norm[0])
bitlen = len(norm) * 8
while not (hbyte & 0x80):
hbyte <<= 1
bitlen -= 1
return bitlen
bit_length = staticmethod(bit_length)
raise SecshException('KexGex asked to handle packet type %d' % ptype)

def generate_x(self):
# generate an "x" (1 < x < (p-1)/2).
Expand Down Expand Up @@ -116,9 +106,9 @@ def parse_kexdh_gex_group(self, m):
self.p = m.get_mpint()
self.g = m.get_mpint()
# reject if p's bit length < 1024 or > 8192
bitlen = self.bit_length(self.p)
bitlen = bit_length(self.p)
if (bitlen < 1024) or (bitlen > 8192):
raise SSHException('Server-generated gex p (don\'t ask) is out of range (%d bits)' % bitlen)
raise SecshException('Server-generated gex p (don\'t ask) is out of range (%d bits)' % bitlen)
self.transport.log(DEBUG, 'Got server p (%d bits)' % bitlen)
self.generate_x()
# now compute e = g^x mod p
Expand All @@ -132,7 +122,7 @@ def parse_kexdh_gex_group(self, m):
def parse_kexdh_gex_init(self, m):
self.e = m.get_mpint()
if (self.e < 1) or (self.e > self.p - 1):
raise SSHException('Client kex "e" is out of range')
raise SecshException('Client kex "e" is out of range')
self.generate_x()
K = pow(self.e, self.x, P)
key = str(self.transport.get_server_key())
Expand Down Expand Up @@ -164,7 +154,7 @@ def parse_kexdh_gex_reply(self, m):
self.f = m.get_mpint()
sig = m.get_string()
if (self.f < 1) or (self.f > self.p - 1):
raise SSHException('Server kex "f" is out of range')
raise SecshException('Server kex "f" is out of range')
K = pow(self.f, self.x, self.p)
# okay, build up the hash H of (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K)
hm = Message().add(self.transport.local_version).add(self.transport.remote_version)
Expand Down
8 changes: 4 additions & 4 deletions kex_group1.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# "g" generator.

from message import Message, inflate_long
from secsh import SSHException
from secsh import SecshException
from transport import MSG_NEWKEYS
from Crypto.Hash import SHA
from logging import DEBUG, INFO, WARNING, ERROR, CRITICAL
Expand Down Expand Up @@ -59,14 +59,14 @@ def parse_next(self, ptype, m):
return self.parse_kexdh_init(m)
elif not self.transport.server_mode and (ptype == MSG_KEXDH_REPLY):
return self.parse_kexdh_reply(m)
raise SSHException('KexGroup1 asked to handle packet type %d' % ptype)
raise SecshException('KexGroup1 asked to handle packet type %d' % ptype)

def parse_kexdh_reply(self, m):
# client mode
host_key = m.get_string()
self.f = m.get_mpint()
if (self.f < 1) or (self.f > P - 1):
raise SSHException('Server kex "f" is out of range')
raise SecshException('Server kex "f" is out of range')
sig = m.get_string()
K = pow(self.f, self.x, P)
# okay, build up the hash H of (V_C || V_S || I_C || I_S || K_S || e || f || K)
Expand All @@ -82,7 +82,7 @@ def parse_kexdh_init(self, m):
# server mode
self.e = m.get_mpint()
if (self.e < 1) or (self.e > P - 1):
raise SSHException('Client kex "e" is out of range')
raise SecshException('Client kex "e" is out of range')
K = pow(self.e, self.x, P)
key = str(self.transport.get_server_key())
# okay, build up the hash H of (V_C || V_S || I_C || I_S || K_S || e || f || K)
Expand Down
7 changes: 3 additions & 4 deletions secsh.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
if (sys.version_info[0] < 2) or ((sys.version_info[0] == 2) and (sys.version_info[1] < 3)):
raise RuntimeError('You need python 2.3 for this module.')

# FIXME rename
class SSHException(Exception):
class SecshException(Exception):
pass


Expand All @@ -17,7 +16,7 @@ class SSHException(Exception):


__author__ = "Robey Pointer <[email protected]>"
__date__ = "18 Sep 2003"
__version__ = "0.1-bulbasaur"
__date__ = "9 Nov 2003"
__version__ = "0.1-charmander"
__credits__ = "Huzzah!"

Loading

0 comments on commit aad7b85

Please sign in to comment.