/
server.py
212 lines (171 loc) · 6.69 KB
/
server.py
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
#!/usr/bin/python2
# Author: Richard Murri
# Date: 9/4/07
# This file is an implementation of an sftp server using python 1.5 and paramiko. To customize this to your own needs
# search for each line in the file that starts with 'FIX', then follow the instructions. A little
# programming knowledge may be required.
# Obviously, you may want to change the way I authenticate and make it more powerful (add password
# authentication, etc.) It should be fairly easy to change. I assume if you are going to try then
# you'll be up to the task. Feel free to use (and mangle) the code in any way. I just ask you to
# leave my name as the author (or one of the authors). Let me know of any improvements!
from __future__ import with_statement
import base64, os, socket, sys, threading, traceback, paramiko, binascii, utils, configure
# setup
privateKey = '/etc/ssh/ssh_host_rsa_key' # FIX - change this to the path of the server private key
openSocket = 2200 # FIX - change this to the port that sftp will run on
customers = {}
# FIX - add correct users that can login, their chroot folders and their public key files
customers['richard'] = ('/home/richard', '/home/richard/.ssh/id_rsa.pub')
customers['murri'] = ('/home/murri', '/home/murri/.ssh/id_rsa.pub')
# get host private key
host_key = paramiko.RSAKey(filename=privateKey)
class Server (paramiko.ServerInterface):
""" Implements an SSH server """
customerInfo = None
def check_channel_request(self, kind, chanid):
""" Only allow session requests """
if kind == 'session':
return paramiko.OPEN_SUCCEEDED
return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
def check_auth_publickey(self, username, key):
""" Ensure proper authentication """
customer = customers[username]
if customer:
# perform validation of customer
line = customer[1]
line = None
with open(file) as f:
line = f.readline()
filekey = line.split(' ')[1]
custKey = paramiko.RSAKey(data=base64.decodestring(filekey))
if custKey == key:
self.customer = customer
return paramiko.AUTH_SUCCESSFUL
return paramiko.AUTH_FAILED
#def check_auth_password (self, username, password):
# return paramiko.AUTH_SUCCESSFUL
def get_allowed_auths(self, username):
""" Only allow public key authentication """
return 'publickey'
# return 'publickey', 'password'
class SFTPHandle (paramiko.SFTPHandle):
""" Represents a handle to an open file """
def stat(self):
try:
return paramiko.SFTPAttributes.from_stat(os.fstat(self.readfile.fileno()))
except OSError, e:
return paramiko.SFTPServer.convert_errno(e.errno)
class SFTPServer (paramiko.SFTPServerInterface):
def __init__(self, server, *largs, **kwargs):
""" Make customer information accessible as well as set chroot jail directory """
self.customer = server.customer
self.ROOT = self.customer[0]
def _realpath(self, path):
""" Enforce the chroot jail """
path = self.ROOT + self.canonicalize(path)
return path
def list_folder(self, path):
""" List the contents of a folder """
path = self._realpath(path)
try:
out = []
flist = os.listdir(path)
for fname in flist:
attr = paramiko.SFTPAttributes.from_stat(os.stat(os.path.join(path, fname)))
attr.filename = fname
out.append(attr)
return out
except OSError, e:
return paramiko.SFTPServer.convert_errno(e.errno)
def stat(self, path):
path = self._realpath(path)
try:
return paramiko.SFTPAttributes.from_stat(os.stat(path))
except OSError, e:
return paramiko.SFTPServer.convert_errno(e.errno)
def lstat(self, path):
path = self._realpath(path)
try:
return paramiko.SFTPAttributes.from_stat(os.lstat(path))
except OSError, e:
return paramiko.SFTPServer.convert_errno(e.errno)
def open(self, path, flags, attr):
path = self._realpath(path)
try:
binary_flag = getattr(os, 'O_BINARY', 0)
flags |= binary_flag
mode = getattr(attr, 'st_mode', None)
if mode is not None:
fd = os.open(path, flags, mode)
else:
# os.open() defaults to 0777 which is
# an odd default mode for files
fd = os.open(path, flags, 0666)
except OSError, e:
return paramiko.SFTPServer.convert_errno(e.errno)
if (flags & os.O_CREAT) and (attr is not None):
attr._flags &= ~attr.FLAG_PERMISSIONS
paramiko.SFTPServer.set_file_attr(path, attr)
if flags & os.O_WRONLY:
if flags & os.O_APPEND:
fstr = 'ab'
else:
fstr = 'wb'
elif flags & os.O_RDWR:
if flags & os.O_APPEND:
fstr = 'a+b'
else:
fstr = 'r+b'
else:
# O_RDONLY (== 0)
fstr = 'rb'
try:
f = os.fdopen(fd, fstr)
except OSError, e:
return paramiko.SFTPServer.convert_errno(e.errno)
fobj = SFTPHandle(flags)
fobj.filename = path
fobj.readfile = f
fobj.writefile = f
return fobj
def remove(self, path):
""" Remove a file """
return paramiko.SFTP_OK
def rename(self, oldpath, newpath):
return paramiko.SFTP_OK
def mkdir(self, path, attr):
return paramiko.SFTP_OK
def rmdir(self, path):
return paramiko.SFTP_OK
def chattr(self, path, attr):
return paramiko.SFTP_OK
def symlink(self, target_path, path):
return paramiko.SFTP_OK
def readlink(self, path):
return paramiko.SFTP_NO_SUCH_FILE
# bind the socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('', openSocket))
# listen for a connection
sock.listen(5)
# accept connections
while True:
client, addr = sock.accept()
try:
# set up server
t = paramiko.Transport(client)
t.load_server_moduli()
t.add_server_key(host_key)
# set up sftp handler
t.set_subsystem_handler('sftp', paramiko.SFTPServer, SFTPServer)
server = Server()
event = threading.Event()
# start ssh server session
t.start_server(event, server)
except Exception, e:
try:
t.close()
except:
pass
raise