mirror of
https://github.com/paradoxxxzero/butterfly.git
synced 2026-05-26 07:08:08 +00:00
Fix utmp and login with different user. Fix #79
This commit is contained in:
@@ -20,6 +20,8 @@ import pty
|
||||
import os
|
||||
import io
|
||||
import struct
|
||||
import string
|
||||
import random
|
||||
import fcntl
|
||||
import termios
|
||||
import tornado.web
|
||||
@@ -89,9 +91,16 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
|
||||
terminals = set()
|
||||
|
||||
def pty(self):
|
||||
self.determine_user()
|
||||
# Make a "unique" id in 4 bytes
|
||||
self.uid = ''.join(
|
||||
random.choice(
|
||||
string.ascii_lowercase + string.ascii_uppercase +
|
||||
string.digits)
|
||||
for _ in range(4))
|
||||
|
||||
self.pid, self.fd = pty.fork()
|
||||
if self.pid == 0:
|
||||
self.determine_user()
|
||||
self.shell()
|
||||
else:
|
||||
self.communicate()
|
||||
@@ -101,10 +110,18 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
|
||||
tornado.options.options.unsecure and
|
||||
tornado.options.options.login):
|
||||
# If callee is now known and we have unsecure connection
|
||||
user = input('login: ')
|
||||
user = ''
|
||||
while user == '':
|
||||
try:
|
||||
user = input('login: ')
|
||||
except (KeyboardInterrupt, EOFError):
|
||||
self.log.debug("Errorin login input", exc_info=True)
|
||||
pass
|
||||
|
||||
try:
|
||||
self.callee = utils.User(name=user)
|
||||
except:
|
||||
except Exception:
|
||||
self.log.debug("Can't switch to user %s" % user, exc_info=True)
|
||||
self.callee = utils.User(name='nobody')
|
||||
elif (tornado.options.options.unsecure and not
|
||||
tornado.options.options.login):
|
||||
@@ -117,8 +134,10 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
|
||||
def shell(self):
|
||||
try:
|
||||
os.chdir(self.path or self.callee.dir)
|
||||
except:
|
||||
pass
|
||||
except Exception:
|
||||
self.log.debug(
|
||||
"Can't chdir to %s" % (self.path or self.callee.dir),
|
||||
exc_info=True)
|
||||
|
||||
env = os.environ
|
||||
# If local and local user is the same as login user
|
||||
@@ -135,6 +154,23 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
|
||||
env["PATH"] = '%s:%s' % (os.path.abspath(os.path.join(
|
||||
os.path.dirname(__file__), 'bin')), env.get("PATH"))
|
||||
|
||||
try:
|
||||
tty = os.ttyname(0).replace('/dev/', '')
|
||||
except Exception:
|
||||
self.log.debug("Can't get ttyname", exc_info=True)
|
||||
tty = ''
|
||||
|
||||
if self.caller != self.callee:
|
||||
try:
|
||||
os.chown(os.ttyname(0), self.callee.uid, -1)
|
||||
except Exception:
|
||||
self.log.debug("Can't chown ttyname", exc_info=True)
|
||||
|
||||
utils.add_user_info(
|
||||
self.uid,
|
||||
tty, os.getpid(),
|
||||
self.callee.name, self.request.headers['Host'])
|
||||
|
||||
if not tornado.options.options.unsecure or (
|
||||
self.socket.local and
|
||||
self.caller == self.callee and
|
||||
@@ -154,9 +190,11 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
|
||||
os.initgroups(self.callee.name, self.callee.gid)
|
||||
os.setgid(self.callee.gid)
|
||||
os.setuid(self.callee.uid)
|
||||
except:
|
||||
print('The server must be run as root '
|
||||
'if you want to log as different user\n')
|
||||
except Exception:
|
||||
self.log.error(
|
||||
'The server must be run as root '
|
||||
'if you want to log as different user\n',
|
||||
exc_info=True)
|
||||
sys.exit(1)
|
||||
|
||||
if tornado.options.options.cmd:
|
||||
@@ -198,10 +236,6 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
|
||||
def communicate(self):
|
||||
fcntl.fcntl(self.fd, fcntl.F_SETFL, os.O_NONBLOCK)
|
||||
|
||||
utils.add_user_info(
|
||||
self.fd, self.pid,
|
||||
self.callee.name, self.request.headers['Host'])
|
||||
|
||||
def utf8_error(e):
|
||||
self.log.error(e)
|
||||
|
||||
@@ -249,6 +283,8 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
|
||||
try:
|
||||
self.callee = utils.User(name=self.user)
|
||||
except LookupError:
|
||||
self.log.debug(
|
||||
"Can't switch to user %s" % self.user, exc_info=True)
|
||||
self.callee = None
|
||||
|
||||
# If no user where given and we are local, keep the same user
|
||||
@@ -324,7 +360,7 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
|
||||
self.log.info('pid is 0')
|
||||
return
|
||||
|
||||
utils.rm_user_info(self.fd, self.pid, self.callee.name)
|
||||
utils.rm_user_info(self.uid, self.pid)
|
||||
|
||||
try:
|
||||
ioloop.remove_handler(self.fd)
|
||||
|
||||
@@ -46,7 +46,7 @@ def get_style():
|
||||
os.path.dirname(__file__), 'sass')
|
||||
try:
|
||||
import sass
|
||||
except:
|
||||
except Exception:
|
||||
log.error('You must install libsass to use sass '
|
||||
'(pip install libsass)')
|
||||
return
|
||||
@@ -124,9 +124,14 @@ class Socket(object):
|
||||
sn = socket.getsockname()
|
||||
self.local_addr = sn[0]
|
||||
self.local_port = sn[1]
|
||||
pn = socket.getpeername()
|
||||
self.remote_addr = pn[0]
|
||||
self.remote_port = pn[1]
|
||||
try:
|
||||
pn = socket.getpeername()
|
||||
self.remote_addr = pn[0]
|
||||
self.remote_port = pn[1]
|
||||
except Exception:
|
||||
log.debug("Can't get peer name", exc_info=True)
|
||||
self.remote_addr = '???'
|
||||
self.remote_port = 0
|
||||
self.user = None
|
||||
self.env = {}
|
||||
|
||||
@@ -188,7 +193,7 @@ def get_procfs_socket_line(port):
|
||||
if line.split()[1] == '0100007F:%X' % port:
|
||||
# We got the socket
|
||||
return line.split()
|
||||
except:
|
||||
except Exception:
|
||||
log.debug('getting socket inet4 line fail', exc_info=True)
|
||||
|
||||
try:
|
||||
@@ -200,7 +205,7 @@ def get_procfs_socket_line(port):
|
||||
'00000000000000000000000001000000:%X' % port):
|
||||
# We got the socket
|
||||
return line.split()
|
||||
except:
|
||||
except Exception:
|
||||
log.debug('getting socket inet6 line fail', exc_info=True)
|
||||
|
||||
|
||||
@@ -268,12 +273,12 @@ UTmp = namedtuple(
|
||||
'sec', 'usec', 'addr0', 'addr1', 'addr2', 'addr3', 'unused'])
|
||||
|
||||
|
||||
def utmp_line(type, pid, fd, user, host, ts):
|
||||
def utmp_line(id, type, pid, fd, user, host, ts):
|
||||
return UTmp(
|
||||
type, # Type, 7 : user process
|
||||
pid, # pid
|
||||
b('pts/%d' % fd), # line
|
||||
b('/%d' % fd), # id
|
||||
b(fd), # line
|
||||
b(id), # id
|
||||
b(user), # user
|
||||
b(host), # host
|
||||
0, # exit 0
|
||||
@@ -289,12 +294,12 @@ def utmp_line(type, pid, fd, user, host, ts):
|
||||
)
|
||||
|
||||
|
||||
def add_user_info(fd, pid, user, host):
|
||||
def add_user_info(id, fd, pid, user, host):
|
||||
# Freebsd format is not yet supported.
|
||||
# Please submit PR
|
||||
if sys.platform != 'linux':
|
||||
return
|
||||
utmp = utmp_line(7, pid, fd, user, host, time.time())
|
||||
utmp = utmp_line(id, 7, pid, fd, user, host, time.time())
|
||||
for kind, file in {
|
||||
'utmp': get_utmp_file(),
|
||||
'wtmp': get_wtmp_file()}.items():
|
||||
@@ -305,7 +310,7 @@ def add_user_info(fd, pid, user, host):
|
||||
s = f.read(utmp_struct.size)
|
||||
while s:
|
||||
entry = UTmp(*utmp_struct.unpack(s))
|
||||
if kind == 'utmp' and entry.id.rstrip(b'\0') == utmp.id:
|
||||
if kind == 'utmp' and entry.id == utmp.id:
|
||||
# Same id recycling
|
||||
f.seek(f.tell() - utmp_struct.size)
|
||||
f.write(utmp_struct.pack(*utmp))
|
||||
@@ -314,13 +319,13 @@ def add_user_info(fd, pid, user, host):
|
||||
else:
|
||||
f.write(utmp_struct.pack(*utmp))
|
||||
except Exception:
|
||||
log.info('Unable to write utmp info to ' + file, exc_info=True)
|
||||
log.debug('Unable to write utmp info to ' + file, exc_info=True)
|
||||
|
||||
|
||||
def rm_user_info(fd, pid, user):
|
||||
def rm_user_info(id, pid):
|
||||
if sys.platform != 'linux':
|
||||
return
|
||||
utmp = utmp_line(8, pid, fd, user, '', time.time())
|
||||
utmp = utmp_line(id, 8, pid, '', '', '', time.time())
|
||||
for kind, file in {
|
||||
'utmp': get_utmp_file(),
|
||||
'wtmp': get_wtmp_file()}.items():
|
||||
@@ -331,17 +336,23 @@ def rm_user_info(fd, pid, user):
|
||||
s = f.read(utmp_struct.size)
|
||||
while s:
|
||||
entry = UTmp(*utmp_struct.unpack(s))
|
||||
if kind == 'utmp' and entry.id.rstrip(b'\0') == utmp.id:
|
||||
# Same id closing
|
||||
f.seek(f.tell() - utmp_struct.size)
|
||||
f.write(utmp_struct.pack(*utmp))
|
||||
break
|
||||
if entry.id == utmp.id:
|
||||
if kind == 'utmp':
|
||||
# Same id closing
|
||||
f.seek(f.tell() - utmp_struct.size)
|
||||
f.write(utmp_struct.pack(*utmp))
|
||||
break
|
||||
else:
|
||||
utmp = utmp_line(
|
||||
id, 8, pid, entry.line, entry.user, '',
|
||||
time.time())
|
||||
|
||||
s = f.read(utmp_struct.size)
|
||||
else:
|
||||
f.write(utmp_struct.pack(*utmp))
|
||||
|
||||
except Exception:
|
||||
log.info('Unable to update utmp info to ' + file, exc_info=True)
|
||||
log.debug('Unable to update utmp info to ' + file, exc_info=True)
|
||||
|
||||
|
||||
class AnsiColors(object):
|
||||
|
||||
Reference in New Issue
Block a user