Fix utmp and login with different user. Fix #79

This commit is contained in:
Florian Mounier
2015-04-30 17:02:40 +02:00
parent 04ff7aea62
commit 38980afe50
2 changed files with 81 additions and 34 deletions

View File

@@ -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)

View File

@@ -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):